zoukankan      html  css  js  c++  java
  • spring aop 源码分析(二) 代理方法的执行过程分析

    在上一篇aop源码分析时,我们已经分析了一个bean被代理的详细过程,参考:https://www.cnblogs.com/yangxiaohui227/p/13266014.html

    本次主要是分析目标方法的执行过程: 测试代码在git : https://gitee.com/yangxioahui/aopdemo.git

    主要代码如下:

    public interface Calc {
        Integer add(int num1,int num2);
    }
    @Component
    public class MyMath implements Calc {
    
       public Integer add(int num1,int num2){
            return num1+num2;
        }
    
    }
    @Aspect
    @Component
    public class MyAspectJ {
        @Pointcut(" execution(* com.yang.xiao.hui.aop.MyMath.*(..))")
        private void pointcut(){}
        @Before(value = "pointcut()")
        public void before(JoinPoint joinPoint){
            System.out.println("before。。。。。");
        }
        @After(value = "pointcut()")
        public void after(JoinPoint joinPoint){
            System.out.println("after。。。。");
        }
    
        @AfterReturning(value = "pointcut()")
        public void afterReturning(){
            System.out.println("afterReturning。。。");
        }
    
        @AfterThrowing(value = "pointcut()")
        public void afterThrowing(JoinPoint joinPoint){
            System.out.println("afterThrowing。。。。");
        }
    
        @Around(value ="pointcut()")
        public void around(ProceedingJoinPoint joinPoint){
            System.out.println("around-before。。。。");
            try {
                Object proceed = joinPoint.proceed();
                System.out.println("around-after_return。。。");
            } catch (Throwable throwable) {
                System.out.println("around-throw。。。"+throwable.getMessage());
            }
            System.out.println("around-after。。。");
        }
    }
    @Configuration
    @ComponentScan("com.yang.xiao.hui.aop")
    @EnableAspectJAutoProxy()
    public class App 
    {
        public static void main( String[] args )
        {
    
            ApplicationContext ctx = new AnnotationConfigApplicationContext(App.class);
            Calc myMath = (Calc)ctx.getBean("myMath");
            myMath.add(3,5);
            System.out.println(ctx.getBean("myMath").getClass());
    
    
        }
    }

    测试: MyMath  类的add 方法执行过程,debug 调试:

     

     上一篇源码分析,我们已经分析过,最终会被JdkDynamicAopProxy 的invoke方法调用,下面分析下该方法:

    @Override
        @Nullable
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
    
            TargetSource targetSource = this.advised.targetSource; //里面封装了被代理对象,也就是MyMath
            Object target = null; //被代理对象
    
            try {
                if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { //处理equals方法
                    // The target does not implement the equals(Object) method itself.
                    return equals(args[0]);
                }
                else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { //处理Hashcode 方法
                    // The target does not implement the hashCode() method itself.
                    return hashCode();
                }
                else if (method.getDeclaringClass() == DecoratingProxy.class) {//显然,我们的方法定义所在的类是Calc 
                    // There is only getDecoratedClass() declared -> dispatch to proxy config.
                    return AopProxyUtils.ultimateTargetClass(this.advised);
                }
             //this.advised.opaque 这个值是说代理类能否强转为Advised,flase是可以强转,在代理对象创建源码时分析过了,代理对象确实实现了Advised接口
    //method.getDeclaringClass().isInterface() 我们的目标方法定义所在类是Calc,确实是一个接口,但该接口没有实现Advised,所以下面方法不会进入
    else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); //这里是直接反射调用方法: method.invoke(target, args); } Object retVal; if (this.advised.exposeProxy) { //@EnableAspectJAutoProxy 注解的一个属性,为true时,就会将代理对象 // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy);//将当前的代理对象存到ThreadLocal中 setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. target = targetSource.getTarget(); //获取被代理类的实例,在这里是MyMath的实例 Class<?> targetClass = (target != null ? target.getClass() : null); //在这里是com.yang.xiao.hui.aop.MyMath // Get the interception chain for this method. 将advisors封装成MethodInterceptor,在代理对象源码的博客介绍过,@before等注解标注的方法会被封装成advisor,所以这里取出来,封装一下,下面会详细分析该方法 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // 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 = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); ////这里是直接反射调用方法: method.invoke(target, args); } else { // We need to create a method invocation... MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); //反射创建一个方法执行器,这里后续会分析,用到了责任链模式 // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed();//开始调用责任链 } // Massage return value if necessary. Class<?> returnType = method.getReturnType(); //获取方法返回值类型 if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { //特殊情况,责任链调用的返回值就是被代理对象(target),而方法的返回类型要求的是代理对象Proxy,这样就将代理对象返回去就可以了 // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { //如果方法的返回类型不是void,并且是基本类型,那么,如果执行调用后,拿到的返回值是空,就抛异常 throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal;//返回得到的结果 } finally { if (target != null && !targetSource.isStatic()) { //如果目标代理对象不为空,同时每次调用targetSource.getTarget()返回的对象不同,也就是非单例的情况下需要释放目标对象 // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); //方法调用完毕,将ThreadLocal中的代理对象设置回之前的那个 } } }

    //通过上面的分析,目标方法在执行时,主要分两步,取得拦截链,调用拦截链:

    先分析: List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 其中AdvisedSupport: advised拥有被代理对象和advisors,在代理对象创建源码分析时,有分析过

     下面分析获取chain的过程:

    @Override
        public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
                Advised config, Method method, @Nullable Class<?> targetClass) {
    
            // This is somewhat tricky... We have to process introductions first,
            // but we need to preserve order in the ultimate list.
            AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
            Advisor[] advisors = config.getAdvisors();  // config 就是AdvisedSupport,它拥有advisors,在代理对象创建过程中分析过
            List<Object> interceptorList = new ArrayList<>(advisors.length); //要将advisor转成方法拦截器,才能起到拦截的作用
            Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());//被代理类
            Boolean hasIntroductions = null;
    
            for (Advisor advisor : advisors) {
                if (advisor instanceof PointcutAdvisor) { //根据advisor的类型不一样,转成MethodInterceptor 逻辑也不一样
                    // Add it conditionally.
                    PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
               //config.isPreFiltered()都是返回true,代表advisors是否已经是过滤过的了
    if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {//主要通过切入点表达式再次校验目标类是否需要被切入 MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();// 如果根据切入点表达式判断了类需要被切入,那之后就要判断方法是否需要切入,因为一个类有很多方法,并不是所有方法都需要被切入 boolean match; if (mm instanceof IntroductionAwareMethodMatcher) { //根据方法匹配器的类型,使用不同发匹配方式 if (hasIntroductions == null) { hasIntroductions = hasMatchingIntroductions(advisors, actualClass); } match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions); } else { match = mm.matches(method, actualClass); } if (match) { MethodInterceptor[] interceptors = registry.getInterceptors(advisor); //如果advisor 匹配上了,那就要封装成方法拦截器 if (mm.isRuntime()) { /mm的类型主要有2种,一种是动态方法匹配器,永远返回true,一种是静态方法匹配器,永远返回false // 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(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); //这里做了一个简单的封装 } } else { interceptorList.addAll(Arrays.asList(interceptors)); //将所有符合要求的interceptors 存起来 } } } } else if (advisor instanceof IntroductionAdvisor) { //逻辑跟之前分析的差不多 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)); } } return interceptorList; }

    上面的方法主要是根据advisor的类型,使用不同的方法匹配器,看看目标的方法是否可以使用该advisor,是的话,就封装成MethodInterceptor,看看封装的逻辑:registry.getInterceptors(advisor);

    @Override
        public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
            List<MethodInterceptor> interceptors = new ArrayList<>(3);
            Advice advice = advisor.getAdvice();
            if (advice instanceof MethodInterceptor) {
                interceptors.add((MethodInterceptor) advice);
            }
            for (AdvisorAdapter adapter : this.adapters) { //三种适配器: MethodBeforeAdviceAdapter/ AfterReturningAdviceAdapter/ThrowsAdviceAdapter 看看哪个匹配上,就用哪一种适配器来封装成拦截器
                if (adapter.supportsAdvice(advice)) 
                    interceptors.add(adapter.getInterceptor(advisor));
                }
            }
            if (interceptors.isEmpty()) {
                throw new UnknownAdviceTypeException(advisor.getAdvice());
            }
            return interceptors.toArray(new MethodInterceptor[0]);
        }

     //advisor封装成interceptor的一个例子:

     拦截器链获取分析完毕了,那么,我们接下来分析下,责任链的执行:

     

     之后我们看看:invocation.proceed();

    public Object proceed() throws Throwable {
    //刚开始currentInterceptorIndex=-1
    // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { //如果最后一个拦截器执行完毕后,就直接反射调用目标方法 return invokeJoinpoint(); //method.invoke(target, args); } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); //获取其中一个拦截器 if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { //判断拦截器是不是动态匹配方法的 // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { //动态拦截器,执行时才去匹配 return dm.interceptor.invoke(this); //匹配得上,就调用拦截方法 } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed();//匹配失败,就调用下一个 } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); //如果是静态的匹配的,就直接调用目标方法 } }

    debug调试调用过程:

    先看看我们的链条都有哪些:

     总共有六个拦截器:

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     最后总结:

     springboot对aop的自动装配机制:我们在springboot项目中,没有自己给启动类贴上@EnableAspectJAutoProxy 注解,aop一样生效,原理是有个配置类:

     

    默认值如下:

     我们可以在yml中配置相应的属性:

  • 相关阅读:
    从B树、B+树、B*树谈到R 树
    从LSM-Tree、COLA-Tree谈到StackOverflow、OSQA
    Linux Kafka源码环境搭建
    CentOS 关闭图形用户界面
    部署与管理ZooKeeper(转)
    spark 2.0.0集群安装与hive on spark配置
    hive on spark的坑
    Spark2.0.0源码编译
    hive spark版本对应关系
    CentOS7 设置集群时间同步
  • 原文地址:https://www.cnblogs.com/yangxiaohui227/p/13273001.html
Copyright © 2011-2022 走看看