SpringAOP代理机制
SpringAOP使用了两种代理机制:一种是基于JDK的动态代理;另一种是基于CGLib的动态代理。之所以需要两种代理机制,很大程度上是因为JDK本身直提供了接口代理,而不支持类的代理。对于singleton的代理对象或者具有实例池的代理,因为无须频繁创建代理对象,所以比较适合用CGLib动态代理技术,反之适合用JDK动态代理技术。
SpringAOP通过pointcut(切点)指定在哪些类的哪些方法上织入横切逻辑,通过Advice(增强)描述横切逻辑和方法的具体织入点(方法前,方法后,方法的两端等)。此外,Spirng通过Advisor(切面)将pointcut和advisor两者组装起来。有了Advisor的信息,Spring就可以利用JDK或CGLib的动态代理技术采用统一的方式为Bean创建织入切面的代理对象了。
增强类型
- 前置增强:org.springframework.aop.BeforeAdvice代表前置增强,因为Spring只支持方法级的增强,所以MethodBeforeAdivce是目前可用的前置增强,表示在目标方法执行前实施增强。
- 后置增强:org.springframework.aop.AfterReturningAdvice代表后置增强,表示在目标方法执行后实施增强。
- 环绕增强:org.aopalliance.intercept.MethodInterceptor代表环绕增强,表示在目标方法执行前后实施增强。
- 异常抛出增强:org.springframework.aop.ThrowsAdvice代表抛出异常增强,表示在目标方法抛出异常后实施增强。
- 引介增强:org.springframework.aop.IntroductionInterceptor代表引介增强,表示在目标类中添加一些新的方法和属性,所以引介增强的连接点是类级别,而非方法级别的。
下面举例前置增强、后置增强、环绕增强、引介增强。
前置增强:
package advice;
public interface Waiter {
void greetTo(String name);
void serveTo(String name);
}
package 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("serve to "+name+"....");
}
}
package advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method arg0, Object[] args, Object obj)
throws Throwable {
String clientName = (String) args[0];
System.out.println("Hello! Mr."+clientName+"!");
}
}
BeforeAdvice是前置增强的接口,方法前置增强的MethodBeforeAdvice接口是其子类。MethodBeforeAdvice 接口仅定义了唯一的方法before(Method method Object[] args, Object obj)throws Throwable,method为目标类的方法,args为目标类方法的入参;obj为目标类实例。
package advice;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
public class TestBeforeAdivce {
/**
* @param args
*/
public static void main(String[] args) {
Waiter target = new NaiveWaiter();
BeforeAdvice advice = new GreetingBeforeAdvice();
//Spring提供的代理工厂
ProxyFactory pf = new ProxyFactory();
//设置代理目标
pf.setTarget(target);
//为代理目标添加增强
pf.addAdvice(advice);
//生成代理实例
Waiter proxy = (Waiter) pf.getProxy();
proxy.greetTo("Ytomson");
proxy.serveTo("Jhon");
}
}
运行结果:
Hello! Mr.Ytomson! ---------通过前置增强引入
greet to Ytomson…
Hello! Mr.Jhon! ------------通过前置增强引入
serve to Jhon…
在Spring中配置,通过ProxyFactoryBean配置代理
<bean id="greetingBefore" class="advice.GreetingBeforeAdvice"></bean>
<bean id="target" class="advice.NaiveWaiter"></bean>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="advice.Waiter"
p:interceptorNames="greetingBefore"
p:target-ref="target"></bean>
proxyInterfaces指定代理的接口,如果是多个接口,请使用<list>元素。
interceptorNames指定使用的增强。
target-ref指定对哪个Bean进行代理。
ProxyFactoryBean是FactoryBean接口的实现类,它负责实例化一个Bean。ProxyFactoryBean负责为其他Bean创建代理实例,它内部使用ProxyFactory来完成这一个工作。
package advice;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeansTestBeforeAdivce {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:advice/beans.xml");
Waiter waiter = (Waiter) ctx.getBean("waiter");
waiter.greetTo("Ytomson");
}
}
运行结果:
Hello! Mr.Ytomson!
greet to Ytomson…
后置增强:
package advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class GreetingAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
System.out.println("Please enjoy yourself!");
}
}
添加后置增强:
<bean id="greetingBefore" class="advice.GreetingBeforeAdvice"></bean>
<bean id="greetingAfter" class="advice.GreetingAfterAdvice"></bean>
<bean id="target" class="advice.NaiveWaiter"></bean>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="advice.Waiter"
p:interceptorNames="greetingBefore,greetingAfter"
p:target-ref="target"></bean>
运行结果:
Hello! Mr.Ytomson! ---------前置增强引入的逻辑
greet to Ytomson…
Please enjoy yourself! ---------后置增强引入的逻辑
环绕增强:
package advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class GreetingInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object[] args = invocation.getArguments();//目标方法入参
String clientName = (String) args[0];
System.out.println("Hello! Mr."+clientName+"!");
Object obj = invocation.proceed();//通过反射机制调用目标方法
System.out.println("Please enjoy yourself!");
return obj;
}
}
添加环绕增强:
<bean id="greetingBefore" class="advice.GreetingBeforeAdvice"></bean>
<bean id="greetingAfter" class="advice.GreetingAfterAdvice"></bean>
<bean id="greetingAround" class="advice.GreetingInterceptor"></bean>
<bean id="target" class="advice.NaiveWaiter"></bean>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="advice.Waiter"
p:interceptorNames="greetingAround"
p:target-ref="target"></bean>
运行结果:
Hello! Mr.Ytomson!
greet to Ytomson…
Please enjoy yourself!
可见,这是前置增强和后置增强的联合效果。
引介增强:
package advice;
public interface Monitorable {
void setMonitorActive(boolean active);
}
通过该接口方法控制业务类性能监控功能的激活和关闭状态。
下面,通过扩展DelegatingIntroductionInterceptor为目标类引入性能监控的可控功能
package 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;
}
}
配置引介增强:
<bean id="pmonitor" class="advice.ControllablePerformanceMonitor"></bean>
<bean id="forumServiceTarget" class="advice.ForumServiceImpl"></bean>
<bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interfaces="advice.Monitorable"
p:target-ref="forumServiceTarget"
p:interceptorNames="pmonitor"
p:proxyTargetClass="true"></bean>
由于引介增强一定要通过创建子类来生成代理,所以需要强制使用CGLib,否则会报错。
测试引介增强:
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeansTestBeforeAdivce {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:advice/beans.xml");
//Waiter waiter = (Waiter) ctx.getBean("waiter");
//waiter.greetTo("Ytomson");
ForumService forumService = (ForumService) ctx.getBean("forumService");
forumService.removeForum(10);
forumService.removeTopic(1022);
Monitorable monitorable = (Monitorable) forumService;
monitorable.setMonitorActive(true);
forumService.removeForum(10);
forumService.removeTopic(1022);
}
}
运行结果:
删除forum记录:10
删除topic记录:1022
begin monitor
删除forum记录:10
begin monitor
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.removeForum花费40毫秒。
begin monitor
删除topic记录:1022
begin monitor
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.removeTopic花费20毫秒。
在一开始只有业务逻辑执行,性能监视的功能没有执行,之后性能监视功能正常启用,两个业务方法都启用了性能监视的功能。