AspectJAware Advisor AutoProxy Creator and Analysis of Generating Agent Timing for Bean s
As mentioned in the previous article, the class org. spring framework. aop. aspectj. autoproxy. AspectJAware Advisor AutoProxy Creator is the core class of AOP provided by Spring to developers. It is AspectJAware Advisor AutoProxy Creator that completes the transformation process of Class/Interface - > Agent. First, let's take a look at AspectJAware Advisor AutoProxy Proxy Creator. The hierarchy of eator:
The most notable point here is the box in the bottom left corner. Let me summarize it in a few words.
- AspectJAware Advisor AutoProxyCreator is the implementation class of BeanPostProcessor interface
- The postProcessBeforeInitialization method and postProcessAfterInitialization method are implemented in the parent AbstractAutoProxyCreator
- The postProcessBeforeInitialization method is an empty implementation
- Logical code in postProcessAfterInitialization method
Based on the above analysis, the time for beans to generate proxies is clear: after each Bean is initialized, if necessary, call postProcess BeforeInitialization in AspectJAware Advisor AutoProxy Creator to generate proxies for beans.
Proxy Object Instance - Determine whether to generate proxy for < bean >
The timing of Bean generation agent is analyzed above. After each Bean is initialized, the code is positioned after the Bean is initialized. First, the initializeBean method of AbstractAutowireCapableBeanFactory is initialized.
1 protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { 2 if (System.getSecurityManager() != null) { 3 AccessController.doPrivileged(new PrivilegedAction<Object>() { 4 public Object run() { 5 invokeAwareMethods(beanName, bean); 6 return null; 7 } 8 }, getAccessControlContext()); 9 } 10 else { 11 invokeAwareMethods(beanName, bean); 12 } 13 14 Object wrappedBean = bean; 15 if (mbd == null || !mbd.isSynthetic()) { 16 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); 17 } 18 19 try { 20 invokeInitMethods(beanName, wrappedBean, mbd); 21 } 22 catch (Throwable ex) { 23 throw new BeanCreationException( 24 (mbd != null ? mbd.getResourceDescription() : null), 25 beanName, "Invocation of init method failed", ex); 26 } 27 28 if (mbd == null || !mbd.isSynthetic()) { 29 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); 30 } 31 return wrappedBean; 32 }
The applyBeanPostProcessors BeforeInitialization method in line 16 before initialization, and the applyBeanPostProcessors AfterInitialization method in line 29 after initialization:
1 public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) 2 throws BeansException { 3 4 Object result = existingBean; 5 for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { 6 result = beanProcessor.postProcessAfterInitialization(result, beanName); 7 if (result == null) { 8 return result; 9 } 10 } 11 return result; 12 }
The postProcessBeforeInitialization method for each BeanPostProcessor is invoked here. According to the previous analysis, take a look at the implementation of AbstractAutoProxyCreator's postProcessAfterInitialization method:
1 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 2 if (bean != null) { 3 Object cacheKey = getCacheKey(bean.getClass(), beanName); 4 if (!this.earlyProxyReferences.contains(cacheKey)) { 5 return wrapIfNecessary(bean, beanName, cacheKey); 6 } 7 } 8 return bean; 9 }
Follow the method wrapIfNecessary in line 5:
1 protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { 2 if (this.targetSourcedBeans.contains(beanName)) { 3 return bean; 4 } 5 if (this.nonAdvisedBeans.contains(cacheKey)) { 6 return bean; 7 } 8 if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { 9 this.nonAdvisedBeans.add(cacheKey); 10 return bean; 11 } 12 13 // Create proxy if we have advice. 14 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); 15 if (specificInterceptors != DO_NOT_PROXY) { 16 this.advisedBeans.add(cacheKey); 17 Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); 18 this.proxyTypes.put(cacheKey, proxy.getClass()); 19 return proxy; 20 } 21 22 this.nonAdvisedBeans.add(cacheKey); 23 return bean; 24 }
Lines 2 to 11 are scenario judgements that do not need to generate agents, which are skipped here. The first question we need to consider is: Which target objects need to generate agents? Because there are many beans in the configuration file, you can't generate proxies for every Bean, so you need a set of rules to determine whether a Bean needs to generate proxies, which is line 14 of the code getAdvices AndAdvisors ForBean:
1 protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) { 2 List<Advisor> candidateAdvisors = findCandidateAdvisors(); 3 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); 4 extendAdvisors(eligibleAdvisors); 5 if (!eligibleAdvisors.isEmpty()) { 6 eligibleAdvisors = sortAdvisors(eligibleAdvisors); 7 } 8 return eligibleAdvisors; 9 }
As the name implies, the method means to find the appropriate Advisor for the specified class.
The second line of code looks for candidate Advisors. According to the configuration file above, there are two candidate Advisors, namely < aop: before > and < aop: after > under < aop: aspect > node. These two have been transformed into RootBean Definition when XML is parsed.
Skip line 3 and look at the code extension Advisors method in line 4 before focusing on line 3. The code extension Advisors method in line 4 adds an org. spring framework. aop. support. DefaultPointcut Advisor to the beginning of the candidate Advisor chain (that is, the location of List.get(0).
Line 3, according to the candidate Advisors, find the Advisor that can be used, follow the method to achieve:
1 public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { 2 if (candidateAdvisors.isEmpty()) { 3 return candidateAdvisors; 4 } 5 List<Advisor> eligibleAdvisors = new LinkedList<Advisor>(); 6 for (Advisor candidate : candidateAdvisors) { 7 if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { 8 eligibleAdvisors.add(candidate); 9 } 10 } 11 boolean hasIntroductions = !eligibleAdvisors.isEmpty(); 12 for (Advisor candidate : candidateAdvisors) { 13 if (candidate instanceof IntroductionAdvisor) { 14 // already processed 15 continue; 16 } 17 if (canApply(candidate, clazz, hasIntroductions)) { 18 eligibleAdvisors.add(candidate); 19 } 20 } 21 return eligibleAdvisors; 22 }
The main judgment of the whole method revolves around the canApply expansion method:
1 public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { 2 if (advisor instanceof IntroductionAdvisor) { 3 return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); 4 } 5 else if (advisor instanceof PointcutAdvisor) { 6 PointcutAdvisor pca = (PointcutAdvisor) advisor; 7 return canApply(pca.getPointcut(), targetClass, hasIntroductions); 8 } 9 else { 10 // It doesn't have a pointcut so we assume it applies. 11 return true; 12 } 13 }
The actual type of the first parameter advisor is AspectJPointcutAdvisor, which is a subclass of PointcutAdvisor, so the method in line 7 is executed:
1 public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { 2 if (!pc.getClassFilter().matches(targetClass)) { 3 return false; 4 } 5 6 MethodMatcher methodMatcher = pc.getMethodMatcher(); 7 IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; 8 if (methodMatcher instanceof IntroductionAwareMethodMatcher) { 9 introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; 10 } 11 12 Set<Class> classes = new HashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); 13 classes.add(targetClass); 14 for (Class<?> clazz : classes) { 15 Method[] methods = clazz.getMethods(); 16 for (Method method : methods) { 17 if ((introductionAwareMethodMatcher != null && 18 introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) || 19 methodMatcher.matches(method, targetClass)) { 20 return true; 21 } 22 } 23 } 24 return false; 25 }
In fact, this method is to make two judgments on the expression corresponding to the current Advisor:
- The target class must satisfy the expression matching rule
- The method in the target class must satisfy the expression matching rule. Of course, not all the methods need to satisfy the expression matching rule, only one method can satisfy the expression matching rule.
If both of the above are satisfied, then the container will judge that the < bean > satisfies the condition and needs to be generated proxy object by returning an array object in which the Advisor corresponding to < bean > is stored.
Instantiation of Proxy Objects - Organizing the Proxy Code Context for <Bean> Generation
After analyzing the conditions for generating agents for < beans > above, let's take a formal look at how the Spring context generates agents for < beans >. Back to the wrapIfNecessary method of AbstractAutoProxyCreator:
1 protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { 2 if (this.targetSourcedBeans.contains(beanName)) { 3 return bean; 4 } 5 if (this.nonAdvisedBeans.contains(cacheKey)) { 6 return bean; 7 } 8 if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { 9 this.nonAdvisedBeans.add(cacheKey); 10 return bean; 11 } 12 13 // Create proxy if we have advice. 14 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); 15 if (specificInterceptors != DO_NOT_PROXY) { 16 this.advisedBeans.add(cacheKey); 17 Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); 18 this.proxyTypes.put(cacheKey, proxy.getClass()); 19 return proxy; 20 } 21 22 this.nonAdvisedBeans.add(cacheKey); 23 return bean; 24 }
Line 14 gets the Advisor array corresponding to < bean > and line 15 judges that if the Advisor array is not empty, the proxy will be created through line 17 with the code < bean>:
1 protected Object createProxy( 2 Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { 3 4 ProxyFactory proxyFactory = new ProxyFactory(); 5 // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig. 6 proxyFactory.copyFrom(this); 7 8 if (!shouldProxyTargetClass(beanClass, beanName)) { 9 // Must allow for introductions; can't just set interfaces to 10 // the target's interfaces only. 11 Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader); 12 for (Class<?> targetInterface : targetInterfaces) { 13 proxyFactory.addInterface(targetInterface); 14 } 15 } 16 17 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); 18 for (Advisor advisor : advisors) { 19 proxyFactory.addAdvisor(advisor); 20 } 21 22 proxyFactory.setTargetSource(targetSource); 23 customizeProxyFactory(proxyFactory); 24 25 proxyFactory.setFrozen(this.freezeProxy); 26 if (advisorsPreFiltered()) { 27 proxyFactory.setPreFiltered(true); 28 } 29 30 return proxyFactory.getProxy(this.proxyClassLoader); 31 }
Lines 4 to 6 new presents a ProxyFactory, Proxy, which, as the name implies, proxy factory, provides a simple way to use code to obtain and configure AOP proxies.
The code in line 8 makes a judgment that proxy-target-class="false" or proxy-target-class is not configured in <aop:config> node, i.e. CGLIB is not used to generate proxies. If the condition is satisfied, make a judgment, get all the interfaces implemented by the current Bean, and say that these interface Class objects are added to the ProxyFactory.
Lines 17 to 28 are not necessary to look at, just add some parameters to ProxyFactory. Focus on line 30, proxyFactory.getProxy(this.proxyClassLoader):
1 public Object getProxy(ClassLoader classLoader) { 2 return createAopProxy().getProxy(classLoader); 3 }
Implementing the code is one line, but it clearly tells us that we did two things:
- Creating AopProxy Interface Implementation Class
- Get the corresponding proxy of <bean> by getProxy method of the implementation class of AopProxy interface
Starting from these two points, it is analyzed in two parts.
Instantiation of Proxy Objects - Creating AopProxy Interface Implementation Classes
Take a look at the implementation of the createAopProxy() method, which is located in the DefaultAopProxyFactory class:
1 protected final synchronized AopProxy createAopProxy() { 2 if (!this.active) { 3 activate(); 4 } 5 return getAopProxyFactory().createAopProxy(this); 6 }
There's no need to look at the previous section. Go straight to the point: the createAopProxy method:
1 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { 2 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { 3 Class targetClass = config.getTargetClass(); 4 if (targetClass == null) { 5 throw new AopConfigException("TargetSource cannot determine target class: " + 6 "Either an interface or a target is required for proxy creation."); 7 } 8 if (targetClass.isInterface()) { 9 return new JdkDynamicAopProxy(config); 10 } 11 if (!cglibAvailable) { 12 throw new AopConfigException( 13 "Cannot proxy target class because CGLIB2 is not available. " + 14 "Add CGLIB to the class path or specify proxy interfaces."); 15 } 16 return CglibProxyFactory.createCglibProxy(config); 17 } 18 else { 19 return new JdkDynamicAopProxy(config); 20 } 21 }
Usually we can summarize three sentences of AOP principle:
- Use CGLIB for class generation agents
- Use JDK native Proxy for interface generation agents
- Proxy generation using CGLIB for interfaces can be specified through configuration files
The source of these three sentences is the createAopProxy method. See that the default line 19 code uses the Proxy generated agent that comes with JDK, with three exceptions:
- ProxyConfig's isOptimize method is true, which means that Spring should optimize itself rather than specify it by the user.
- ProxyConfig's isProxyTargetClass method is true, which means that proxy-target-class="true" is configured.
- ProxyConfig satisfies the hasNoUserSupplied ProxyInterfaces method with a true execution result, which means that the < bean > object does not implement any interfaces or that the interface implemented is the SpringProxy interface.
After entering the if judgment in line 2, the type of AopProxy to return is determined according to the type of target < bean >. To sum up, it is:
- Proxy-target-class is not configured or proxy-target-class="false" returns JdkDynamicAopProxy
- proxy-target-class="true" or <bean> objects do not implement any interfaces or only SpringProxy interfaces, returning Cglib2AopProxy
Of course, whether it's JdkDynamicAopProxy or Cglib2AopProxy, AdvisedSupport is passed in as a constructor parameter, which stores specific Advisor s.
Instantiation of Proxy Objects - Get the corresponding proxy of <bean> by getProxy method
In fact, the code has been analyzed to JdkDynamicAopProxy and glib2AopProxy, the rest is nothing to say, just to see the familiarity of these two ways to generate agents.
Cglib2AopProxy generates proxy code not to look at, to Cglib unfamiliar friends can see Cglib and its basic use One article.
JdkDynamicAopProxy generates proxies in a slight way:
1 public Object getProxy(ClassLoader classLoader) { 2 if (logger.isDebugEnabled()) { 3 logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); 4 } 5 Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); 6 findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); 7 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); 8 }
This explains the code in lines 5 and 6. The function of the code in line 5 is to get all the interfaces to be proxied. The function of the code in line 6 is to try to find out if there are equals methods and hashCode methods in these interface methods. At the same time, if there are any, mark the end of the search. The equals method and hashCode method have special treatment. .
Finally, the proxy object corresponding to the interface/class is obtained through the Proxy.newProxyInstance method in line 7. Proxy is the way JDK natively supports the generation of proxies.
Principle of proxy method invocation
The principle of generating proxies for interfaces/classes has been analyzed in detail before. After generating proxies, the method will be called. Here's a look at the principle of using JdkDynamicAopProxy to call methods.
Because JdkDynamicAopProxy itself implements the InvocationHandler interface, the logic of pre-and post-processing of specific proxy is in invoke method:
1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 2 MethodInvocation invocation; 3 Object oldProxy = null; 4 boolean setProxyContext = false; 5 6 TargetSource targetSource = this.advised.targetSource; 7 Class targetClass = null; 8 Object target = null; 9 10 try { 11 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { 12 // The target does not implement the equals(Object) method itself. 13 return equals(args[0]); 14 } 15 if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { 16 // The target does not implement the hashCode() method itself. 17 return hashCode(); 18 } 19 if (!this.advised.opaque && method.getDeclaringClass().isInterface() && 20 method.getDeclaringClass().isAssignableFrom(Advised.class)) { 21 // Service invocations on ProxyConfig with the proxy config... 22 return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); 23 } 24 25 Object retVal; 26 27 if (this.advised.exposeProxy) { 28 // Make invocation available if necessary. 29 oldProxy = AopContext.setCurrentProxy(proxy); 30 setProxyContext = true; 31 } 32 33 // May be null. Get as late as possible to minimize the time we "own" the target, 34 // in case it comes from a pool. 35 target = targetSource.getTarget(); 36 if (target != null) { 37 targetClass = target.getClass(); 38 } 39 40 // Get the interception chain for this method. 41 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 42 43 // Check whether we have any advice. If we don't, we can fallback on direct 44 // reflective invocation of the target, and avoid creating a MethodInvocation. 45 if (chain.isEmpty()) { 46 // We can skip creating a MethodInvocation: just invoke the target directly 47 // Note that the final invoker must be an InvokerInterceptor so we know it does 48 // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. 49 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); 50 } 51 else { 52 // We need to create a method invocation... 53 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); 54 // Proceed to the joinpoint through the interceptor chain. 55 retVal = invocation.proceed(); 56 } 57 58 // Massage return value if necessary. 59 if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) && 60 !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { 61 // Special case: it returned "this" and the return type of the method 62 // is type-compatible. Note that we can't help if the target sets 63 // a reference to itself in another returned object. 64 retVal = proxy; 65 } 66 return retVal; 67 } 68 finally { 69 if (target != null && !targetSource.isStatic()) { 70 // Must have come from TargetSource. 71 targetSource.releaseTarget(target); 72 } 73 if (setProxyContext) { 74 // Restore old proxy. 75 AopContext.setCurrentProxy(oldProxy); 76 } 77 } 78 }
Lines 11 to 18 show that even if the equals method and hashCode method satisfy the expression rule, they will not generate proxy content for them. They call the equals method and hashCode method of JdkDynamicAopProxy. As for what these two methods do, you can check the source code for yourself.
Lines 19 to 23 show that the lass to which the method belongs is an interface and that the lass to which the method belongs is the parent or parent of AdvisedSupport, calling the method directly through reflection.
Lines 27 to 30 are used to determine whether the agent is exposed, and are configured by expose-proxy="true/false" in the <aop:config> tag.
Line 41 of the code, get a list of all interceptors and dynamic interceptors in AdvisedSupport for interception methods, specific to our actual code, there are three Object s in the list, respectively:
- chain.get(0): ExposeInvocation Interceptor, which is the default interceptor, corresponds to the original Advisor DefaultPointcut Advisor
- chain.get(1): Method BeforeAdvice Interceptor for interception before actual method calls, corresponding to the original Advisor AspectJMethod BeforeAdvice
- chain.get(2): AspectJAfterAdvice for processing after actual method calls
Lines 45 to 50 are normal if the interceptor list is empty, because a method under a class/interface may not satisfy the expression matching rule, so this method is called directly by reflection.
Lines 51 to 56, if the interceptor list is not empty, according to the meaning of the comment, we need a Reflective Method Invocation, and intercept the original method through proceed method. Friends interested in proceed method can look at it. It uses the idea of recursion to layers the Object in the chain. Call.