专栏首页架构探险之道​[Spring] Spring AOP 实现原理剖析(三)

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

手机用户请横屏获取最佳阅读体验,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

  • 实现类体系
public interface PointcutAdvisor extends Advisor {

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

}

主要实现类:

  • StaticMethodMatcherPointcutAdvisor 静态方法匹配器定义切点的切面,默认情况下匹配所有目标类;
  • AspectJPointcutAdvisor 用于AspectJ语法定义切点的切面,内部定义了个抽象类AbstractAspectJAdvice用于接收增强类
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构造切点。
/**
    * 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来定义。

代码实战

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

静态普通方法匹配切面

切面定义

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 配置

<?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>

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

测试输出

 /**
     * 静态普通方法匹配切面
     */
    @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 配置

<?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>

测试输出

 /**
     * 静态正则表达式方法匹配切面
     */
    @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.*的方法才会被增强。

动态切面

切面定义

@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 配置

<?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>

测试输出

 /**
     *动态切面
     */
    @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 - 模拟人员数据添加

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

流程切面

切面定义

@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 配置

<?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>

测试输出

/**
     * 流程切面
     */
    @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满足条件时,其方法才会被增强。

复合切面

切面定义

@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 配置

<?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>

测试输出

 /**
     * 复合切面
     */
    @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 配置

<?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>

测试输出

 /**
     * 引介切面
     */
    @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
  • 所有的切面构造,其切点设计越复杂,对应的性能会越差,一般需要根据需要进行指定方法增强。

本文分享自微信公众号 - 架构探险之道(zacsnz1314),作者:MasterYang

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-12-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

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

    前四种比较好理解,大致对应于被增强方法的执行时间,前、后、前后、异常抛出四个连接点。最后一种引介增强:IntroductionInterceptor 表示在目标...

    架构探险之道
  • [Spring] Spring AOP 实现原理剖析(一)

    此处不对这几个术语做冗长的介绍,为了便于记忆,上面的元素在AOP中充当何种角色?我们随着实战的深入慢慢来讲。

    架构探险之道
  • Spring Boot 整合 Activiti 6.0.0 工作流引擎开发

    本教程基于Activiti 6.0.0 ,着力介绍工作流引擎Activiti6.0.0引擎和Spring Boot的整合开发,帮助初学者入门。

    架构探险之道
  • [Spring] Spring AOP 实现原理剖析(二)

    前四种比较好理解,大致对应于被增强方法的执行时间,前、后、前后、异常抛出四个连接点。最后一种引介增强:IntroductionInterceptor 表示在目标...

    架构探险之道
  • Spring Cloud微服务项目实战--Eureka服务搭建

    今天我们要开始SpringCloud的微服务项目系列实战,我仍然以电商项目展开,从搭建一步步深入。在开始实战之前,我们先熟悉下SpringCloud。

    攻城狮的那点事
  • OpenStack实践(九):Open vSwitch方式实现floating IP

    openstack安装详见:OpenStack实践(一):Ubuntu16.04下DevStack方式搭建p版OpenStack

    loong576
  • 【一起学源码-微服务】Nexflix Eureka 源码十三:Eureka源码解读完结撒花篇~!

    实话实说,从最开始Eureka Server和Eureka Client初始化的流程还是一脸闷逼,到现在Eureka各种操作都了然于心了。

    一枝花算不算浪漫
  • 真正掌握vuex的使用方法(五)

    上面的代码并不复杂,相信大家也都可以看的明白。通过以上代码咱们可以实现一个简单的切换,通过这种形式来完成的切换可以称其为乞丐版的切换。因为里面的数据都被写死了!...

    用户1272076
  • [WCF REST] WebHttpBinding与消息编码

    不论是我们采用SOAP还是REST架构风格,运行时框架体系依然不曾改变,终结点也仍旧是通信的核心。在Web HTTP编程模型中,我们采用基于WebHttpBin...

    蒋金楠
  • OpenStack云计算之路-Mitaka 版本

    1.1 云计算简介 云计算(英语:cloud computing ),是一种基于互联网的计算方式,通过这种方式,共享的软硬件资源和信息可以按需求提供给计算机各种...

    惨绿少年

扫码关注云+社区

领取腾讯云代金券