This is the 30th day of my participation in the August Challenge

AOP to achieve is on the basis of our original written code, for certain packaging, such as in the method before execution, method return, method after throwing exceptions and other places for certain interception or enhancement processing.

AOP is implemented not because Java provides some magic hook that tells us the lifetimes of a method, but because we are implementing a proxy and the actual running instance is actually an instance of the generated proxy class.

On the Java platform, there are three ways to weave AOP:

  1. Compile-time: At compile time, aspect calls are compiled into bytecode by the compiler, which requires defining new keywords and extending the compiler. AspectJ extends the Java compiler to use keyword aspects for weaving;
  2. Classloader: When the target class is loaded into the JVM, the bytecode of the target class is re-” enhanced “through a special classloader;
  3. Runtime: The target objects and facets are ordinary Java classes that are dynamically woven into runtime through the JVM’s dynamic proxy capabilities or third-party libraries.

The simplest approach is the third, where AOP implementations of Spring are jVM-based dynamic proxies. Because the JVM’s dynamic proxy requires that interfaces be implemented, if a common class does not have a business interface, it needs to be implemented through a third-party library such as CGLIB or Javassist.

AOP concepts

In AOP programming, you often come across the following concepts:

Aspect: a function that spans multiple core logic, or system concerns. In Spring AOP, aspects pass through regular classes and regular classes that use the annotation @aspect. The most common configuration is transaction management;

Joinpoint: Joinpoint, which defines where the execution of the facets are inserted into the application process;

Advice: Enhanced, for actions performed at a particular join point. Different types of enhancements include “around”, “before” and “after” enhancements. Many AOP frameworks, including Spring, model enhancements as interceptors and maintain a series of interceptors around join points;

Pointcut: A Pointcut, a collection of join points. Enhance the association with pointcut expressions and run at any join point that matches a pointcut (for example, execute a method with a specific name). The concept of join points matched by pointcut expressions is at the heart of AOP, and by default Spring uses AspectJ pointcuts to express the language. There are many ways to declare pointcuts and many syntax rules, such as the following declaration of any public method that executes business logic under the com.Miracle. service package as a pointcut.

Introduction: Dynamically adding a new interface to an existing Java object;

Weaving: Weaving refers to integrating aspects into the execution of programs.

Interceptor: an Interceptor, which is a way to implement enhancements;

Target Object: The Target Object is the core logical Object that performs services.

AOP Proxy: AN AOP Proxy that is an enhanced object reference held by the client.

Using Spring AOP

@aspectj support

You can use XML or Java style configuration to enable @AspectJ support. In either case, you also need to ensure that AspectJ’s AspectJweaver. Jar library is on the classpath of your application.

Enable @AspectJ support through Java configuration

To enable @AspectJ support for @Configuration using Java, add the @enableAspectJAutoProxy annotation, as shown in the following example:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}Copy the code
Enable @AspectJ support through XML configuration

To enable @AspectJ support with an XML-based configuration, use the AOP: Aspectj-AutoProxy element, as shown in the following example:

<aop:aspectj-autoproxy/>
Copy the code

Declare an aspect

<! - regular bean -- -- >
<aop:config>
    <aop:aspect id="myAspect" ref="aBean">.</aop:aspect>
</aop:config>

<bean id="aBean" class="...">.</bean>
Copy the code
// Based on annotations
@Aspect
@Component
public class MyAspect {}Copy the code

Declare a pointcut

<! -- Syntax * : matches any number of characters; . : matches any number of repetitions of characters, such as any number of subpackages in a type pattern; Matches any number of parameters in the method parameter pattern. + : matches a subtype of the specified type; Can only be placed after a type pattern as a suffix. -->
<! Declare pointcuts -->
<! Any method in the service layer -->
<aop:config>
    <aop:pointcut id="businessService"
        expression="execution(* com.miracle.service.*.*(..) )"/>
</aop:config>
<! -- Annotation mode -->@Pointcut("execution(* com.miracle.service.*.*(..) )"Copy the code
Supported pointcut indicators

Spring AOP supports the following AspectJ pointcut indicators (PCD) used in pointcut expressions:

  • execution: Used to match join points for method execution. This is the primary pointcut indicator to use when using Spring AOP.
  • within: limits matching to join points within certain types.
  • this: limits the specified instance to join points.
  • target: Limits the match to join points in cases where the target object is an instance of a given type.
  • args: Limits the match to join points in cases where the argument is an instance of a given type.
  • @target: Limits matching to join points (method execution when using Spring AOP) in cases where the class of the executing object has an annotation of a given type.
  • @args: limits matching join points where the runtime type of the actual parameter passed has an annotation of the given type.
  • @within: limits matching to join points within a type with a given annotation.
  • @annotation: limits matching to join points where the topic of the join point has a given annotation.

In addition to the pointcut designators above, Spring AOP provides an extension to specify a bean as a pointcut using beans (“beanName”).

Composite pointcut expressions

AspectJ use and (&), or (| |), not (!) To combine pointcut expressions. Under the Schema style, since in the XML using a “&” you need to use the escape character “&” instead of it, so it is not convenient, so the Spring ASP provides the and, or, not to replace &&, | |,! .

Pointcut expression details
execution

Execution (< modifier pattern >? < return type mode >< method name mode >(< parameter mode >)< exception mode >? All items are optional except return type mode, method name mode, and parameter mode.

Parameter modes are as follows:

() Matches a method that takes no arguments (..) Matching a method that takes any number of arguments () matches a method that takes one argument of any type (,String) matches a method that takes two arguments, where the first argument is of any type and the second argument must be of type String

For example:

// Match all target class public methods
execution(public* * (..) )// Matches all methods with the suffix Toexecution(* *To(..) )// Matches all methods in the UserService interfaceexecution(* com.miracle.service.UserService.*(..) )// Matches the methods in the UserService interface and its implementation classexecution(* com.miracle.service.UserService+.*(..) )// Match all methods of all classes in the com.miracle.service packageexecution(* com.miracle.service.*(..) )// Match com.miracle.service package, all methods of all classes in the descendant packageexecution(* com.miracle.service.. * (..) )// Match any package whose class name is prefixed with com with the suffix Service. The method must be prefixed with queryexecution(* com.. *.*Service.query*(..) )// Matches the save(String name,int age) function
execution(* save(String,int))

// Matches the save(String name,*) function with the second argument of any type
execution(* save(String,*))

// Match save(String name,..) The function accepts any input argument of any type, except the first oneexecution(* save(String,..) )// Matches the save(String+) function String+ to indicate that the input type is a subclass of String
execution(* save(String+))
Copy the code
within

Within is used to specify the type, and all methods within the specified type will be intercepted.

For example:

// all methods to match com.miracle and subpackageswithin(com.miracle.. *)// Matches all methods of the UserServiceImpl class
within(com.miracle.service.UserServiceImpl)
Copy the code

Because execution matches packages, classes, and methods, whereas Within matches only packages and classes, execution can completely replace the functionality of Within.

this

This represents the proxy object.

For example:

// represents all join points of proxy objects that match the UserService interface
this(com.miracle.service.UserService) 
Copy the code
target

Target Indicates the target object to be proxied. Use the same way as this.

args

Args is used to match method parameters.

  • args()Matches any method that takes no arguments.
  • args(java.lang.String)Matches any method that takes a single argument of type String.
  • args(..)A method that takes arbitrary parameters.
  • args(java.lang.String,..)Matches a method that takes any argument, but the first argument is of type String.
  • args(.. ,java.lang.String)Matches a method that takes any number of arguments, but the last argument is of type String.
@target

Matches when the proxied target object has specified annotations on its corresponding type and its parent type.

Example:

// Any join point of the target object with the @Transactional annotation
@target(org. Springframework. Transaction. The annotation. Transactional)Copy the code
@args

Matches the case where the method being called has parameters and the corresponding parameter type has the specified annotation.

@args(com.Miracle. MyAnnotation) Matches the method call with the MyAnnotation annotation on the method parameter type. If we have a method add(MyParam Param) that takes a parameter of type MyParam, and MyParam is a class that has the MyAnnotation, It can be @ the args Pointcut expression (com. Elim. Spring. Support. MyAnnotation) match.

@within

The declaration type of the target object has any join point for the specified annotation.

Example:

 // The declaration type of the target object has any join point with the @Transactional annotation
 @within(org. Springframework. Transaction. The annotation. Transactional)Copy the code
@annotation

Execute a method at any join point with the specified annotation.

Example:

Execute any join point of the method with the @transactional annotation
@annotation(org. Springframework. Transaction. The annotation. Transactional)Copy the code
bean

Bean (idOrNameOfBean) : Matches the bean name (Spring AOP only)

The source code is briefly

Debugging code

public interface UserService {

    User queryUser(a);

    User createUser(int id, String userName, int age);
}

@Component
public class UserServiceImpl implements UserService{

    @Override
    public User queryUser(a) {
        User user = new User();
        user.setId(1);
        user.setAge(10);
        user.setUserName("xiaoming");
        return user;
    }

    @Override
    public User createUser(int id, String userName, int age) {
        User user = new User();
        user.setId(id);
        user.setAge(age);
        user.setUserName(userName);
        returnuser; }}public interface MailService {

    Mail createMail(int id, String address, String name);
}

@Component
public class MailServiceImpl implements MailService {

    @Override
    public Mail createMail(int id, String address, String name) {
        Mail mail = new Mail();
        mail.setAddress(address);
        mail.setId(id);
        mail.setName(name);
        returnmail; }}/ / start AOP
@EnableAspectJAutoProxy
@Component
public class AppConfig {}@Aspect
@Component
public class LogAop {

    Execute before each method of UserService is executed
    @Before("execution(public * com.miracle.aop.UserService.*(..) )"
    public void doAccessCheck(a) {
        System.err.println("[Before] do access check...");
    }

    Execute before and after each method of MailService
    @Around("execution(public * com.miracle.aop.MailService.*(..) )"
    public Object doLogging(ProceedingJoinPoint pjp) throws Throwable {
        System.err.println("[Around] start " + pjp.getSignature());

        Object retVal = pjp.proceed();
        System.err.println("[Around] done " + pjp.getSignature());
        returnretVal; }}/ / start the class
public class SpringAopDemoApplication {

    public static void main(String[] args) {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "application.xml");

        UserService userService = context.getBean(UserService.class);
        MailService mailService = context.getBean(MailService.class);

        userService.createUser(1."xiaoming".12);
        userService.queryUser();

        mailService.createMail(1."[email protected]"."miracle"); }}// Execution result
[Before] do access check...
[Before] do access check...
[Around] start Mail com.miracle.aop.MailService.createMail(int,String,String)
[Around] done Mail com.miracle.aop.MailService.createMail(int,String,String)

Copy the code

@EnableAspectJAutoProxy

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	boolean proxyTargetClass(a) default false;

	boolean exposeProxy(a) default false;

}
Copy the code

This annotation @ Import a configuration class AspectJAutoProxyRegistrar is introduced. Down with code to key AnnotationAwareAspectJAutoProxyCreator class.

AnnotationAwareAspectJAutoProxyCreator

First see AnnotationAwareAspectJAutoProxyCreator class inheritance structure:

We can find that AnnotationAwareAspectJAutoProxyCreator finally was a BeanPostProcessor, said at the time of Spring IoC source code analysis, BeanPostProcessor of two methods, Are executed before and after init-method.

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object var1, String var2) throws BeansException;

    Object postProcessAfterInitialization(Object var1, String var2) throws BeansException;
}
Copy the code

Recall the source code for Spring IoC.

protected Object doCreateBean(final String beanName, 
                              final RootBeanDefinition mbd, final Object[] args) {
   // ...
   Object exposedObject = bean;
   try {
      // Load attributes
      populateBean(beanName, mbd, instanceWrapper);
      if(exposedObject ! =null) {
         / / initializationexposedObject = initializeBean(beanName, exposedObject, mbd); }}// ...

   return exposedObject;
}
Copy the code
protected Object initializeBean(final String beanName, 
                                final Object bean, RootBeanDefinition mbd) {
   // ...
   Object wrappedBean = bean;
   if (mbd == null| |! mbd.isSynthetic()) {/ / perform each BeanPostProcessor postProcessBeforeInitialization method
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }

   // ...

   if (mbd == null| |! mbd.isSynthetic()) {/ / perform each BeanPostProcessor postProcessAfterInitialization method
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}
Copy the code

We focus on execution after initialization postProcessAfterInitialization () method, Spring AOP will at the end of the IOC container to create bean instance to deal with the bean. This is where agent enhancement takes place.

Came AnnotationAwareAspectJAutoProxyCreator to find postProcessAfterInitialization () method in its parent class AbstractAutoProxyCreator in written.

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   if(bean ! =null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code

Continue with the wrapIfNecessary() method.

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   if(beanName ! =null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // Create proxy if we have advice.
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), 
                                                                beanName, null);
   if(specificInterceptors ! = DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);
      // Create the proxy
      Object proxy = createProxy(bean.getClass(), beanName, 
                                 specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}
Copy the code

Code simple compared with the Spring IoC source code a lot, but one of the points, getAdvicesAndAdvisorsForBean () method returns all can be used to intercept the current bean Advice, Advisor and Intercepter.

protected Object createProxy(Class
        beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this
                                       .beanFactory, beanName, beanClass);
   }

   // Create a ProxyFactory instance
   ProxyFactory proxyFactory = new ProxyFactory();
   proxyFactory.copyFrom(this);

   
      
   @enableAspectJAutoProxy (proxyTargetClass = true)
   // Force the proxy generation using cglib
   if(! proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         / / a interface: proxyFactory addInterface (ifc);
         / / there is no interface: proxyFactory setProxyTargetClass (true);
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }

   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   proxyFactory.addAdvisors(advisors);
   proxyFactory.setTargetSource(targetSource);
   customizeProxyFactory(proxyFactory);

   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }

   return proxyFactory.getProxy(getProxyClassLoader());
}
Copy the code

This code basically creates the ProxyFactory instance, adds some properties, and finally calls the getProxy() method to generate the proxy.

public Object getProxy(ClassLoader classLoader) {
   return createAopProxy().getProxy(classLoader);
}
Copy the code
protected final synchronized AopProxy createAopProxy(a) {
   if (!this.active) {
      activate();
   }
   return getAopProxyFactory().createAopProxy(this);
}
Copy the code
// DefaultAopProxyFactory.java line 50
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   if(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<? > targetClass = config.getTargetClass();if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return newJdkDynamicAopProxy(config); }}Copy the code

Create an AopProxy instance and generate JdkDynamicAopProxy and ObjenesisCglibAopProxy based on whether there are interfaces.

Finally, execute the getProxy(ClassLoader ClassLoader) method based on the generated AopProxy instance.

JdkDynamicAopProxy.getProxy(ClassLoader classLoader)
public Object getProxy(ClassLoader classLoader) {
   if (logger.isDebugEnabled()) {
      logger.debug("Creating JDK dynamic proxy: target source is " + 
                   this.advised.getTargetSource()); } Class<? >[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
Copy the code
CglibAopProxy.getProxy(ClassLoader classLader)
public Object getProxy(ClassLoader classLoader) {
   if (logger.isDebugEnabled()) {
      logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
   }

   try{ Class<? > rootClass =this.advised.getTargetClass(); Assert.state(rootClass ! =null."Target class must be available for creating a CGLIB proxy"); Class<? > proxySuperClass = rootClass;if(ClassUtils.isCglibProxyClass(rootClass)) { proxySuperClass = rootClass.getSuperclass(); Class<? >[] additionalInterfaces = rootClass.getInterfaces();for(Class<? > additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface); }}// Validate the class, writing log messages as necessary.
      validateClassIfNecessary(proxySuperClass, classLoader);

      // Configure CGLIB Enhancer...
      Enhancer enhancer = createEnhancer();
      if(classLoader ! =null) {
         enhancer.setClassLoader(classLoader);
         if (classLoader instanceof SmartClassLoader &&
               ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
            enhancer.setUseCache(false);
         }
      }
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(newClassLoaderAwareUndeclaredThrowableStrategy(classLoader)); Callback[] callbacks = getCallbacks(rootClass); Class<? >[] types =newClass<? >[callbacks.length];for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }
      // fixedInterceptorMap only populated at this point, after getCallbacks call above
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
          this.advised.getConfigurationOnlyCopy(), 
          this.fixedInterceptorMap, this.fixedInterceptorOffset));
       
      enhancer.setCallbackTypes(types);

      // Generate the proxy class and create a proxy instance.
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   // ...
}
Copy the code

The whole process of Spring Aop is like this, this article is not as specific as Spring IoC, itself Spring Aop source code is not complex, it depends on Spring IoC, is a kind of extended support for IoC.