Spring中的增强类型

Spring通过增强类型定义横切逻辑,同时由于Spring只支持方法连接点,增强还包括了在方法的哪一点加入横切代码的方位信息,所以增强既包括横切逻辑,还包括部分连接点的信息。

增强包括以下几类:

  1. 前置增强:org.springframework.aop.BeforeAdvice代表前置增强,表示在目标方法整形前实施增强

  2. 后置增强:org.springframework.aop.AfterReturningAdvice代表后置增强,表示在目标方法执行后实施增强

  3. 环绕增强:org.springframework.aop.MethodInterceptor代表环绕增强,表示在目标方法执行前后实施增强

  4. 异常抛出增强 :org.springframework.aop.ThrowsAdvice代表抛出异常增强,表示在目标方法抛出异常后实施增强

  5. 引介增强:org.springframework.aop.IntroductionInterceptor代表引介增强,表示在目标类中添加一些新的方法和属性

前置增强

下面通过一个服务生服务前的礼貌用语来举例增强:

Waiter.java

public interface Waiter {
   void greetTo(String name);
   void serveTo(String name);
}

未添加前置增强前:
NaiveWaiter.java

public class NaiveWaiter implements Waiter {

    public void greetTo(String name) {
        System.out.println("greet to "+name+"...");
    }

    public void serveTo(String name){
        System.out.println("serving "+name+"...");
    }
}

前置增强类如下:
GreetingBeforeAdvice.java

public class GreetingBeforeAdvice implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object obj) throws Throwable {
        String clientName = (String)args[0];
        System.out.println("How are you!Mr."+clientName+".");
    }
}

MethodBeforeAdvice是前置增强BeforeAdvice接口的子类。其中仅有唯一的方法:before(Method method, Object[] args, Object obj) throws Throwable,method为目标类的方法,args为目标类方法的入参,obj为目标类的实例,当该方法发生异常时,将会阻止目标类方法的执行。

前置增强应用如下:
TestBeforeAdvice.java

public class TestBeforeAdvice {
    public static void main(String[] args) {
        Waiter target = new NaiveWaiter();
        //创建增强
        BeforeAdvice  advice = new GreetingBeforeAdvice();
        //Spring提供的代理工厂
        ProxyFactory pf = new ProxyFactory();
        //指定对接口进行代理
        pf.setInterfaces(target.getClass().getInterfaces());
        //启用优化
        pf.setOptimize(true);
        //设置代理目标
        pf.setTarget(target);
        //为代理目标添加增强
        pf.addAdvice(advice);
        //生成代理实例
        Waiter proxy = (Waiter)pf.getProxy(); 
        proxy.greetTo("John");
        proxy.serveTo("Tom");
    }
}

运行结果如下:可以发现每个方法执行前都引入了前置增强的语句

这里写图片描述

在上面的代理工厂中就是通过JDK代理或CGLib代理的技术,如果通过setInterfaces方法指定对接口进行代理就会使用JdkDynamicAopProxy,如果是针对类的代理,则使用Cglib2AopProxy。同时,如上如果启动了优化代理的方式,针对接口的代理也会使用Cglib2AopProxy。

在上面的源码中可以通过ProxyFactory的addAdvice()方法添加一个增强,用户可以添加多个增强,多个增强的调用顺序和添加顺序一致,也可以通过addAdvice(int Advice)将增强添加到增强链的具体位置(第一个位置为0)

Spring中配置如下:

<bean id="greetingBefore" class="com.baobaotao.advice.GreetingBeforeAdvice" />

<bean id="target" class="com.baobaotao.advice.NaiveWaiter" />
<bean id="waiter"
        class="org.springframework.aop.framework.ProxyFactoryBean"
        p:proxyInterfaces="com.baobaotao.advice.Waiter" p:target-ref="target"
        p:interceptorNames="greetingAdvice"/>

ProxyFactoryBean的常用属性如下:

  • target:代理的目标对象

  • proxyInterfaces:代理所要实现的接口,可以是多个接口。属性的另一个别名interfaces

  • interceptorNames:需要植入目标的Bean列表,采用Bean的名称指定,指定使用的增强。这些Bean必须是实现了org.aopalliance.intercept.MethodInterceptor或org.springframework.aop.Advisor的Bean,即就是一个增强

  • singleton:返回的代理是否是单实例,默认为单实例

  • optimize:设置为true时,强制使用CGLib代理,对于singleton的代理,推荐使用CGLib,因为CGLib创建代理速度慢,而创建出的对象运行效率高,JDK代理的正好相反

  • proxyTargetClass:是否对类进行代理,设置为true时,使用CGLib代理

测试增强的代码如下:

public class TestAdvice1 {
    public static void main(String[] args) {
        String configPath = "com/baobaotao/advice/beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter");
        waiter.greetTo("John");
    }
}

后置增强

后置增强在目标类方法调用后执行如下新建一个后置增强

GreetingAfterAdvice.java

public class GreetingAfterAdvice implements AfterReturningAdvice {

    //在目标方法调用后执行
    public void afterReturning(Object returnObj, Method method, Object[] args,
            Object obj) throws Throwable {
        System.out.println("Please enjoy yourself!");
    }
}

后置增强需要实现AfterReturningAdvice来定义后置增强的逻辑,AfterReturningAdvice接口定义了唯一的方法afterReturning(Object returnObj, Method method, Object[] args, Object obj) throws Throwable,returnObj为目标类实例。如果在后置增强中抛出异常,若该异常是目标方法生命的异常,则该异常归并到目标方法中;如果不是目标方法所生命的异常,则将其作为运行期异常抛出

环绕增强

环绕增强综合实现了前置、后置增强两者的功能。

如下示例:
GreetingInterceptor.java

public class GreetingInterceptor implements MethodInterceptor {
    //用来截获目标方法的执行,并在前后添加横切逻辑
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object[] args = invocation.getArguments();
        String clientName = (String)args[0];
        //目标方法执行前的调用
        System.out.println("How are you!Mr."+clientName+".");
        //通过反射机制调用目标方法
        Object obj = invocation.proceed();
        //目标方法执行后的调用        
        System.out.println("Please enjoy yourself!");

        return obj;
    }
}

如上源码所示,MethodInterceptor作为环绕增强的接口。该接口拥有唯一的接口方法Object invoke(MethodInvocation var1) throws Throwable;

MethodInvocation封装了目标方法、入参数组以及目标方法所在的实例对象,通过getArguments()可以获得目标方法的入参数组,通过proceed()反射调用目标实例相应的方法。

异常抛出增强

异常抛出增强适合事务管理的场景,这里用一个模拟出错抛出异常,来使用异常抛出增强的例子:

ForumService.java

public class ForumService {
    public void removeForum(int forumId) {
        // do sth...
        throw new RuntimeException("运行异常。");
    }
    public void updateForum(Forum forum) throws Exception{
        // do sth...
        throw new SQLException("数据更新操作异常。");

    }
}

如下定义一个异常抛出增强:
TransactionManager.java

public class TransactionManager implements ThrowsAdvice {
    public void afterThrowing(Method method, Object[] args, Object target,
            Exception ex) throws Throwable {
        System.out.println("-----------");
        System.out.println("method:" + method.getName());
        System.out.println("抛出异常:" + ex.getMessage());
        System.out.println("成功回滚事务。");
    }
}

afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable

ThrowsAdvice 异常增强接口并无任何方法,是一个标识接口,在运行期间使用反射机制自行判断,采用方法名为afterThrowing的方法定义增强方法。方法入参中前三个是可选的(一起选或不选),最后一个参数为Throwable或其子类,可以在一个异常抛出增强中同时定义多个afterThrowing(), Spring会选择最匹配的增强方法,即就是类的继承关系越近相似度越高,匹配度就越高。

例如,若定义了afterThrowing(SQLException e)和afterThrowing(Throwable e);而抛出了SQLException时就会匹配afterThrowing(SQLException e)。

引介增强

引介增强并非在目标方法周围织入增强,而是为目标类创建新的方法和属性,所以引介增强的连接点是类级别的,而非方法级别的,通过引介增强,可以为目标类添加一个接口的实现。如下面这个例子:

Monitorable.java

public interface Monitorable {
   void setMonitorActive(boolean active);
}

ControllablePerformaceMonitor.java

public class ControllablePerformaceMonitor
        extends
            DelegatingIntroductionInterceptor implements Monitorable {
    private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<Boolean>();
    public void setMonitorActive(boolean active) {
        MonitorStatusMap.set(active);
    }
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object obj = null;
        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;
    }
}

第一段源码定义了一个接口,第二段源码定义了一个引介增强,继承了DelegatingIntroductionInterceptor类,实现了Monitorable接口,在类中实现了setMonitorActive方法,该方法设置了类中的一个变量,这个变量用来控制是否开启性能监控功能。

实现接口的方法配合拦截的方法就可以支持性能监视可控代理。

引介增强的配置如下:

    <bean id="pmonitor" class="com.baobaotao.introduce.ControllablePerformaceMonitor" />
    <bean id="forumServiceTarget" class="com.baobaotao.introduce.ForumService" />
    <bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"
        p:interfaces="com.baobaotao.introduce.Monitorable" 
        p:target-ref="forumServiceTarget"
        p:interceptorNames="pmonitor" 
        p:proxyTargetClass="true" />

引介增强的配置需要制定引介增强所实现的接口,其次需要将proxyTargetClass设置为true。

其他类的代码和本文开头的类一致,引介增强测试类如下:
TestIntroduce.java

public class TestIntroduce {
    public static void main(String[] args) {
        String configPath = "beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        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); 
    }
}

执行结果如下:

这里写图片描述

从运行结果可以看出在未开启性能监控时是按照正常的逻辑运行的,开启性能监控之后就横切入了增强的逻辑。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值