林炳文Evankaka原创作品。转载请注明出处http://blog.youkuaiyun.com/evankaka
Spring AOP 提供了 5 种类型的通知,它们分别是 Before Advice(前置通知)、After Returning Advice(后置通知)、Interception Around Advice(周围通知)、Throws Advice(异常通知)和 Introduction Advice(引介通知)。下面分别进行介绍。
1、前置通知(前置增强)
前置通知顾名思意,该通知织入在方法调用之前。我们可以通过Spring 的MethodBeforeAdvice 接口实现。MethodBeforeAdvice 只提供方法before(),该方法无返回值,方法接受调用方法,方法参数和目标对象这3个参数。这里需要注意的是,虽然before()方法得到了调用方法的参数,这里值得注意的是方法参数可以进行更改!
创建一个工人接口类:
/**
*功能 工人接口类
*作者 林炳文(ling20081005@126.com 博客:http://blog.youkuaiyun.com/evankaka)
*时间 2015.4.22
*/
package com.mucfc.advice;
public interface Worker {
void getTool(String tool);
void digSomething(String type);
void initWork();
}
工人实现类:
/**
*功能 工人实现类
*作者 林炳文(ling20081005@126.com 博客:http://blog.youkuaiyun.com/evankaka)
*时间 2015.4.22
*/
package com.mucfc.advice;
import javax.management.RuntimeErrorException;
public class DigWorker implements Worker{
@Override
public void getTool(String tool) {
System.out.println("---------------调用方法--------------");
System.out.println("领取工具:"+tool);
// throw new RuntimeException("运行异常");
}
@Override
public void digSomething(String type) {
System.out.println("---------------调用方法--------------");
System.out.println("开始卖力挖取:"+type);
}
@Override
public void initWork() {
System.out.println("生成一个DigWorker实例");
}
}
3、创建前置增强的方法:
/**
*功能 前置增强方法
*作者 林炳文(ling20081005@126.com 博客:http://blog.youkuaiyun.com/evankaka)
*时间 2015.4.22
*/
package com.mucfc.advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class WearingBeforeAdvice implements MethodBeforeAdvice{
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
String thing=(String)arg1[0];
System.out.println("--------------进行前置增强---------------");
System.out.println("得到"+thing+"之前先穿上工作服!!");
}
}
4、将增强方法织入到相应的地方
//1、不使用xml配置实例化
Worker target=new DigWorker();
BeforeAdvice advice=new WearingBeforeAdvice();
//Spring提供的代理工厂
ProxyFactory pFactory=new ProxyFactory();
//设置代理目标,默认使用Cglib动态代理
pFactory.setTarget(target);
//为代理目标添加前置增强,这里会为目标的每个方法都增加增强
pFactory.addAdvice(advice);
//生成代理实例
Worker proxy=(Worker)pFactory.getProxy();
proxy.getTool("钻机");
proxy.digSomething("煤炭");
结果:在工人实现类的两个方法调用之前都进行了前置增强
接口代理默认是使用JDK动态代理,如要使用CGLIB动态代码,在加上:
//指定做优化,又将使用cglib动态
pFactory.setOptimize(true);
若要使用JDK动态代理,加上:
//指定对接口进行代理,通过JDK动态代理
pFactory.setInterfaces(target.getClass().getInterfaces());
二、后置增强
后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
1、增强方法代理
/**
*功能 后置增强方法
*作者 林炳文(ling20081005@126.com 博客:http://blog.youkuaiyun.com/evankaka)
*时间 2015.4.22
*/
package com.mucfc.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class WeaningAfterAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
System.out.println("--------------进行后置增强---------------");
System.out.println("工作结束,请先脱掉工作服");
}
}
工人还是上面的工人,其它方法不变。
2、我们在进行增强配置时,可以直接使用API函数,但是这样比较复杂,下面我们来用XML注解配置的方法来同时实现前置和后后置增强
在src文件夹下新建 一个XML文件,取名为beans.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
然后就是下面的配置如下:
<!-- 要进行增强的目标 -->
<bean id="target" class="com.mucfc.advice.DigWorker" init-method="initWork"/>
<!-- 前置增强的方法 -->
<bean id="beforeAdvice" class="com.mucfc.advice.WearingBeforeAdvice" />
<!-- 后置增强的方法 -->
<bean id="afterAdvice" class="com.mucfc.advice.WeaningAfterAdvice" />
<!--同时设置前置增强和后置增强方法 -->
<bean id="worker1" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="com.mucfc.advice.Worker" p:interceptorNames="beforeAdvice,afterAdvice"
p:target-ref="target" lazy-init="true"/>
这里 p:interceptorNames="beforeAdvice,afterAdvice"表示同时加入了前置和后置增强。
lazy-init="true"表示能要用的时候才初始化这个实例
3、调用
//2、下面使用Spring的xml配置实例化
ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
//worker1带前置和后置增强
Worker worker1=(Worker)ctx.getBean("worker1");
System.out.println("worker1进入工作:");
worker1.getTool("锄头");
结果如下:
在调用方法之前都进行了前置和后置增强,注意,这里会为工人类的所有方法都织入增强!
三、环绕增强
环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
1、添加增强方法类:
/**
*功能 环绕增强方法
*作者 林炳文(ling20081005@126.com 博客:http://blog.youkuaiyun.com/evankaka)
*时间 2015.4.22
*/
package com.mucfc.advice;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;;
public class WearingInterceptor implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
Object[] args=arg0.getArguments();//目标方法入参
String toolname=(String)args[0];
System.out.println("--------------环绕增强开始---------------");
System.out.println("得到"+toolname+"之前先穿上工作服");
Object object=arg0.proceed();
System.out.println("工作结束,请先脱掉工作服");
System.out.println("--------------环绕增强结束---------------");
; return object;
}
}
2、然后还是在beans.xml中配置:
<!-- 环绕增强的方法 -->
<bean id="aroundAdvice" class="com.mucfc.advice.WearingInterceptor" />
<!--设置环绕增强方法 -->
<bean id="worker2" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="com.mucfc.advice.Worker" p:interceptorNames="aroundAdvice"
p:target-ref="target" lazy-init="true"/>
3、使用
//work2带环绕增强
Worker worker2=(Worker)ctx.getBean("worker2");
System.out.println("worker2进入工作:");
worker2.getTool("钻机");
结果:
三、异常抛出增强
异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
1、将前置增强方法中的工人实现 类的方法增个, throw new RuntimeException("运行异常");
@Override
public void getTool(String tool) {
System.out.println("---------------调用方法--------------");
System.out.println("领取工具:"+tool);
throw new RuntimeException("运行异常");
}
2、处理异常抛出的类
/**
*功能 异常抛出增强类
*作者 林炳文(ling20081005@126.com 博客:http://blog.youkuaiyun.com/evankaka)
*时间 2015.4.22
*/
package com.mucfc.advice;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class WeaningThrowAdvice 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("成功回滚事务");
}
}
3、XML配置
<!-- 异常抛出增强的方法 -->
<bean id="thorwAdvice" class="com.mucfc.advice.WeaningThrowAdvice" />
<!--设置异常抛出增强方法 proxyTargetClass="true",使用CgLib代理-->
<bean id="worker3" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="thorwAdvice"
p:target-ref="target"
p:proxyTargetClass="true"/>
4、使用
//work3带环绕增强
Worker worker3=(Worker)ctx.getBean("worker3");
System.out.println("worker3进入工作:");
worker3.getTool("锤子");
注意,由于这里给方法强制抛出了异常,所以要运行上面的代码,记得要把前面的增强方法测试的代码都给去掉,只留下以下代码:
五、引介增强
引介增强是一种比较特殊的增强类型,它不是在目标方法周围织入增强,而是为目标类创建新的方法和属性,所以引介增强的连接点是类级别的,而非方法级别的。通过引介增强,我们可以为目标类添加一个接口的实现,即原来目标类未实现某个接口,通过引介增强可以为目标类创建实现该接口的代理。一般用得比较 少,这里就不写代码了
林炳文Evankaka原创作品。转载请注明出处http://blog.youkuaiyun.com/evankaka