AOP aspect-oriented programming, relative to OOP object-oriented programming.
Spring AOP exists to decouple. AOP allows a group of classes to share the same behavior. In OOP, code coupling can only be enhanced by inheriting classes and implementing interfaces, and class inheritance can only be a single inheritance, which prevents more behaviors from being added to a group of classes. AOP compensates for the shortcomings of OOP.
Spring supports AspectJ's annotated aspect programming, which is basically defined as follows:
- Declare a facet with @AspectJ.
- By using @After, @Before, @Around definition advices, interception rules (cut points) can be directly taken as parameters.
- The interception rules of @After, @Before, @Around parameters are PointCut. In order to reuse the cut points, we can use @PointCut to define the interception rules, and then call them in the parameters of @After, @Before, @Around.
- Each of the eligible intercepted points is a Join Point.
AspectJ provides five annotations for defining notifications:
- @ Before: Pre-notification, which performs tasks defined by notifications before calling the target method
- @ After: Post-notification, after the execution of the target method, executes the tasks defined by the notification regardless of the execution result
- @ AfterReturning: Post-notification, which executes tasks defined by notifications when the target method is successfully executed after execution
- @ AfterThrowing: An exception notification that performs the task defined by the notification if an exception is thrown during the execution of the target method
- @ Around notifications, tasks defined by notifications need to be performed before and after the target method is executed
Example
Define an entry point
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Log { String value() default ""; }
Declare a facet
// section @Aspect public class LogAspect { // Tangent point @Pointcut("@annotation(com.xiaolyuh.aop.annotations.Log)") public void pointCutMethod() { } @Pointcut("@within(com.xiaolyuh.aop.annotations.Log)") public void pointCutType() { } // Suggestions @Before("pointCutMethod() || pointCutType()") // @Before("execution(* com.xiaolyuh.aop.controller.UserController.*(..))") public void before(JoinPoint joinPoint) { // By reflection, you can get the attributes on the annotations, and then do the logging related operations. MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Log log = method.getAnnotation(Log.class); System.out.println("Log section befor: " + log.value() + ":::" + JSON.toJSONString(joinPoint.getArgs())); } @After(value = " pointCutMethod() || pointCutType()") public void after(JoinPoint joinPoint) { // By reflection, you can get the attributes on the annotations, and then do the logging related operations. MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Log log = method.getAnnotation(Log.class); System.out.println("Log section after: " + log.value()); } @AfterReturning(value = " pointCutMethod() || pointCutType()", returning = "result") public void afterReturning(JoinPoint joinPoint, Object result) { // By reflection, you can get the attributes on the annotations, and then do the logging related operations. MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Log log = method.getAnnotation(Log.class); System.out.println("Log section afterReturning: " + log.value() + ":::" + JSON.toJSONString(result)); } @AfterThrowing(value = " pointCutMethod() || pointCutType()", throwing = "t") public void afterThrowing(JoinPoint joinPoint, Throwable t) { // By reflection, you can get the attributes on the annotations, and then do the logging related operations. MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Log log = method.getAnnotation(Log.class); System.out.println("Log section afterThrowing: " + log.value() + ":::" + JSON.toJSONString(t.getStackTrace())); } @Around(value = " pointCutMethod() || pointCutType()") public Object Around(ProceedingJoinPoint joinPoint) throws Throwable { // By reflection, you can get the attributes on the annotations, and then do the logging related operations. MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Log log = method.getAnnotation(Log.class); System.out.println("Log section Around before: " + log.value() + ":::" + JSON.toJSONString(joinPoint.getArgs())); Object result = joinPoint.proceed(); System.out.println("Log section Around after: " + log.value() + ":::" + JSON.toJSONString(result)); return result; } }
Turn on AOP support
@Configuration @EnableAspectJAutoProxy @ComponentScan({"com.xiaolyuh.aop"}) public class AopConfig { @Bean public LogAspect logAspect() { return new LogAspect(); } }
How business classes are used
@Controller public class UserController { // Connection point @Log("Division interface") public Object div(int i, int j) { System.out.println("Business Interface Execution-------------------"); return i / j; } public Object add(int i, int j) { return i + j; } }
Test class
public class AopTest extends BaseTest { @Before public void before() { super.before(AopConfig.class); } @Test public void contextTest() { PrintSpringBeanUtil.printlnBean(context); System.out.println("Start comparing containers Bean"); UserController bean = context.getBean(UserController.class); bean.div(4, 2); } }
Implementation results:
Log section Around before: division interface: [4,2] Log section befor: division interface::[4,2] Business Interface Implementation Log section Around after: division interface::2 Log section after: division interface After Returning of Log Aspect: Division Interface::2
Register Annotation Aware Aspect JAuto ProxyCreator Post Processor
@EnableAspectJAutoProxy
@ EnableAspectJAutoProxy's main role is to turn on AOP support. The source code is as follows:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { // true: Use CGLIB, false: If the interface uses JDK, not CGLIB boolean proxyTargetClass() default false; boolean exposeProxy() default false; }
The main function is @Import (AspectJAuto ProxyRegistrar. class). The main function of the AspectJAuto Proxy Registrar Registrar is to register the Annotation Aware Aspect JAuto Proxy Creator post processor into the container.
AspectJAutoProxyRegistrar
AspectJAuto Proxy Registrar implements the ImportBean Definition Registrar interface. When implementing this interface, Spring calls the ImportBean Definition Registrar# registerBean Definitions () method through the Configuration ClassPostProcessor when parsing the configuration class, and Annotation Aware Aspect JAoProxy Creator () method. Bean definitions of the post processor are registered in the container.
For AOP implementation, it is basically done by Annotation Aware Aspect JAuto Proxy Creator, which can automatically proxy matching bean s according to the cut points defined by the @Point annotation. But to make it easy to configure, Spring uses a custom configuration to help us automatically register Annotation Aware Aspect JAuto Proxy Creator. The realization is as follows:
Call link:
registerOrEscalateApcAsRequired:121, AopConfigUtils (org.springframework.aop.config) registerAspectJAnnotationAutoProxyCreatorIfNecessary:90, AopConfigUtils (org.springframework.aop.config) registerAspectJAnnotationAutoProxyCreatorIfNecessary:86, AopConfigUtils (org.springframework.aop.config) registerBeanDefinitions:45, AspectJAutoProxyRegistrar (org.springframework.context.annotation) loadBeanDefinitionsFromRegistrars:360, ConfigurationClassBeanDefinitionReader (org.springframework.context.annotation) loadBeanDefinitionsForConfigurationClass:144, ConfigurationClassBeanDefinitionReader (org.springframework.context.annotation) loadBeanDefinitions:116, ConfigurationClassBeanDefinitionReader (org.springframework.context.annotation) processConfigBeanDefinitions:320, ConfigurationClassPostProcessor (org.springframework.context.annotation) postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (org.springframework.context.annotation) invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (org.springframework.context.support) invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (org.springframework.context.support) invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (org.springframework.context.support) refresh:525, AbstractApplicationContext (org.springframework.context.support) <init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)
Source code:
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) { // Register or upgrade AbstractAdvisor AutoProxy Creator as needed return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); } 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)) { // If an automatic agent creator already exists and the existing automatic agent creator is inconsistent with the current one, then you need to decide which AbstractAdvisor AutoProxy Creator to use based on priority. BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { // The most important thing to change a bean is to change the className attribute of the bean. apcDefinition.setBeanClassName(cls.getName()); } } // If the existing proxy creator is the same as it is now, there is no need to create it again return null; } // Register abstractadvisor autoproxycreator 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); return beanDefinition; }
AnnotationAwareAspectJAutoProxyCreator
The following is the class diagram of Annotation Aware Aspect JAuto Proxy Creator, from which we can see that Annotation Aware Aspect JAuto Proxy Creator is a post processor that implements the Ordered interface. Like other post processors, post processors are instantiated in the registerBeanPostProcessors method and registered in the Spring container.
Analytical Section Class Acquisition Enhancer
Find the enhancer
Call link:
buildAspectJAdvisors:109, BeanFactoryAspectJAdvisorsBuilder (org.springframework.aop.aspectj.annotation) findCandidateAdvisors:90, AnnotationAwareAspectJAutoProxyCreator (org.springframework.aop.aspectj.annotation) shouldSkip:103, AspectJAwareAdvisorAutoProxyCreator (org.springframework.aop.aspectj.autoproxy) postProcessBeforeInstantiation:248, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy) applyBeanPostProcessorsBeforeInstantiation:1045, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) resolveBeforeInstantiation:1019, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) createBean:473, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) getObject:312, AbstractBeanFactory$1 (org.springframework.beans.factory.support) getSingleton:230, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support) doGetBean:308, AbstractBeanFactory (org.springframework.beans.factory.support) getBean:197, AbstractBeanFactory (org.springframework.beans.factory.support) preInstantiateSingletons:761, DefaultListableBeanFactory (org.springframework.beans.factory.support) finishBeanFactoryInitialization:867, AbstractApplicationContext (org.springframework.context.support) refresh:543, AbstractApplicationContext (org.springframework.context.support) <init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)
Source code:
@Override protected List<Advisor> findCandidateAdvisors() { // Maintaining support for XML configuration List<Advisor> advisors = super.findCandidateAdvisors(); // Support for annotations advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); return advisors; } public List<Advisor> buildAspectJAdvisors() { List<String> aspectNames = this.aspectBeanNames; if (aspectNames == null) { synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { List<Advisor> advisors = new LinkedList<Advisor>(); aspectNames = new LinkedList<String>(); // Get all beanName String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); // Traversing beanName to find the corresponding class object for (String beanName : beanNames) { if (!isEligibleBean(beanName)) { continue; } // Getting class objects from beanName Class<?> beanType = this.beanFactory.getType(beanName); if (beanType == null) { continue; } // Determine whether it is a tangent class (determine whether there is an @Aspect annotation) if (this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); AspectMetadata amd = new AspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // Enhancement Method in AspectJ Annotation of Analytical Scale List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); // Add comments to the cache if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } ... } } this.aspectBeanNames = aspectNames; return advisors; } } } ... return advisors; }
In fact, the parsing of faceted classes is to find ways to configure classes with Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class annotations. Call link:
findAspectJAnnotationOnMethod:129, AbstractAspectJAdvisorFactory (org.springframework.aop.aspectj.annotation) getPointcut:215, ReflectiveAspectJAdvisorFactory (org.springframework.aop.aspectj.annotation) getAdvisor:203, ReflectiveAspectJAdvisorFactory (org.springframework.aop.aspectj.annotation) getAdvisors:136, ReflectiveAspectJAdvisorFactory (org.springframework.aop.aspectj.annotation) buildAspectJAdvisors:109, BeanFactoryAspectJAdvisorsBuilder (org.springframework.aop.aspectj.annotation)
Source code:
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) { // Commentary Classes of Tangent Points and Comments Class<?>[] classesToLookFor = new Class<?>[] { Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class}; for (Class<?> c : classesToLookFor) { // Find corresponding annotations AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c); if (foundAnnotation != null) { return foundAnnotation; } } return null; }
By calling the link, we can see that the first parsing aspect class executes the postProcessBeforeInstantiation method of the post processor AnnotationAwareAspectJAutoProxyCreator when registering the first single instance Bean to the container. Subsequent singleton beans are registered with the container directly to the comments in the cache and will not parse the facet class again.
Enhancer Generation Based on Tangent Point Information
When we find the enhancer, it will be uniformly encapsulated as the InstantiationModelAwarePointcutAdvisorImpl type, which is a subclass of Advisor. Because different enhancements embody different logic, such as @Before ("test()") and After("tes())") tags differ in the location of enhancer enhancements, so different enhancers are needed to complete different logic. Call link:
getAdvice:255, ReflectiveAspectJAdvisorFactory (org.springframework.aop.aspectj.annotation) instantiateAdvice:160, InstantiationModelAwarePointcutAdvisorImpl (org.springframework.aop.aspectj.annotation) <init>:106, InstantiationModelAwarePointcutAdvisorImpl (org.springframework.aop.aspectj.annotation) getAdvisor:209, ReflectiveAspectJAdvisorFactory (org.springframework.aop.aspectj.annotation) getAdvisors:136, ReflectiveAspectJAdvisorFactory (org.springframework.aop.aspectj.annotation) buildAspectJAdvisors:109, BeanFactoryAspectJAdvisorsBuilder (org.springframework.aop.aspectj.annotation) findCandidateAdvisors:90, AnnotationAwareAspectJAutoProxyCreator (org.springframework.aop.aspectj.annotation) shouldSkip:103, AspectJAwareAdvisorAutoProxyCreator (org.springframework.aop.aspectj.autoproxy) postProcessBeforeInstantiation:248, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy) applyBeanPostProcessorsBeforeInstantiation:1045, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) resolveBeforeInstantiation:1019, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) createBean:473, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) getObject:312, AbstractBeanFactory$1 (org.springframework.beans.factory.support) getSingleton:230, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support) doGetBean:308, AbstractBeanFactory (org.springframework.beans.factory.support) getBean:197, AbstractBeanFactory (org.springframework.beans.factory.support) preInstantiateSingletons:761, DefaultListableBeanFactory (org.springframework.beans.factory.support) finishBeanFactoryInitialization:867, AbstractApplicationContext (org.springframework.context.support) refresh:543, AbstractApplicationContext (org.springframework.context.support) <init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)
Source code:
@Override public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { ... AbstractAspectJAdvice springAdvice; // Encapsulating different enhancers according to different annotation types switch (aspectJAnnotation.getAnnotationType()) { case AtBefore: springAdvice = new AspectJMethodBeforeAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfter: springAdvice = new AspectJAfterAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfterReturning: springAdvice = new AspectJAfterReturningAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; case AtAfterThrowing: springAdvice = new AspectJAfterThrowingAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; case AtAround: springAdvice = new AspectJAroundAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtPointcut: if (logger.isDebugEnabled()) { logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); } return null; default: throw new UnsupportedOperationException( "Unsupported advice type on method: " + candidateAdviceMethod); } // Now to configure the advice... springAdvice.setAspectName(aspectName); springAdvice.setDeclarationOrder(declarationOrder); String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); } springAdvice.calculateArgumentBindings(); return springAdvice; }
As you can see from the function, Spring generates different enhancers based on different annotations, such as @Before AspectJMethodBeforeAdvice, and in AspectJMethodBeforeAdvice, the logic of the enhancement method is completed.
MethodBeforeAdviceInterceptor
Each Advice corresponds to a Method Interceptor, for example, Method BeforeAdvice corresponds to Method BeforeAdvice Interceptor. Our call to the enhancer starts with the invoke method of the Method Interceptor. Let's try to analyze the enhancer implementation of MethodBeforeAdvice:
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable { // MethodBeforeAdvice stands for AspectJMethodBeforeAdvice private MethodBeforeAdvice advice; /** * Create a new MethodBeforeAdviceInterceptor for the given advice. * @param advice the MethodBeforeAdvice to wrap */ public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } @Override public Object invoke(MethodInvocation mi) throws Throwable { // Calling the before method of the pre-enhancer this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); return mi.proceed(); } } // AspectJMethodBeforeAdvice @Override public void before(Method method, Object[] args, Object target) throws Throwable { invokeAdviceMethod(getJoinPointMatch(), null, null); } protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable { return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex)); } protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable { Object[] actualArgs = args; if (this.aspectJAdviceMethod.getParameterTypes().length == 0) { actualArgs = null; } try { // Call the enhancer through reflection ReflectionUtils.makeAccessible(this.aspectJAdviceMethod); // TODO AopUtils.invokeJoinpointUsingReflection return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs); } catch (IllegalArgumentException ex) { throw new AopInvocationException("Mismatch on arguments to advice method [" + this.aspectJAdviceMethod + "]; pointcut expression [" + this.pointcut.getPointcutExpression() + "]", ex); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } }
The overall process of acquiring the enhancer is as follows:
- Traverse through all beanNames registered in the container and get the corresponding Class according to the beanName
- Find out all classes declaring the @Aspect annotation based on Class
- To extract an enhancer from a class marked @Aspect is to find a way to add @Before, @Around, @After, @AfterReturning, @AfterThrowing, @Pointcut annotations.
- Encapsulate the enhancer into the InstantiationModelAwarePointcutAdvisorImpl type
- Add the extracted results to the cache
Creating Enhanced Bean s
Initialize ProxyFactory
For agent creation and processing, Spring is delegated to ProxyFactory for execution. Here is the initialization of ProxyFactory. Call link:
createProxy:466, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy) wrapIfNecessary:349, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy) postProcessAfterInitialization:298, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy) applyBeanPostProcessorsAfterInitialization:423, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) initializeBean:1638, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) doCreateBean:555, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) createBean:483, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) getObject:312, AbstractBeanFactory$1 (org.springframework.beans.factory.support) getSingleton:230, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support) doGetBean:308, AbstractBeanFactory (org.springframework.beans.factory.support) getBean:197, AbstractBeanFactory (org.springframework.beans.factory.support) preInstantiateSingletons:761, DefaultListableBeanFactory (org.springframework.beans.factory.support) finishBeanFactoryInitialization:867, AbstractApplicationContext (org.springframework.context.support) refresh:543, AbstractApplicationContext (org.springframework.context.support) <init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)
Source code:
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(); // Get the relevant attributes in the current class proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } // Get the Enhancer Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); // Put the enhancer in the agent factory proxyFactory.addAdvisors(advisors); // Setting up the class to proxy proxyFactory.setTargetSource(targetSource); // Custom agent customizeProxyFactory(proxyFactory); // Used to control whether modification notifications are allowed after the proxy factory is configured, default is false proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } return proxyFactory.getProxy(getProxyClassLoader()); }
Implementation process:
- Get properties in the current class
- Add proxy interface
- Encapsulate Advisor and add it to ProxyFactory
- Setting up proxy classes
- Execute the custom function customizeProxyFactory, where you can further encapsulate the ProxyFactory
- Using ProxyFactory to Get Agents
Create AopProxy proxy class
To create a ProxyFactory proxy, you first need to determine whether to use a JDK proxy or a Cglib proxy.
public Object getProxy(ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
The createAopProxy() method is used to confirm which proxy needs to be used, and then the getProxy() method of the corresponding proxy is called to generate the proxy object.
Call link:
createAopProxy:51, DefaultAopProxyFactory (org.springframework.aop.framework) createAopProxy:105, ProxyCreatorSupport (org.springframework.aop.framework) getProxy:109, ProxyFactory (org.springframework.aop.framework) createProxy:466, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy) wrapIfNecessary:349, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy) postProcessAfterInitialization:298, AbstractAutoProxyCreator (org.springframework.aop.framework.autoproxy) applyBeanPostProcessorsAfterInitialization:423, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) initializeBean:1638, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) doCreateBean:555, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) createBean:483, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) getObject:312, AbstractBeanFactory$1 (org.springframework.beans.factory.support) getSingleton:230, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support) doGetBean:308, AbstractBeanFactory (org.springframework.beans.factory.support) getBean:197, AbstractBeanFactory (org.springframework.beans.factory.support) preInstantiateSingletons:761, DefaultListableBeanFactory (org.springframework.beans.factory.support) finishBeanFactoryInitialization:867, AbstractApplicationContext (org.springframework.context.support) refresh:543, AbstractApplicationContext (org.springframework.context.support) <init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)
From the call link, we can see that creating enhanced beans is executed after the initialization of beans in the postProcess AfterInitialization method of the post processor Annotation Aware Aspect JAuto Proxy Creator.
Source code:
@Override 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."); } // Determine whether to use an interface, and if so, use a JDK proxy if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } // Using CGliB proxy return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
Generating proxy classes
public Object getProxy(ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
The implementation of Jdk DynamicAopProxy and Objenesis CglibAopProxy to create agents can be referred to. Cglib and JDK Dynamic Agent.
Perform Enhanced Bean
Jdk DynamicAopProxy and CglibAopProxy adopt two different ways:
- Proxy. newProxyInstance () - > JdkDynamicAopProxy (inheriting Invocation Handler interface). invoke(), then matching notification type to call notification (notification is @Before, @After, etc.), and finally calling target method
- Enhancer.Callback, Callback is similar to Invocation Handler. The class DynamicAdvisedInterceptor inherits Callback, and its intercept() method is similar to invoke(), then matches the notification type to invoke the notification, and finally invokes the target method.
Whether it's JdkDynamicAopProxy or CglibAopProxy, the interception chain is eventually encapsulated in the ReflectiveMethodInvocation class and the proceed() method is called to execute the interception link. Finally, each enhancement logic is executed through the MethodInterceptor.invoke() method. The general process is as follows:
Call volume link:
before:33, LogAspect (com.xiaolyuh.aop.aspect) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) invokeAdviceMethodWithGivenArgs:627, AbstractAspectJAdvice (org.springframework.aop.aspectj) invokeAdviceMethod:609, AbstractAspectJAdvice (org.springframework.aop.aspectj) before:43, AspectJMethodBeforeAdvice (org.springframework.aop.aspectj) invoke:51, MethodBeforeAdviceInterceptor (org.springframework.aop.framework.adapter) proceed:168, ReflectiveMethodInvocation (org.springframework.aop.framework) invoke:47, AspectJAfterAdvice (org.springframework.aop.aspectj) proceed:168, ReflectiveMethodInvocation (org.springframework.aop.framework) invoke:52, AfterReturningAdviceInterceptor (org.springframework.aop.framework.adapter) proceed:168, ReflectiveMethodInvocation (org.springframework.aop.framework) invoke:62, AspectJAfterThrowingAdvice (org.springframework.aop.aspectj) proceed:168, ReflectiveMethodInvocation (org.springframework.aop.framework) invoke:92, ExposeInvocationInterceptor (org.springframework.aop.interceptor) proceed:179, ReflectiveMethodInvocation (org.springframework.aop.framework) intercept:671, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework) div:-1, UserController$$EnhancerBySpringCGLIB$$a4147a00 (com.xiaolyuh.aop.controller) contextTest:22, AopTest (com.xiaolyuh.aop)
Source code:
@Override public Object proceed() throws Throwable { // Execute the pointcut method (business method) after all enhancements have been executed if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } // Get the next interceptor to execute Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Dynamic matching InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; // Determine whether interceptors need to be executed if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching fails, the interceptor should not be executed and the next loop should be entered. return proceed(); } } else { // Common interceptors execute directly, such as: Method BeforeAdvice Interceptor, AspectJAfterAdvice, AspectJAroundAdvice, etc. // this is passed as a parameter to ensure the execution of the calling link in the current instance return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
The main responsibility of Reflective Method Invocation is to maintain the counter of link invocation and record the location of the current invocation link so that the chain can proceed in an orderly manner. Reflective Method Invocation is only responsible for intercepting the invocation of the chain, and all enhancement logic is implemented in each enhancer.
Before the interception chain is encapsulated in Reflective Method Invocation, the order of the interception chain has been beaten according to certain rules. The execution of the interception chain is actually a recursive call of proceed() method.