最近新公司在用Spring MVC,跟踪Spring的Service发现是通过动态代理来实现的,而公司的事务是配置在Service层。所以想看下Spring 的AOP的具体实现。本文源码基于Spring 4.0。
We can use debug to track the overall process of a service call, and we can clearly see the process processing:
Cglibaopproxy.intercept method in which the method is passed
this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Gets a list intercept chain, and then passes
newmethod, args, targetClass, chain, methodProxy).proceed();
Method executes the corresponding interception chain to perform the processing.
Finally passed,
method, retVal);
Processes the return value and returns.
The whole process is fully implemented by dynamic Proxy mode. The main code is as follows:
@Override PublicObject Intercept (object proxy,Method Method,Object[]args,Methodproxy Methodproxy)throws Throwable {Object oldproxy = null; Boolean setproxycontext = false; Class<?> targetclass = null; Object target = null; try {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,inch Case it comes from a Pool...Target=Gettarget(); if(Target! = null){targetclass = Target.getclass (); }List<object> chain = This.advised.getInterceptorsAndDynamicInterceptionAdvice ( method, targetclass);Object RetVal;//Check Whether we only has 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 is a invokerinterceptor, so we know//it does nothing but a refle Ctive operation on the target, and no hot//swapping or fancy proxying. RetVal = Methodproxy.invoke (target, args); } Else {//We need to create a method invocation ... 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); }} }
The code looks very simple.
Below we can analyze the code line by row.
Method Signature:
publicMethod method, Object[] args, MethodProxy methodProxy) throws Throwable
As you can see, the method is passed in to the proxy object, the method object, the parameters, and the method proxy. Methodproxy contains the method information that we need to be proxied, including the method full signature.
if (this.advised.exposeProxy) { if necessary. oldProxy = AopContext.setCurrentProxy(proxy); settrue; }
Here the code is to determine whether the agent is available, presumably this means that the code here is not very clear.
target = getTarget(); ifnull) { targetClass = target.getClass(); }
Here is the Get target class object.
Down is to get the corresponding chain object.
List<object> chain = This.advised.getInterceptorsAndDynamicInterceptionAdvice ( method, targetclass); PublicList<object> Getinterceptorsanddynamicinterceptionadvice ( method , Class<?> targetclass) {Methodcachekey CacheKey = 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); }return cached; }
In this method, the more important thing is to obtain the CacheKey.
/** * Simple wrapper class around a Method. Used as the key when * caching methods, for efficient equals and hashcode comparisons. */ Private Static class methodcachekey { Private FinalMethod method;Private Final intHashcode; Public Methodcachekey(method) { This. method = method; This. hashcode = Method.hashcode (); }@Override Public Boolean equals(Object Other) {if(Other = = This) {return true; } Methodcachekey OtherKey = (methodcachekey) Other;return( This. method = = Otherkey.method); }@Override Public int hashcode() {return This. hashcode; } }}
This class is relatively simple and defines the corresponding method object, as well as the corresponding hashcode of method.
this.methodCache.get(cacheKey); /** Cache with Method as key and advisor chain List as value */ privatetransient Map<MethodCacheKey, List<Object>> methodCache;
The corresponding interceptor chain is cached here. If NULL, it is instantiated by factory and placed in the cache.
if(Cached = =NULL) {cached = This. Advisorchainfactory.getinterceptorsanddynamicinterceptionadvice ( This, method, Targetclass); This. Methodcache.put (CacheKey, cached); }@Override PublicList<object>Getinterceptorsanddynamicinterceptionadvice(advised config, method, class<?> Targetclass) {//This is somewhat tricky ... We have to process introductions first, //But we need to preserve order in the ultimate list.List<object> interceptorlist =NewArraylist<object> (Config.getadvisors (). length); Class<?> Actualclass = (Targetclass! =NULL? TargetClass:method.getDeclaringClass ());Booleanhasintroductions = hasmatchingintroductions (config, actualclass); Advisoradapterregistry registry = Globaladvisoradapterregistry.getinstance (); for(Advisor advisor:config.getAdvisors ()) {if(AdvisorinstanceofPointcutadvisor) {//Add it conditionally.Pointcutadvisor pointcutadvisor = (pointcutadvisor) advisor;if(config.isprefiltered () | | pointcutadvisor.getpointcut (). Getclassfilter (). Matches (Actualclass)) {methodinterceptor[] interceptors = registry.getinterceptors (advisor); Methodmatcher mm = Pointcutadvisor.getpointcut (). Getmethodmatcher ();if(Methodmatchers.matches (mm, method, Actualclass, hasintroductions)) {if(Mm.isruntime ()) {//Creating A new object instance in the Getinterceptors () method //isn ' t a problem as we normally cache created chains. for(Methodinterceptor interceptor:interceptors) {Interceptorlist.add (NewInterceptoranddynamicmethodmatcher (Interceptor, MM)); } }Else{Interceptorlist.addall (Arrays.aslist (interceptors)); } } } }Else if(AdvisorinstanceofIntroductionadvisor) {Introductionadvisor ia = (introductionadvisor) advisor;if(config.isprefiltered () | | ia.getclassfilter (). Matches (Actualclass)) {interceptor[] interceptors = registry.getinterceptors (advisor); Interceptorlist.addall (Arrays.aslist (interceptors)); } }Else{interceptor[] interceptors = registry.getinterceptors (advisor); Interceptorlist.addall (Arrays.aslist (interceptors)); } }returnInterceptorlist; }
Although this method is very long, the actual content is relatively simple.
First, get all the pointcuts.
config.getAdvisors()
Determine the different pointcut types and put them in the list.
Executes the corresponding list method.
RetVal =NewCglibmethodinvocation (proxy, Target, method, args, Targetclass, chain, Methodproxy). proceed ();@Override PublicObjectProceed()throwsThrowable {//We start with an index of-1 and increment early. if( This. Currentinterceptorindex = = This. Interceptorsanddynamicmethodmatchers.size ()-1) {returnInvokejoinpoint (); } Object Interceptororinterceptionadvice = This. Interceptorsanddynamicmethodmatchers.get (+ + This. Currentinterceptorindex);if(InterceptororinterceptionadviceinstanceofInterceptoranddynamicmethodmatcher) {//Evaluate dynamic method Matcher here:static part would already has //been evaluated and found to match.Interceptoranddynamicmethodmatcher DM = (interceptoranddynamicmethodmatcher) Interceptororinterceptiona Dvice;if(Dm.methodMatcher.matches ( This. method, This. Targetclass, This. arguments)) {returnDm.interceptor.invoke ( This); }Else{//Dynamic matching failed. //Skip This interceptor and invoke the next in the chain. returnProceed (); } }Else{//It's an interceptor, so we just invoke it:the pointcut would have //been evaluated statically before this object is constructed. return((Methodinterceptor) interceptororinterceptionadvice). Invoke ( This); } }
To process the return value:
method, retVal); return retVal;
AOP Source Analysis-cglibaopproxy Dynamicadvisedinterceptor