Spring-AOP 通过配置文件实现 引介增强

来源:互联网 发布:学生背包品牌知乎 编辑:程序博客网 时间:2024/05/17 20:39

  • 概述
  • 实例

概述

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

通过引介增强我们可以为目标类添加一个接口的实现即原来目标类未实现某个接口,那么通过引介增强可以为目标类创建实现某接口的代理。

Spring定义了引介增强接口IntroductionInterceptor,该接口没有定义任何方法
这里写图片描述

Spring为该接口提供了DelegatingIntroductionInterceptor实现类,一般情况下,通过扩展该实现类定义自己的引介增强类。


实例

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

我们一直使用的性能检测的例子。 开启监控会影响业务系统的性能,我们可以设置是否启用性能监视为可控的,那我们改如何使用引介增强实现这一个诱人的功能呢?

我们创建一个示例来演示下,步骤如下:
创建接口类:Monitorable.java
创建业务类:PerformanceMonitor.java
创建增强类:ControllablePerformanceMonitor.java
创建配置文件:conf-advice.xml
创建增强测试类:DelegatingTest.java

这里写图片描述

接口类:Monitorable.java

package com.xgj.aop.spring.advice.DelegatingIntroductionInterceptor;public interface Monitorable {    void setMonitorActive(boolean active);}

该接扣仅包含一个setMonitorActive方法,我们希望通过该接口方法控制业务类性能监视功能的激活和关闭状态

接下来创建业务类

package com.xgj.aop.spring.advice.DelegatingIntroductionInterceptor;public class PerformanceMonitor {    private static ThreadLocal<MethodPerformace> performaceRecord = new ThreadLocal<MethodPerformace>();    public static void begin(String method) {        System.out.println("begin monitor...");        MethodPerformace mp = performaceRecord.get();        if (mp == null) {            mp = new MethodPerformace(method);            performaceRecord.set(mp);        } else {            mp.reset(method);        }    }    public static void end() {        System.out.println("end monitor...");        MethodPerformace mp = performaceRecord.get();        mp.printPerformace();    }}

接下来创建增强类ControllablePerformanceMonitor

package com.xgj.aop.spring.advice.DelegatingIntroductionInterceptor;import org.aopalliance.intercept.MethodInvocation;import org.springframework.aop.support.DelegatingIntroductionInterceptor;public class ControllablePerformaceMonitor extends        DelegatingIntroductionInterceptor implements Monitorable {    private static final long serialVersionUID = 1L;    // 定义ThreadLocal类型的变量,用于保存性能监视开关状态。 为了解决单实例线程安全的问题,通过ThreadLocal    // 让每个线程单独使用一个状态    private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<Boolean>();    @Override    public void setMonitorActive(boolean active) {        MonitorStatusMap.set(active);    }    /**     * 拦截方法     */    @Override    public Object invoke(MethodInvocation mi) throws Throwable {        Object obj = null;        // 对于支持新跟那个监视可控代理,通过判断其状态决定是否开启性能监控功能        // Java5.0的自动拆包功能,get方法返回的Boolean被自动拆包为boolean类型的值        if (MonitorStatusMap.get() != null && MonitorStatusMap.get()) {            PerformanceMonitor.begin(mi.getClass().getName() + "."                    + mi.getMethod().getName());            obj = super.invoke(mi);            PerformanceMonitor.end();        } else {            obj = super.invoke(mi);        }        return obj;    }}

接下来创建所要增强的方法类

package com.xgj.aop.spring.advice.DelegatingIntroductionInterceptor;public class ForumService {    public void removeTopic(int topicId) {        System.out.println("模拟删除Topic记录:" + topicId);        try {            Thread.currentThread().sleep((long) (Math.random() * 1000 * 20));        } catch (Exception e) {            throw new RuntimeException(e);        }    }    public void removeForum(int forumId) {        System.out.println("模拟删除Forum记录:" + forumId);        try {            Thread.currentThread().sleep((long) (Math.random() * 1000 * 20));        } catch (Exception e) {            throw new RuntimeException(e);        }    }}
package com.xgj.aop.spring.advice.DelegatingIntroductionInterceptor;public class MethodPerformace {    private long begin;    private long end;    private String serviceMethod;    public MethodPerformace(String serviceMethod) {        reset(serviceMethod);    }    public void printPerformace() {        end = System.currentTimeMillis();        long elapse = end - begin;        System.out.println(serviceMethod + "花费" + elapse + "毫秒。");    }    public void reset(String serviceMethod) {        this.serviceMethod = serviceMethod;        this.begin = System.currentTimeMillis();    }}

创建配置文件来将所设置的代码组合起来

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="pmonitor"        class="com.xgj.aop.spring.advice.DelegatingIntroductionInterceptor.ControllablePerformaceMonitor" />    <bean id="forumServiceTarget" class="com.xgj.aop.spring.advice.DelegatingIntroductionInterceptor.ForumService" />    <bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"        p:interfaces="com.xgj.aop.spring.advice.DelegatingIntroductionInterceptor.Monitorable"        p:target-ref="forumServiceTarget"         p:interceptorNames="pmonitor"        p:proxyTargetClass="true" /></beans>

引介增强的配置和一般配置有较大的区别,

  • 首先需要指定引介增强所实现的接口 ,如 p:interfaces ,这里的引介增强实现了Monitorable接口。
  • 其次,由于只能通过为目标类创建子类的方式生成引介增强的代理,所以必须将 p:proxyTargetClass=”true”

如果没有对ControllablePerformaceMonitor进行线程安全的处理,就必须将singleton属性设置为false, 让ProxyFactoryBean产生prototype的作用域类型的代理。 这里就带来了一个严重的性能问题,因为cglib动态创建代理的性能很低,而每次getBean方法从容器中获取作用域为prototype的Bean时都将返回一个新的代理实例,所以这种影响是巨大的,这也是为什么通过ThreadLocal对ControllablePerformaceMonitor的开关进行线程安全化处理的原因。 通过线程安全处理后,就可以使用默认的singleton Bean作用域,这样创建代理的动作仅发生一次。

创建对应的测试类

package com.xgj.aop.spring.advice.DelegatingIntroductionInterceptor;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class DelegatingTest {    @Test    public void test() {        ApplicationContext ctx = new ClassPathXmlApplicationContext(                "classpath:com/xgj/aop/spring/advice/DelegatingIntroductionInterceptor/conf-advice.xml");        ForumService forumService = (ForumService) ctx.getBean("forumService");        forumService.removeForum(10);        forumService.removeTopic(1022);        Monitorable moniterable = (Monitorable) forumService;        moniterable.setMonitorActive(true);        forumService.removeForum(10);        forumService.removeTopic(1022);    }}

运行结果

这里写图片描述


原创粉丝点击