​[Spring] Spring AOP 实现原理剖析(三)

2019-12-17 18:09:44 浏览数 (1)

手机用户请横屏获取最佳阅读体验,REFERENCES中是本文参考的链接,如需要链接和更多资源,可以关注其他博客发布地址。

平台

地址

CSDN

https://blog.csdn.net/sinat_28690417

简书

https://www.jianshu.com/u/3032cc862300

个人博客

https://yiyuery.github.io/NoteBooks/


正文

[Spring] Spring AOP 实现原理剖析(三)

Spring AOP 切面和切点

经过前文的介绍,我们可以看到AOP的增强可以针对一个类的所有方法进行增强(异常增强除外),那么,如何控制在类的指定方法进行增强?这便是接下来 AOP 的切点和切面的作用。

简言之,AOP编程的最主要工作,就是描述连接点的位置,并在对应位置织入增强。

Spring 通过Pointcut接口描述切点,其类图结果如下:

  • PointCut 通过 ClassFilterMethodMather 构成,通过ClassFilter定位到某些特定方法上。
  • ClassFilter 只有一个入参,用于判断被检测的类是否符合过滤条件。
  • 对于方法匹配,Spring支持两种模式,分别是isRuntime=true(动态方法匹配)和false(静态方法匹配)
    • 静态:
      • 只对方法签名(方法名、入参类型、顺序)进行匹配
      • 只判断一次
    • 动态:
      • 每次运行期检查方法入参的值。
      • 每次方法调用都会判断,对性能影响大,一般情况,不常使用
  • Spring2.0 还支持注解切点(Java5.0的注解定义切点)和表达式切点(字符串表达式)定义切点

切点类型

  • 静态方法切点
  • 动态方法切点
  • 注解切点
  • 表达式切点
  • 流程切点
  • 复合切点

其中:

  1. 静态方法切点匹配通过方法签名进行匹配;
  2. 动态方法切点默认匹配所有类;
  3. 注解切点基于Java5.0注解直接定义切点;
  4. 流程切点根据程序执行堆栈信息查看目标方法是否由某一个方法直接或间接发起调用,以此判断是否为匹配的切点;
  5. 复合切点链式构造切点定义,逐个匹配是否是切点。

切面类型

  • 一般切面
  • 切点切面
  • 引介切面

一般切面 Advisor

仅包含一个Advice,由于Advice本身包含了横切代码和连接点信息,所以其本身也是个简单的切面。

切点切面 PointcutAdvisor

  • 实现类体系
代码语言:javascript复制
public interface PointcutAdvisor extends Advisor {

    /**
     * Get the Pointcut that drives this advisor.
     */
    Pointcut getPointcut();

}

主要实现类:

  • StaticMethodMatcherPointcutAdvisor 静态方法匹配器定义切点的切面,默认情况下匹配所有目标类;
  • AspectJPointcutAdvisor 用于AspectJ语法定义切点的切面,内部定义了个抽象类AbstractAspectJAdvice用于接收增强类
代码语言:javascript复制
public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation, Serializable {

...
    /**
     * Key used in ReflectiveMethodInvocation userAtributes map for the current joinpoint.
     */
    protected static final String JOIN_POINT_KEY = JoinPoint.class.getName();


    /**
     * Lazily instantiate joinpoint for the current invocation.
     * Requires MethodInvocation to be bound with ExposeInvocationInterceptor.
     * <p>Do not use if access is available to the current ReflectiveMethodInvocation
     * (in an around advice).
     * @return current AspectJ joinpoint, or through an exception if we're not in a
     * Spring AOP invocation.
     */
    public static JoinPoint currentJoinPoint() {
        MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
        if (!(mi instanceof ProxyMethodInvocation)) {
            throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: "   mi);
        }
        ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
        JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
        if (jp == null) {
            jp = new MethodInvocationProceedingJoinPoint(pmi);
            pmi.setUserAttribute(JOIN_POINT_KEY, jp);
        }
        return jp;
    }
    //...
  • AspectJExpressionPointcutAdvisor 用于AspectJ切点表达式定义切点的切面
  • DefaultPointcutAdvisor 最常用的切面类型,可以通过任意Pointcut和Advice定义一个切面,但是不支持引介切面,一般可以通过扩展该类实现自定义切面
  • NameMatchMethodPointcutAdvisor 指定方法名定义切点的切面
  • RegexpMethodPointcutAdvisor 对于按照正则表达式匹配方法名进行切点定义的切面,可以通过扩展该类进行操作。内部通过JdkRegexpMethodPointcut构造切点。
代码语言:javascript复制
/**
    * Initialize the singleton Pointcut held within this Advisor.
    */
@Override
public Pointcut getPointcut() {
    synchronized (this.pointcutMonitor) {
        if (this.pointcut == null) {
            this.pointcut = createPointcut();
            if (this.patterns != null) {
                this.pointcut.setPatterns(this.patterns);
            }
        }
        return this.pointcut;
    }
}

/**
    * Create the actual pointcut: By default, a {@link JdkRegexpMethodPointcut}
    * will be used.
    * @return the Pointcut instance (never {@code null})
    */
protected AbstractRegexpMethodPointcut createPointcut() {
    return new JdkRegexpMethodPointcut();
}

引介切面 IntroductionAdvisor

引介切点定义的特殊切面,应用于类层面,扩展类的属性和方法,引介切点使用ClassFilter来定义,顺带提下,其余集中增强对应的切点,都是方法层面的,通过MethodMather来定义。

代码实战

定位切点,构造增强,织入切面

静态普通方法匹配切面

切面定义

代码语言:javascript复制
public class BusinessLogHandlerStaticAdvisor extends StaticMethodMatcherPointcutAdvisor {

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return method.getName().startsWith("simpleAdd");
    }

    @Override
    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            @Override
            public boolean matches(Class<?> clazz) {
                return PersonManagerServiceImpl.class.isAssignableFrom(clazz);
            }
        };
    }
}

Spring 配置

代码语言:javascript复制
<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1、被增强类-->
    <bean id="target" class="com.example.spring.aop.service.impl.PersonManagerServiceImpl"/>
    <!--2、增强类-->
    <bean id="businessLogHandlerBeforeAdvice" class="com.example.spring.aop.advice.BusinessLogHandlerBeforeAdvice"/>
    <!--3、代理工厂定义-->
    <!--3.1 指定代理的接口-->
    <!--3.2 指定使用的增强-->
    <!--3.3 指定对哪个bean进行代理-->
   <!-- <bean id="personManagerImpl" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.example.spring.aop.service.IPersonManagerService"
          p:interceptorNames="businessLogHandlerBeforeAdvice"
          p:target-ref="target"/>-->

    <!--定义切面-->
    <bean id="businessLogHandlerStaticAdvisor" class="com.example.spring.aop.advisor.BusinessLogHandlerStaticAdvisor"
          p:advice-ref="businessLogHandlerBeforeAdvice"/>
    <!--定义公共抽象类-->
    <bean id="parentAdvisor" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="businessLogHandlerStaticAdvisor"
          p:proxyTargetClass="true"
          />
    <bean id="personManagerImpl" parent="parentAdvisor"
    p:target-ref="target"/>

</beans>

注释部分为原先切点定义逻辑,可以看出增加了切面定义,但是切面引入了切点,原来的方式属于增强定义,增强逻辑中本身就包含了连接点信息,即一般普通切面。

测试输出

代码语言:javascript复制
 /**
     * 静态普通方法匹配切面
     */
    @Test
    public void testStaticMethodAdvisor(){
        ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/advisor/aop-before-advice-advisor.xml");
        PersonManagerServiceImpl bean = (PersonManagerServiceImpl) context.getBean("personManagerImpl");
        bean.deletePerson();
        log.info("--------------------------");
        bean.simpleAddPerson();
    }
//20:04:01.318 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
//20:04:04.321 [main] INFO com.example.spring.aop.service.impl.PersenMangaerServiceImplAdvisorTest - --------------------------
//20:04:04.326 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
//20:04:04.326 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据添加

通过日志可以看出实现了指定方法(开头为simpleAdd的方法)的增强。

静态正则表达式方法匹配切面

切面定义

通过默认实现类org.springframework.aop.support.RegexpMethodPointcutAdvisor实现

Spring 配置

代码语言:javascript复制
<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1、被增强类-->
    <bean id="target" class="com.example.spring.aop.service.impl.PersonManagerServiceImpl"/>
    <!--2、增强类-->
    <bean id="businessLogHandlerBeforeAdvice" class="com.example.spring.aop.advice.BusinessLogHandlerBeforeAdvice"/>
    <!--3、代理工厂定义-->
    <!--3.1 指定代理的接口-->
    <!--3.2 指定使用的增强-->
    <!--3.3 指定对哪个bean进行代理-->
   <!-- <bean id="personManagerImpl" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.example.spring.aop.service.IPersonManagerService"
          p:interceptorNames="businessLogHandlerBeforeAdvice"
          p:target-ref="target"/>-->

    <!--定义切面-->
    <bean id="businessLogHandlerRegexAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
          p:advice-ref="businessLogHandlerBeforeAdvice"
          p:pattern=".*Add.*"/>
    <bean id="parentAdvisor" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="businessLogHandlerRegexAdvisor"
          p:proxyTargetClass="true"
          />
    <bean id="personManagerImpl" parent="parentAdvisor"  p:target-ref="target"/>

</beans>

测试输出

代码语言:javascript复制
 /**
     * 静态正则表达式方法匹配切面
     */
    @Test
    public void testRegexMethodAdvisor() {
        ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/advisor/aop-before-advice-regex-advisor.xml");
        PersonManagerServiceImpl bean = (PersonManagerServiceImpl)context.getBean("personManagerImpl");
        bean.deletePerson();
        log.info("--------------------------");
        bean.simpleAddPerson();
    }
    //20:18:10.789 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
    //20:18:13.794 [main] INFO com.example.spring.aop.service.impl.PersonMangaerServiceImplAdvisorTest - --------------------------
    //20:18:13.800 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
    //20:18:13.801 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据添加

通过日志可以看出,匹配正则.*Add.*的方法才会被增强。

动态切面

切面定义

代码语言:javascript复制
@Slf4j
public class BusinessLogHandlerDynamicAdvisorPointcut extends DynamicMethodMatcherPointcut {

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        log.info("BusinessLogHandlerDynamicAdvisor 动态检查-1: "  method.getName());
        return method.getName().startsWith("simple");
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        log.info("BusinessLogHandlerDynamicAdvisor 静态检查-1: "  method.getName());
        return method.getName().startsWith("simple");
    }

    @Override
    public ClassFilter getClassFilter() {
        log.info("BusinessLogHandlerDynamicAdvisor 静态检查-2: getClassFilter");
        return PersonManagerServiceImpl.class::isAssignableFrom;
    }
}

与前面两种切面有点儿不一样的是,动态切面的定义是基于默认切面,定义切点逻辑来扩展实现的。而非直接提供对应实现类。主要也是考虑到动态逻辑设计的复杂性和可扩展性。像正则表达式和静态方法匹配,其切点的定义策略不会有太大差异。

Spring 配置

代码语言:javascript复制
<?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:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!--1、被增强类-->
    <bean id="target" class="com.example.spring.aop.service.impl.PersonManagerServiceImpl"/>
    <!--2、增强类-->
    <bean id="businessLogHandlerBeforeAdvice" class="com.example.spring.aop.advice.BusinessLogHandlerBeforeAdvice"/>
    <!--3、代理工厂定义-->
    <!--3.1 指定代理的接口-->
    <!--3.2 指定使用的增强-->
    <!--3.3 指定对哪个bean进行代理-->
   <!-- <bean id="personManagerImpl" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.example.spring.aop.service.IPersonManagerService"
          p:interceptorNames="businessLogHandlerBeforeAdvice"
          p:target-ref="target"/>-->

    <!--定义切面-->
    <bean id="businessLogHandlerDynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
          p:advice-ref="businessLogHandlerBeforeAdvice">
        <property name="pointcut">
            <bean class="com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut"/>
        </property>

    </bean>
    <bean id="parentAdvisor" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="businessLogHandlerDynamicAdvisor"
          p:proxyTargetClass="true"
          />
    <bean id="personManagerImpl" parent="parentAdvisor"  p:target-ref="target"/>

</beans>

测试输出

代码语言:javascript复制
 /**
     *动态切面
     */
    @Test
    public void testDynamicMethodAdvisor() {
        ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/advisor/aop-before-advice-dynamic-advisor.xml");
        PersonManagerServiceImpl bean = (PersonManagerServiceImpl)context.getBean("personManagerImpl");
        log.info("--------------------------");
        bean.deletePerson();
        log.info("--------------------------");
        bean.simpleAddPerson();
        log.info("--------------------------");
        bean.simpleAddPerson();
    }
//20:31:52.546 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; recaching singleton instance
//20:31:52.649 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-2: getClassFilter
//20:31:52.651 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-1: addPerson
//20:31:52.651 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-2: getClassFilter
//20:31:52.651 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-1: modifyPerson
//20:31:52.651 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-2: getClassFilter
//20:31:52.651 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-1: deleteThrowException
//20:31:52.651 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-2: getClassFilter
//20:31:52.651 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-1: deletePerson
//20:31:52.651 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-2: getClassFilter
//20:31:52.651 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-1: simpleAddPerson
//20:31:52.653 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-2: getClassFilter
//20:31:52.653 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-1: toString
//20:31:52.653 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-2: getClassFilter
//20:31:52.653 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-1: clone
//20:31:52.678 [main] INFO com.example.spring.aop.service.impl.PersonMangaerServiceImplAdvisorTest - --------------------------
//20:31:52.678 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-2: getClassFilter
//20:31:52.678 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-1: deletePerson
//20:31:52.693 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
//20:31:55.698 [main] INFO com.example.spring.aop.service.impl.PersonMangaerServiceImplAdvisorTest - --------------------------
//20:31:55.698 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-2: getClassFilter
//20:31:55.698 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 静态检查-1: simpleAddPerson
//20:31:55.701 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 动态检查-1: simpleAddPerson
//20:31:55.702 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
//20:31:55.702 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据添加
//20:31:58.703 [main] INFO com.example.spring.aop.service.impl.PersonMangaerServiceImplAdvisorTest - --------------------------
//20:31:58.703 [main] INFO com.example.spring.aop.advisor.BusinessLogHandlerDynamicAdvisorPointcut - BusinessLogHandlerDynamicAdvisor 动态检查-1: simpleAddPerson
//20:31:58.703 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
//20:31:58.703 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据添加

通过日志可以看出,每次切点匹配到的方法都会执行动态检查,而静态方法检查只会执行一次。主要实检查方法签名是否匹配。

流程切面

切面定义

代码语言:javascript复制
@Slf4j
public class BusinessLogHandlerComposablePointcut extends ComposablePointcut{

    public BusinessLogHandlerComposablePointcut() {
        Pointcut simpleAddPointCut = new ControlFlowPointcut(PersonManagerProxyService.class, "simpleAdd");

        Pointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
        ((NameMatchMethodPointcut)nameMatchMethodPointcut).setMappedName("simpleAddPersonWithFilter");

        this.intersection(simpleAddPointCut).intersection(nameMatchMethodPointcut);
    }
}
//定义流程节点
@AllArgsConstructor
public class PersonManagerProxyService {

    private IPersonManagerService personManagerService;

    public void simpleAdd(){
        personManagerService.simpleAddPerson();
        //personManagerService.simpleAddPersonWithFilter();
    }
}

和动态切面一样,流程切面也是直接定义切点后,基于默认的切面实现类扩展,来实现流程切面的。

Spring 配置

代码语言:javascript复制
<?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:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!--1、被增强类-->
    <bean id="target" class="com.example.spring.aop.service.impl.PersonManagerServiceImpl"/>
    <!--2、增强类-->
    <bean id="businessLogHandlerBeforeAdvice" class="com.example.spring.aop.advice.BusinessLogHandlerBeforeAdvice"/>
    <!--3、代理工厂定义-->
    <!--3.1 指定代理的接口-->
    <!--3.2 指定使用的增强-->
    <!--3.3 指定对哪个bean进行代理-->
   <!-- <bean id="personManagerImpl" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.example.spring.aop.service.IPersonManagerService"
          p:interceptorNames="businessLogHandlerBeforeAdvice"
          p:target-ref="target"/>-->

    <!--定义流程切点-->
    <bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
        <!--流程节点代理执行类-->
        <constructor-arg type="java.lang.Class" value="com.example.spring.aop.service.impl.PersonManagerProxyService"/>
        <!--定义切点方法-->
        <constructor-arg type="java.lang.String" value="simpleAdd"/>
    </bean>
    <!--定义切面-->
    <bean id="businessLogHandlerControlFlowAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
          p:advice-ref="businessLogHandlerBeforeAdvice">
        <property name="pointcut" ref="controlFlowPointcut"/>
    </bean>
    <!--抽象切面父类-->
    <bean id="parentAdvisor" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="businessLogHandlerControlFlowAdvisor"
          p:proxyTargetClass="true"
          />
    <!--实际执行类-->
    <bean id="personManagerImpl" parent="parentAdvisor"  p:target-ref="target"/>

</beans>

测试输出

代码语言:javascript复制
/**
     * 流程切面
     */
    @Test
    public void testControlFlowAdvisor() {
        ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/advisor/aop-before-advice-flow-advisor.xml");
        PersonManagerServiceImpl bean = (PersonManagerServiceImpl)context.getBean("personManagerImpl");
        log.info("--------------------------");
        bean.deletePerson();
        log.info("--------------------------");
        bean.simpleAddPerson();
        log.info("--------------------------");
        PersonManagerProxyService personManagerProxyService = new PersonManagerProxyService(bean);
        personManagerProxyService.simpleAdd();

    }
    //20:47:35.962 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; recaching singleton instance
    //20:47:36.070 [main] INFO com.example.spring.aop.service.impl.PersonMangaerServiceImplAdvisorTest - --------------------------
    //20:47:36.084 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
    //20:47:39.086 [main] INFO com.example.spring.aop.service.impl.PersonMangaerServiceImplAdvisorTest - --------------------------
    //20:47:39.086 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据添加
    //20:47:42.087 [main] INFO com.example.spring.aop.service.impl.PersonMangaerServiceImplAdvisorTest - --------------------------
    //20:47:42.088 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
    //20:47:42.089 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据添加

通过日志可以看出,只有被定义为特定流程的切点PersonManagerProxyService.simpleAdd满足条件时,其方法才会被增强。

复合切面

切面定义

代码语言:javascript复制
@Slf4j
public class BusinessLogHandlerComposablePointcut extends ComposablePointcut{

    public BusinessLogHandlerComposablePointcut() {
        Pointcut simpleAddPointCut = new ControlFlowPointcut(PersonManagerProxyService.class, "simpleAdd");

        Pointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
        ((NameMatchMethodPointcut)nameMatchMethodPointcut).setMappedName("simpleAddPersonWithFilter");

        this.intersection(simpleAddPointCut).intersection(nameMatchMethodPointcut);
    }
}

和动态切面一样,复合切面也是直接定义切点后,基于默认的切面实现类扩展,来实现流程切面的。特别的是,复合切面的切点可以整合好几种不同的切点共同编织切面逻辑。

Spring 配置

代码语言:javascript复制
<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1、被增强类-->
    <bean id="target" class="com.example.spring.aop.service.impl.PersonManagerServiceImpl"/>
    <!--2、增强类-->
    <bean id="businessLogHandlerBeforeAdvice" class="com.example.spring.aop.advice.BusinessLogHandlerBeforeAdvice"/>
    <!--3、代理工厂定义-->
    <!--3.1 指定代理的接口-->
    <!--3.2 指定使用的增强-->
    <!--3.3 指定对哪个bean进行代理-->
   <!-- <bean id="personManagerImpl" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.example.spring.aop.service.IPersonManagerService"
          p:interceptorNames="businessLogHandlerBeforeAdvice"
          p:target-ref="target"/>-->


    <bean id="composablePointcut" class="com.example.spring.aop.advisor.BusinessLogHandlerComposablePointcut"/>

    <!--定义切面-->
    <bean id="businessLogHandlerComposableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
          p:pointcut-ref="composablePointcut"
          p:advice-ref="businessLogHandlerBeforeAdvice"/>


    <!--定义公共抽象类-->
    <bean id="parentAdvisor" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="businessLogHandlerComposableAdvisor"
          p:proxyTargetClass="true"/>
    <bean id="personManagerImpl" parent="parentAdvisor"   p:target-ref="target"/>

</beans>

测试输出

代码语言:javascript复制
 /**
     * 复合切面
     */
    @Test
    public void testComposablePointcutAdvisor() {
        ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/advisor/aop-before-composable-advisor.xml");
        PersonManagerServiceImpl bean = (PersonManagerServiceImpl)context.getBean("personManagerImpl");
        log.info("--------------------------");
        bean.deletePerson();
        log.info("--------------------------");
        bean.simpleAddPerson();
        log.info("--------------------------");
        PersonManagerProxyService personManagerProxyService = new PersonManagerProxyService(bean);
        personManagerProxyService.simpleAdd();
    }
    //21:28:10.708 [main] INFO com.example.spring.aop.service.impl.PersonMangaerServiceImplAdvisorTest - --------------------------
    //21:28:10.724 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
    //21:28:13.727 [main] INFO com.example.spring.aop.service.impl.PersonMangaerServiceImplAdvisorTest - --------------------------
    //21:28:13.727 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据添加
    //21:28:16.728 [main] INFO com.example.spring.aop.service.impl.PersonMangaerServiceImplAdvisorTest - --------------------------
    //21:28:16.728 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据添加
    //21:28:19.737 [main] INFO com.example.spring.aop.monitor.BusinessLogMonitor - > simpleAddPersonWithFilter: begin monitor...
    //21:28:19.738 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据添加

从日子可以看出,只有满足流程切点和静态方法(NameMatchMethodPointcutStaticMethodMatcherPointcut的一个子类)切点同时满足匹配逻辑的情况下,对应方法才会被增强。

引介切面

切面定义

引介增强由于是面对类层面的,所以,对应切面也有默认的实现类,只需要向默认的切面实现类中,传入引介增强类即可。

  • 接口 IntroductionAdvisor
  • 实现类 DefaultIntroductionAdvisor
  • 实现类 DeclareParentsAdvisor

Spring 配置

代码语言:javascript复制
<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1、被增强类-->
    <bean id="target" class="com.example.spring.aop.service.impl.PersonManagerServiceImpl"/>
    <!--2、增强类-->
    <bean id="businessLogHandlerIntroduceAdvice" class="com.example.spring.aop.advice.introduce.BusinessLogHandlerIntroduceAdvice"/>
    <!--3、代理工厂定义-->
    <!--3.1 指定代理的接口-->
    <!--3.2 指定使用的增强-->
    <!--3.3 指定对哪个bean进行代理-->
   <!-- <bean id="personManagerImpl" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.example.spring.aop.service.IPersonManagerService"
          p:interceptorNames="businessLogHandlerBeforeAdvice"
          p:target-ref="target"/>-->

    <!--定义切面-->
    <bean id="businessLogHandlerIntroductionAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor">
        <constructor-arg ref="businessLogHandlerIntroduceAdvice"/>
    </bean>
    <!--定义公共抽象类-->
    <bean id="parentAdvisor" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="businessLogHandlerIntroductionAdvisor"
          p:proxyTargetClass="true"
          />
    <bean id="personManagerImpl" parent="parentAdvisor"
    p:target-ref="target"/>

</beans>

测试输出

代码语言:javascript复制
 /**
     * 引介切面
     */
    @Test
    public void testIntroduceAdvisor() {
        ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/advisor/aop-before-introduce-advisor.xml");
        PersonManagerServiceImpl bean = (PersonManagerServiceImpl)context.getBean("personManagerImpl");
        log.info("--------------------------");
        bean.deletePerson();
        BusinessLogHandlerIntroduceSwitch businessLogHandlerIntroduceSwitch = (BusinessLogHandlerIntroduceSwitch) bean;
        businessLogHandlerIntroduceSwitch.setSwitch(true);
        bean.deletePerson();
    }
    //22:14:59.875 [main] INFO com.example.spring.aop.service.impl.PersonManagaerServiceImplAdvisorTest - --------------------------
    //22:14:59.892 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
    //22:15:02.896 [main] INFO com.example.spring.aop.monitor.BusinessLogMonitor - > deletePerson: begin monitor...
    //22:15:02.897 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
    //22:15:05.902 [main] INFO com.example.spring.aop.monitor.BusinessLogMonitor - > end monitor....

沿用之前定义的引介增强定义BusinessLogHandlerIntroduceSwitch,我们织入默认的切面实现类org.springframework.aop.support.DefaultIntroductionAdvisor,便可完成引介切面的定义。

总结

通过上述介绍,我们总结下切点和切面的特征,我们AOP的编程核心目的是指定方法或类对其进行增强。

  • 这个过程中,切点对应一系列的匹配规则,切面由切点绘制而成,编织切面进行代码逻辑的增强织入,即AOP
  • 所有的切面构造,其切点设计越复杂,对应的性能会越差,一般需要根据需要进行指定方法增强。

0 人点赞