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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

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):在方法抛出异常退出时执行的通知

20191102100777\_1.png

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.源码分析

20191102100777\_2.png

(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);  
    3} else {  
    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


来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring源码分析-AOP实现原理分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏