zoukankan      html  css  js  c++  java
  • SpringAop代理对象调用过程(八)

      之前研究了AOP代理对象的创建过程以及注入到Spring的过程,接下来研究AOP的调用过程。

    0. 代码同上一节的测试代码

    1. pom

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>cn.qz</groupId>
        <artifactId>springlearn</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.1.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <name>springlearn</name>
        <!-- FIXME change it to the project's website -->
        <url>http://www.example.com</url>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <!-- 引入 spring aop 依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
                <plugins>
                    <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
                    <plugin>
                        <artifactId>maven-clean-plugin</artifactId>
                        <version>3.1.0</version>
                    </plugin>
                    <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
                    <plugin>
                        <artifactId>maven-resources-plugin</artifactId>
                        <version>3.0.2</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <version>3.8.0</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>2.22.1</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-jar-plugin</artifactId>
                        <version>3.0.2</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-install-plugin</artifactId>
                        <version>2.5.2</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-deploy-plugin</artifactId>
                        <version>2.8.2</version>
                    </plugin>
                    <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
                    <plugin>
                        <artifactId>maven-site-plugin</artifactId>
                        <version>3.7.1</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-project-info-reports-plugin</artifactId>
                        <version>3.0.0</version>
                    </plugin>
                </plugins>
            </pluginManagement>
        </build>
    </project>

    2.  Aop相关配置类: 这里使用两种方式实现AOP

    (1) @Aspect 相关

    package cn.qz.aop;
    
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    @Aspect // 表示该类是一个通知类
    @Component // 交给spring管理
    public class MyAdvice {
    
        // 定义一个空方法,借用其注解抽取切点表达式
        @Pointcut("execution(* cn.qz..*.*(..))")
        public void pc() {
        }
    
        // 前置通知
        @Before("MyAdvice.pc()")
        public void before(JoinPoint joinPoint) throws Exception {
            System.out.println("---------------前置通知开始~~~~~~~~~~~");
            // 获取到类名
            String targetName = joinPoint.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到方法名
            String methodName = joinPoint.getSignature().getName();
            System.out.println("增强的方法是:" + methodName);
            // 获取到参数
            Object[] parameter = joinPoint.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取字节码对象
            Class<?> targetClass = Class.forName(targetName);
            // 获取所有的方法
            Method[] methods = targetClass.getMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == parameter.length) {
                        System.out.println("找到这个方法");
                        //处理一些业务逻辑
                        break;
                    }
                }
            }
            System.out.println("---------------前置通知结束~~~~~~~~~~~");
        }
    
        // 后置通知(异常发生后不会调用)
        @AfterReturning("MyAdvice.pc()")
        public void afterRunning() {
            System.out.println("这是后置通知(异常发生后不会调用)");
        }
    
        // 环绕通知(推荐下面这种方式获取方法)
        @Around("MyAdvice.pc()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("----------------环绕通知之前 的部分----------------");
            // 获取到类名
            String targetName = pjp.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到参数
            Object[] parameter = pjp.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取到方法签名,进而获得方法
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            System.out.println("增强的方法名字是:" + method.getName());
            //处理一些业务逻辑
    
    
            // 获取参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            System.out.println("参数类型是:" + parameterTypes.toString());
    
            //让方法执行(proceed是方法的返回结果,可以针对返回结果处理一下事情)
            System.out.println("--------------方法开始执行-----------------");
            Object proceed = pjp.proceed();
    
            //环绕通知之后的业务逻辑部分
            System.out.println("----------------环绕通知之后的部分----------------");
            return proceed;
        }
    
        // 异常通知
        @AfterThrowing("MyAdvice.pc()")
        public void afterException() {
            System.out.println("这是异常通知(发生异常后调用)~~~~~~~~~~~");
        }
    
        // 最终通知(发生异常也会在最终调用)
        @After("MyAdvice.pc()")
        public void after() {
            System.out.println("这是后置通知(发生异常也会在最终调用)");
        }
    }

    (2) 基于类继承实现AOP

    增强类:

    package cn.qz.aop;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    public class MyInterceptor implements MethodInterceptor {
    
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println(invocation.getMethod() + "==方法执行前==");
            Object proceed = invocation.proceed();
            System.out.println(invocation.getArguments() + "--方法执行后--");
            return proceed;
        }
    
    }

    切点:

    package cn.qz.aop;
    
    import org.springframework.aop.ClassFilter;
    import org.springframework.aop.MethodMatcher;
    import org.springframework.aop.Pointcut;
    
    import java.lang.reflect.Method;
    
    public class MyPoint implements Pointcut {
    
        @Override
        public ClassFilter getClassFilter() {
            return new ClassFilter() {
                @Override
                public boolean matches(Class<?> clazz) {
                    if (clazz != null && clazz.getName().contains("cn.qz")) {
                        return true;
                    }
    
                    return false;
                }
            };
        }
    
        @Override
        public MethodMatcher getMethodMatcher() {
            return new MethodMatcher() {
                /**
                 * 判断方法是否匹配
                 */
                @Override
                public boolean matches(Method method, Class<?> targetClass, Object... args) {
                    return "test".equals(method.getName());
                }
    
                /**
                 * 判断方法是否匹配
                 */
                @Override
                public boolean matches(Method method, Class<?> targetClass) {
                    return "test".equals(method.getName());
                }
    
                @Override
                public boolean isRuntime() {
                    return false;
                }
            };
        }
    }

    切面:

    package cn.qz.aop;
    
    import org.aopalliance.aop.Advice;
    import org.springframework.aop.Pointcut;
    import org.springframework.aop.PointcutAdvisor;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyAdvicer implements PointcutAdvisor {
    
        @Override
        public Advice getAdvice() {
            return new MyInterceptor();
        }
    
        @Override
        public boolean isPerInstance() {
            return true;
        }
    
        @Override
        public Pointcut getPointcut() {
            return new MyPoint();
        }
    
    }

    3. 测试类:

    DeptDao

    package cn.qz.user;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class DeptDao {
    
        public void test() {
            System.out.println("test");
        }
    }

    UserDao接口:

    package cn.qz.user;
    
    public interface UserDao {
    
        void addUser();
    }

    UserDaoImpl实现类:

    package cn.qz.user;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserDaoImpl implements  UserDao{
        @Override
        public void addUser() {
            System.out.println("addUser");
        }
    }

    1. 研究Cglib代理的调用过程

       这里以DeptDao类为例研究其调用过程,其没有实现接口默认会用Cglib代理。

    package cn.qz;
    
    import cn.qz.user.DeptDao;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @ComponentScan
    @EnableAspectJAutoProxy
    public class App {
        public static void main(String[] args) {
            //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西
            System.setProperty("cglib.debugLocation", "G:/proxy");
    
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class);
            DeptDao bean = applicationContext.getBean(DeptDao.class);
            bean.test();
        }
    }

    打印日志如下:

    public void cn.qz.user.DeptDao.test()==方法执行前==
    ----------------环绕通知之前 的部分----------------
    代理的类是:cn.qz.user.DeptDao
    传入的参数是:[]
    增强的方法名字是:test
    参数类型是:[Ljava.lang.Class;@33990a0c
    --------------方法开始执行-----------------
    ---------------前置通知开始~~~~~~~~~~~
    代理的类是:cn.qz.user.DeptDao
    增强的方法是:test
    传入的参数是:[]
    找到这个方法
    ---------------前置通知结束~~~~~~~~~~~
    test
    这是后置通知(异常发生后不会调用)
    这是后置通知(发生异常也会在最终调用)
    ----------------环绕通知之后的部分----------------
    [Ljava.lang.Object;@429bffaa--方法执行后--

      接下来研究其AOP调用过程。

    1. 打断点查看applicationContext.getBean(DeptDao.class); 的返回类的信息

     2. 反编译查看代理类信息,主要查看test方法(可以通过arthas查看,也可以通到导出的代理类查看)

        public final void test() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                var10000.intercept(this, CGLIB$test$0$Method, CGLIB$emptyArgs, CGLIB$test$0$Proxy);
            } else {
                super.test();
            }
        }

     可以看到核心的方法是获取到内部属性CGLIB$CALLBACK_0,  然后调用其intercept 方法。

    3. 断点打到org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept

    源码如下:

            @Override
            @Nullable
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                Object oldProxy = null;
                boolean setProxyContext = false;
                Object target = null;
                TargetSource targetSource = this.advised.getTargetSource();
                try {
                    if (this.advised.exposeProxy) {
                        // Make invocation available if necessary.
                        oldProxy = AopContext.setCurrentProxy(proxy);
                        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();
                    Class<?> targetClass = (target != null ? target.getClass() : null);
                    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                    Object retVal;
                    // Check whether we only have 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 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 = methodProxy.invoke(target, argsToUse);
                    }
                    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 && !targetSource.isStatic()) {
                        targetSource.releaseTarget(target);
                    }
                    if (setProxyContext) {
                        // Restore old proxy.
                        AopContext.setCurrentProxy(oldProxy);
                    }
                }
            }

    4.  获取到的chain  链如下:

     查看其获取方式:

    1》org.springframework.aop.framework.AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice

        public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable 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;
        }

    2 》 org.springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice

        @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();
            List<Object> interceptorList = new ArrayList<>(advisors.length);
            Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
            Boolean hasIntroductions = null;
    
            for (Advisor advisor : advisors) {
                if (advisor instanceof PointcutAdvisor) {
                    // Add it conditionally.
                    PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                    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);
                            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(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                                }
                            }
                            else {
                                interceptorList.addAll(Arrays.asList(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;
        }

    断点查看信息:

    2.1》registry如下:

    2.2》 advisors如下:

    2.3》MethodInterceptor[] interceptors = registry.getInterceptors(advisor); 会根据advisor处理到对应的MethodInteceptor

    org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#getInterceptors

        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) {
                if (adapter.supportsAdvice(advice)) {
                    interceptors.add(adapter.getInterceptor(advisor));
                }
            }
            if (interceptors.isEmpty()) {
                throw new UnknownAdviceTypeException(advisor.getAdvice());
            }
            return interceptors.toArray(new MethodInterceptor[0]);
        }

    adapters如下:

    判断是否满足是以是否是某个类型来判断,比如:org.springframework.aop.framework.adapter.MethodBeforeAdviceAdapter#supportsAdvice 

        public boolean supportsAdvice(Advice advice) {
            return (advice instanceof MethodBeforeAdvice);
        }

    可以看到是获取到Advisor里面的Advise(MethodInterceptor)

     2.4》经过处理后返回的interceptorList 如下:

     5. 接下来就是用获取到的chain来进行调用 retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

    org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation#proceed

            public Object proceed() throws Throwable {
                try {
                    return super.proceed();
                }
                catch (RuntimeException ex) {
                    throw ex;
                }
                catch (Exception ex) {
                    if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) {
                        throw ex;
                    }
                    else {
                        throw new UndeclaredThrowableException(ex);
                    }
                }
            }

    调用到父类也就是调用org.springframework.aop.framework.ReflectiveMethodInvocation#proceed

        @Override
        @Nullable
        public Object proceed() throws Throwable {
            // We start with an index of -1 and increment early.
            if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                return invokeJoinpoint();
            }
    
            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);
            }
        }

    代码解释:

    (1) 下面代码先判断是否是最后最后一个inteceptor,currentInterceptorIndex 一开始是-1。

            if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                return invokeJoinpoint();
            }

     (2) 下面代码获取到链条中的inteceptor进行调用

            Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

    interceptorsAndDynamicMethodMatchers 就是之前获取到的chain,如下:

    (3) 接下来走((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);,第一次会到org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke 方法

        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            MethodInvocation oldInvocation = invocation.get();
            invocation.set(mi);
            try {
                return mi.proceed();
            }
            finally {
                invocation.set(oldInvocation);
            }
        }

    可以看到这里面没做深恶业务处理然后调用到mi.proceed(), mi 也就是上面的ReflectiveMethodInvocation 对象(这样可以实现责任链模式)

     (4) 接下来继续到org.springframework.aop.framework.ReflectiveMethodInvocation#proceed 方法,当前的advisor下标+1并获取到advisor,也就是MyInteceptor

     接下来调用到cn.qz.aop.MyInterceptor#invoke,如下:

        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println(invocation.getMethod() + "==方法执行前==");
            Object proceed = invocation.proceed();
            System.out.println(invocation.getArguments() + "--方法执行后--");
            return proceed;
        }

    (5) 打印完信息之后继续调用invocation.proceed() 方法,也就是递归调用org.springframework.aop.framework.ReflectiveMethodInvocation#proceed, 继续调用下一个advisor的方法,也就是

     接下来调用其invoke方法:org.springframework.aop.aspectj.AspectJAroundAdvice#invoke

        public Object invoke(MethodInvocation mi) throws Throwable {
            if (!(mi instanceof ProxyMethodInvocation)) {
                throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
            }
            ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
            ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
            JoinPointMatch jpm = getJoinPointMatch(pmi);
            return invokeAdviceMethod(pjp, jpm, null, null);
        }

    接下来调用到:org.springframework.aop.aspectj.AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs

        protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
            Object[] actualArgs = args;
            if (this.aspectJAdviceMethod.getParameterCount() == 0) {
                actualArgs = null;
            }
            try {
                ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
                // TODO AopUtils.invokeJoinpointUsingReflection
                return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
            }
            catch (IllegalArgumentException ex) {
                throw new AopInvocationException("Mismatch on arguments to advice method [" +
                        this.aspectJAdviceMethod + "]; pointcut expression [" +
                        this.pointcut.getPointcutExpression() + "]", ex);
            }
            catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }

    aspectJAdviceMethod 方法也就是我们的切面方法。如下:

     接下来通过方法的invoke调用到cn.qz.aop.MyAdvice#around

        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("----------------环绕通知之前 的部分----------------");
            // 获取到类名
            String targetName = pjp.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到参数
            Object[] parameter = pjp.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取到方法签名,进而获得方法
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            System.out.println("增强的方法名字是:" + method.getName());
            //处理一些业务逻辑
    
    
            // 获取参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            System.out.println("参数类型是:" + parameterTypes.toString());
    
            //让方法执行(proceed是方法的返回结果,可以针对返回结果处理一下事情)
            System.out.println("--------------方法开始执行-----------------");
            Object proceed = pjp.proceed();
    
            //环绕通知之后的业务逻辑部分
            System.out.println("----------------环绕通知之后的部分----------------");
            return proceed;
        }

    pjp如下:

    pjp.proceed() 调用到:org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint#proceed(),源码如下:

        @Override
        public Object proceed() throws Throwable {
            return this.methodInvocation.invocableClone().proceed();
        }

    this.methodInvocation.invocableClone() 也就是上面的ReflectiveMethodInvocation对象:

     (6) 接下来继续返回到org.springframework.aop.framework.ReflectiveMethodInvocation#proceed 处理下一个advisor

    接下来调用到:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor#invoke

        public Object invoke(MethodInvocation mi) throws Throwable {
            this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
            return mi.proceed();
        }

    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());调用到org.springframework.aop.aspectj.AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs

        protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
            Object[] actualArgs = args;
            if (this.aspectJAdviceMethod.getParameterCount() == 0) {
                actualArgs = null;
            }
            try {
                ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
                // TODO AopUtils.invokeJoinpointUsingReflection
                return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
            }
            catch (IllegalArgumentException ex) {
                throw new AopInvocationException("Mismatch on arguments to advice method [" +
                        this.aspectJAdviceMethod + "]; pointcut expression [" +
                        this.pointcut.getPointcutExpression() + "]", ex);
            }
            catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }

    这里会调到cn.qz.aop.MyAdvice#before。

    调用完之后继续下一个调用链的执行。

    (7) 加下来下标为4的advisor开始执行

    调用 org.springframework.aop.aspectj.AspectJAfterAdvice#invoke

        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                return mi.proceed();
            }
            finally {
                invokeAdviceMethod(getJoinPointMatch(), null, null);
            }
        }

        这个方法先调用 调用org.springframework.aop.framework.ReflectiveMethodInvocation#proceed处理下一个,也就是先调用进行下面的(8)操作。

        然后返回之前finally调用 invokeAdviceMethod 调用我们定义的方法,和上面一个套路,调用到org.springframework.aop.aspectj.AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs,然后调到自定义的方法

    (8)  org.springframework.aop.framework.ReflectiveMethodInvocation#proceed 处理下标为5的调用链

     org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor#invoke

        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            Object retVal = mi.proceed();
            this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
            return retVal;
        }

    这里和上面套路一样,先调用下一个调用链,然后调用自定义的方法

    (9) org.springframework.aop.framework.ReflectiveMethodInvocation#proceed 处理下标为6的调用链

     调用到: org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#invoke

        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                return mi.proceed();
            }
            catch (Throwable ex) {
                if (shouldInvokeOnThrowing(ex)) {
                    invokeAdviceMethod(getJoinPointMatch(), null, ex);
                }
                throw ex;
            }
        }

    可以看到先调用下一个advisor发生异常后调用自己的异常通知

    (10) 接下来继续调用org.springframework.aop.framework.ReflectiveMethodInvocation#proceed

    这时候没有可用的调用器链,则会调用invokeJoinpoint 方法,会调到: org.springframework.aop.framework.ReflectiveMethodInvocation#invokeJoinpoint

            @Override
            protected Object invokeJoinpoint() throws Throwable {
                if (this.methodProxy != null) {
                    return this.methodProxy.invoke(this.target, this.arguments);
                }
                else {
                    return super.invokeJoinpoint();
                }
            }

    接着调用org.springframework.cglib.proxy.MethodProxy#invoke

        public Object invoke(Object obj, Object[] args) throws Throwable {
            try {
                init();
                FastClassInfo fci = fastClassInfo;
                return fci.f1.invoke(fci.i1, obj, args);
            }
            catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
            catch (IllegalArgumentException ex) {
                if (fastClassInfo.i1 < 0)
                    throw new IllegalArgumentException("Protected method: " + sig1);
                throw ex;
            }
        }

    fci.f1是下面对象:

      实际里面就是反射调用对应的方法了。

    (11) 反射调用完方法之后会一直回调刚才未调用的方法以及环绕通知后半部分的逻辑等过程。

    补充:再增加一个切面查看其advisor通知链

    package cn.qz.aop;
    
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    @Aspect // 表示该类是一个通知类
    @Component // 交给spring管理
    public class MyAdvice2 {
    
        // 定义一个空方法,借用其注解抽取切点表达式
        @Pointcut("execution(* cn.qz..*.*(..))")
        public void pc() {
        }
    
        // 前置通知
        @Before("cn.qz.aop.MyAdvice2.pc()")
        public void before(JoinPoint joinPoint) throws Exception {
            System.out.println("---------------前置通知开始~~~~~~~~~~~");
            // 获取到类名
            String targetName = joinPoint.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到方法名
            String methodName = joinPoint.getSignature().getName();
            System.out.println("增强的方法是:" + methodName);
            // 获取到参数
            Object[] parameter = joinPoint.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取字节码对象
            Class<?> targetClass = Class.forName(targetName);
            // 获取所有的方法
            Method[] methods = targetClass.getMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == parameter.length) {
                        System.out.println("找到这个方法");
                        //处理一些业务逻辑
                        break;
                    }
                }
            }
            System.out.println("---------------前置通知结束~~~~~~~~~~~");
        }
    
        // 后置通知(异常发生后不会调用)
        @AfterReturning("cn.qz.aop.MyAdvice2.pc()")
        public void afterRunning() {
            System.out.println("这是后置通知(异常发生后不会调用)");
        }
    
        // 环绕通知(推荐下面这种方式获取方法)
        @Around("cn.qz.aop.MyAdvice2.pc()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("----------------环绕通知之前 的部分----------------");
            // 获取到类名
            String targetName = pjp.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到参数
            Object[] parameter = pjp.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取到方法签名,进而获得方法
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            System.out.println("增强的方法名字是:" + method.getName());
            //处理一些业务逻辑
    
    
            // 获取参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            System.out.println("参数类型是:" + parameterTypes.toString());
    
            //让方法执行(proceed是方法的返回结果,可以针对返回结果处理一下事情)
            System.out.println("--------------方法开始执行-----------------");
            Object proceed = pjp.proceed();
    
            //环绕通知之后的业务逻辑部分
            System.out.println("----------------环绕通知之后的部分----------------");
            return proceed;
        }
    
        // 异常通知
        @AfterThrowing("cn.qz.aop.MyAdvice2.pc()")
        public void afterException() {
            System.out.println("这是异常通知(发生异常后调用)~~~~~~~~~~~");
        }
    
        // 最终通知(发生异常也会在最终调用)
        @After("cn.qz.aop.MyAdvice2.pc()")
        public void after() {
            System.out.println("这是后置通知(发生异常也会在最终调用)");
        }
    }

    查看调用链:chain

     打印的日志如下:

    public void cn.qz.user.DeptDao.test()==方法执行前==
    ----------------环绕通知之前 的部分----------------
    代理的类是:cn.qz.user.DeptDao
    传入的参数是:[]
    增强的方法名字是:test
    参数类型是:[Ljava.lang.Class;@1755e85b
    --------------方法开始执行-----------------
    ---------------前置通知开始~~~~~~~~~~~
    代理的类是:cn.qz.user.DeptDao
    增强的方法是:test
    传入的参数是:[]
    找到这个方法
    ---------------前置通知结束~~~~~~~~~~~
    ----------------环绕通知之前 的部分----------------
    代理的类是:cn.qz.user.DeptDao
    传入的参数是:[]
    增强的方法名字是:test
    参数类型是:[Ljava.lang.Class;@736d6a5c
    --------------方法开始执行-----------------
    ---------------前置通知开始~~~~~~~~~~~
    代理的类是:cn.qz.user.DeptDao
    增强的方法是:test
    传入的参数是:[]
    找到这个方法
    ---------------前置通知结束~~~~~~~~~~~
    test
    这是后置通知(异常发生后不会调用)
    这是后置通知(发生异常也会在最终调用)
    ----------------环绕通知之后的部分----------------
    这是后置通知(异常发生后不会调用)
    这是后置通知(发生异常也会在最终调用)
    ----------------环绕通知之后的部分----------------
    [Ljava.lang.Object;@7ce97ee5--方法执行后--

     2. JDK代理调用规则

     这个查看其生成时的InvocationHandler类如下:org.springframework.aop.framework.JdkDynamicAopProxy#invoke

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
    
            TargetSource targetSource = this.advised.targetSource;
            Object target = null;
    
            try {
                if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                    // The target does not implement the equals(Object) method itself.
                    return equals(args[0]);
                }
                else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                    // The target does not implement the hashCode() method itself.
                    return hashCode();
                }
                else if (method.getDeclaringClass() == DecoratingProxy.class) {
                    // There is only getDecoratedClass() declared -> dispatch to proxy config.
                    return AopProxyUtils.ultimateTargetClass(this.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);
                }
    
                Object retVal;
    
                if (this.advised.exposeProxy) {
                    // Make invocation available if necessary.
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    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();
                Class<?> targetClass = (target != null ? target.getClass() : null);
    
                // Get the interception chain for this method.
                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);
                }
                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())) {
                    // 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()) {
                    throw new AopInvocationException(
                            "Null return value from advice does not match primitive return type for: " + method);
                }
                return retVal;
            }
            finally {
                if (target != null && !targetSource.isStatic()) {
                    // Must have come from TargetSource.
                    targetSource.releaseTarget(target);
                }
                if (setProxyContext) {
                    // Restore old proxy.
                    AopContext.setCurrentProxy(oldProxy);
                }
            }
        }

       可以看到其调用过程最终也会调到org.springframework.aop.framework.ReflectiveMethodInvocation#proceed:(调用过程和上面一样使用责任链模式进行调用)

        public Object proceed() throws Throwable {
            // We start with an index of -1 and increment early.
            if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                return invokeJoinpoint();
            }
    
            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);
            }
        }

    补充:调用过程图表展示,以一开始的调用过程为例

    程序的入口是:org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept。首先调用getInterceptorsAndDynamicInterceptionAdvice获取到 inteceptors 链条(chain), 然后进行链条的调用,大致过程如下

       可以看到调用目标方法是在chain链条调完之后才调用的,之所以能实现后置、环绕等的效果,是因为方法的递归调用;调用完目标方法之后会处理通知中没有处理的代码逻辑。也就是实现了整个链式效果。chian相当于个调度器,在调度整个链条的处理。

     补充: 一般也不会都用到这五种通知,一般一个Around 通知就够用了,比如Spring的事务

    org.springframework.transaction.interceptor.TransactionInterceptor#invoke

        public Object invoke(MethodInvocation invocation) throws Throwable {
            // Work out the target class: may be {@code null}.
            // The TransactionAttributeSource should be passed the target class
            // as well as the method, which may be from an interface.
            Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    
            // Adapt to TransactionAspectSupport's invokeWithinTransaction...
            return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
        }

    接着调用org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

        protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
                final InvocationCallback invocation) throws Throwable {
    
            // If the transaction attribute is null, the method is non-transactional.
            TransactionAttributeSource tas = getTransactionAttributeSource();
            final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
            final PlatformTransactionManager tm = determineTransactionManager(txAttr);
            final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
            if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
                // Standard transaction demarcation with getTransaction and commit/rollback calls.
                TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    
                Object retVal;
                try {
                    // This is an around advice: Invoke the next interceptor in the chain.
                    // This will normally result in a target object being invoked.
                    retVal = invocation.proceedWithInvocation();
                }
                catch (Throwable ex) {
                    // target invocation exception
                    completeTransactionAfterThrowing(txInfo, ex);
                    throw ex;
                }
                finally {
                    cleanupTransactionInfo(txInfo);
                }
                commitTransactionAfterReturning(txInfo);
                return retVal;
            }
    
            else {
                Object result;
                final ThrowableHolder throwableHolder = new ThrowableHolder();
    
                // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
                try {
                    result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
                        TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                        try {
                            return invocation.proceedWithInvocation();
                        }
                        catch (Throwable ex) {
                            if (txAttr.rollbackOn(ex)) {
                                // A RuntimeException: will lead to a rollback.
                                if (ex instanceof RuntimeException) {
                                    throw (RuntimeException) ex;
                                }
                                else {
                                    throw new ThrowableHolderException(ex);
                                }
                            }
                            else {
                                // A normal return value: will lead to a commit.
                                throwableHolder.throwable = ex;
                                return null;
                            }
                        }
                        finally {
                            cleanupTransactionInfo(txInfo);
                        }
                    });
                }
                catch (ThrowableHolderException ex) {
                    throw ex.getCause();
                }
                catch (TransactionSystemException ex2) {
                    if (throwableHolder.throwable != null) {
                        logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                        ex2.initApplicationException(throwableHolder.throwable);
                    }
                    throw ex2;
                }
                catch (Throwable ex2) {
                    if (throwableHolder.throwable != null) {
                        logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                    }
                    throw ex2;
                }
    
                // Check result state: It might indicate a Throwable to rethrow.
                if (throwableHolder.throwable != null) {
                    throw throwableHolder.throwable;
                }
                return result;
            }
        }

      可以看到核心的逻辑是自己操作的,前面先准备了事务相关信息,然后try(操作)、catch(事务回滚)、finally(清除事务相关资源) 进行操作

    【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
  • 相关阅读:
    pch文件配置出现 Expected unqualified-id 和 Unkown type name 'NSString'
    App Store Connect Operation Error ERROR ITMS-90032: "Invalid Image Path
    Xcode面板的使用
    KVO的使用
    苹果开发者账号注册-您在注册时提供的地址无效或者不完整
    Apple导出p12证书 导出证书为p12 Apple开发
    iOS开发-导出profile文件
    App Store提交审核报错 ERROR ITMS-90087解决办法
    Win10 的操作中心如果不见了
    什么时候调用dealloc
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/14461186.html
Copyright © 2011-2022 走看看