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

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

平台地址
CSDNhttps://blog.csdn.net/sinat_28690417
简书https://www.jianshu.com/u/3032cc862300
个人博客https://yiyuery.github.io/NoteBooks/

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

Spring AOP 增强 Advice

Spring 使用增强类定义横切逻辑,同时由于Spring方法只支持方法连接点,增强还包括在方法的哪一点加入横切代码的方位信息。所以增强包括:1、横切逻辑;2、部分连接点的信息。

类图结构

抽象接口 org.springframework.aop

在这里插入图片描述

在这里插入图片描述

按照增强在目标类方法中的连接点位置,可以分为以下5类:

  • 前置增强
  • 后置增强
  • 环绕增强
  • 异常抛出增强
  • 引介增强

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

Advice 增强实战

前置增强

BeforeAdvice

package org.springframework.aop;

import org.aopalliance.aop.Advice;

/**
 * Common marker interface for before advice, such as {@link MethodBeforeAdvice}.
 *
 * <p>Spring supports only method before advice. Although this is unlikely to change,
 * this API is designed to allow field advice in future if desired.
 *
 * @author Rod Johnson
 * @see AfterAdvice
 */
public interface BeforeAdvice extends Advice {

}

目标:记录方法执行开始时间

在上一篇《[Spring] Spring AOP 实现原理剖析(一)》的基础上继续开展实战

实现一个前置增强类用于业务类方法执行前的增强。

//方法增强接口(Spring中定义)
public interface MethodBeforeAdvice extends BeforeAdvice {

	/**
	 * Callback before a given method is invoked.
	 * @param method method being invoked
	 * @param args arguments to the method
	 * @param target target of the method invocation. May be {@code null}.
	 * @throws Throwable if this object wishes to abort the call.
	 * Any exception thrown will be returned to the caller if it's
	 * allowed by the method signature. Otherwise the exception
	 * will be wrapped as a runtime exception.
	 */
	void before(Method method, Object[] args, @Nullable Object target) throws Throwable;

}

//增强类
public class BusinessLogHandlerBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        BusinessLogMonitor.begin(method.getName());
    }

}

测试输出


/**
 * 前置增强
 */
@Test
public void deletePersonWithBeforeAdvice() {

    //创建被增强实例
    PersonManagerServiceImpl personManagerService = new PersonManagerServiceImpl();

    //创建前置增强Advice
    BeforeAdvice beforeAdvice = new BusinessLogHandlerBeforeAdvice();

    //Spring 提供的代理工厂
    ProxyFactory factory = new ProxyFactory();

    //设置代理目标
    factory.setTarget(personManagerService);

    //为代理目标添加增强
    factory.addAdvice(beforeAdvice);

    //生成代理实例
    PersonManagerServiceImpl proxy = (PersonManagerServiceImpl)factory.getProxy();
    //执行方法
    proxy.deletePerson();
}
//23:44:01.043 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
//23:44:01.065 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除


可以看到在 deletePerson方法执行前,增强逻辑生效了,输出了

om.example.spring.aop.simple.BusinessLogMonitor - begin monitor...

代理工厂 ProxyFactory

在上一篇《[Spring] Spring AOP 实现原理剖析(一)》曾提到过Spring AOP的底层实现用的还是JDK和CGLib的动态代理技术。

但是我们在使用CGLib时定义了代理类生成的一个辅助构造类

public class CglibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    /**
     * 创建代理类
     * @param clazz
     * @return
     */
    public Object createProxy(Class clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        BusinessLogMonitor.begin(obj.getClass().getName()+"."+method.getName());
        Object result = proxy.invokeSuper(obj, args);
        BusinessLogMonitor.end();
        return result;
    }
}

Spring中,也定义了一个AopProxy,并提供了2个实现类:

  • CglibAopProxy

  • JdkDynamicAopProxy

public interface AopProxy {

	/**
	 * Create a new proxy object.
	 * <p>Uses the AopProxy's default class loader (if necessary for proxy creation):
	 * usually, the thread context class loader.
	 * @return the new proxy object (never {@code null})
	 * @see Thread#getContextClassLoader()
	 */
	Object getProxy();

	/**
	 * Create a new proxy object.
	 * <p>Uses the given class loader (if necessary for proxy creation).
	 * {@code null} will simply be passed down and thus lead to the low-level
	 * proxy facility's default, which is usually different from the default chosen
	 * by the AopProxy implementation's {@link #getProxy()} method.
	 * @param classLoader the class loader to create the proxy with
	 * (or {@code null} for the low-level proxy facility's default)
	 * @return the new proxy object (never {@code null})
	 */
	Object getProxy(@Nullable ClassLoader classLoader);

}

从名字可以看出,分别对应两种AOP实现机制,如果是针对接口进行代理,则使用JdkDynamicAopProxy;如果是针对实例类的代理,则使用CglibAopProxy

相应的,若指定JDK代理技术,需要传入interfaces参数。另外,ProxyFactory可以通过factory.setOptimize(true); 启动优化代理方式,这样,针对接口的代理也会使用CglibAopProxy

Spring 中 xml 配置

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

</beans>

测试输出

/**
 * Spring XML 配置前置增强
 */
@Test
public void deletePersonWithBeforeAdviceBySpringXML() {
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/aop-before-advice.xml");
    IPersonManagerService bean = (IPersonManagerService)context.getBean("personManagerImpl");
    bean.deletePerson();
    log.info("-----------------------");
    bean.modifyPerson(new PersonDO("xx1"));
}
//00:27:02.946 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
//00:27:02.946 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
//00:27:05.951 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImplTest - -----------------------
//00:27:05.951 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
//00:27:05.952 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据修改:xx1

分析:

  • 增强类需要实现接口MethodBeforeAdvice
  • 增强了目标类的所有接口方法
  • 实现了前置增强

后置增强

其实看了前置增强,后置、坏绕对应的实现也大相径庭,此处就不再追溯,贴了代码,直接看吧。

后置实现:

public interface AfterAdvice extends Advice {

}

public interface AfterReturningAdvice extends AfterAdvice {

	/**
	 * Callback after a given method successfully returned.
	 * @param returnValue the value returned by the method, if any
	 * @param method method being invoked
	 * @param args arguments to the method
	 * @param target target of the method invocation. May be {@code null}.
	 * @throws Throwable if this object wishes to abort the call.
	 * Any exception thrown will be returned to the caller if it's
	 * allowed by the method signature. Otherwise the exception
	 * will be wrapped as a runtime exception.
	 */
	void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;

}

public class BusinessLogHandlerAfterAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        BusinessLogMonitor.end();
    }
}
/**
 * Spring XML 配置后置增强
 */
@Test
public void deletePersonWithAfterAdviceBySpringXML() {
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/aop-after-advice.xml");
    IPersonManagerService bean = (IPersonManagerService)context.getBean("personManagerImpl");
    bean.deletePerson();
    log.info("-----------------------");
    bean.modifyPerson(new PersonDO("xx1"));
}
//00:38:24.737 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
//00:38:27.739 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - end monitor....
//00:38:27.740 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImplTest - -----------------------
//00:38:27.740 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据修改:xx1
//00:38:30.745 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - end monitor....

分析:

  • 增强类需要实现接口AfterAdvice
  • 增强了目标类的所有接口方法
  • 实现了后置增强

此处顺便提下如何配置多个增强:

比如同时配置前置和后置增强:

<?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"/>
    <bean id="businessLogHandlerAfterAdvice" class="com.example.spring.aop.advice.BusinessLogHandlerAfterAdvice"/>
    <!--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,businessLogHandlerAfterAdvice"
          p:target-ref="target"
    />

</beans>

测试输出

  /**
     * Spring XML 配置前、后置增强
     */
    @Test
    public void deletePersonWithBeforeAndAfterAdviceBySpringXML() {
        ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/aop-before-and-after-advice.xml");
        IPersonManagerService bean = (IPersonManagerService)context.getBean("personManagerImpl");
        bean.deletePerson();
        log.info("-----------------------");
        bean.modifyPerson(new PersonDO("xx1"));
    }
    //23:28:11.627 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
    //23:28:11.628 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
    //23:28:14.629 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - end monitor....
    //23:28:14.630 [main] INFO com.example.spring.aop.simple.BusinessLogHandler - deletePerson执行耗时3001毫秒
    //23:28:14.630 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImplTest - -----------------------
    //23:28:14.630 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
    //23:28:14.630 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据修改:xx1
    //23:28:17.633 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - end monitor....
    //23:28:17.633 [main] INFO com.example.spring.aop.simple.BusinessLogHandler - modifyPerson执行耗时3003毫秒

分析:

  • 增强了目标类的所有接口方法
  • 实现了前置和后置增强
  • 配置多个增强通过p:interceptorNames="businessLogHandlerBeforeAdvice,businessLogHandlerAfterAdvice"实现。

interceptorNames 是数组形式的参数,接受增强Bean的名称。也可以采用如下方式配置:

   <bean id="personManagerImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces" value="com.example.spring.aop.service.IPersonManagerService"/>
        <property name="interceptorNames">
            <list>
                <idref bean="businessLogHandlerAfterAdvice"/>
                <idref bean="businessLogHandlerBeforeAdvice"/>
            </list>

        </property>
        <property name="target" ref="target"/>
    </bean>

便于 IDEA 可以自动检查出错误并提示。

坏绕增强

综合实现前置、后置增强。具体如下:

// 调整编辑方法,使其修改传入数据后并返回,模拟有数据返回的情况
@Override
public PersonDO modifyPerson(PersonDO personDO) {
    log.info("模拟人员数据修改:"+personDO.getName());
    try {
        Thread.sleep(3000);
    } catch (Exception e) {
        log.error("PersonManagerServiceImpl modifyPerson failed!");
    }
    personDO.setName("被修改_"+personDO.getName());
    return personDO;
}

//增强类实现

public class BusinessLogHandlerAroundAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        BusinessLogMonitor.begin(invocation.getMethod().getName());

        Object object = invocation.proceed();

        BusinessLogMonitor.end();

        return object;
    }
}

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="businessLogHandlerAroundAdvice" class="com.example.spring.aop.advice.BusinessLogHandlerAroundAdvice"/>
    <!--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="businessLogHandlerAroundAdvice"
          p:target-ref="target"/>-->

    <bean id="personManagerImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces" value="com.example.spring.aop.service.IPersonManagerService"/>
        <property name="interceptorNames">
            <list>
                <idref bean="businessLogHandlerAroundAdvice"/>
            </list>

        </property>
        <property name="target" ref="target"/>
    </bean>

</beans>

编写测试类并输出测试结果:

 /**
     * 环绕增强
     */
    @Test
    public void deletePersonWithAroundAdviceBySpringXML() {
        ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/aop-around-advice.xml");
        IPersonManagerService bean = (IPersonManagerService)context.getBean("personManagerImpl");
        bean.deletePerson();
        log.info("-----------------------");
        PersonDO xx1 = bean.modifyPerson(new PersonDO("xx1"));
        log.info("修改返回结果>>>>"+xx1.getName());
    }
    //00:18:11.646 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
    //00:18:11.646 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
    //00:18:14.650 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - end monitor....
    //00:18:14.651 [main] INFO com.example.spring.aop.simple.BusinessLogHandler - deletePerson执行耗时3005毫秒
    //00:18:14.651 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImplTest - -----------------------
    //00:18:14.651 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
    //00:18:14.651 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据修改:xx1
    //00:18:17.656 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - end monitor....
    //00:18:17.656 [main] INFO com.example.spring.aop.simple.BusinessLogHandler - modifyPerson执行耗时3005毫秒
    //00:18:17.656 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImplTest - 修改返回结果>>>>被修改_xx1

分析

  • 增强类需要实现接口org.aopalliance.intercept.MethodInterceptor
  • 通过invocation.proceed()方法执行前后加入增强逻辑来实现环绕增强
  • 支持返回值
  • 可以实现前置、后置增强的综合效果

异常抛出增强

异常增强和前几个增强接口定义不太一样,仅仅定义了个标签接口ThrowsAdvice,实际运行时 Spring 根据反射机制自行判断符合条件的方法签名,进行增强。

首先,补充个会抛出异常的方法定义:

@Override
public void deleteThrowException() {
    log.info("模拟人员数据删除,抛出异常");
    try {
        Thread.sleep(3000);
    } catch (Exception e) {
        log.error("PersonManagerServiceImpl deletePerson failed!");
    }
    throw new IllegalArgumentException("删除失败,抛出异常");
}

然后,定义增强接口来处理异常:

@Slf4j
public class BusinessLogHandlerThrowExceptionAdvice implements ThrowsAdvice {

    /**
     * ThrowsAdvice 是个标签接口,运行期 Spring 使用反射机制自行判断,必须采用签名形式定义异常抛出的增强方法
     * void afterThrowing(Method method,Object args,Object target,Throwable ex)
     * @param method
     * @param args
     * @param target
     * @param ex
     * @throws Throwable
     */
    public void afterThrowing(Method method,Object args,Object target,Exception ex) throws Throwable{
        log.error("BusinessLogHandlerThrowExceptionAdvice >>> method: "+ method.getName());
    }
}

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="businessLogHandlerThrowExceptionAdvice" class="com.example.spring.aop.advice.BusinessLogHandlerThrowExceptionAdvice"/>
    <!--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="businessLogHandlerThrowExceptionAdvice"
          p:target-ref="target"/>-->

    <bean id="personManagerImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces" value="com.example.spring.aop.service.IPersonManagerService"/>
        <property name="interceptorNames">
            <list>
                <idref bean="businessLogHandlerThrowExceptionAdvice"/>
            </list>

        </property>
        <property name="target" ref="target"/>
    </bean>

</beans>

测试输出

/**
 * 异常增强
 */
@Test
public void deletePersonWithThrowAdviceBySpringXML() {
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/aop-throw-exception-advice.xml");
    IPersonManagerService bean = (IPersonManagerService)context.getBean("personManagerImpl");
    bean.deletePerson();
    log.info("-----------------------");
    try {
        bean.deleteThrowException();
    }catch (Exception e){
        log.error("deletePersonWithThrowAdviceBySpringXML catch exception!",e);
    }
}
//23:44:52.021 [main] DEBUG org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor - Found exception handler method on throws advice: public void com.example.spring.aop.advice.BusinessLogHandlerThrowExceptionAdvice.afterThrowing(java.lang.reflect.Method,java.lang.Object,java.lang.Object,java.lang.Exception) throws java.lang.Throwable
//23:44:52.023 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
//23:44:55.027 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImplTest - -----------------------
//23:44:55.027 [main] DEBUG org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor - Found exception handler method on throws advice: public void com.example.spring.aop.advice.BusinessLogHandlerThrowExceptionAdvice.afterThrowing(java.lang.reflect.Method,java.lang.Object,java.lang.Object,java.lang.Exception) throws java.lang.Throwable
//23:44:55.028 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除,抛出异常
//23:44:58.031 [main] ERROR com.example.spring.aop.advice.BusinessLogHandlerThrowExceptionAdvice - BusinessLogHandlerThrowExceptionAdvice >>> method: deleteThrowException
//23:44:58.036 [main] ERROR com.example.spring.aop.service.impl.PersonManagerServiceImplTest - deletePersonWithThrowAdviceBySpringXML catch exception!
//java.lang.IllegalArgumentException: 删除失败,抛出异常
//	at com.example.spring.aop.service.impl.PersonManagerServiceImpl.deleteThrowException(PersonManagerServiceImpl.java:74)
//	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
//	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
//	at java.lang.reflect.Method.invoke(Method.java:498)

分析

  • 实现标记接口
  • 按照约定的方法签名定义异常增强方法

规则:

  1. 签名模板: void afterThrowing(Method method,Object args,Object target,Throwable ex)
  2. 前3个入参,要么提供,要么不提供
  3. 最后一个入参必须为Throwable及其子类
  4. 关于异常的匹配规则,在类的继承树上,两个类的距离月近,这相似度越高,匹配时优先选择相识度高的afterThrowing方法。

引介增强

一种特殊形式增强,它不是在目标方法周围织入增强,而是为目标类创建新的方法和属性,所以引介增强的连接点是类级别的,而非方法级别。

特点

  1. 可以通过引介增强为目标类添加一个接口的实现。
  2. 可以为目标类创建实现某接口的代理。

前文,我们对所有方法织入了业务日志的增强,由于业务日志的输出往往会增加系统的负担,我们可以通过引介增强来实现这个业务日志输出的功能开关。

首先,定义一个被增强类需要增强的能力接口,目的是通过其子类实现该接口能力

public interface BusinessLogHandlerIntroduceSwitch {
    /*定义开关*/
    void setSwitch(boolean open);

}

然后,定义增强逻辑,可以加上接口种定义的方法,实现模板方法类型的效果:

/*根据手动设置的开关,判断业务日志增强是否开启*/
public class BusinessLogHandlerIntroduceAdvice extends DelegatingIntroductionInterceptor implements BusinessLogHandlerIntroduceSwitch {

    private ThreadLocal<Boolean> switchMap = new ThreadLocal<>();

    @Override
    public void setSwitch(boolean open) {
        switchMap.set(open);
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object obj = null;
        if (Objects.nonNull(switchMap.get()) && switchMap.get()) {
            BusinessLogMonitor.begin(mi.getMethod().getName());
            obj = super.invoke(mi);
            BusinessLogMonitor.end();
        }else{
            obj = super.invoke(mi);
        }
        return obj;
    }
}

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="personManagerImplTarget" 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:interfaces="com.example.spring.aop.advice.introduce.BusinessLogHandlerIntroduceSwitch"
          p:interceptorNames="businessLogHandlerIntroduceAdvice"
          p:proxyTargetClass="true"
          p:target-ref="personManagerImplTarget"/>-->

    <bean id="personManagerImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces" value="com.example.spring.aop.advice.introduce.BusinessLogHandlerIntroduceSwitch"/>
        <property name="interceptorNames">
            <list>
                <idref bean="businessLogHandlerIntroduceAdvice"/>
            </list>

        </property>
        <property name="target" ref="personManagerImplTarget"/>
        <!--由于引介增强一定要通过创建子类来生成代理,所以需要强制使用CGLib-->
        <property name="proxyTargetClass" value="true"/>

    </bean>

</beans>

测试

/**
 * 引介增强
 */
@Test
public void deletePersonWithIntroduceAdviceBySpringXML() {
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring/aop-introduce-advice.xml");
    PersonManagerServiceImpl bean = (PersonManagerServiceImpl)context.getBean("personManagerImpl");
    bean.deletePerson();
    log.info("--------------------------");

    //开启增强
    BusinessLogHandlerIntroduceSwitch enhancer = (BusinessLogHandlerIntroduceSwitch)bean;
    enhancer.setSwitch(true);

    bean.deletePerson();
}

//00:25:12.774 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
//00:25:15.778 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImplTest - --------------------------
//00:25:15.780 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - begin monitor...
//00:25:15.782 [main] INFO com.example.spring.aop.service.impl.PersonManagerServiceImpl - 模拟人员数据删除
//00:25:18.783 [main] INFO com.example.spring.aop.simple.BusinessLogMonitor - end monitor....
//00:25:18.784 [main] INFO com.example.spring.aop.simple.BusinessLogHandler - deletePerson执行耗时3002毫秒

分析

  • 和前几种增强的区别在于,引介增强强制使用CGLib
  • 需要配置被增强类需要实现的增强接口interfaces,而不再是通过JDK通道代理,生成被增强类的代理类
  • 增强类,需要继承Spring的默认实现DelegatingIntroductionInterceptor,并通过覆盖父类的invoke方法,结合增强接口,实现自身增强逻辑。
  • 增强类,支持通过模板方法的形式提供增强逻辑的执行流程控制。

总结

本文,通过实战代码,详细介绍了Spring中五种增强方式各自的实现方式。其中,前、后、坏绕、异常增强使用的是JDK动态代理,引介增强强制使用CGLib的方式实现。

下一篇,我们将就切点、切面的控制做进一步的了解。

To be continue....

更多

扫码关注架构探险之道,回复『源码』,获取本文相关源码和资源链接

在这里插入图片描述

知识星球(扫码加入获取历史源码和文章资源链接)

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值