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

本文介绍Spring AOP中的引介增强技术,通过实例演示如何为目标类添加接口实现,实现性能监控功能的动态开关。

概述

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

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

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);
    }
}

运行结果

这里写图片描述


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小工匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值