In the last spring-AOP (PART 1) implementation Principles, we learned how to use ProxyFactory to create AOP proxy objects, but the process requires some interface implementation and some complex configuration. Therefore, after spring2.0, a more convenient approach is provided. Using the @ Aspect annotations declared a plane class, after the @ EnableAspectJAutoProxy generated class AnnotationAwareAspectJAutoProxyCreator annotations to registration agency. Let’s see how it works

How do YOU use @AspectJ in Spring

Woven into the way

Manual weaving

  1. First you need to define an Aspect
package com.luhc.springaop.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/ * * *@author luhuancheng
 * @date2018/11/20 * /
@Aspect
public class PerformanceTraceAspect {

    @Pointcut("execution(* *.. *method1()) || execution(* *.. *method2())")
    public void pointcutName(a){}

    @Pointcut("@annotation(AnyJoinpointAnnotation)")
    public void matchPointcut(a){}

    @Before("matchPointcut()")
    public void before(a) {
        System.out.println("+++++++++@annotation++++++++++");
    }

    @Around("pointcutName()")
    public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        try {
            return joinPoint.proceed();
        } finally {
            System.out.println(String.format("cost time %s", System.currentTimeMillis() - start)); }}}Copy the code
  1. Manually woven through AspectJProxyFactory
private static void manualWeaver(a) {
    // Manually weave
    AspectJProxyFactory weaver = new AspectJProxyFactory();
    weaver.setProxyTargetClass(true);
    // Declare the target object
    weaver.setTarget(new Foo());
    // Declare the cut
    weaver.addAspect(PerformanceTraceAspect.class);
    // Get the proxy
    Object proxy = weaver.getProxy();
    // Execute the method already woven into the aspect logic
    ((Foo) proxy).method1(new FlyImpl());
    ((Foo) proxy).method2();
}
Copy the code

Automatic weave

Automatic weaving way need AnnotationAwareAspectJAutoProxyCreator kind support, By AnnotationAwareAspectJAutoProxyCreator and necessary Aspect Aspect, as well as the target object statement in the IOC container, the container during startup, The target object AnnotationAwareAspectJAutoProxyCreator will automatically generate the proxy object of weaving section of logic

  1. The statement configuration
package com.luhc.springaop.aspect;

import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/ * * *@author luhuancheng
 * @date2018/11/21 * /
@Configuration
public class AspectConfigure {

    /** * automatic weavers *@return* /
    @Bean
    public AnnotationAwareAspectJAutoProxyCreator proxyCreator(a) {
        AnnotationAwareAspectJAutoProxyCreator proxyCreator = new AnnotationAwareAspectJAutoProxyCreator();
        // The default is false. If the target object does not implement an interface, its proxy object is also generated by cglib
        proxyCreator.setProxyTargetClass(false);
        return proxyCreator;
    }

    /** * Target object of unimplemented interface *@return* /
    @Bean
    public Foo foo(a) {
        return new Foo();
    }

    /** ** section *@return* /
    @Bean
    public PerformanceTraceAspect performanceTraceAspect(a) {
        return newPerformanceTraceAspect(); }}Copy the code
  1. Extract the proxy object woven into the cut from the IOC container
private static void autoWeaver(a) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfigure.class);
    Foo foo = context.getBean(Foo.class);
    / / foo object at this time, and after dealing with the AnnotationAwareAspectJAutoProxyCreator proxy objects
    foo.method1(new FlyImpl());
    foo.method2();
}
Copy the code

The @aspectJ form of the Pointcut declaration

Spring AOP supports the following Pointcut expressions

// Any method in any package with any parameters and any return values
// @Pointcut("execution(* *.. * (..) )"

// any class under any subpackage of com.luhc.springaop. Springaop only supports JoinPoint at the method level, so this expression will match all method implementations declared by the specified class
// @Pointcut("within(com.luhc.springaop.. *) ")

// Matches all method-level JoinPoints whose proxy object type is Foo
// @Pointcut("this(Foo)")

// Match all method level JoinPoints whose target object type is Fly
// @Pointcut("target(Fly)")

// Match JoinPoint for all methods that pass in arguments of type Fly and Foo, regardless of which class the method is defined in
// @Pointcut("args(Fly,Foo)")

// @within @target differs in that @within is a static match and @target is a dynamic match at runtime
// match all method-level joinpoints of classes annotated with AnyJoinpointAnnotation
// @Pointcut("@within(AnyJoinpointAnnotation)")

// match all target objects by annotating all method-level joinpoints of the classes provided by AnyJoinpointAnnotation
// @Pointcut("@target(AnyJoinpointAnnotation)")

// Match method parameter types annotated AnyJoinpointAnnotation for all method-level JoinPoints
// @Pointcut("@args(AnyJoinpointAnnotation)")

// The matching method is annotated AnyJoinpointAnnotation for all joinPoints at the method level
// @Pointcut("@annotation(AnyJoinpointAnnotation)")

/ / you can use the | | and && to express the logic operations between pointcut
// @Pointcut("execution(* *.. *method1()) || execution(* *.. *method2())")
Copy the code

Disentangle @aspectj in SpringAOP

Pointcut in the form of @aspectj

AnnotationAwareAspectJAutoProxyCreator through reflection to obtain the information of the @pointcut annotation, internally AspectJExpressionPointcut object instance. AspectJExpressionPointcut ClassFilter, MethodMatcher, its internal implementation logic agent gave PointcutParser, At most, it becomes an instance of PointcutExpression(PointcutExpression ImpL implementation class)

Advice in the form of @aspectj

Access method arguments at Joinpoint in the Advice definition

Using org. Aspectj. Lang. JoinPoint

Will be the first parameter to the Advice method statement for org. Aspectj. Lang. JoinPoint type, we can call org. Aspectj. Lang. JoinPoint relevant methods obtain the data needed

@Before("matchPointcut()")
public void before(org.aspectj.lang.JoinPoint joinPoint) {
    // Get the method name
    System.out.println(joinPoint.getSignature().getName());
    System.out.println("+++++++++@annotation++++++++++");
}
Copy the code

Bind using the ARGS identifier

// You can use both the identifier and JoinPoint, but JoinPoint must be placed on the first parameter
@Before("matchPointcut() && args(name)")
public void before(JoinPoint joinPoint, String name) {
    System.out.println("Get the input parameter to Joinpoint:" + name);
    System.out.println("Get Joinpoint method name:" + joinPoint.getSignature().getName());
}
Copy the code

Catch an exception @afterThrowing

@AfterThrowing(pointcut = "matchPointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, RuntimeException e) {
    System.out.println("Method:" + joinPoint.getSignature().getName() + "An exception occurs:" + e.getMessage());
}
Copy the code

Capture the return value @afterRETURNING

@AfterReturning(pointcut = "pointcutName()", returning = "result")
public void afterReturning(JoinPoint joinPoint, String result) {
    System.out.println("Method:" + joinPoint.getSignature().getName() + "Get return value:" + result);
}
Copy the code

After the method completes normally (without throwing an exception) @after

@After("pointcutName()")
public void after(JoinPoint joinPoint) {
    System.out.println("Method:" + joinPoint.getSignature().getName() + ": Execution completed");
}
Copy the code

@around wrap method

@ Around, unlike several other Advice notes, in @ Around approach, the first argument must be a org. Aspectj. Lang. ProceedingJoinPoint

@Around("pointcutName()")
public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();

    try {
        return joinPoint.proceed();
    } finally {
        System.out.println(String.format("cost time %s", System.currentTimeMillis() - start)); }}Copy the code

Exposes the currently invoked proxy object

// In the target object method, this method is used to get the proxy object of the current target object
AopContext.currentProxy() 
Copy the code

Principle of automatic generation of Spring AOP proxies in the form of @AspectJ

A case in point

A demo of Spring AOP configured using annotations (new versions of Spring recommend using annotations to configure containers)

// Suppose this is a business interface
public interface Fly {
    void fly(a);
}

// Business interface implementation
public class FlyImpl implements Fly {
    @Override
    public void fly(a) {
        System.out.println("++++++++++++++++ Fly ++++++++++++++++"); }}// Declare an aspect
@Aspect
public class PerformanceTraceAspect {

    // Match the fly method with any return value, any package, and any parameters. In this demo, will match to com. The luhc. Springaop. Aspect. FlyImpl# fly this method
    @Pointcut("execution(* *.. *fly(..) )")
    public void pointcutName(a){}

    // Declare the cut before the logic
    @Before("pointcutName()")
    public void before(JoinPoint joinPoint) {
        // You can use JoinPoint to obtain the pointcut method details
        System.out.println("Before --> get Joinpoint method name:" + joinPoint.getSignature().getName());
    }

    // Declare the post-logic of the cut
    @After("pointcutName()")
    public void after(JoinPoint joinPoint) {
        // You can use JoinPoint to obtain the pointcut method details
        System.out.println("After --> get Joinpoint method name:" + joinPoint.getSignature().getName());
    }

    // Declare the enclosing logic of the cut (i.e. the cut logic before and after the method execution)
    @Around("pointcutName()")
    public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        try {
            // Call the execution chain
            return joinPoint.proceed();
        } finally {
            System.out.println(String.format("cost time %s", System.currentTimeMillis() - start)); }}}/ / configuration class
@Configuration
/ / enabled AOP
@EnableAspectJAutoProxy
public class AspectConfigure {

    /** * Implements the target object of the interface *@return* /
    @Bean
    public Fly fly(a) {
        return new FlyImpl();
    }

    /** ** section *@return* /
    @Bean
    public PerformanceTraceAspect performanceTraceAspect(a) {
        return newPerformanceTraceAspect(); }}// Run the application
public class AspectJDemo {
    // Use the configuration class to initialize the container
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfigure.class);
    // Get the business interface from the container (at this point it is already a proxy object that has been processed, i.e. cut into the aspect logic)
    Fly fly = context.getBean(Fly.class);
    fly.fly();
}
Copy the code

Analyze the Operating mechanism of Demo

Start with the @enableAspectJAutoProxy annotation

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

	// Whether to use cglib to generate proxies
	boolean proxyTargetClass(a) default false;

	// Whether to bind the proxy to ThreadLocal, you can then use aopContext.currentProxy () in the target class to fetch the proxy object
	boolean exposeProxy(a) default false;

}
Copy the code
AspectJAutoProxyRegistrar configuration class

Focus on its yuan notes @ Import (have a chance to look at the @ Import annotations about spring Import mechanism), Import the configuration class AspectJAutoProxyRegistrar

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@Override
	public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // key!! The injected AnnotationAwareAspectJAutoProxyCreator class to the container
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
		}
		if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); }}}Copy the code
The registration process
public abstract class AopConfigUtils {
    /** * Stores the auto proxy creator classes in escalation order. */
	private static finalList<Class<? >> APC_PRIORITY_LIST =newArrayList<Class<? > > ();/** * Setup the escalation List. * In Spring, there are three proxy generation classes by default. Priorities are sorted from top to bottom, with higher priorities */
	static {
		APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); // Highest priority
	}

    private static BeanDefinition registerOrEscalateApcAsRequired(Class
        cls, BeanDefinitionRegistry registry, Object source) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			if(! cls.getName().equals(apcDefinition.getBeanClassName())) {/ / if the container is currently registered agent generator class, then compares them with the AnnotationAwareAspectJAutoProxyCreator priority. Take the one with the highest priority and register it in the container as the agent generator.
                / / AnnotationAwareAspectJAutoProxyCreator obviously been registered into the container
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if(currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); }}return null;
		}
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		returnbeanDefinition; }}Copy the code
Summary of registration Process
  1. @ EnableAspectJAutoProxy AspectJAutoProxyRegistrar annotations to import the configuration class
  2. The configuration class generator AnnotationAwareAspectJAutoProxyCreator to invoke the AopConfigUtils AspectJAutoProxyRegistrar registration agency

AnnotationAwareAspectJAutoProxyCreator how to automatically generate the agent

Class structure

Main process

Source code analysis
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor.BeanFactoryAware {

    @Override
	public Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(beanClass, beanName);

		if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null; }}// Create proxy here if we have a custom TargetSource.
		// Suppresses unnecessary default instantiation of the target bean:
		// The TargetSource will handle target instances in a custom fashion.
		if(beanName ! =null) {
			TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
			if(targetSource ! =null) {
				this.targetSourcedBeans.add(beanName);
				Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
				Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
				this.proxyTypes.put(cacheKey, proxy.getClass());
				returnproxy; }}return null;
	}

    // Generate a proxy object
    @Override
	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;
	}

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

		// Create proxy if we have advice.
        // Get the section Advisor
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if(specificInterceptors ! = DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);
			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;
	}

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

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

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if(! proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
                // Parse the target object interfaceevaluateProxyInterfaces(beanClass, proxyFactory); }}/ / generated Advisor
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

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

        // Generate the proxy
		returnproxyFactory.getProxy(getProxyClassLoader()); }}Copy the code

conclusion

  1. Describes how Spring AOP in the form of @AspectJ is used
  2. Spring AOP can use @AspectJ identifiers such as execution, within, this, target, @annotation, and so on
  3. Describes how spring uses the @EnableAspectJAutoProxy annotation internally to enable Spring AOP functionality, And how the internal process of agent generator AnnotationAwareAspectJAutoProxyCreator according to @ Aspect class, to automatically generate the agent