在上一篇中,我们介绍了aop相关的基本概念,简单介绍了下Spring AOP中的advice,这一篇我们就来熟悉下这些advice的使用。
增强类型
按照增强在目标类方法中的连接点位置,增强分为以下五类:
- 前置增强
BeforeAdvice是spring中提供的前置接口,但因为目前只支持方法级别的通知,所以真正使用的前置增强接口是MethodBeforeAdvice,BeforeAdvice则是留给以后扩展.
- 后置增强
和BeforeAdvice类似,目前使用的后置增强接口是AfterReturningAdvice,AfterAdvice留给以后扩展。
- 环绕增强
通过MethodInterceptor表示环绕增强,在目标方法执行前后实施增强。
- 异常抛出增强
通过ThrowsAdvice表示异常抛出增强,这是一个标签接口,类似Serializable,用于告诉其他对象这个会对抛出的一场进行处理.
- 引介增强
引介增强是一种特殊的增强,它可以通过给目标类实现一个新的接口来创建方法和属性。
增强使用
我们继续用上篇中的村口喇叭的场景来帮助我们理解增强使用。
Trumpet:
public class Trumpet {
/**
* 广播
*/
public void broadcast(){
System.out.println("广播中。。。");
}
}
前置增强
在广播前,我们对喇叭进行一次检修:
BeforeAdvice:
public class BeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("对喇叭进行检修。。。");
}
}
test:
public class TestBeforeAdvice {
public static void main(String[] args) {
Advice beforeAdvice = new BeforeAdvice();
Trumpet target = new Trumpet();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvice(beforeAdvice);
Trumpet proxy = (Trumpet) factory.getProxy();
proxy.broadcast();
}
}
运行结果:
后置增强
在广播后,我们对喇叭进行一次保养
AfterAdvice:
public class AfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("对喇叭进行保养。。。");
}
}
test:
public class TestAfterAdvice {
public static void main(String[] args) {
Advice afterAdvice = new AfterAdvice();
Trumpet target = new Trumpet();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvice(afterAdvice);
Trumpet proxy = (Trumpet) factory.getProxy();
proxy.broadcast();
}
}
运行结果:
环绕增强
在广播前,例行检查,在广播结束后,也进行保养
AroundAdvice:
public class AroundAdvice implements MethodInterceptor {
private void before() {
System.out.println("对喇叭进行检修。。。");
}
private void after() {
System.out.println("对喇叭进行保养。。。");
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
before();
Object result = methodInvocation.proceed();
after();
return result;
}
}
test:
public class TestAroundAdvice {
public static void main(String[] args) {
Trumpet target = new Trumpet();
Advice aroundAdvice = new AroundAdvice();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvice(aroundAdvice);
Trumpet proxy = (Trumpet)factory.getProxy();
proxy.broadcast();
}
}
运行结果:
异常抛出增强
喇叭坏了(发生异常),通知广播取消
Trumpet修改,模拟异常情况:
/**
* 模拟异常情况
*/
public void broken(){
throw new RuntimeException();
}
ThrowsAdvice:
public class ThrowsAdvice implements org.springframework.aop.ThrowsAdvice {
public void afterThrowing(Method method, Object[] args,Object target, Exception ex){
System.out.println("喇叭坏了,今天休息。。。");
}
}
test:
public class TestThrowsAdvice {
public static void main(String[] args) {
Trumpet target = new Trumpet();
Advice throwsAdvice = new ThrowsAdvice();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvice(throwsAdvice);
Trumpet proxy = (Trumpet)factory.getProxy();
try{
proxy.broken();
}catch (Exception e){
}
}
}
运行结果:
*我们可以看到,我们的异常增强类实现了ThrowsAdvice接口,这个接口和
Serializable一样,没有提供抽象方法,就是一个标签接口,用来告诉其他对象他实现了什么功能,在我们这里,我们里面处理的方法名只能是afterthrowing(Method method,Object[] args,Object target, Excetion ex)
引介增强
引介增强是一种特殊的增强,它不仅仅体现与在目标类的周围增加横切逻辑,更厉害的是,它可以给目标类实现本来没有实现的接口,从而达到扩展方法和属性的目的。
还是继续用喇叭的例子,村里公共设施在配电室有个单独开关(扩展的接口),可以开启录音功能。
MonitorAccess:
public interface MonitorAccess {
void setMonitorActive(Boolean b);
}
MonitorIntroduction:
public class MonitorIntroduction extends DelegatingIntroductionInterceptor implements MonitorAccess {
private ThreadLocal<Boolean> monitor = new ThreadLocal<>();
@Override
public void setMonitorActive(Boolean b) {
monitor.set(b);
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
if(monitor.get() != null && monitor.get()){
System.out.println("对广播进行录音。。。");
}
return super.invoke(mi);
}
}
xml:
<bean id="target" class="com.ljw.testSpringMode.aop.advice.Trumpet"></bean>
<bean id="introduction" class="com.ljw.testSpringMode.aop.advice.MonitorIntroduction"/>
<bean id="trumpet" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="com.ljw.testSpringMode.aop.advice.MonitorAccess"
p:interceptorNames="introduction"
p:target-ref="target"
p:proxyTargetClass="true"
/>
test:
public class TestIntroduction {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:Trumpet.xml");
Trumpet trumpet = (Trumpet) context.getBean("trumpet");
trumpet.broadcast();
MonitorAccess monitorAccess = (MonitorAccess) trumpet;
monitorAccess.setMonitorActive(true);
trumpet.broadcast();
}
}
运行结果:
首先不开启开关:
开启以后:
总结
在上面我们通过接口的形式来实现增强,通过编程的方式实现了代理,主要使用到了ProxyFactory和ProxyFactoryBean,有关它们的解析会在后面的源码分析中进行。