We have previously analyzed the general flow of IOC. In this issue, we will take you to analyze the implementation principle of AOP, another major feature in Spring. It is recommended to understand the principle of IOC, the use of AOP and the basic concepts before reading this article, because the implementation of AOP is based on IOC.
When learning AOP, everyone has heard that the principle of AOP is dynamic agent more or less, but have you ever thought about the premise of dynamic agent? Whether it is a JDK or CGlib proxy, you have to pass in the proxy object to the proxy class, which means that the target object has been created at this time. Taking this into the IOC process, we can guess that the implementation of AOP must be after the bean is instantiated. So what are the processes that can perform some operations on beans in the life cycle of beans? That is, the subclass of BeanPostProcessor can be completed. With this in mind, we can go to the subclass of BeanPostProcessor to see which one is related to AOP. Here we'd better show what it is and find the specific implementation class.
To enable the AOP function with annotations, you need to add the @ EnableAspectJAutoProxy annotation. Follow up on the annotation
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented //Automatic proxy registrar, follow in @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; } #AspectJAutoProxyRegistrar @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //Keep following AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); //...... } public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) { //You can see that a bean of type AnnotationAwareAspectJAutoProxyCreator is registered return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); }
Let's take a look at the inheritance system of annotation awareaspectjauto proxycreator
It shows that it is the BeanPostProcessor we are looking for, and the method we want to analyze is in its base class AbstractAutoProxyCreator. Let's first look at the life cycle of beans in IOC.
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { //...... Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //postProcessBeforeInitialization callback for BeanPostProcessor wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //Init method callback invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { //postProcessAfterInitialization callback for BeanPostProcessor wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } //Return wrapped Bean instance return wrappedBean; }
Friends familiar with IOC should know this method, and handle the callback method and init method of BeanPostProcessor here. For each singleton bean initialization, some processor callbacks are required, and the implementation of AOP starts from this.
#AbstractAutoProxyCreator
//Bean: bean instance after initialization //beanName: the name of the bean public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); //Prevent duplicate proxy of a bean if (this.earlyProxyReferences.remove(cacheKey) != bean) { //Start of AOP return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //The TargetSource scenario can be understood by yourself if you are interested. It will operate in the postprocessbeforeinstance of the current class if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } //Isinstructureclass() determines whether it is the type of the basic framework //shouldSkip() determines whether beanName ends with. ORIGINAL //Neither of these can be enhanced if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } //Find the notification for the Class of the current bean Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); //DO_NOT_PROXY==null if (specificInterceptors != DO_NOT_PROXY) { //Indicates that enhancements are needed this.advisedBeans.put(cacheKey, Boolean.TRUE); //Create proxy object Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); //Return proxy object return proxy; } //This indicates that the bean does not need to be enhanced and directly returns the original instance this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
It can be seen from this that after the postProcessAfterInitialization() method of AbstractAutoProxyCreator, the bean to be proxied will eventually return the proxy object and put it into the container. Next, we should analyze how Spring parses the notification method we configured and how to create a proxy object.
There are two methods: one is to get the notification getAdvicesAndAdvisorsForBean(), and the other is to create the proxy object createProxy(). Let's look at them respectively.
getAdvicesAndAdvisorsForBean() looks for enhanced notifications suitable for the current bean
#AbstractAdvisorAutoProxyCreator protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { //Gets the notification appropriate for the current bean type List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { //null returned if null return DO_NOT_PROXY; } //Convert to data return return advisors.toArray(); } #AbstractAdvisorAutoProxyCreator protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { //Get all available advisors in the current container. This method is overridden in AnnotationAwareAspectJAutoProxyCreator List<Advisor> candidateAdvisors = findCandidateAdvisors(); //Filter the Advisor obtained above and select the Advisor suitable for the current bean type List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); //An Advisor of type ExposeInvocationInterceptor will be added at the position where index==0 extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { //Sort all notifications eligibleAdvisors = sortAdvisors(eligibleAdvisors); } //Returns the notification collection that matches successfully return eligibleAdvisors; }
This method has analyzed the specific functions of internal methods. Because the call chain is complex, friends in need can choose to see it. Friends without needs can skip this part. Understand the functions of these methods and directly see the process of creating proxy objects.
Findcandidedadvisors() finds all notifications
#AnnotationAwareAspectJAutoProxyCreator @Override protected List<Advisor> findCandidateAdvisors() { //The following methods will parse Advisor in two ways //1. Get the bean instance of Advisor configured through bean //Here, the #beanfactory advisor retrievalhelper's findadvisor beans () is finally called List<Advisor> advisors = super.findCandidateAdvisors(); //2. Extract the Advisor information defined in the @ Aspect annotation class advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); return advisors; } //1. Get the bean instance of Advisor configured through bean #BeanFactoryAdvisorRetrievalHelper public List<Advisor> findAdvisorBeans() { String[] advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { //Query all beannames of subclasses of Advisor configured through beans advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } if (advisorNames.length == 0) { return new ArrayList<>(); } List<Advisor> advisors = new ArrayList<>(); //Traversal beanName for (String name : advisorNames) { if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isTraceEnabled()) { logger.trace("Skipping currently created advisor '" + name + "'"); } } else { try { //Take out the instance of Advisor from the container advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { //...... } } } } //Return to all Advisor s return advisors; } //2. Extract the Advisor information defined in the @ Aspect annotation class #BeanFactoryAspectJAdvisorsBuilder public List<Advisor> buildAspectJAdvisors() { //Get cache List<String> aspectNames = this.aspectBeanNames; if (aspectNames == null) { synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { //Save Advisor defined by @ Aspect annotation List<Advisor> advisors = new LinkedList<Advisor>(); //Save the beanName of the class annotated with @ Aspect aspectNames = new LinkedList<String>(); //Get all beannames in the container String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); //Traverse all beannames for (String beanName : beanNames) { if (!isEligibleBean(beanName)) { continue; } Class<?> beanType = this.beanFactory.getType(beanName); if (beanType == null) { continue; } //Query whether the current type, parent class, parent class of parent class... Have @ Aspect annotation. If yes, it indicates that the current type is Aspect if (this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); AspectMetadata amd = new AspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { //Use factory pattern to manage Aspect metadata and associate instance objects of @ Aspect annotation MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); //Get the Advisor information in the @ Aspect class and follow it List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } //Add all advisors in the current @ Aspect annotation class advisors.addAll(classAdvisors); } else { if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton"); } MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); this.aspectFactoryCache.put(beanName, factory); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } //Cache the beanName of the @ Aspect class just after cyclic processing, indicating that the class has extracted the Advisor this.aspectBeanNames = aspectNames; return advisors; } } } if (aspectNames.isEmpty()) { return Collections.emptyList(); } //Get from cache List<Advisor> advisors = new LinkedList<Advisor>(); for (String aspectName : aspectNames) { List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } else { MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; } #ReflectiveAspectJAdvisorFactory public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { //Get the Class of @ Aspect Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); //Get the beanName of @ Aspect String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); validate(aspectClass); MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); List<Advisor> advisors = new LinkedList<Advisor>(); //getAdvisorMethods() will get @ Before @After @Around @AfterReturning @AfterThrowing these methods //Exclude @ PointCut for (Method method : getAdvisorMethods(aspectClass)) { //Wrap the current method as Advisor and follow it Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } } //...... //Returns all the advisors in the current @ Aspect annotation class return advisors; } #ReflectiveAspectJAdvisorFactory public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) { validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); //Obtain the pointcut information. candidateAdviceMethod is the enhanced method in the @ Aspect class. Parse the pointcut from the annotation of the method AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); //Description the corresponding Advisor cannot be created if (expressionPointcut == null) { return null; } //Wrap the method as an instance of type InstantiationModelAwarePointcutAdvisorImpl //expressionPointcut: tangent expression //Candidate advice method: enhancement method //This: build the factory of Advisor. Remember this //Follow in return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); } #InstantiationModelAwarePointcutAdvisorImpl public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { //A series of assignment operations //...... //Assign an instance of reflective aspectJAdvisorFactory to aspectJAdvisorFactory this.aspectJAdvisorFactory = aspectJAdvisorFactory; if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { //...... } else { //Aspect handling single instances this.pointcut = this.declaredPointcut; this.lazy = false; //Enter this method this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); } } #InstantiationModelAwarePointcutAdvisorImpl private Advice instantiateAdvice(AspectJExpressionPointcut pcut) { //Call getAdvice through aspectJAdvisorFactory, which is reflective aspectJAdvisorFactory //Keep going return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName); } #ReflectiveAspectJAdvisorFactory //In this method, you can finally see that the enhancement method in @ Aspect is packaged as Advisor public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { //Gets the Class of the @ Aspect to which the current enhancement method belongs Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); validate(candidateAspectClass); //Gets the annotation information defined on the current enhancement method AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); //...... AbstractAspectJAdvice springAdvice; switch (aspectJAnnotation.getAnnotationType()) { //Before advice case AtBefore: springAdvice = new AspectJMethodBeforeAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; //Post notification case AtAfter: springAdvice = new AspectJAfterAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; //Return notification case AtAfterReturning: springAdvice = new AspectJAfterReturningAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; //Exception notification case AtAfterThrowing: springAdvice = new AspectJAfterThrowingAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; //Around Advice case AtAround: springAdvice = new AspectJAroundAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; //Pointcut failed to generate Advisor case AtPointcut: if (logger.isDebugEnabled()) { logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); } return null; default: throw new UnsupportedOperationException( "Unsupported advice type on method: " + candidateAdviceMethod); } springAdvice.setAspectName(aspectName); springAdvice.setDeclarationOrder(declarationOrder); String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); } springAdvice.calculateArgumentBindings(); //Returns the wrapped Advisor of the current method return springAdvice; }
Here, the whole call chain returns (#abstractadvisor autoproxycreator's findEligibleAdvisors() method). At present, all the advisors in the container have been created. The next step is to filter out the advisors suitable for the current bean.
findAdvisorsThatCanApply() filters out notifications suitable for the current bean
#AbstractAdvisorAutoProxyCreator protected List<Advisor> findAdvisorsThatCanApply( List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) { ProxyCreationContext.setCurrentProxiedBeanName(beanName); try { //Keep following return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); } finally { ProxyCreationContext.setCurrentProxiedBeanName(null); } } #AopUtils //Candidate advisors: contains all the advisors //clazz: Class of the current bean public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz){ if (candidateAdvisors.isEmpty()) { //Return without notification return candidateAdvisors; } List<Advisor> eligibleAdvisors = new ArrayList<>(); //Not considered for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { //Not considered if (candidate instanceof IntroductionAdvisor) { continue; } //true, indicating that the current advisor matches clazz if (canApply(candidate, clazz, hasIntroductions)) { //Add advisor eligibleAdvisors.add(candidate); } } return eligibleAdvisors; } public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { //Not considered if (advisor instanceof IntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } //PointcutAdvisor is a sub interface of Advisor else if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pca = (PointcutAdvisor) advisor; //Keep following return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { return true; } } public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { Assert.notNull(pc, "Pointcut must not be null"); //Judge whether the pointcut matches the current Class if (!pc.getClassFilter().matches(targetClass)) { return false; } //Get method matcher MethodMatcher methodMatcher = pc.getMethodMatcher(); //A matcher of type TRUEMethodMatcher matches all methods if (methodMatcher == MethodMatcher.TRUE) { return true; } //...... //Save the Class of the current object, the interface implemented by itself, the parent Class, the parent Class of the parent Class, and the interface implemented Set<Class<?>> classes = new LinkedHashSet<>(); //Ensure that targetClass has not been proxied and is a native Class if (!Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } //Add interface classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); //Traverse all classes and check whether all methods will be matched by the method matcher //As long as one method matches successfully, it indicates that the current bean needs to be enhanced and returns true for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
At this step, all the advisors suitable for the current bean are also filtered out, and we return to the main branch.
#AbstractAdvisorAutoProxyCreator protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { //Get all available advisors in the current container List<Advisor> candidateAdvisors = findCandidateAdvisors(); //Filter the Advisor obtained above and select the Advisor suitable for the current bean type List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); //An Advisor of type ExposeInvocationInterceptor will be added at the position where index==0 extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { //Sort all notifications eligibleAdvisors = sortAdvisors(eligibleAdvisors); } //Returns the notification collection that matches successfully return eligibleAdvisors; }
It took so much effort to filter out and return all the advisors suitable for the current bean in the container. The next step is to create an agent through these advisors.
createProxy() creates a proxy object
Remember where we came from? We called it from #AbstractAutoProxyCreator's postProcessAfterInitialization() method, and finally came to this method.
#AbstractAutoProxyCreator protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //...... //Find the notification for the Class of the current bean //All the above processes begin with this method call Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); //DO_NOT_PROXY==null if (specificInterceptors != DO_NOT_PROXY) { //Indicates that enhancements are needed this.advisedBeans.put(cacheKey, Boolean.TRUE); //Create proxy object Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); //Return proxy object return proxy; } //This indicates that the bean does not need to be enhanced and directly returns the original instance this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
Next, let's look at how Spring creates proxy objects
#AbstractAutoProxyCreator 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 factory for proxy objects ProxyFactory proxyFactory = new ProxyFactory(); //Copy some information to proxyFactory and assign some related attributes internally proxyFactory.copyFrom(this); //ProxyTargetClass is the configuration attribute of AOP. If it is true, it indicates that CGlib is used for proxy. The default is false if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } //Specific interceptors: the Advisor created earlier and suitable for the current bean //Buildawisors () internally provides and adds a user-defined public Advisor Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); for (Advisor advisor : advisors) { //Add Advisor to proxyFactory proxyFactory.addAdvisor(advisor); } //Add target object to proxyFactory proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { //It indicates that these advisors passed to proxyFactory have been matched on a basic basis and classFilter has been matched proxyFactory.setPreFiltered(true); } //proxyFactory creates proxy objects return proxyFactory.getProxy(getProxyClassLoader()); } #ProxyFactory public Object getProxy(ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); } #ProxyCreatorSupport protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } //getAopProxyFactory() is proxyFactory //Look at createAopProxy() return getAopProxyFactory().createAopProxy(this); } #DefaultAopProxyFactory //Here you can see how Spring selects JDK or CGlib for dynamic proxy public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { //config.isProxyTargetClass() corresponds to the ProxyTargetClass configuration //hasNoUserSuppliedProxyInterfaces(config) determines whether the proxy object has an implementation interface 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."); } //Judge whether the Class of the proxied object is an interface, or whether it is already a proxied type if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { //Only JDK dynamic proxy can be used return new JdkDynamicAopProxy(config); } //Using CGlib dynamic proxy return new ObjenesisCglibAopProxy(config); } else { //JDK dynamic agent return new JdkDynamicAopProxy(config); } }
From here, we can know that proxyFactory.getProxy() is the getProxy() called through JdkDynamicAopProxy or ObjenesisCglibAopProxy. This article only analyzes the dynamic proxy process of JDK, so let's take a look at the source code of JDK dynamic aopproxy.
Source code analysis of JdkDynamicAopProxy
First look at the interface that this class inherits
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable
Is one of the interfaces familiar? InvocationHandler is the key to jdk dynamic proxy, so the invoke method will be rewritten. Therefore, for this class, we will focus on getProxy and invoke.
public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } //Gets the array of interfaces that require a proxy Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); //Find all the interfaces that need proxy at present, see if there are equals and hashcode methods, and mark them if there are findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); //Return proxy class object return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
Friends who have written jdk dynamic agent demo must be very familiar with it. They don't do too much analysis and look directly at the invoke method
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //It is used to drive the execution chain of Advisor, which will be analyzed later MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; //Gets the target object inside ProxyFactory TargetSource targetSource = this.advised.targetSource; Class<?> targetClass = null; Object target = null; try { //Note: if equals is not defined in the interface implemented by the proxy class and the current method is equals, use the equals provided by JdkDynamicAopProxy //It's the tag in getProxy() //The same is true below if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { return equals(args[0]); } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { return hashCode(); } else if (method.getDeclaringClass() == DecoratingProxy.class) { return AopProxyUtils.ultimateTargetClass(this.advised); } else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } //Method return value Object retVal; //Do you need to expose the proxy object to the AOP context if (this.advised.exposeProxy) { //For special cases, for example, an object has both A and B methods that need to be enhanced, but B is also called inside A //If the proxy object is not exposed, the enhanced A calls B of the target object, and B is not enhanced //Therefore, after exposing the proxy object, enhancement A can call enhancement B of the proxy object oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } //Real target object target = targetSource.getTarget(); if (target != null) { //Gets the Class of the target object targetClass = target.getClass(); } //Find all method interceptors suitable for the current method, where the Advisor will be converted into the corresponding method interceptor List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); //If there is no interceptor if (chain.isEmpty()) { //This indicates that the current method does not need to be enhanced Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); //Equivalent to method.invoke retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { //Description method needs to be enhanced //The reflective method invocation core passes in the proxy object, target object, current method and interceptor chain invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); //procced() turns on the interceptor call link retVal = invocation.proceed(); } //Method return value type Class<?> returnType = method.getReturnType(); //If the return value type of method is the target object if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { //Replace here and return the proxy object retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } //Method return value return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } }
Next, analyze the conversion process from Advisor to method interceptor
#AdvisedSupport public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) { MethodCacheKey cacheKey = new MethodCacheKey(method); List<Object> cached = this.methodCache.get(cacheKey); if (cached == null) { //Follow in cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this, method, targetClass); this.methodCache.put(cacheKey, cached); } return cached; } #DefaultAdvisorChainFactory //config: proxyFactory, which internally holds all the advisors of the current bean public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, Class<?> targetClass) { //Interceptor list List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length); //Class of the target object Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); //Traverse all the advisors of the current bean for (Advisor advisor : config.getAdvisors()) { //PointcutAdvisor contains pointcut information if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; //isPreFiltered() has been set before. If it is true, it indicates that type matching has been done if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { //Wrap Advisor as MethodInterceptor MethodInterceptor[] interceptors = registry.getInterceptors(advisor); //Get method matcher MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); //Static matching if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) { //Involving runtime matching if (mm.isRuntime()) { for (MethodInterceptor interceptor : interceptors) { interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); } } else { //The match is successful. Add the interceptor to the list interceptorList.addAll(Arrays.asList(interceptors)); } } } } //Not considered else if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } //Describe all current Advisor matching methods else { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } //Returns the list of interceptors return interceptorList; }
The above is the process of converting Advisor into MethodInterceptor, and the next is the core interceptor call chain.
//The reflective method invocation core passes in the proxy object, target object, current method and interceptor chain invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); //procced() turns on the interceptor call link retVal = invocation.proceed();
Let's take a look at #ReflectiveMethodInvocation, which is a class used to promote interceptor execution.
//Joinpoint, the top-level interface of ProxyMethodInvocation, will provide a processed () method. Focus on this method public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable protected ReflectiveMethodInvocation( Object proxy, Object target, Method method, Object[] arguments, Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) { //Perform some assignment operations this.proxy = proxy; this.target = target; this.targetClass = targetClass; this.method = BridgeMethodResolver.findBridgedMethod(method); this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments); this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers; } public Object proceed() throws Throwable { //Interceptors anddynamicmethodmatchers: interceptor chain //The initial value of currentInterceptorIndex is - 1 //This condition determines which subscript the current interceptor chain executes to. If the subscript is equal to the interceptor length - 1 //It indicates that all interceptors have been executed if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { //Call target method return invokeJoinpoint(); } //Get next interceptor Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); //Determine whether runtime matching is required if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { //Runtime matching failed. Skip the current interceptor and execute the next interceptor logic return proceed(); } } else { //It will be executed normally //Execute the logic of the interceptor and pass this in //this:ReflectiveMethodInvocation //invoke here is the interface method of MethodInterceptor. Let's take @ Before and @ After implementation classes for details return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
@Before
#MethodBeforeAdviceInterceptor //mi: ReflectiveMethodInvocation public Object invoke(MethodInvocation mi) throws Throwable { //First execute the @ Before method this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); //Then call the next interceptor method in the interceptor chain return mi.proceed(); }
@After
#AspectJAfterAdvice //mi: ReflectiveMethodInvocation public Object invoke(MethodInvocation mi) throws Throwable { try { //Let the interceptor chain complete all interception execution return mi.proceed(); } finally { //Execute @ After method again invokeAdviceMethod(getJoinPointMatch(), null, null); } }
Other notification types are similar calling processes. Please check the source code and analyze the specific implementation. Seeing this, some friends may not know the call link of the interceptor, so let's customize a simple Spring AOP to help you sort out the interceptor process.
Custom AOP
AOP core interface and implementation class
//The execution flow used to drive the interceptor public interface MethodInvocation { //Drive interceptor chain, execute enhanced logic + called by proxy method Object proceed()throws Throwable; }
//Custom interceptor interface public interface MethodInterceptor { //Method interceptor interface, enhanced logic, all written in it Object invoke(MethodInvocation mi) throws Throwable; }
//MethodInvocation implementation class, corresponding to ReflectiveMethodInvocation public class MethodInvocationImpl implements MethodInvocation{ private Object target; private Method method; private Object[] args; private List<MethodInterceptor> interceptorList; //Subscript of current interceptor execution private int currentInterceptorIndex=-1; public MethodInvocationImpl(Object target, Method method,Object[] args,List<MethodInterceptor> interceptorList){ this.target=target; this.method=method; this.args=args; this.interceptorList=interceptorList; } @Override public Object proceed() throws Throwable { //If interceptor chain execution is complete if(currentInterceptorIndex==interceptorList.size()-1){ //Call target method return method.invoke(target,args); } //This indicates that there are still interceptors not executed MethodInterceptor interceptor=interceptorList.get(++currentInterceptorIndex); return interceptor.invoke(this); } }
//Agent factory public class ProxyFactory { //Target object private Object target; //Store interceptor private List<MethodInterceptor> interceptorList; public ProxyFactory(Object target) { this.target = target; this.interceptorList = new ArrayList<>(); } //Adding Advisor just ensures that the method names are the same, so the transformation from Advisor to MethodInterceptor will not be realized public void addAdvisor(MethodInterceptor interceptor){ this.interceptorList.add(interceptor); } public Object getProxy(){ return new JdkDynamicAopProxy(this).getProxy(); } public List<MethodInterceptor> getInterceptorList() { return interceptorList; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } }
//JDK dynamic proxy class public class JdkDynamicAopProxy implements InvocationHandler { private ProxyFactory proxyFactory; public JdkDynamicAopProxy(ProxyFactory proxyFactory){ this.proxyFactory=proxyFactory; } //Create proxy object public Object getProxy(){ Object target=proxyFactory.getTarget(); return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { List<MethodInterceptor> interceptorList = proxyFactory.getInterceptorList(); Object target = proxyFactory.getTarget(); MethodInvocation mi=new MethodInvocationImpl(target,method,args,interceptorList); return mi.proceed(); } }
Two custom interceptors are provided
//before public class MethodBeforeInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("Method pre enhancement!"); return mi.proceed(); } }
//after public class MethodAfterInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation mi) throws Throwable { try{ return mi.proceed(); }finally { System.out.println("Method post enhancement!"); } } }
Related test classes
public interface Animal { public void eat(); }
public class Dog implements Animal{ @Override public void eat() { System.out.println("Dogs eat bones!"); } }
public class Main { public static void main(String[] args) { //Create target object Dog dog=new Dog(); //Create agent factory ProxyFactory proxyFactory=new ProxyFactory(dog); //Add Advisor to agent factory proxyFactory.addAdvisor(new MethodAfterInterceptor()); proxyFactory.addAdvisor(new MethodBeforeInterceptor()); //Get proxy object Animal proxy = (Animal)proxyFactory.getProxy(); //Execution target method proxy.eat(); } }
Final execution result
Method pre enhancement! Dogs eat bones! Method post enhancement!
You can debug by yourself. This demo is equivalent to simplifying and implementing the core processes of AOP. I believe that after you understand this process, you can well understand the interceptor process of Spring AOP.
The above is the whole content of Spring AOP source code analysis. There are too many contents involved. You can only analyze some general processes, and you need to read the source code and study some details.