In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly introduces how to achieve aop in spring, has a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let the editor take you to understand it.
A brief introduction to the principle of aop implementation
First of all, we all know that the basic principle of aop is the idea of dynamic agent. The use and basic principles of these two dynamic agents have been introduced in the agent pattern of the design pattern, but they are no longer described.
What is analyzed here is how to implement aop based on the idea of dynamic agent in spring. In order to understand the following source code analysis, we simplify a flow chart to analyze the basic implementation idea of aop.
So, based on the above process, step by step analyzes the implementation of aop in the spring source code.
Using a simple aop example, using the aspect configuration based on annotation configuration, a simple Before AOP example is analyzed. Run the following simple example under spring boot.
Advisor and advice configuration for AOP.
@ Component@Aspectpublic class AopConfig {@ Pointcut ("execution (* com.garine.debug.testcase.model.AopObject..* (..)") Public void mypoint () {/ / Section definition} @ Before ("mypoint ()") public void doAround () throws Throwable {System.out.println ("before logic");}}
AopObject, the object to be intercepted by the proxy.
@ Componentpublic class AopObject {public void aoped () {System.out.println ("logic");} Agent implemented processor (BeanPostProcessor)
The first step is to parse the content of our AOP configuration in AopConfig and save it to BeanFactory. The process is to parse and save the aspect information.
The Source of Agent implementation-AnnotationAwareAspectJAutoProxyCreator
After a code trace, I learned that annotated AOP configuration is inseparable from a class-AnnotationAwareAspectJAutoProxyCreator, which inherits the BeanPostProcessor interface. We all know that the implementation class of BeanPostProcessor has multiple execution processing nodes, one of which is after Bean instantiation. It is at this time that AnnotationAwareAspectJAutoProxyCreator intercepts the initialization process of bean and tries to adapt the bean method according to the aspect information obtained by parsing in advance. If there is a match, you need to create a proxy.
The first thing analyzed here is AnnotationAwareAspectJAutoProxyCreator. When bean instantiates for the first time querying all the aspect information, it parses and saves the Aop information to the instance and tracks the following code.
AbstractApplicationContext#refresh (context initialization trunk method)
AbstractApplicationContext#registerBeanPostProcessors (performs instantiation and saves all classes that implement the BeanPostProcessor interface)
According to the above logic, registerBeanPostProcessors executes earlier than normal bean instantiation logic, so we only need to analyze the initialization process of AnnotationAwareAspectJAutoProxyCreator.
Inheritance structure of AnnotationAwareAspectJAutoProxyCreator
As you can see from the figure above, AnnotationAwareAspectJAutoProxyCreator inherits the BeanfactoryAware interface, so the setFactory method is executed when instantiated. The timing of BeanFactoryAspectJAdvisorsBuilderAdapter initialization for all aspect information parsing is also in the setFactory method.
The tracking code is as follows.
AbstractAdvisorAutoProxyCreator#setBeanFactory
AnnotationAwareAspectJAutoProxyCreator#initBeanFactory
In this method, a new BeanFactoryAspectJAdvisorsBuilderAdapter is created, and the object is parsed and saved according to the aop configuration information in the Beanfactory. However, it is important to note that although a new BeanFactoryAspectJAdvisorsBuilderAdapter object is created at this time. However, the aop configuration is not parsed immediately at this time, and the aop configuration needs to be parsed only the first time a normal bean is instantiated. The way to parse is
BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors, which starts when AnnotationAwareAspectJAutoProxyCreator invokes postProcessBeforeInitialization for the first time.
Protected void initBeanFactory (ConfigurableListableBeanFactory beanFactory) {super.initBeanFactory (beanFactory); / / aspectJAdvisorsBuilder#buildAspectJAdvisors is the parsing configuration entry this.aspectJAdvisorsBuilder = new BeanFactoryAspectJAdvisorsBuilderAdapter (beanFactory, this.aspectJAdvisorFactory);} proxy object (Proxy) creation parsing and caching aspects
As mentioned above, in the inheritance structure diagram, AnnotationAwareAspectJAutoProxyCreator implements the InstantiationAwareBeanPostProcessor interface, and the postProcessBeforeInitialization method defined by the InstantiationAwareBeanPostProcessor interface is an interface that can edit instances of bean objects that have been injected with dependent properties.
AbstractAutowireCapableBeanFactory#doCreateBean
AbstractAutowireCapableBeanFactory#initializeBean (String, Object, RootBeanDefinition)
AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation is executed in the method, which is where the cache aspect information is initialized for the first time.
The specific call chain is shown above. The postProcessBeforeInstantiation method here is actually called by an instance of AnnotationAwareAspectJAutoProxyCreator, and AnnotationAwareAspectJAutoProxyCreator implements the InstantiationAwareBeanPostProcessor interface.
Let's go to the InstantiationAwareBeanPostProcessor#postProcessBeforeInitialization method analysis code.
AbstractAutoProxyCreator#postProcessBeforeInstantiation
AspectJAwareAdvisorAutoProxyCreator#shouldSkip (critical code)
Enter the following code AbstractAutoProxyCreator, which is the AnnotationAwareAspectJAutoProxyCreator instance initialized earlier, and enter the shouldSkip method of the instance
@ Override protected boolean shouldSkip (Class beanClass, String beanName) {/ / TODO: Consider optimization by caching the list of the aspect names / / pre-parse cache section information List candidateAdvisors = findCandidateAdvisors () For (Advisor advisor: candidateAdvisors) {if (advisor instanceof AspectJPointcutAdvisor) {if (AbstractAspectJAdvice) advisor.getAdvice ()). GetAspectName (). Equals (beanName)) {return true Return super.shouldSkip (beanClass, beanName);}
AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
The findCandidateAdvisors code of the method is as follows. Here, all the aspect advisor information is pre-parsed and cached. Note that this step is done in AbstractAutoProxyCreator#postProcessBeforeInitialization processing, that is, the section parsing operation mentioned at the beginning, and cached as soon as the parsing is completed.
@ Overrideprotected List findCandidateAdvisors () {/ / Add all the Spring advisors found according to superclass rules. List advisors = super.findCandidateAdvisors (); / / Build Advisors for all AspectJ aspects in the bean factory. / / this is the call point advisors.addAll (this.aspectJAdvisorsBuilder.buildAspectJAdvisors ()); return advisors;} where BeanFactoryAspectJAdvisorsBuilder parses all aspect information mentioned earlier.
Then let's take a look at how the aop configuration is parsed in advance here. Tracking BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
Public List buildAspectJAdvisors () {List aspectNames = null; synchronized (this) {aspectNames = this.aspectBeanNames; if (aspectNames = = null) {List advisors = new LinkedList (); aspectNames = new LinkedList (); / / query all registered BeanName String [] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors (this.beanFactory, Object.class, true, false) in Beanfactory For (String beanName: beanNames) {if (! isEligibleBean (beanName)) {continue;} / / We must be careful not to instantiate beans eagerly as in this / / case they would be cached by the Spring container but would not / / have been weaved Class beanType = this.beanFactory.getType (beanName) If (beanType = = null) {continue;} / / determine whether Bean is a section Bean,isAspect method to determine [label 1] 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); / / parses the configuration of aop class, and the package returns the Advisor object [tag 2] List classAdvisors = this.advisorFactory.getAdvisors (factory) If (this.beanFactory.isSingleton (beanName)) {this.advisorsCache.put (beanName, classAdvisors);} else {this.aspectFactoryCache.put (beanName, factory);} advisors.addAll (classAdvisors) } else {/ / Per target or per this. 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));} this.aspectBeanNames = aspectNames; return advisors;}} if (aspectNames.isEmpty ()) {return Collections.emptyList ();} List advisors = new LinkedList (); for (String aspectName: aspectNames) {List 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;}
* * [Note 1] how to determine whether a class is an aop aspect configuration class? **
Through the following code.
@ Overridepublic boolean isAspect (Class clazz) {return (hasAspectAnnotation (clazz) & &! compiledByAjc (clazz));} private boolean hasAspectAnnotation (Class clazz) {/ / contains @ Aspect annotation return (AnnotationUtils.findAnnotation (clazz, Aspect.class)! = null);}
[annotation 2] how to resolve to Advisor objects?
ReflectiveAspectJAdvisorFactory#getAdvisors traverses all methods that are not annotated by @ PointCut, that is, traversing section content methods
ReflectiveAspectJAdvisorFactory#getAdvisor handles all methods that are not annotated by @ PointCut annotations, candidate aspect content methods
The code is as follows.
@ Overridepublic Advisor getAdvisor (Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) {validate (aspectInstanceFactory.getAspectMetadata (). GetAspectClass ()); / / parse to determine whether the candidate method has annotations such as @ Before,@After,@Around, and if so, continue to create a new Advisor object. AspectJExpressionPointcut expressionPointcut = getPointcut (candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata (). GetAspectClass ()); if (expressionPointcut = = null) {return null;} / / create advisor return new InstantiationModelAwarePointcutAdvisorImpl (expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName);}
Finally, looping parsing, @ Before,@After,@Around and other annotation methods will create a new Advisor object. The newly created Advisor objects are saved in BeanFactoryAspectJAdvisorsBuilder#advisorsCache, and when AnnotationAwareAspectJAutoProxyCreator intercepts the creation process of bean, it adapts whether there are aspects available.
The Advisor parsed here has the following information. In the following information, the @ PointCut annotation is not processed, and the pointCut attribute only results in a "mypoint ()". At this point, the actual intercept expression corresponding to Advisor is not known.
The intercept expression is still empty and will be parsed the first time AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInstantiation executes it.
Adaptive tangent plane
When AbstractAutoProxyCreator#postProcessAfterInitialization executes, find all the aspect information of the above AbstractAutoProxyCreator#postProcessBeforeInitialization cache, and then how do you adapt the aspect to determine whether you need to create a proxy object?
When the AbstractAutoProxyCreator#postProcessAfterInitialization method is called, a facet adaptation is made, and a proxy object is created based on the adaptation. According to the following call chain.
AbstractAutoProxyCreator#postProcessAfterInitialization
AbstractAutoProxyCreator#wrapIfNecessary
Protected Object wrapIfNecessary (Object bean, String beanName, Object cacheKey) {if (beanName! = null & & this.targetSourcedBeans.contains (beanName)) {return bean;} if (Boolean.FALSE.equals (this.advisedBeans.get (cacheKey) {return bean;} if (isInfrastructureClass (bean.getClass ()) | shouldSkip (bean.getClass (), beanName)) {this.advisedBeans.put (cacheKey, Boolean.FALSE); return bean } / / Create proxy if we have advice. / / find matching section Object [] specificInterceptors = getAdvicesAndAdvisorsForBean (bean.getClass (), beanName, null); if (specificInterceptors! = DO_NOT_PROXY) {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 } this.advisedBeans.put (cacheKey, Boolean.FALSE); return bean;}
AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
Protected List findEligibleAdvisors (Class beanClass, String beanName) {/ / fetches all the section information from the cache List candidateAdvisors = findCandidateAdvisors (); / / matches class's List eligibleAdvisors = findAdvisorsThatCanApply (candidateAdvisors, beanClass, beanName); extendAdvisors (eligibleAdvisors); if (! eligibleAdvisors.isEmpty ()) {eligibleAdvisors = sortAdvisors (eligibleAdvisors);} return eligibleAdvisors;} according to the expression in the cache.
At this point, if the adaptation method findAdvisorsThatCanApply is executed for the first time, the intercept expression in candidateAdvisors is still empty, so you need to get the expression, that is, the value of @ Pointcut. The operation of spring parses to get the value of the intercept expression when the findAdvisorsThatCanApply is executed for the first time. After obtaining the value of the interceptor expression, it matches the current class method to see if a proxy is required.
Keep tracking the code.
AopUtils#canApply (org.springframework.aop.Advisor, java.lang.Class, boolean)
AopUtils#canApply (org.springframework.aop.Pointcut, java.lang.Class, boolean)
AspectJExpressionPointcut#getClassFilter
AspectJExpressionPointcut#checkReadyToMatch
Private void checkReadyToMatch () {if (get_Expression () = = null) {throw new IllegalStateException ("Must set property 'expression' before attempting to match");} if (this.pointcutExpression = = null) {this.pointcutClassLoader = (this.beanFactory instanceof ConfigurableBeanFactory? ((ConfigurableBeanFactory) this.beanFactory) .getBeanClassLoader (): ClassUtils.getDefaultClassLoader ()); / / parses to get the intercepting expression, for example, the corresponding expression this.pointcutExpression = buildPointcut_Expression (this.pointcutClassLoader) is queried according to @ Before's value;}}
After the final parsing, the expression information structure in advisor is shown in the following figure. Contained in the pointcut property, the method of matching the class according to the pointcutExpression loop when matching. If you are interested, you can continue debugging to see how the matching expression is implemented.
# # creating proxy object
If a suitable section is found during the matching section above, then the proxy object needs to be created.
Let's go back to the above AbstractAutoProxyCreator#wrapIfNecessary, mainly looking at the code as follows.
/ / Create proxy if we have advice. / / find matching section Object [] specificInterceptors = getAdvicesAndAdvisorsForBean (bean.getClass (), beanName, null); if (specificInterceptors! = DO_NOT_PROXY) {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;}
So, keep looking.
AbstractAutoProxyCreator#createProxy
Gets or sets the method of creating a proxy object Set up ProxyFactory all the information you need to create a Proxy.
Protected Object createProxy (Class beanClass, String beanName, Object [] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass ((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);} / / New proxy object factory ProxyFactory proxyFactory = new ProxyFactory (); proxyFactory.copyFrom (this) / / set factory proxy class if (! proxyFactory.isProxyTargetClass ()) {if (shouldProxyTargetClass (beanClass, beanName)) {proxyFactory.setProxyTargetClass (true);} else {evaluateProxyInterfaces (beanClass, proxyFactory);}} / / set intercept section Advisor [] advisors = buildAdvisors (beanName, specificInterceptors); for (Advisor advisor: advisors) {proxyFactory.addAdvisor (advisor) } / / set proxy object proxyFactory.setTargetSource (targetSource); customizeProxyFactory (proxyFactory); proxyFactory.setFrozen (this.freezeProxy); if (advisorsPreFiltered ()) {proxyFactory.setPreFiltered (true);} / / create proxy object return proxyFactory.getProxy (getProxyClassLoader ());}
Let's see how ProxyFactory creates a proxy object and continues to trace proxyFactory.getProxy (getProxyClassLoader ()).
Public Object getProxy (ClassLoader classLoader) {return createAopProxy () .getProxy (classLoader);}
CreateAopProxy () function is based on the type of class to judge the use of proxy, see the following implementation
DefaultAopProxyFactory#createAopProxy
@ Overridepublic AopProxy createAopProxy (AdvisedSupport config) throws AopConfigException {if (config.isOptimize () | | config.isProxyTargetClass () | | hasNoUserSuppliedProxyInterfaces (config)) {Class targetClass = config.getTargetClass (); if (targetClass = = null) {throw new AopConfigException ("TargetSource cannot determine target class:" + "Either an interface or a target is required for proxy creation.") } if (targetClass.isInterface () | | Proxy.isProxyClass (targetClass)) {/ / using jdk dynamic proxy must be based on interface return new JdkDynamicAopProxy (config);} / / implementing proxy based on cglib does not require interface return new ObjenesisCglibAopProxy (config) } else {return new JdkDynamicAopProxy (config);}}
So in the current debugging example, use the cglib proxy. So execute the following agent.
@ Overridepublic Object getProxy (ClassLoader classLoader) {/ / / / Configure CGLIB Enhancer... Enhancer enhancer = createEnhancer (); if (classLoader! = null) {enhancer.setClassLoader (classLoader); if (classLoader instanceof SmartClassLoader & ((SmartClassLoader) classLoader) .isClassReloadable (proxySuperClass)) {enhancer.setUseCache (false);}} enhancer.setSuperclass (proxySuperClass); enhancer.setInterfaces (AopProxyUtils.completeProxiedInterfaces (this.advised)); enhancer.setNamingPolicy (SpringNamingPolicy.INSTANCE) Enhancer.setStrategy (new ClassLoaderAwareUndeclaredThrowableStrategy (classLoader)); / / get the intercept callback function Callback [] callbacks = getCallbacks (rootClass); Class [] types = new Class [callbacks.length]; for (int x = 0; x < types.length; xclass +) {types [x] = callbacks [x] .getClass () } / / fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter (new ProxyCallbackFilter (this.advised.getConfigurationOnlyCopy (), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes (types); / / Generate the proxy class and create a proxy instance. / / return a cglib proxy object return createProxyClassAndInstance (enhancer, callbacks);} catch (CodeGenerationException ex) {/ /,}}
GetCallbacks (rootClass); in this method of getting a callback function, the callback function used by a normal aop is as follows.
/ / Choose an "aop" interceptor (used for AOP calls). Callback aopInterceptor = new DynamicAdvisedInterceptor (this.advised)
The aop callback function of cglib is as follows.
Public Object intercept (Object proxy, Method method, Object [] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null; boolean setProxyContext = false; Class targetClass = null; Object target = null; try {/ / the advised injected here is the previously created ProxyFactory object if (this.advised.exposeProxy) {/ / Make invocation available if necessary. OldProxy = AopContext.setCurrentProxy (proxy); setProxyContext = true;} / / May be null. Get as late as possible to minimize the time we / / "own" the target, in case it comes from a pool... Target = getTarget (); if (target! = null) {targetClass = target.getClass ();} / / create an aspect content call chain based on aspect information List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice (method, targetClass); Object retVal; / / Check whether we only have one InvokerInterceptor: that is, / / no real advice, but just reflective invocation of the target. If (chain.isEmpty () & Modifier.isPublic (method.getModifiers () {/ / We can skip creating a MethodInvocation: just invoke the target directly. / / Note that the final invoker must be an InvokerInterceptor, so we know / / it does nothing but a reflective operation on the target, and no hot / / swapping or fancy proxying. Object [] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary (method, args); retVal = methodProxy.invoke (target, argsToUse);} else {/ / We need to create a method invocation... / / create a method call object. The specific implementation of the call is not analyzed. The Before logic probably calls the aspect first, calling the target method retVal = new CglibMethodInvocation (proxy, target, method, args, targetClass, chain, methodProxy). Proceed ();} retVal = processReturnType (proxy, target, method, retVal); return retVal;} finally {if (target! = null) {releaseTarget (target) } if (setProxyContext) {/ / Restore old proxy. AopContext.setCurrentProxy (oldProxy);} Thank you for reading this article carefully. I hope the article "how to achieve aop in spring" shared by the editor will be helpful to you. At the same time, I hope you will support us and pay attention to the industry information channel. More related knowledge is waiting for you to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.