Spring AOP 创建增强类

    AOP联盟为增强定义了org.aopalliance.aop.Advice接口,Spring支持5种类型的增强:

    1)前置增强:org.springframework.aop.BeforeAdvice 代表前置增强,因为Spring 只支持方法级的增强,所有MethodBeforeAdvice是目前可用的前置增强,表示在目标方法执行前实施增强,而BeforeAdvice是为了将来版本扩展需要而定义的;
    2)后置增强:org.springframework.aop.AfterReturningAdvice 代表后增强,表示在目标方法执行后实施增强;
    3)环绕增强:org.aopalliance.intercept.MethodInterceptor 代表环绕增强,表示在目标方法执行前后实施增强;
    4)异常抛出增强:org.springframework.aop.ThrowsAdvice 代表抛出异常增强,表示在目标方法抛出异常后实施增强;
    5)引介增强:org.springframework.aop.IntroductionInterceptor 代表引介增强,表示在目标类中添加一些新的方法和属性。
 
1、前置增强
    模拟服务员向顾客表示欢迎和对顾客提供服务。
Waiter接口:
package com.yyq.advice;
public interface Waiter {
    void greetTo(String name);
    void serveTo(String name);
}

NaiveWaiter服务类:

package com.yyq.advice;
public class NaiveWaiter implements Waiter {
    @Override
    public void greetTo(String name) {
        System.out.println("greet to " + name + "...");
    }
    @Override
    public void serveTo(String name) {
        System.out.println("serving to " + name + "...");
    }
}

前置增强实现类:

package com.yyq.advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        String clientName = (String) objects[0];
        System.out.println("How are you ! Mr." + clientName + ".");
    }
}

测试方法:

@Test
    public void testBeforeAdvice(){
        Waiter target = new NaiveWaiter();
        BeforeAdvice advice = new GreetingBeforeAdvice();
        ProxyFactory pf = new ProxyFactory(); //Spring提供的代理工厂
        pf.setTarget(target);    //设置代理目标
        pf.addAdvice(advice);
        Waiter proxy = (Waiter)pf.getProxy();    //生成代理实例
        proxy.greetTo("John");
        proxy.serveTo("Tom");
    }
输出结果:
How are you ! Mr.John.
greet to John...
How are you ! Mr.Tom.
serving to Tom...
 
在Spring中配置:beans.xml
<bean id="greetingAdvice" class="com.yyq.advice.GreetingBeforeAdvice"/>
<bean id="target" class="com.yyq.advice.NaiveWaiter"/>

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

  ProxyFactoryBean 是FactoryBean接口的实现类。

    · target:代理的目标对象;
    · proxyInterfaces:代理所实现的接口,可以是多个接口。该属性还有一个别名属性interfaces;
    · interceptorNames:需要织入目标对象的Bean列表,采用Bean的名称指定。这些Bean必须是实现了org.aopalliance.intercept.Method 或 org.springframework.aop.Advisor的Bean,配置中的顺序对应调用的顺序;
    · singleton:返回的代理是否是单实例,默认为单实例;
    · optimize:当设置为true时,强制使用CGLib代理。对于singleton的代理,我们推荐使用CGLib,对于其他作用域类型的代理,最好使用JDK代理。原因是CGLib创建代理时速度慢,而创建出的代理对象运行效率较高,而使用JDK代理的表现正好相反;
    · proxyTargetClass:是否对类进行代理(而不是对接口进行代理),设置为true时,使用CGLib代理。
测试方法:
@Test
    public void testBeforeAdvice2(){
        String configPath = "com\\yyq\\advice\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter1");
        waiter.greetTo("Joe");
    }
输出结果:
How are you ! Mr.Joe.
greet to Joe...
 
2、后置增强
    后置增强在目标类方法调用后执行。模拟服务员在每次服务后使用礼貌用语。
GreetingAfterAdvice后置增强实现类:
package com.yyq.advice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class GreetingAfterAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o2) throws Throwable {
        System.out.println("Please enjoy yourself.");
    }
}

 在beans.xml文件添加后置增强:

<bean id="greetingBefore" class="com.yyq.advice.GreetingBeforeAdvice"/>
<bean id="greetingAfter" class="com.yyq.advice.GreetingAfterAdvice"/>
<bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean"
     p:proxyInterfaces="com.yyq.advice.Waiter"
     p:interceptorNames="greetingBefore,greetingAfter"
     p:target-ref="target"/>

测试方法:

 @Test
    public void testBeforeAndAfterAdvice(){
        String configPath = "com\\yyq\\advice\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter2");
        waiter.greetTo("Joe");
    }
结果输出:
How are you ! Mr.Joe.
greet to Joe...
Please enjoy yourself.
 
3、环绕增强
    环绕增强允许在目标类方法调用前后织入横切逻辑,它综合实现了前置、后置增强两者的功能。
GreetingInterceptor环绕增强实现类:
package com.yyq.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class GreetingInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Object[] args = methodInvocation.getArguments();
        String clientName = (String) args[0];
        System.out.println("Hi,Mr " + clientName + ".");
        Object obj = methodInvocation.proceed();
        System.out.println("Please enjoy yourself~");
        return obj;
    }
}

   Spring 直接使用AOP联盟所定义的MethodInterceptor作为环绕增强的接口。该接口拥有唯一的接口方法 Object invoke(MethodInvocation invocation), MethodInvocation不但封装目标方法及其入参数组,还封装了目标方法所在的实例对象,通过MethodInvocation的getArguments()可以获取目标方法的入参数组,通过proceed()反射调用目标实例相应的方法。

在beans.xml文件添加环绕增强:
 <bean id="greetingAround" class="com.yyq.advice.GreetingInterceptor"/>
    <bean id="waiter3" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.yyq.advice.Waiter"
          p:interceptorNames="greetingAround"
          p:target-ref="target"/>

测试方法:

  @Test
    public void testAroundAdvice(){
        String configPath = "com\\yyq\\advice\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter3");
        waiter.greetTo("Joe");
    }
结果输出:
Hi,Mr Joe.
greet to Joe...
Please enjoy yourself~
 
4、异常抛出增强
    异常抛出增强最适合的应用场景是事务管理,当参与事务的某个Dao发送异常时,事务管理器就必须回滚事务。
Forum业务类:
package com.yyq.advice;
public class Forum {
    private int forumId;
    public int getForumId() {
        return forumId;
    }
    public void setForumId(int forumId) {
        this.forumId = forumId;
    }
}

 ForumService业务类:

package com.yyq.advice;
import java.sql.SQLException;
public class ForumService {
    public void removeForum(int forumId){
        System.out.println("removeForum....");
        throw new RuntimeException("运行异常");
    }
    public void updateForum(Forum forum)throws Exception{
        System.out.println("updateForum");
        throw new SQLException("数据更新操作异常。");
    }
}

TransactionManager异常抛出增强实现类:

package com.yyq.advice;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
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("成功回滚事务。");
    }
}
    ThrowAdvice异常抛出增强接口没有定义任何方法,它是一个标识接口,在运行期Spring使用反射的机制自行判断,我们采用以下签名形式定义异常抛出的增强方法:void afterThrowing(Mehod method, Object[] args, Object target, Throwable);方法名必须为afterThrowing,方法入参规定,前三个参数是可选的,要么三个入参提供,要么不提供,最后一个入参是Throwable或者子类。
在beans.xml文件添加异常抛出增强:
<bean id="transactionManager" class="com.yyq.advice.TransactionManager"/>
    <bean id="forumServiceTarget" class="com.yyq.advice.ForumService"/>
    <bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="transactionManager"
          p:target-ref="forumServiceTarget"
          p:proxyTargetClass="true"/>

 测试方法:

@Test
    public void testThrowsAdvice(){
        String configPath = "com\\yyq\\advice\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        ForumService fs = (ForumService)ctx.getBean("forumService");
        try{
            fs.removeForum(10);
        } catch (Exception e) {}
        try{
            fs.updateForum(new Forum());
        } catch (Exception e) {}
    }
结果输出:
----------------
method:removeForum
抛出异常:运行异常
成功回滚事务。
updateForum
----------------
method:updateForum
抛出异常:数据更新操作异常。
成功回滚事务。
 
5、引介增强
        引介增强为目标类创建新的方法和属性,所以引介增强的连接点是类级别的,而非方法级别的。通过引介增强,我们可以为目标类添加一个接口的实现,即原来目标类未实现某个接口,通过引介增强可以为目标类创建实现某个接口的代理。Spring定义了引介增强接口IntroductionInterceptor,该接口没有定义任何的方法,Spring为该接口提供了DelegatingIntroductionInterceptor实现类。
Monitorable:用于标识目标类是否支持性能监视的接口
package com.yyq.advice;
public interface Monitorable {
    void setMonitorActive(boolean active);
}

 ControllablePerformanceMonitor 为引介增强实现类:

package com.yyq.advice;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
public class ControllablePerformanceMonitor extends DelegatingIntroductionInterceptor implements Monitorable {
    private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<Boolean>();
    @Override
    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;
    }
}

PerformanceMonitor监视类:

package com.yyq.advice;

public class PerformanceMonitor {
    private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>();
    public static void begin(String method) {
        System.out.println("begin monitor...");
        MethodPerformance mp = new MethodPerformance(method);
        performanceRecord.set(mp);
    }
    public static void end(){
        System.out.println("end monitor...");
        MethodPerformance mp = performanceRecord.get();
        mp.printPerformance();
    }
}

MethodPerformance记录性能信息:

public class MethodPerformance {
    private long begin;
    private long end;
    private String serviceMethod;
    public MethodPerformance(String serviceMethod){
        this.serviceMethod = serviceMethod;
        this.begin = System.currentTimeMillis();
    }
    public void printPerformance(){
        end = System.currentTimeMillis();
        long elapse = end - begin;
        System.out.println(serviceMethod + "花费" + elapse + "毫秒。");
    }
}

在beans.xml文件添加引介增强:

<bean id="pmonitor" class="com.yyq.advice.ControllablePerformanceMonitor"/>
<bean id="forumServiceImplTarget" class="com.yyq.advice.ForumServiceImpl"/>
<bean id="forumService2" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interfaces="com.yyq.advice.Monitorable"
          p:interceptorNames="pmonitor"
          p:target-ref="forumServiceImplTarget"
          p:proxyTargetClass="true"/>

 

测试方法:
@Test
    public void testIntroduce(){
        String configPath = "com\\yyq\\advice\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        ForumServiceImpl forumServiceImpl = (ForumServiceImpl)ctx.getBean("forumService2");
        forumServiceImpl.removeForum(23);
        forumServiceImpl.removeTopic(1023);
        Monitorable monitorable = (Monitorable)forumServiceImpl;
        monitorable.setMonitorActive(true);
        forumServiceImpl.removeForum(22);
        forumServiceImpl.removeTopic(1023);
    }
结果输出:
模拟删除Forum记录:23
模拟删除Topic记录:1023
begin monitor...
模拟删除Forum记录:22
end monitor...
org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.removeForum花费40毫秒。
begin monitor...
模拟删除Topic记录:1023
end monitor...
org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.removeTopic花费21毫秒。
 
 
posted @ 2016-05-05 22:55  我是一名老菜鸟  阅读(4354)  评论(1编辑  收藏  举报