目录
advance,通知,在Spring中叫增强也许更加合理。所谓增强,其实就是向各个程序内部注入一些代码从而增强原有程序的功能。
Spring使用增强类定义横切逻辑,同时由于Spring只支持方法连接点,增强还包括在方法的哪一点加入横切代码的方位信息,所以增强即包括横切逻辑,又包含部分连接点的信息。
按照增强在目标类方法连接点的位置可以将增强划分为以下五类:
增强名称 | 接口 | 描述 |
前置增强 | org.springframework.aop.BeforeAdvice 因为Spring只支持方法级的增强,所以MethodBeforeAdvance接口是目前可用的前置增强。 |
在目标方法执行前来实施增强 |
后置增强 | org.springframework.aop.AfterReturningAdvice | 在目标方法执行后来实施增强 |
环绕增强 | org.aopalliance.intercept.MethodInterceptor | 在目标方法执行前、后实施增强。环绕增强是AOP联盟定义的接口,其他四种增强接口则是Spring定义的接口。 |
异常抛出增强 | org.springframework.aop.ThrowsAdvice | 在目标方法抛出异常后来实施增强 |
引介增强 | org.springframework.aop.Introductioninterceptor | 表示在目标类中添加一些新的方法和属性。引介增强是一种特殊的增强。他可以在目标类中添加属性和方法,通过拦截定义一个接口,让目标代理实现这个接口。他的连接点是类级别的,而前面的几种则是方法级别的。 |
通过实现这些增强接口,在实现这些接口的方法当中定义横切逻辑,就可以将它们织入目标类方法的相应连接点位置。
一、前置增强
增强目标类
public class NativeWaiter {
public void greetTo(String name) {
System.out.println("greet to " + name + "...");
}
public void serveTo(String name) {
System.out.println("serving to " + name + "...");
}
}
增强类,实现 MethodBeforeAdvice 接口
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
/**
* 前置增强方法
* 当该方法发生异常时,将阻止目标方法的执行
*
* @param method 目标类方法
* @param objects 目标类方法入参
* @param o 目标类对象实例
*/
public void before(Method method, Object[] objects, Object o) throws Throwable {
String name = (String) objects[0];
System.out.println("How are you!Mr." + name + ".");
}
}
测试类,使用cglib生成代理对象
@Test
public void before_cglib_proxy() {
//创建目标对象
NativeWaiter target = new NativeWaiter();
//创建前置增强器
BeforeAdvice advice = new GreetingBeforeAdvice();
//创建代理工厂对象
ProxyFactory pf = new ProxyFactory();
//设置代理类
pf.setTarget(target);
//也可以启动优化代理方式,会使用cglib来生成代理。当使用接口时,只要调用了该方法,都会使用cglib方式来生成代理对象
//pf.setOptimize(true);
//设置增强类
pf.addAdvice(advice);
//生成代理实例
NativeWaiter proxy = (NativeWaiter) pf.getProxy();
proxy.greetTo("John");
proxy.serveTo("Tom");
}
运行结果
How are you!Mr.John.
greet to John...
How are you!Mr.Tom.
serving to Tom...
总结:
1、增强器类,实现 MethodBeforeAdvice 接口;
2、利用Spring的ProxyFactory,加入代理目标和增强类,然后生成代理对象;
二、ProxyFactory介绍
1、基本使用
其实ProxyFactory代理技术就是利用jdk代理或者cglib代理的技术,将增强应用到目标类当中。
Spring定义的AopProxy接口具有两个final类型的实现类:
CglibAopProxy : 是使用cglib代理技术来创建代理
JdkDynamicAopProxy : 是使用jdk代理技术来创建代理
使用proxyFactory.setInterfaces(target.getClass().getInterfaces())的方法指定目标接口进行代理,则ProxyFactory使用JdkDynamicAopProxy;如果针对类的代理,则使用CglibAopProxy,此外还可以通过ProxyFactory的setOptimize(true)方法启动优化代理方式,这样针对接口的代理也会使用CglibAopProxy。比如上面的前置增强BeforeAdviceTest就是使用CglibAopProxy来生成动态代理类的。
那么使用JDK代理如何来实现的呢?代码如下:
首先,定义接口Waiter,并让NaiveWaiter实现该接口
public interface Waiter {
void greetTo(String name);
void serveTo(String name);
}
public class NaiveWaiter implements Waiter {
//该类具体实现代码不变
}
然后,使用jdk代理具体实现方法
@Test
public void before_jdk_proxy() {
//创建目标对象
Waiter target = new NaiveWaiter();
//创建前置增强器
BeforeAdvice advice = new GreetingBeforeAdvice();
//创建代理工厂对象
ProxyFactory pf = new ProxyFactory();
//设置代理接口
pf.setInterfaces(target.getClass().getInterfaces());
//设置代理类
pf.setTarget(target);
//设置增强类
pf.addAdvice(advice);
//生成代理实例
Waiter proxy = (Waiter) pf.getProxy();
proxy.greetTo("John");
proxy.serveTo("Tom");
}
如上ProxyFactory.setInterfaces(target.getClass().getInterfaces())方法,该方法会获取到接口并将其加载进去。当目标对象如果没有实现任何接口时,那么即使使用了该方法,生成代理对象时也是使用CglibAopProxy来生成的。如果目标对象实现了一个接口,那么ProxyFactory.getProxy()方法,返回对象必须转换成接口类型。
ProxyFactory通过addAdvice来增加一个增强。用户可以使用该方法增加多个增强,通过增强形成一个增强链,他们的调用顺序和添加顺序是一致的
2、Spring通过配置文件实现增强
在bean.xml配置文件中进行如下配置
<bean id="gerrtingBefore" class=" demo04.advance.GreetingBeforeAdvice"/>
<bean id="target" class="demo04.advance.NativeWaiter"/>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="demo04.advance.Waiter"
p:interceptorNames="gerrtingBefore"
p:target-ref="target"
/>
根据以上配置信息,介绍下ProxyFactoryBean配置文件当中常用的属性:
target:我们需要代理的目标对象
proxyInterfaces:代理所要实现的接口,可以是多个接口
interceptorNames:需要织入的目标对象的Bean的列表(增强类的Bean列表),使用Bean的名称来指定。
singleton:确定返回的代理是不是单实例的,系统默认返回的是单实例的。
optimize:当值为true时,强制使用cglib代理。当是singleton的实例时我们推荐使用cglib代理,当是其他作用域的时候,推荐使用JDK的代理。原因是cglib创建代理速度比较慢,但是运行效率高。JDK代理则刚好相反。
proxyTargetClass:是否对类进行代理而不是对接口进行代理,当值为true的时候使用cglib代理。
测试代码:
@Test
public void before_advice_xml_test() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans/beans.xml");
Waiter waiter = context.getBean("waiter", Waiter.class);
waiter.greetTo("JayChou");
}
三、后置增强和异常增强
在上面的例子中,前置增强实现了MethodBeforeAdvice接口,后置增强、异常增强和前置增强一样,只需实现相应的接口就可以了。
四、环绕增强
实现接口MethodInterceptor,其他与前置增强一样
public class GreetingInterceptor implements MethodInterceptor{
/**
* 业务逻辑实现类
* @param methodInvocation 封装了目标方法和入参数组以及目标方法所带的实例对象
* @return 代理对象
* @throws Throwable
*/
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//获取目标方法的入参
Object[] args=methodInvocation.getArguments();
//获取方法名称
String clickName= (String) args[0];
System.out.println("GreetingInterceptor:How are you!");
//利用反射机制来调用目标方法
Object object=methodInvocation.proceed();
System.out.println("GreetingInterceptor: please enjoy youself!");
return object;
}
}
五、引介增强
引介增强的连接点是类级别的而非方法级别的,通过引介增强我们可以为目标类添加一个接口的实现即原来目标类未实现某个接口,那么通过引介增强可以为目标类创建实现某接口的代理。Spring为引介增强提供了Introductioninterceptor接口,该接口未提供任何方法,所以一般在它的实现类DelegatingIntroductionInterceptor中进行扩展。
总结:
1、按照增强在目标类方法连接点的位置可以将增强划分为五类:前置增强、后置增强、环绕增强、异常增强、引介增强;
2、Spring定义了内部代理工厂类ProxyFactory,它利用jdk代理或者cglib代理的技术,将增强应用到目标类当中。
3、Spring定义的AopProxy接口具有两个final类型的实现类:
CglibAopProxy : 是使用cglib代理技术来创建代理
JdkDynamicAopProxy : 是使用jdk代理技术来创建代理
4、实现一个目标类的增强,首先实现增强相关接口,然后将目标类和增强对象放入代理工厂中,最后代理工厂生成代理对象;