Spring's AOP implementation principle, brewing some days, before writing blog confidence is not very sufficient, so re-read one side of the implementation of AOP core code, but also from the Internet to find some examples of spring AOP analysis, but found that vinegar buy dog meat too much, the title tall, Most of the content is relatively superficial some of the introduction, may also be due to the relatively few people read this part of the core code logic, and then write this part of the people estimate is also less, but to tell the truth, the core principle of Spring AOP implementation of the introduction is really not very good to write, the call between the classes involved is still pretty much, The picture of the relationship drawing is also difficult to draw, and the most important point is that if the concept of AOP and spring XML parsing, label parsing, annotation implementation, as well as the Java agent, the knowledge does not have a good understanding of the implementation of the detailed logic of AOP can not have a better understanding. So, the advice is to know about these pre-knowledge, and then to see the implementation of the AOP, or to read the source code, so that learning will be much easier.
The process of learning the source code is more boring, especially the spring more rigorous, call hierarchy more, no picture sequence diagram words may really be around halo, so it is recommended to learn the timing diagram, and then follow the debug mode process to go through.
Let's start with a warm-up, look at the introduction to AOP, and then go on to the subject later.
Introduction to AOP Concepts
Facets (Aspect): The official abstraction is defined as "the modularity of a focus, which may cross over multiple objects."
Connection Point (Joinpoint): A behavior during the execution of a program.
Notification (Advice): The action that the slice produces for a connection point.
Pointcut (Pointcut): an assertion that matches a connection point, a notification in AOP, and a Pointcut expression association.
Target object: An object that is notified by one or more facets. The
AOP Proxy has two proxy methods in spring AOP, the JDK dynamic agent and the Cglib agent.
Notification (Advice) type
Pre-notification (before Advice): A notification that is performed before a connection point (Joinpoint), but this notification does not prevent execution before the connection point. The ApplicationContext uses <aop:before> elements to declare in <aop:aspect>.
Post notification (after advice): A notification that is executed when a connection point exits (whether it is a normal return or an unexpected exit). The ApplicationContext uses <aop:after> elements to declare in <aop:aspect>.
Notify after return advice: A notification that is executed after a connection point is completed normally, excluding the case where an exception was thrown. The ApplicationContext uses <after-returning> elements to declare in <aop:aspect>.
Surround Notification (Around advice): A notification that surrounds a connection point, similar to the Dofilter method of the filter in the servlet specification in the Web. You can customize the behavior before and after the call to the method, or you can choose not to execute. The ApplicationContext uses <aop:around> elements to declare in <aop:aspect>. The
notifies after an exception is thrown (after throwing advice): the notification that is executed when the method throws an unexpected exit. The ApplicationContext uses <aop:after-throwing> elements to declare in <aop:aspect>.
Pointcut expression: such as execution (* com.spring.service.*.* (..))
Characteristics
1, reduce the coupling between the modules
2, make the system easy to expand
3, better code reuse.
Examples of dynamic AOP usage
Since the previous blog, has introduced the spring Source Analysis (v) using AOP to implement the custom spring annotations in the introduction to the simple use of AOP, I believe that we have to see the principle of AOP is also more familiar with the use of AOP, so here also no longer repeat the show.
Dynamic AOP Custom Labels
My previous blog has said, how to customize the spring label, as well as the custom spring tag of the approximate parsing process, in fact, the definition of the AOP label is similar to the previous logic, first on the timing diagram:
Timing Diagram
Process description
1) Definition of the AOP tag Liu Bone is definitely from the Namespacehandlersupport implementation class began to parse, this implementation class is Aopnamespacehandler. As to why it is starting from the Namespacehandlersupport implementation class, I think the reader can go back to see the spring Custom label parsing process, which is said in more detail.
2) To enable AOP, we typically configure <aop:aspectj-autoproxy/> in spring, So in the configuration file we will use the Aspectjautoproxybeandefinitionparser parser when we encounter the aspectj-autoproxy tag.
3) After entering the Aspectjautoproxybeandefinitionparser parser, Call Aspectjautoproxybeandefinitionparser has overridden Beandefinitionparser's parser method, Then the parser method forwarded the request to Aopnamespaceutils's registeraspectjannotationautoproxycreatorifnecessary to deal with it.
4) After entering the Registeraspectjannotationautoproxycreatorifnecessary method of Aopnamespaceutils, Call Aopconfigutils's Registeraspectjannotationautoproxycreatorifnecessary method first, Inside the forwarding call to registerorescalateapcasrequired, register or upgrade the Annotationawareaspectjautoproxycreator class. The implementation of AOP is basically done by Annotationawareaspectjautoproxycreator, which can broker matching beans based on the pointcut defined by the @point annotation.
5) After the Aopconfigutils Registeraspectjannotationautoproxycreatorifnecessary method is processed, Next, Useclassproxyingifnecessary () is called to handle the Proxy-target-class and Expose-proxy properties. If Proxy-target-class is set to True, then the Cglib proxy is enforced, otherwise the JDK dynamic agent is used, and the Expose-proxy property is to resolve that sometimes the self-invocation inside the target object cannot implement the aspect enhancement.
6) Finally call the Registercomponentifnecessary method, register the build and notify the listener to do further processing.
Create an AOP agent
It says that the core logic of AOP is implemented in the Annotationawareaspectjautoproxycreator class, so let's take a look at the hierarchical relationship of this class.
We can see that this class implements the Beanpostprocessor interface, which means that the class will call the Postprocessafterinitialization method before the spring loading instantiation, and the logic for AOP begins.
Timing Diagram
Process description
1) The spring container starts, and each bean is instantiated before it goes through the postprocessafterinitialization () method of the Abstractautoproxycreator class. Then the next step is to call the Wrapifnecessary method.
/** * Create a proxy with the configured interceptors if the beans is * identified as one-to-proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */public Object <strong>postProcessAfterInitialization</strong> ( Object Bean, String beanname) throws Beansexception {if (bean! = null) {Object CacheKey = Getcachekey (Bean.getclass (), Bea Nname); if (!this.earlyproxyreferences.containskey (CacheKey)) {return wrapifnecessary (bean, beanname, CacheKey);}} return bean;}
2) After entering the Wrapifnecessary method, we look directly at the method Getadvicesandadvisorsforbean that focuses on implementing the logic, which extracts all the enhancements of the current bean and then obtains the appropriate enhancement method for the current bean. The enhanced method is then sorted, and finally returned.
/** * Wrap The given bean if necessary, i.e. if it is a eligible for being proxied. * @param beans The raw bean instance * @param beanname the name of the bean * @param cacheKey the cache key for metadata AC Cess * @return A proxy wrapping the bean, or the raw Bean instance as-is */protected Object Wrapifnecessary (Object bean, S Tring Beanname, Object cacheKey) {if (beanname! = null && This.targetSourcedBeans.containsKey (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. object[] specificinterceptors = <strong>getAdvicesAndAdvisorsForBean</strong> (Bean.getclass (), Beanname, NULL); if (specificinterceptors! = do_not_proxy) {this.advisedBeans.put (CacheKey, boolean.true); Object PROXY = Createproxy (Bean.getclass (), Beanname, specificinterceptors, new SiNgletontargetsource (Bean)); This.proxyTypes.put (CacheKey, Proxy.getclass ()); return proxy;} This.advisedBeans.put (CacheKey, boolean.false); return bean;}
3) After obtaining the enhancement method for the current bean, call the Createproxy method and create the proxy. First create the Agent factory Proxyfactory, then get the current bean's enhancer advisors, add the currently acquired enhancer to the agent factory Proxyfactory, and then set the current agent's proxy target object to the current bean, Finally, the dynamic agent factory of the JDK is created according to the configuration, or the dynamic agent factory of Cglib, then returns proxyfactory
/** * Create An AOP proxy for the given bean. * @param beanclass The class of the bean * @param beanname The name of the bean * @param specificinterceptors the set of I Nterceptors that was * specific to this bean (could be empty, and NOT NULL) * @param targetsource the Targetsource for the PR Oxy, * already pre-configured to access the beans * @return The AOP proxy for the bean * @see #buildAdvisors */protected Ob Ject Createproxy (class<?> beanclass, String beanname, object[] specificinterceptors, Targetsource TargetSource) { Proxyfactory proxyfactory = new Proxyfactory ()//Copy Our properties (Proxytargetclass etc) inherited from PROXYCONFIG.PR Oxyfactory.copyfrom (This): if (!shouldproxytargetclass (Beanclass, Beanname)) {//must allow for introductions; can ' t Just set interfaces to//the target ' s interfaces only. class<?>[] targetinterfaces = Classutils.getallinterfacesforclass (Beanclass, This.proxyClassLoader); for ( Class<?> targetinterface:targetinterfaces) {proxyfactory.aDdinterface (Targetinterface);}} Advisor[] Advisors = buildadvisors (Beanname, specificinterceptors); for (Advisor advisor:advisors) { Proxyfactory.addadvisor (advisor);} Proxyfactory.<strong>settargetsource</strong> (Targetsource); customizeproxyfactory (proxyFactory); Proxyfactory.setfrozen (This.freezeproxy); if (advisorsprefiltered ()) {proxyfactory.setprefiltered (true);} Return Proxyfactory.getproxy (This.proxyclassloader);}
AOP Dynamic Agent ExecutionWith respect to the dynamic agent execution of AOP, there are two main ways for the dynamic agent of the JDK and the dynamic agent of Cglib, if the two agents are not very familiar with it, it is recommended to look at a blog I wrote before the Java agent (static agent and JDK Dynamic agent and Cglib proxy) There is a description of the use of these two agents and implementation methods.
Next, we take a look at the implementation of the AOP dynamic Agent selection method, first on the core implementation code:
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 PR Oxy creation. "); if (Targetclass.isinterface ()) {return new Jdkdynamicaopproxy (config);} return Cglibproxyfactory.createcglibproxy (config);} else {return new Jdkdynamicaopproxy (config);}}
Spring JDK Dynamic Proxy implementation
In the third step above or depending on the user's configuration (for example, if you configured theProxytargetclassproperty is True), select the type of proxy created, this proxy type is divided into two implementations, are more efficient, the following according to the JDK dynamic agent to illustrate the implementation of AOP, but also first jdkdynamicaopproxy the core code invoke method:
public object invoke (object proxy, Method method, object[] args) throwsthrowable {methodinvocation invocation = nul L Object oldproxy = null; Boolean setproxycontext = false; Targetsource Targetsource = This.advised.targetSource; Class targetclass = null; Object target = null; The try {//eqauls () method, with the target object, does not implement this method if (!this.equalsdefined && Aoputils.isequalsmethod (method)) { Return (Equals (Args[0])? Boolean.TRUE:Boolean.FALSE); }//hashcode () method, with the target object not implemented by this method if (!this.hashcodedefined && Aoputils.ishashcodemethod (method)) { Return Newinteger (Hashcode ()); The//advised interface, or the method defined in its parent interface, directly reflects the call without applying the notification if (!this.advised.opaque &&method.getdeclaringclass (). Isinterface () &&method.getdeclaringclass (). IsAssignableFrom (Advised.class)) {// Service invocations onproxyconfig with the proxy conFig ... Return aoputils.invokejoinpointusingreflection (This.advised,method, args); } Object retVal = null; if (this.advised.exposeProxy) {//Make invocation available ifnecessary. Oldproxy = Aopcontext.setcurrentproxy (proxy); Setproxycontext = true; }//Get the target object's class target = Targetsource.gettarget (); if (target! = null) {Targetclass = Target.getclass (); }//Gets the list of interceptor that can be applied to this method lists chain = This.advised.getInterceptorsAndDynamicInterceptionAdvice (M Ethod,targetclass); If there is no notification (interceptor) that can be applied to this method, this direct reflection calls Method.invoke (target, args) if (Chain.isempty ()) {RetVal = Aoputils.invokejoinpointusingreflection (Target,method, args); } else {//create methodinvocation invocation = Newreflectivemethodinvocation (proxy, Target, meth OD, args, TARGETCL, chain); RetVal = Invocation.proceed (); }//Massage return value if necessary. if (retVal! = null && RetVal = = Target &&method.getreturntype (). Isinstance (proxy) & &! RawTargetAccess.class.isAssignableFrom (Method.getdeclaringclass ())) {//Special case:it returned ' this ' an D The return type of the method//Is type-compatible. Notethat we can ' t help if the target sets//a reference to itself Inanother returned object. RetVal = proxy; } return retVal; } finally {if (target! = null &&!targetsource.isstatic ()) {//must has come fromtarge TSource. Targetsource.releasetarget (target); } if (Setproxycontext) {//Restore old proxy. Aopcontext.setcurrentproxy (Oldproxy); } } }
In fact, the above comments are more clear, the instructions for each step: 1) Get interceptor 2) determine if the interceptor chain is empty, if it is empty, call the Tangent Method 3) If the interceptor is not empty then create the Reflectivemethodinvocation class, The Interceptor method is encapsulated inside, that is, the executionGetinterceptorsanddynamicinterceptionadvice Method
Public list<object> Getinterceptorsanddynamicinterceptionadvice (method, Class targetclass) { Methodcachekeycachekey = new Methodcachekey (method); list<object>cached = This.methodCache.get (CacheKey); if (cached = = null) { cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice (This , method, Targetclass); This.methodCache.put (cachekey,cached); } returncached; }
4) In factThe actual acquisition is actually done by Advisorchainfactory. Getinterceptorsanddynamicinterceptionadvice () This method to complete, the obtained results will be cached, the following to analyze the implementation of this method:
/** * Get the Advisor list from the provided configuration instance config and traverse through the advisors. If it's introductionadvisor, * Determines whether the advisor can be applied to the target class Targetclass. If it is pointcutadvisor, then judge * Whether this advisor can be applied to the target method. The advisor that meets the criteria is converted to the Interceptor list by Advisoradaptor. */Publiclist Getinterceptorsanddynamicinterceptionadvice (advised config, Methodmethod, Class targetclass) {//This is somewhat t Ricky. We have to process introductions first,//and we need to preserve order in the ultimate list. List interceptorlist = new ArrayList (config.getadvisors (). length); See if it contains introductionadvisor boolean hasintroductions = Hasmatchingintroductions (Config,targetclass); Here you actually register a series of Advisoradapter to convert the advisor to Methodinterceptor Advisoradapterregistry Registry = Globaladvisoradapterregistry.getinstance (); Advisor[] Advisors = config.getadvisors (); for (int i = 0; I <advisors.length; i++) {Advisor advisor = Advisors[i]; if (Advisor instanceof Pointcutadvisor) { Add it conditionally. Pointcutadvisor pointcutadvisor= (pointcutadvisor) advisor; if (config.isprefiltered () | | | Pointcutadvisor.getpointcut (). Getclassfilter (). Matches (Targetclass)) {//todo: The location of the two methods can be interchanged in this place. Convert Advisor to Interceptor methodinterceptor[]interceptors = Registry.getinterceptors (adviso R); Checks whether the pointcut of the current advisor can match the current method methodmatcher mm =pointcutadvisor.getpointcut (). Getmethodmatcher (); if (Methodmatchers.matches (Mm,method, Targetclass, hasintroductions)) {if (mm.is Runtime ()) {//Creating a NewObject instance in the Getinterceptors () method Isn ' t a problemas we normally cache created chains. for (INTJ = 0; J < Interceptors.length; J + +) {Interceptorlist.add (new InterceptoranddyNamicmethodmatcher (interceptors[j],mm)); }} else {Interceptorlist.addall (arrays.aslist (interceptors)); }}}} else if (advisor instanceof Introductionadvisor) { Introductionadvisor ia = (introductionadvisor) advisor; if (config.isprefiltered () | | ia.getclassfilter (). Matches (Targetclass)) {interceptor[] interceptors= re Gistry.getinterceptors (advisor); Interceptorlist.addall (Arrays.aslist (interceptors)); }} else {interceptor[] interceptors =registry.getinterceptors (advisor); Interceptorlist.addall (Arrays.aslist (interceptors)); }} return interceptorlist;}
5) After completion of this method, the advisor configured in advised to be applied to the connection point or target class is all converted to Methodinterceptor.
6) Next to the proceed method in the Invoke method, we look at how the interceptor chain works, that is, the execution of the proceed method.
Public Object Proceed () throws Throwable {//We start with an index Of-1and increment early. if (This.currentinterceptorindex = = This.interceptorsAndDynamicMethodMatchers.size ()-1) {//If Interceptor is done, then Execute Joinpoint return invokejoinpoint (); } Object Interceptororinterceptionadvice = This.interceptorsAndDynamicMethodMatchers.get (++this.currentin Terceptorindex); If you want to dynamically match Joinpoint if (interceptororinterceptionadvice instanceof Interceptoranddynamicmethodmatcher) {// Evaluate Dynamic Method Matcher here:static part would already has//been evaluated and found to match. Interceptoranddynamicmethodmatcher DM = (interceptoranddynamicmethodmatcher) Interceptororinterceptiona Dvice; Dynamic match: Whether the run-time parameter satisfies the match condition if (dm.methodMatcher.matches (This.method, this.targetclass,this.arguments)) { Executes the current Intercetpor Returndm.interceptor.invoKe (this); } else {//If the dynamic match fails, skip the current Intercetpor and call the next interceptor return proceed (); }} else {//It ' s an interceptor, so we just invoke it:the Pointcutwill has//been Eva Luated statically before this object is constructed. Executes the current Intercetpor return ((methodinterceptor) interceptororinterceptionadvice). Invoke (this); }}
7) Okay, the interceptor can be executed here, and the complex agent can finally play his part. Spring Cglib Dynamic Proxy implementationBecause of the Cglib dynamic Agent code is relatively long, in this does not post out the code, in fact, the implementation of the two agents are similar, are created method call chain, different is the JDK dynamic agent created isreflectivemethodinvocationCall chain, while Cglib creates a cglibmethodinvocation。
Analysis of Spring source code (vi) Analysis of the implementation principle of AOP