一、前置通知和后置通知
本篇博客记录一下spring的前置通知、后置通知、环绕通知以及异常通知。目前我所能想到的就是可以利用这些通知来做日志或者事务方面的事情,具体的引用场景根据业务场景来定。下面首先来看看前置通知以及后置通知的使用。
①、首先定义接口如下:
public interface ISomeService {
void doStart();
void sayHello();
String doEnd();
}
②、实现接口
public class SomeServiceImpl implements ISomeService {
@Override
public void doStart() {
System.out.println("doStart执行了.....");
}
@Override
public void sayHello() {
System.out.println("sayHello执行了.....");
}
@Override
public String doEnd() {
System.out.println("doEnd执行了.....");
return "abcd";
}
}
③、实现前置通知和后置通知接口
public class MyMethodAdvice implements MethodBeforeAdvice, AfterReturningAdvice{
/**
* 在目标方法执行之前执行
* @param method
* @param objects
* @param o
* @throws Throwable
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("在执行方法前执行了增强方法....");
}
/**
* 后置通知方法
*
* 后置通知可以获取到目标方法的返回值,但是不能改变目标方法的返回值
* @param o
* @param method
* @param objects
* @param o1
* @throws Throwable
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("在执行方法后执行了增强方法......返回值:");
}
}
④、spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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="someService" class="cn.lwb.spring.aop._01_aop.SomeServiceImpl" />
<bean id="myMethodAdvice" class="cn.lwb.spring.aop._01_aop.MyMethodAdvice" />
<bean id="someServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="someService" />
<property name="interfaces" value="cn.lwb.spring.aop._01_aop.ISomeService"/>
<property name="interceptorNames" value="myMethodAdvice"/>
</bean>
</beans>
⑤、测试
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("cn/lwb/spring/aop/_01_aop/applicationContext.xml");
//需要注意的是,这里获取的是实现通知接口的对象
ISomeService someServiceProxy = (ISomeService) context.getBean("someServiceProxy");
String s = someServiceProxy.doEnd();
System.out.println("doEnd的返回值:" + s);
}
}
⑥、运行结果
⑦、总结:
定义前置通知,需要实现MethodBeforeAdvice接口,该接口中有一个befor(),会在目标方法执行之前执行。前置通知的特点是:
1、在目标方法之前执行。
2、不改变目标方法的执行流程,前置通知的代码不能阻止目标方法的执行。
3、不改变目标方法的执行结果。
定义后置通知,需要实现接口AfterReturningAdvice。该方法中有一个afterReturning(),会在目标方法执行之后执行。后置通知的特点是:
1、在目标方法之前执行。
2、不改变目标方法的执行流程,后置通知的代码不能阻止目标方法的执行。
3、不改变目标方法的执行结果。
二、环绕通知
定义环绕通知,需要实现Methodlnterceptor接口。环绕通知,也叫方法拦截器,可以 在目标方法调用之前及之后做处理,可以改变目标方法的返回值,也可以改变程序执行流程。
代码和前面的代码类似,只是实现接口的代码变了:
public class MyMethodInterceptor implements MethodInterceptor {
/**
* 环绕通知可以改变方法的返回结果
* @param methodInvocation
* @return
* @throws Throwable
*/
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕通知,在方法执行之前.....");
Object proceed = methodInvocation.proceed();
System.out.println("环绕通知,在方法执行之后.....");
return proceed.toString().toUpperCase();
}
}
可以看见,最后的执行结果是改变了本来应该返回的结果。所以,环绕通知是可以改变目标方法的执行结果的。
三、异常通知
定义异常通知,需要实现ThrowsAdvice接口。该接口的主要作用是,在目标方法抛出异常后,根据异常的不同做出相应的处理。当该接口处理完异常后,会简单地将异常再次抛出给目标方法。不过,这个接口较为特殊,从形式上看,该接口中没有必须要实现的方法。但是这个接 口却确实有必须要实现的方法afterThrowing()。这个方法重载了四种形式。由于使用时,一 般只使用其中一种,若要都定义到接口中,则势必要使程序员在使用时必须要实现这四个方 法。这是很麻烦的。所以就将该接口定义为了标识接口(没有方法的接口)。这四个方法在打幵ThrowsAdvice源码后,上侧的注释部分可以看到: