spring源码分析-AOP实现原理分析

1.简介
2.如何实现
3.源码分析
(1)找入口
(2)ProxyFactory类的getProxy
(3)JdkDynamicAopProxy类的getProxy(ClassLoader classLoader)
(4)JdkDynamicAopProxy类的invoke方法
(5)Advised.getInterceptorsAndDynamicInterceptionAdvice()
(6)getInterceptorsAndDynamicInterceptionAdvice()
(7)chain
(8)proceed()
4.总结

5.简单实现源码

1.简介

a)切面(Aspect):官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”,在本例中,“切面”就是类TestAspect所关注的具体行为,例如,AServicelmpl.barA()的调用就是切面TestAspect所关注的行为之一。“切面”在ApplicationContext中来配置。(切面就是对于bean的规则)

b)连接点:程序执行过程中的某—行为,例如,UserService.get的调用或者UserService.delete抛出异常等行为。(连接点就是Bean里面方法的规则,规定切面中调用方法的一些规则)

c)通知(Advice):“切面”对于某个“连接点”所产生的动作,例如,TestAspect中对com.spring.service包下所有类的方法进行日志记虽的动作就是一个Advice。其中,一个“切面”可以包含多个“Advice”,例如ServiceAspect

d)切入点(Pointcut):匹配连接点的断言,在AOP中通知和一个切入点表达式关联。例如,TestAspect中的所有通知所关注的连接点,都由切入点表达式execution(
com.spring.service..*(..))来决定。(切入点就是Bean里面的方法,进入切面的一个入口)

e)目标对象(TargetObject):被一个或者多个切面所通知的对象。例如,AServicelmpl和BServicelmpl,当然在实际运行时,SpringAOP采用代理实现,实际AOP操作的是TargetObject的代理对象。(目标对象就是代理需要拿到的对象引用)

f)AOP代理(AOP Proxy):在SpringAOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情況下,TargetQbject实现了接口时,则采用JDK动态代理,例如AServicelmpl反之.采用CGLIB代理,例如,BServiceImpl。强制使用CGLIB代理需要将的proxy-target-class属性设为true。

通知(Advice)的类型
前置通知(Before advice):在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。
后置通知(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
返回后通知(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情況。
环绕通知(around advice):包围一个连接点的通知,类似Web中的Filter

抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知

《spring源码分析-AOP实现原理分析》

2.如何实现
有两种方式
a)配置

 1<aop:aspectj-autoproxy proxy-target-class="true"/>
 2<!-- 声明一个需要织入到虚拟切面的逻辑(切面) -->
 3<bean id="logAspect" class="com.gupaoedu.vip.aop.aspect.LogAspect"></bean>
 4
 5<aop:config>
 6    <aop:aspect ref="logAspect">
 7        <aop:pointcut expression="execution(* com.gupaoedu.vip.aop.service..*(..))" id="logPointcut"/>
 8        <aop:before method="before" pointcut-ref="logPointcut"/>
 9        <aop:after-returning method="afterReturn" returning="boolean"  pointcut-ref="logPointcut"/>
10        <aop:after method="after" pointcut-ref="logPointcut"/>
11        <aop:after-throwing method="afterThrow" pointcut-ref="logPointcut"/>
12    </aop:aspect>
13</aop:config> 

b)注解

 1<aop:aspectj-autoproxy proxy-target-class="true"/>
 2
 3public class LogAspect {
 4    private final static Logger LOG = Logger.getLogger(LogAspect.class);
 5    //声明切点
 6    //因为要利用反射机制去读取这个切面中的所有的注解信息
 7    @Pointcut("execution(* com.gupaoedu.vip.aop.service..*(..))")
 8    public void pointcutConfig(){}
 9
10    @Before("pointcutConfig()")
11    public void before(JoinPoint joinPoint){
12        LOG.info("调用方法之前执行" + joinPoint);
13    }
14    @After("pointcutConfig()")
15    public void after(JoinPoint joinPoint){
16        LOG.info("调用之后执行" + joinPoint);
17    }
18    @AfterReturning(returning="returnValue",value="pointcutConfig()")
19    public void afterReturn(JoinPoint joinPoint,Object returnValue){
20        LOG.info("调用获得返回值" + returnValue);
21    }
22    @AfterThrowing("pointcutConfig()")
23    public void afterThrow(JoinPoint joinPoint){
24        LOG.info("抛出异常之后执行" + joinPoint);
25    }
26}

c)表达式解析

3.源码分析

《spring源码分析-AOP实现原理分析》

(1)找入口

查看源码的第一步就是找入口,由IOC的源码分析可以知道,IOC的入口是Factory,那么我们尝试从Factory入手

(2)ProxyFactory类的getProxy
通过查找我们找到了ProxyFactory类的getProxy方法,并且我们知道代理的实现方式有两种:JDK与CGLIB

1public Object getProxy() {
2//createAopProxy() 这里面对代理的方式进行了选择,如果代理类实现了接口则用JDK代理,否则使用CGLIB
3    return createAopProxy().getProxy();
4}

(3)JdkDynamicAopProxy类的getProxy(ClassLoader classLoader)
继续跟踪getProxy()方法我们可以得到JdkDynamicAopProxy类的getProxy(ClassLoader classLoader)

 1/**  2    * <ol>  3    * <li>获取代理类要实现的接口,除了Advised对象中配置的,还会加上SpringProxy, Advised(opaque=false)  4    * <li>检查上面得到的接口中有没有定义 equals或者hashcode的接口  5    * <li>调用Proxy.newProxyInstance创建代理对象  6    * </ol>  7    */
 8   public Object getProxy(ClassLoader classLoader) {
 9       if (logger.isDebugEnabled()) {
10           logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource());
11       }
12       Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised);
13       findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
14       return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
15}

该方法的目的就是为了获取代理对象,代理对象获得之后,怎么织入切面呢?

(4)JdkDynamicAopProxy类的invoke方法
由代理的实现原理我们知道,代理会实现InvocationHandler接口,那么我们分析一下JdkDynamicAopProxy类的invoke方法

 1publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable {  
 2   MethodInvocation invocation = null;  
 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       //eqauls()方法,具目标对象未实现此方法  
12       if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){  
13            return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);  
14       }  
15
16       //hashCode()方法,具目标对象未实现此方法  
17       if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){  
18            return newInteger(hashCode());  
19       }  
20
21       //Advised接口或者其父接口中定义的方法,直接反射调用,不应用通知  
22       if (!this.advised.opaque &&method.getDeclaringClass().isInterface()  
23                &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {  
24            // Service invocations onProxyConfig with the proxy config...  
25            return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);  
26       }  
27
28       Object retVal = null;  
29
30       if (this.advised.exposeProxy) {  
31            // Make invocation available ifnecessary.  
32            oldProxy = AopContext.setCurrentProxy(proxy);  
33            setProxyContext = true;  
34       }  
35
36       //获得目标对象的类  ,获得之后就可以对类进行修改等操作了
37       target = targetSource.getTarget();  
38       if (target != null) {  
39            targetClass = target.getClass();  
40       }  
41
42       //获取可以应用到此方法上的Interceptor列表  (拦截器链,为环绕通知做准备)
43       List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);  
44
45       //如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args)  
46       if (chain.isEmpty()) {  
47            retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);  
48       } else {  
49            //创建MethodInvocation  
50            invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
51            retVal = invocation.proceed();  
52       }  
53
54       // Massage return value if necessary.  
55       if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)  
56                &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {  
57            // Special case: it returned"this" and the return type of the method  
58            // is type-compatible. Notethat we can't help if the target sets  
59            // a reference to itself inanother returned object.  
60            retVal = proxy;  
61       }  
62       return retVal;  
63   } finally {  
64       if (target != null && !targetSource.isStatic()) {  
65            // Must have come fromTargetSource.  
66           targetSource.releaseTarget(target);  
67       }  
68       if (setProxyContext) {  
69            // Restore old proxy.  
70            AopContext.setCurrentProxy(oldProxy);  
71       }  
72   }  
73}  

主流程可以简述为:获取可以应用到此方法上的通知链(Interceptor Chain),如果有,则应用通知,并执行joinpoint; 如果没有,则直接反射执行joinpoint。而这里的关键是通知链是如何获取的以及它又是如何执行的,下面逐一分析下

(5)Advised.getInterceptorsAndDynamicInterceptionAdvice()
首先,从上面的代码可以看到,通知链是通过Advised.getInterceptorsAndDynamicInterceptionAdvice()这个方法来获取的,我们来看下这个方法的实现:

 1public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {  
 2           MethodCacheKey cacheKey = new MethodCacheKey(method);  
 3           List<Object> cached = this.methodCache.get(cacheKey);  
 4           if(cached == null) {  
 5                    cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(  
 6                                       this,method, targetClass);  
 7                    this.methodCache.put(cacheKey,cached);  
 8           }  
 9           returncached;  
10 }  

可以看到实际的获取工作其实是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()这个方法来完成的,获取到的结果会被缓存。

(6)getInterceptorsAndDynamicInterceptionAdvice()
下面来分析下这个方法的实现:

 1/**   2* 从提供的配置实例config中获取advisor列表,遍历处理这些advisor.如果是IntroductionAdvisor,   3* 则判断此Advisor能否应用到目标类targetClass上.如果是PointcutAdvisor,则判断   4* 此Advisor能否应用到目标方法method上.将满足条件的Advisor通过AdvisorAdaptor转化成Interceptor列表返回.   5*/  
 6publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass{  
 7   // This is somewhat tricky... we have to process introductions first,  
 8   // but we need to preserve order in the ultimate list.  
 9   List interceptorList = new ArrayList(config.getAdvisors().length);  
10
11   //查看是否包含IntroductionAdvisor  
12   boolean hasIntroductions = hasMatchingIntroductions(config,targetClass);  
13
14   //这里实际上注册一系列AdvisorAdapter,用于将Advisor转化成MethodInterceptor  
15   AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();  
16
17   Advisor[] advisors = config.getAdvisors();  
18    for (int i = 0; i <advisors.length; i++) {  
19       Advisor advisor = advisors[i];  
20       if (advisor instanceof PointcutAdvisor) {  
21            // Add it conditionally.  
22            PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor;  
23            if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {  
24                //TODO: 这个地方这两个方法的位置可以互换下  
25                //将Advisor转化成Interceptor  
26                MethodInterceptor[]interceptors = registry.getInterceptors(advisor);  
27
28                //检查当前advisor的pointcut是否可以匹配当前方法  
29                MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher();  
30
31                if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) {  
32                    if(mm.isRuntime()) {  
33                        // Creating a newobject instance in the getInterceptors() method  
34                        // isn't a problemas we normally cache created chains.  
35                        for (intj = 0; j < interceptors.length; j++) {  
36                           interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm));  
37                        }  
38                    } else {  
39                        interceptorList.addAll(Arrays.asList(interceptors));  
40                    }  
41                }  
42            }  
43       } else if (advisor instanceof IntroductionAdvisor){  
44            IntroductionAdvisor ia =(IntroductionAdvisor) advisor;  
45            if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {  
46                Interceptor[] interceptors= registry.getInterceptors(advisor);  
47                interceptorList.addAll(Arrays.asList(interceptors));  
48            }  
49       } else {  
50            Interceptor[] interceptors =registry.getInterceptors(advisor);  
51            interceptorList.addAll(Arrays.asList(interceptors));  
52       }  
53   }  
54   return interceptorList;  
55

这个方法执行完成后,Advised中配置能够应用到连接点或者目标类的Advisor全部被转化成了MethodInterceptor.

(7)chain
接下来我们再看下得到的拦截器链是怎么起作用的。

1if (chain.isEmpty()) {  
2    retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);  
3else {  
4    //创建MethodInvocation  
5    invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
6    retVal = invocation.proceed();  
7

从这段代码可以看出,如果得到的拦截器链为空,则直接反射调用目标方法,否则创建MethodInvocation,调用其proceed方法

(8)proceed()
触发拦截器链的执行,来看下具体代码

 1 public Object proceed() throws Throwable {  
 2       //  We start with an index of -1and increment early.  
 3       if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) {  
 4           //如果Interceptor执行完了,则执行joinPoint  (通知)
 5           return invokeJoinpoint();  
 6       }  
 7
 8       Object interceptorOrInterceptionAdvice =  
 9           this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);  
10
11       //如果要动态匹配joinPoint  (joinPoint 对象注入)
12       if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher){  
13           // Evaluate dynamic method matcher here: static part will already have  
14           // been evaluated and found to match.  
15           InterceptorAndDynamicMethodMatcher dm =  
16                (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;  
17           //动态匹配:运行时参数是否满足匹配条件  
18           if (dm.methodMatcher.matches(this.method, this.targetClass,this.arguments)) {  
19                //执行当前Intercetpor  
20                returndm.interceptor.invoke(this);  
21           }  
22           else {  
23                //动态匹配失败时,略过当前Intercetpor,调用下一个Interceptor  
24                return proceed();  
25           }  
26       }  
27       else {  
28           // It's an interceptor, so we just invoke it: The pointcutwill have  
29           // been evaluated statically before this object was constructed.  
30           //执行当前Intercetpor  
31           return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);  
32       }  
33}  

4.总结
(1)加载配置信息解析成AopConfig(IOC是解析成BeanDefinition)
(2)交给AopProxyFactory,调用一个createAopProxy方法
(3)JdkDynamicAopProxy调用AdvisedSupport的getInsterceptorsAdnDynamicInterceptionAdvice方法得到拦截器链,并保存到一个List容器(IOC容器是Map)
(4)递归执行拦截器proceed()方法

5.简单实现源码
简单源码实现:https://blog.csdn.net/charjay_lin/article/details/80948427

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/CharJay_Lin/article/details/80961446
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞