zoukankan      html  css  js  c++  java
  • 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一、AOP的核心概念回顾

    https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#aop

    我们先来看一下下面的这张图

     

    说明:

    程序运行时会调用很多方法,调用的很多方法就叫做Join points(连接点,可以被选择来进行增强的方法点),在方法的前或者后选择一个地方来切入,切入的的地方就叫做Pointcut(切入点,选择增强的方法),然后把要增强的功能(Advice)加入到切入点所在的位置。Advice和Pointcut组成一个切面(Aspect)

    AOP的几个概念:

    Advice、Pointcut、Weaving的特点:

    Advice(功能增强):

    1)用户性:由用户提供增强功能的逻辑代码

    2)变化的:不同的增强需求,会有不同的逻辑

    3)可选时机:可选择在方法前、后、异常时进行功能增强

    4)多重的:同一个切入点上可以有多重增强 

    Pointcut(切入点):

    1)用户性:由用户来指定

    2)变化的:用户可灵活指定

    3)多点性:用户可以选择在多个点上进行功能增强

    Weaving(织入):

    1)无侵入性,因为不改变原类的代码

    2)我们在框架中实现

    二、Spring中AOP的用法

    1. 传统Advisor方式

    掌握用法:
    1)编程提供Advice,实现对应的Advice接口
    2)配置Advisor(advice+pointcut)

    Advice接口:

    示例代码:

    被增强的目标对象:

    BeanQ

    package com.study.leesmall.spring.sample.aop;
    
    //被增强的目标对象
    public class BeanQ {
    
        public void do1(String task, int time) {
            System.out.println("-------------do1 do " + task + " time:" + time);
        }
    
        public String service1(String name) {
            System.out.println("-------------servce1 do " + name);
            return name;
        }
    
        public String service2(String name) {
            System.out.println("-------------servce2 do " + name);
            if (!"s1".equals(name)) {
                throw new IllegalArgumentException("参数 name != s1, name=" + name);
            }
    
            return name + " hello!";
        }
    }

     编程提供Advice,实现对应的Advice接口:

    前置增强:

    MyBeforeAdvice

    package com.study.leesmall.spring.sample.aop;
    
    import java.lang.reflect.Method;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    //前置增强
    public class MyBeforeAdvice implements MethodBeforeAdvice {
    
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("------ MyBeforeAdvice before 增强 " + target + " " + method);
        }
    
    }

    环绕增强:

    MyArroundAdvice

    package com.study.leesmall.spring.sample.aop;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    //环绕增强
    public class MyArroundAdvice implements MethodInterceptor {
    
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("--------- 环绕 -前增强");
            Object ret = invocation.proceed();
            System.out.println("--------- 环绕 -后增强");
            return ret;
        }
    
    }

    在/spring-source-study/src/main/java/com/study/leesmall/spring/sample/aop/application.xml里面配置Advisor(advice+pointcut):

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <!-- 传统方式的aop begin -->
        <!--被增强的目标对象  -->
        <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
        
        <!--配置advice  -->
        <bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />
        <bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />
        
        <!--配置pointcut  -->
        <aop:config >

    <!--全局切入点,任何一个aop-config都可以使用 --> <aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" /> <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" /> <aop:advisor advice-ref="yyArroundAdvice" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/> </aop:config> <!-- 传统方式的aop end --> </beans>

    配置文件里面的注意点:

    <aop:config> 的属性了解

    proxy-target-class="false"使用jdk的动态代理 默认配置
    proxy-target-class="true"使用cglib的动态代理

     

    掌握 spring Aop 的 API:

    Advice:

    Advisor的代码:

     * Copyright 2002-2017 the original author or authors.
    
    package org.springframework.aop;
    
    import org.aopalliance.aop.Advice;
    
    /**
     * Base interface holding AOP <b>advice</b> (action to take at a joinpoint)
     * and a filter determining the applicability of the advice (such as
     * a pointcut). <i>This interface is not for use by Spring users, but to
     * allow for commonality in support for different types of advice.</i>
     *
     * <p>Spring AOP is based around <b>around advice</b> delivered via method
     * <b>interception</b>, compliant with the AOP Alliance interception API.
     * The Advisor interface allows support for different types of advice,
     * such as <b>before</b> and <b>after</b> advice, which need not be
     * implemented using interception.
     *
     * @author Rod Johnson
     * @author Juergen Hoeller
     */
    public interface Advisor {
    
        /**
         * Common placeholder for an empty {@code Advice} to be returned from
         * {@link #getAdvice()} if no proper advice has been configured (yet).
         * @since 5.0
         */
        Advice EMPTY_ADVICE = new Advice() {};
    
    
        /**
         * Return the advice part of this aspect. An advice may be an
         * interceptor, a before advice, a throws advice, etc.
         * @return the advice that should apply if the pointcut matches
         * @see org.aopalliance.intercept.MethodInterceptor
         * @see BeforeAdvice
         * @see ThrowsAdvice
         * @see AfterReturningAdvice
         */
        Advice getAdvice();
    
        /**
         * Return whether this advice is associated with a particular instance
         * (for example, creating a mixin) or shared with all instances of
         * the advised class obtained from the same Spring bean factory.
         * <p><b>Note that this method is not currently used by the framework.</b>
         * Typical Advisor implementations always return {@code true}.
         * Use singleton/prototype bean definitions or appropriate programmatic
         * proxy creation to ensure that Advisors have the correct lifecycle model.
         * @return whether this advice is associated with a particular target instance
         */
        boolean isPerInstance();
    
    }

     

    Pointcut:

     

    Pointcut的子类:

    Advisor的子类:

     

    PointcutAdvisor扩展了Advisor以后就会有一个切面Aspect=Advice+Pointcut

    PointcutAdvisor的代码:

     * Copyright 2002-2012 the original author or authors.
    
    package org.springframework.aop;
    
    /**
     * Superinterface for all Advisors that are driven by a pointcut.
     * This covers nearly all advisors except introduction advisors,
     * for which method-level matching doesn't apply.
     *
     * @author Rod Johnson
     */
    public interface PointcutAdvisor extends Advisor {
    
        /**
         * Get the Pointcut that drives this advisor.
         */
        Pointcut getPointcut();
    
    }

    PointcutAdvisor的子类:

    测试类:

    AopMain

    package com.study.leesmall.spring.sample.aop;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.GenericXmlApplicationContext;
    
    public class AopMain {
        public static void main(String[] args) {
            ApplicationContext context = new GenericXmlApplicationContext(
                    "classpath:com/study/leesmall/spring/sample/aop/application.xml");
    
            BeanQ bq = context.getBean(BeanQ.class);
            bq.do1("task1", 20);
            System.out.println();
    
            bq.service1("service1");
    
            System.out.println();
            bq.service2("ssss");
        }
    }

    测试结果:

    ------ MyBeforeAdvice before 增强 com.study.leesmall.spring.sample.aop.BeanQ@1d119efb public void com.study.leesmall.spring.sample.aop.BeanQ.do1(java.lang.String,int)
    -------------do1 do task1 time:20
    
    --------- 环绕 -前增强
    -------------servce1 do service1
    --------- 环绕 -后增强
    
    --------- 环绕 -前增强
    -------------servce2 do ssss
    Exception in thread "main" java.lang.IllegalArgumentException: 参数 name != s1, name=ssss
        at com.study.leesmall.spring.sample.aop.BeanQ.service2(BeanQ.java:18)
        at com.study.leesmall.spring.sample.aop.BeanQ$$FastClassBySpringCGLIB$$3d1515ac.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at com.study.leesmall.spring.sample.aop.MyArroundAdvice.invoke(MyArroundAdvice.java:12)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
        at com.study.leesmall.spring.sample.aop.BeanQ$$EnhancerBySpringCGLIB$$2fc5343.service2(<generated>)
        at com.study.leesmall.spring.sample.aop.AopMain.main(AopMain.java:18)

    2. Aspect语法方式

    Aspect的advice是基于方法的。

    掌握用法:

    1)定义包含Advice方法的Bean类

    2)配置Bean定义

    3)配置Aspect(引用包含advice方法的bean),在里面配置各种Advice(method+pointcut)

    定义包含Advice方法的Bean类:

    AspectAdviceBean:

    package com.study.leesmall.spring.sample.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    
    //定义包含 Advice 方法的 Bean 类
    public class AspectAdviceBean {
    
        public void before1() {
            System.out.println("----------- AspectAdviceBean before1 增强 ");
        }
    
        //JoinPoint看具体哪个方法被增强了,JoinPoint一定要放在第一个参数
        public void before2(JoinPoint jp) {
            System.out.println("----------- AspectAdviceBean before2 增强 for " + jp);
        }
    
        //调用被增强的方法时传参数
        //<aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) 
        //and args(tk,..)" arg-names=""/>
        //args(tk,..)有两个意思,第一个意思是被增强的方法的第一个参数的类型要和before3的参数tk的类型一样
        //第二个意思是被增强的方法的第一个参数tk要赋值给before3的参数tk
        //arg-names="" 当不能确定方法参数的顺序时可以用这个参数指定arg-names="param1,param2"
        public void before3(String tk) {
            System.out.println("----------- AspectAdviceBean before3  增强  参数tk= " + tk);
        }
    
        //调用被增强的方法时传参数
        public void before4(String tk, int ti) {
            System.out.println("----------- AspectAdviceBean before4  增强  参数tk= " + tk + " ti=" + ti);
        }
    
        //ProceedingJoinPoint正在处理的方法
        public Object arround1(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("----------- AspectAdviceBean arround1 环绕-前增强 for " + pjp);
            Object ret = pjp.proceed();
            System.out.println("----------- AspectAdviceBean arround1 环绕-后增强 for " + pjp);
            return ret;
        }
    
        public Object arround2(ProceedingJoinPoint pjp, String name) throws Throwable {
            System.out.println("--------- AspectAdviceBean arround2 参数 name=" + name);
            System.out.println("----------- AspectAdviceBean arround2 环绕-前增强 for " + pjp);
            Object ret = pjp.proceed();
            System.out.println("----------- AspectAdviceBean arround2 环绕-后增强 for " + pjp);
            return ret;
        }
    
        public void afterReturning(Object retValue) {
            System.out.println("----------- AspectAdviceBean afterReturning 增强 , 返回值为: " + retValue);
        }
    
        public void afterThrowing(JoinPoint jp, Exception e) {
            System.out.println("----------- AspectAdviceBean afterThrowing 增强  for " + jp);
            System.out.println("----------- AspectAdviceBean afterThrowing 增强  异常 :" + e);
        }
    
        public void after(JoinPoint jp) {
            System.out.println("----------- AspectAdviceBean after 增强  for " + jp);
        }
    
    }

    JoinPoint和ProceedingJoinPoint :

    在/spring-source-study/src/main/java/com/study/leesmall/spring/sample/aop/application.xml里面配置Bean定义

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <!-- 传统方式的aop begin -->
        <!--被增强的目标对象  -->
        <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
        
        <!--配置advice  -->
        <bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />
        <bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />
        
        <!--配置pointcut  -->
        <aop:config >
            <aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" />
            <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />
            
            <aop:advisor advice-ref="yyArroundAdvice"
                pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/>
        </aop:config>
        <!-- 传统方式的aop end -->
        
        <!-- AspectJ的aop begin -->
        <!-- 配置了包含advice方法的Bean -->
        <bean id="aspectAdviceBean" class="com.study.leesmall.spring.sample.aop.AspectAdviceBean" />
    
        
    </beans>
        
        
        

    3)配置Aspect(引用包含advice方法的bean),在里面配置各种Advice(method+pointcut)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <!-- 传统方式的aop begin -->
        <!--被增强的目标对象  -->
        <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
        
        <!--配置advice  -->
        <bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />
        <bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />
        
        <!--配置pointcut  -->
        <aop:config >
            <aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" />
            <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />
            
            <aop:advisor advice-ref="yyArroundAdvice"
                pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/>
        </aop:config>
        <!-- 传统方式的aop end -->
        
        <!-- AspectJ的aop begin -->
        <!-- 配置了包含advice方法的Bean -->
        <bean id="aspectAdviceBean" class="com.study.leesmall.spring.sample.aop.AspectAdviceBean" />
        
        <!--配置 Aspect (引用包含 advice 方法的 bean),在里面配置各种 Advice(method + pointcut)  -->
        <aop:config>
            <aop:pointcut id="services" expression="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))" />
            <aop:aspect id="a1" ref="aspectAdviceBean" order="1">
                <aop:before method="before1" pointcut-ref="doMethods" />
                <aop:before method="before2" pointcut-ref="doMethods"/>
                <!--args(tk,..)有两个意思,第一个意思是被增强的方法的第一个参数的类型要和before3的参数tk的类型一样
                第二个意思是被增强的方法的第一个参数tk要赋值给before3的参数tk;
                arg-names="" 当不能确定方法参数的顺序时可以用这个参数指定arg-names="param1,param2"
                  -->
                <aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,..)"/>
                <aop:before method="before4" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,ti)"/>
                <aop:around method="arround1" pointcut-ref="services"/>
                <aop:around method="arround2" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..)) and args(name)"/>
                <aop:after-returning method="afterReturning" pointcut-ref="services" returning="retValue"/>
                <aop:after-throwing method="afterThrowing" pointcut-ref="services" throwing="e"/>
                <aop:after method="after" pointcut-ref="services"/>
            </aop:aspect>
        </aop:config>
        <!-- AspectJ的aop end -->
    
        
    </beans>
        
        
        

    被增强的目标对象:

    package com.study.leesmall.spring.sample.aop;
    
    //被增强的目标对象
    public class BeanQ {
    
        public void do1(String task, int time) {
            System.out.println("-------------do1 do " + task + " time:" + time);
        }
    
        public String service1(String name) {
            System.out.println("-------------servce1 do " + name);
            return name;
        }
    
        public String service2(String name) {
            System.out.println("-------------servce2 do " + name);
            /**if (!"s1".equals(name)) {
                throw new IllegalArgumentException("参数 name != s1, name=" + name);
            }**/
    
            return name + " hello!";
        }
    }

    测试类:

    AopMain

    package com.study.leesmall.spring.sample.aop;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.GenericXmlApplicationContext;
    
    public class AopMain {
        public static void main(String[] args) {
            ApplicationContext context = new GenericXmlApplicationContext(
                    "classpath:com/study/leesmall/spring/sample/aop/application.xml");
    
            BeanQ bq = context.getBean(BeanQ.class);
            bq.do1("task1", 20);
            System.out.println();
    
            bq.service1("service1");
    
            System.out.println();
            bq.service2("ssss");
        }
    }

    测试结果:

    ----------- AspectAdviceBean before1 增强 
    ----------- AspectAdviceBean before2 增强 for execution(void com.study.mike.spring.sample.aop.BeanQ.do1(leesmall,int))
    ----------- AspectAdviceBean before3  增强  参数tk= task1
    ----------- AspectAdviceBean before4  增强  参数tk= task1 ti=20
    ------ MyBeforeAdvice before 增强 com.study.mike.spring.sample.aop.BeanQ@3a0baae5 public void com.study.mike.spring.sample.aop.BeanQ.do1(java.lang.leesmall,int)
    -------------do1 do task1 time:20
    
    ----------- AspectAdviceBean arround1 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
    --------- AspectAdviceBean arround2 参数 name=service1
    ----------- AspectAdviceBean arround2 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
    --------- 环绕 -前增强
    -------------servce1 do service1
    --------- 环绕 -后增强
    ----------- AspectAdviceBean arround2 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
    ----------- AspectAdviceBean arround1 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
    ----------- AspectAdviceBean afterReturning 增强 , 返回值为: service1
    ----------- AspectAdviceBean after 增强  for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
    
    ----------- AspectAdviceBean arround1 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
    --------- AspectAdviceBean arround2 参数 name=ssss
    ----------- AspectAdviceBean arround2 环绕-前增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
    --------- 环绕 -前增强
    -------------servce2 do ssss
    --------- 环绕 -后增强
    ----------- AspectAdviceBean arround2 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
    ----------- AspectAdviceBean arround1 环绕-后增强 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
    ----------- AspectAdviceBean afterReturning 增强 , 返回值为: ssss hello!
    ----------- AspectAdviceBean after 增强  for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))

    3. AspectJ注解方式

    1)先在pom.xml文件里面引入AspectJ的依赖

            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.1</version>
            </dependency>

    2)被增强的目标对象

    package com.study.leesmall.spring.sample.aop;
    
    //被增强的目标对象
    public class BeanQ {
    
        public void do1(String task, int time) {
            System.out.println("-------------do1 do " + task + " time:" + time);
        }
    
        public String service1(String name) {
            System.out.println("-------------servce1 do " + name);
            return name;
        }
    
        public String service2(String name) {
            System.out.println("-------------servce2 do " + name);
            if (!"s1".equals(name)) {
                throw new IllegalArgumentException("参数 name != s1, name=" + name);
            }
    
            return name + " hello!";
        }
    }

    3)AspectJ注解方式实现AOP

    AspectAdviceBeanUseAnnotation

    package com.study.leesmall.spring.sample.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    
    //AspectJ注解方式
    @Aspect
    public class AspectAdviceBeanUseAnnotation {
    
        // 定义一个全局的Pointcut
        @Pointcut("execution(* com.study.leesmall.spring.sample.aop.*.do*(..))")
        public void doMethods() {
        }
    
        // 定义一个全局的Pointcut
        @Pointcut("execution(* com.study.leesmall.spring.sample.aop.*.service*(..))")
        public void services() {
        }
    
        // 定义一个Before Advice
        @Before("doMethods() and args(tk,..)")
        public void before3(String tk) {
            System.out.println("----------- AspectAdviceBeanUseAnnotation before3  增强  参数tk= " + tk);
        }
    
        //环绕增强
        @Around("services() and args(name,..)")
        public Object around2(ProceedingJoinPoint pjp, String name) throws Throwable {
            System.out.println("--------- AspectAdviceBeanUseAnnotation arround2 参数 name=" + name);
            System.out.println("----------- AspectAdviceBeanUseAnnotation arround2 环绕-前增强 for " + pjp);
            Object ret = pjp.proceed();
            System.out.println("----------- AspectAdviceBeanUseAnnotation arround2 环绕-后增强 for " + pjp);
            return ret;
        }
    
        @AfterReturning(pointcut = "services()", returning = "retValue")
        public void afterReturning(Object retValue) {
            System.out.println("----------- AspectAdviceBeanUseAnnotation afterReturning 增强 , 返回值为: " + retValue);
        }
    
        @AfterThrowing(pointcut = "services()", throwing = "e")
        public void afterThrowing(JoinPoint jp, Exception e) {
            System.out.println("----------- AspectAdviceBeanUseAnnotation afterThrowing 增强  for " + jp);
            System.out.println("----------- AspectAdviceBeanUseAnnotation afterThrowing 增强  异常 :" + e);
        }
    
        @After("doMethods()")
        public void after(JoinPoint jp) {
            System.out.println("----------- AspectAdviceBeanUseAnnotation after 增强  for " + jp);
        }
    
    }

    4)在/spring-source-study/src/main/java/com/study/leesmall/spring/sample/aop/application2.xml里面配置bean和开启AspectJ注解的支持

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <!--被增强的目标对象  -->
        <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
        
        <!--AspectJ注解方式实现的AOP  -->
        <bean id="aspectAdviceBeanUseAnnotation" class="com.study.leesmall.spring.sample.aop.AspectAdviceBeanUseAnnotation" />
        
        <!--开启AspectJ注解的支持  -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
        
    </beans>
        
        
        

    开启@Aspectj注解方式支持:
    Xml中<aop:aspectj-autoproxy></aop:aspectj-autoproxy>注意了解它的属性、及子元素

    注解方式开启:

    @Configuration
    @EnableAspectJAutoProxy
    public class AppConfig {
    }

    5)测试类

    package com.study.leesmall.spring.sample.aop;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.GenericXmlApplicationContext;
    
    public class AopMainUseAspectAnnotation {
        public static void main(String[] args) {
            ApplicationContext context = new GenericXmlApplicationContext(
                    "classpath:com/study/leesmall/spring/sample/aop/application2.xml");
    
            BeanQ bq = context.getBean(BeanQ.class);
            bq.do1("task1", 20);
            System.out.println();
    
            bq.service1("service1");
    
        }
    }

    7)测试结果

    ----------- AspectAdviceBeanUseAnnotation before3  增强  参数tk= task1
    -------------do1 do task1 time:20
    ----------- AspectAdviceBeanUseAnnotation after 增强  for execution(void com.study.leesmall.spring.sample.aop.BeanQ.do1(String,int))
    
    --------- AspectAdviceBeanUseAnnotation arround2 参数 name=service1
    ----------- AspectAdviceBeanUseAnnotation arround2 环绕-前增强 for execution(String com.study.leesmall.spring.sample.aop.BeanQ.service1(String))
    -------------servce1 do service1
    ----------- AspectAdviceBeanUseAnnotation arround2 环绕-后增强 for execution(String com.study.leesmall.spring.sample.aop.BeanQ.service1(String))
    ----------- AspectAdviceBeanUseAnnotation afterReturning 增强 , 返回值为: service1

    三、Spring AOP 源码学习

    1、spring aop的工作流程是怎样?以传统的Advisor配置为例进行思考

    2. 源码阅读思路

    1、先看配置解析,看标签解析过程都做了什么、完成了什么。

    入口:
    E: epositoryorgspringframeworkspring-aop5.1.3.RELEASEspring-aop-5.1.3.RELEASE.jar/META-INF/spring.handlers

    http://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

    org.springframework.aop.config.AopNamespaceHandler:

    a)解析<aop:config> 及它的子元素

    org.springframework.aop.config.ConfigBeanDefinitionParser:

     

    1)注册了autoProxyCreator的Bean定义,它是一个ProxyConfig、BeanFactoryAwrare、BeanPostProcessor

    下面来看一下配置自动代理的创建者的代码:

    org.springframework.aop.config.ConfigBeanDefinitionParser.configureAutoProxyCreator(ParserContext, Element)

     ->

    org.springframework.aop.config.AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(ParserContext, Element)

     ->

    org.springframework.aop.config.AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry, Object)

     ->

    org.springframework.aop.config.AopConfigUtils.registerOrEscalateApcAsRequired(Class<?>, BeanDefinitionRegistry, Object)

    所以说自动代理创建者是一个ProxyConfig、BeanFactoryAwrare、BeanPostProcessor

     ->

    org.springframework.aop.config.AopConfigUtils.findPriorityForClass(String)

     

    说明:

    org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator现在已经不使用了

    org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator对应于AspectJ的xml方式

    <aop:config>
            <aop:pointcut id="services" expression="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))" />
            <aop:aspect id="a1" ref="aspectAdviceBean" order="1">
                <aop:before method="before1" pointcut-ref="doMethods" />
                <aop:before method="before2" pointcut-ref="doMethods"/>
                <!--args(tk,..)有两个意思,第一个意思是被增强的方法的第一个参数的类型要和before3的参数tk的类型一样
                第二个意思是被增强的方法的第一个参数tk要赋值给before3的参数tk;
                arg-names="" 当不能确定方法参数的顺序时可以用这个参数指定arg-names="param1,param2"
                  -->
                <aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,..)" arg-names=""/>
                <aop:before method="before4" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,ti)"/>
                <aop:around method="arround1" pointcut-ref="services"/>
                <aop:around method="arround2" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..)) and args(name)"/>
                <aop:after-returning method="afterReturning" pointcut-ref="services" returning="retValue"/>
                <aop:after-throwing method="afterThrowing" pointcut-ref="services" throwing="e"/>
                <aop:after method="after" pointcut-ref="services"/>
            </aop:aspect>
        </aop:config>

     查看org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator的继承体系:

     父类:

    子类:

    查看org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator的继承体系,搞清楚它继承实现了什么,他的父类和子类有哪些

    父类:

     子类:

    org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator对应于AspectJ的注解方式:

    <bean id="aspectAdviceBeanUseAnnotation" class="com.study.leesamll.spring.sample.aop.AspectAdviceBeanUseAnnotation" />
        
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

     <aop:config proxy-target-class="false" expose-proxy="false">对应代码:

    org.springframework.aop.config.AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(ParserContext, Element)

    ->
    org.springframework.aop.config.AopNamespaceUtils.useClassProxyingIfNecessary(BeanDefinitionRegistry, Element)

    2)两种配置方式(advisor和aspectJ)解析之后都是向Bean工厂注册了Pointcut和Advisor的Bean定义

    b)<aop:aspectj-autoproxy>开启AspectJ注解支持
    1)注册了autoProxyCreator的Bean定义,它是一个ProxyConfig、BeanFactoryAwrare、BeanPostProcessor

    org.springframework.aop.config.AopNamespaceHandler.init()

     

    org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser

    org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser.parse(Element, ParserContext)

    org.springframework.aop.config.AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext, Element)

     

    <!--开启AspectJ注解的支持  -->
        <aop:aspectj-autoproxy>
            <!-- 满足name里面的表达式(bean名称的表达式)的才进行切面的处理 -->
            <aop:include name="bean名称的表达式"/>
        </aop:aspectj-autoproxy>

    <aop:include name="bean名称的表达式"/>对应源码:

     

    c)AspectJ注解的切面在哪里读取加载的?

    可能的地方:

    BeanDefinitionRegistryPostProcessor x

    BeanFactoryPostProcessor x

    InstantiationAwareBeanPostProcessor Bean 实例创建前后

    BeanPostProcessor

    Xml方式的解析:
    org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator
    注解方式的解析:
    org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
    两者的关系:
    public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator

    org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()

     BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors() 中完成了注解的读取、Advisor对象的创建及缓存。

     org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()

     

    org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory.getAdvisors(MetadataAwareAspectInstanceFactory)

     

    2、 看织入的过程

    a) 在哪里做的织入?

    org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()里面打个断点拿到调用栈

    入口:

    com.study.leesmall.spring.sample.aop.AopMainUseAspectAnnotation

     

    b) 如何判断 Bean 要不要被创建代理?如何排除 advice Bean 的?

    排除 advice Bean :

    跳过advice bean 跳过带有@Aspect注解的自己,不能自己为自己创建代理,否则进入死循环,如AspectAdviceBeanUseAnnotation

    org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(Class<?>, String)

     

     再次进来,是在 PostProcessAfterInitialization()

     

    创建代理的方法:

    org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(Object, String, Object)

    如何判断 Bean 要不要被创建代理:

     

    c)如何选择 jdk动态代理 还是cglib的动态代理

    org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(Class<?>, String, Object[], TargetSource)

    org.springframework.aop.framework.ProxyFactory.getProxy(ClassLoader)

    org.springframework.aop.framework.ProxyCreatorSupport.createAopProxy()

    org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(AdvisedSupport)

     

    d) 如何来创建代理的,涉及哪些类,如何协作的。

    AutoProxyCreator

    ProxyConfig ProcxyFactory

    AopProxyFactory

    AopProxy

    3 、看方法被调用时的增强过程

    a) 在代理中如何决定对当前方法合格的 advice 的?
      调用的 Advisor 中 Pointcut 进行匹配
    b) 如何组织多个 advice 执行的?
      责任链模式

    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Object, Method, Object[])

    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()

    4、源码对应的类图

  • 相关阅读:
    C. 1D Sokoban 二分,思维
    E. Almost Fault-Tolerant Database 暴力枚举 + 分类讨论 + 思维 Codeforces Round #704 (Div. 2)
    Tkinter(六):Checkbutton 复选按钮
    LeetCode260. 只出现一次的数字 III
    LeetCode297. 二叉树的序列化与反序列化
    LeetCode300. 最长上升子序列
    LeetCode299. 猜数字游戏
    LeetCode295. 数据流的中位数
    你真的知道嵌入式系统的优先级吗?
    学习4412开发板gdb和gdbserver的调试
  • 原文地址:https://www.cnblogs.com/leeSmall/p/10236553.html
Copyright © 2011-2022 走看看