前一篇文章Spring AOP之—基于JDK动态代理和CGLib动态代理的AOP实现 介绍了AOP的底层实现,基于JDK动态代理和CGLib动态代理。手工编码的方式很繁琐,本文介绍通过ProxyFactory和配置的方式实现AOP,方便快捷。
一、Spring支持的5种增强类型
增强是织入目标类连接点上的一段程序代码。
- 前置增强:MethodBeforeAdvice方法执行前实施增强
- 后置增强:AfterReturningAdvice方法执行后实施增强
- 环绕增强: MethodInterceptor方法执行前后实施增强
- 异常抛出增强: ThrowsAdvice方法抛出异常后实施增强,最适合的应用场景就是事物管理。
- 引介增强:IntroductionInterceptor在目标类中添加一些新的方法和属性
二、类编码的方式实现增强织入
1、目标类需要实现的接口 — Waiter
package com.mistra.aop.createAdvice;
/**
* @author Mistra-WangRui
* @create 2018/3/28 15:50
* @desc 目标类要实现的接口
*/
public interface Waiter {
void greetTo(String name);
void serveTo(String name);
}
2、目标类 — NaiveWaiter
package com.mistra.aop.createAdvice;
/**
* @author Mistra-WangRui
* @create 2018/3/28 15:51
* @desc 目标类
*/
public class NaiveWaiter implements Waiter{
public void greetTo(String name) {
System.out.println("greet to + "+name+"...");
}
public void serveTo(String name) {
System.out.println("serving to + "+name+"...");
}
}
3、前置增强类 — GreetingBeforeAdvice
package com.mistra.aop.createAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @author Mistra-WangRui
* @create 2018/3/28 15:55
* @desc 前置增强类
*/
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
String clientName = (String)objects[0];
System.out.println("How are you! Mr."+clientName+".");
}
}
MethodBeforeAdvice 接口唯一的方法before(),method为目标类的方法,objects为method的入参,o为目标类实例。
4、类编码方式实现增强织入
package com.mistra.aop.createAdvice;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.testng.annotations.Test;
/**
* @author Mistra-WangRui
* @create 2018/3/28 15:58
* @desc 编码方式实现横切逻辑增强
*/
public class BeforeAdviceTest {
@Test
public void before(){
Waiter waiter = new NaiveWaiter();
BeforeAdvice advice = new GreetingBeforeAdvice();
ProxyFactory pf = new ProxyFactory();//Spring提供的代理工厂
//pf.setInterfaces(waiter.getClass().getInterfaces());
//pf.setOptimize(true);
pf.setTarget(waiter);//设置代理目标
pf.addAdvice(advice);//为代理目标添加增强
Waiter proxy = (Waiter)pf.getProxy();//生成代理实例
proxy.greetTo("古天乐");
proxy.serveTo("吴彦祖");
}
}
运行结果:
5、ProxyFactory 解析
在BeforeAdviceTest中使用ProxyFactory代理工厂将GreetingBeforeAdvice的增强织入目标类NaiveWaiter中。ProxyFactory 内部就是使用JDK或CGLib动态代理技术将增强应用到目标类中的。
setInterfaces()指定目标接口进行代理,则ProxyFactory使用JDK动态代理(实现类:JdkDynamicAopProxy)技术创建代理。如果针对类的代理,则使用CGLib动态代理(实现类:Cglib2AopProxy)。
setOptimize(true)是启动优化处理方式,这样针对接口的代理也会使用CGLib动态代理。
可以为该方法添加多个增强,形成一个增强链,它们的调用顺序和添加顺序一致。可以通过addAdvice(int,Advice)方法将增强添加到增强链具体位置(第一个位置为0)。
三、Spring配置的方式实现增强织入
包结构目录放在前面:
1、配置文件 — 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="greetingAdvice" class="com.mistra.aop.createAdvice.GreetingBeforeAdvice"/>
<bean id="target" class="com.mistra.aop.createAdvice.NaiveWaiter"/>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="com.mistra.aop.createAdvice.Waiter"
p:interceptorNames="greetingAdvice"
p:target-ref="target"
/>
</beans>
ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,ProxyFactoryBean负责为其他Bean创建代理实例,内部使用ProxyFactory来完成这项工作,ProxyFactoryBean的几个常用配置属性:
- target:代理的目标对象
- proxyInterfaces:代理所需实现的接口,可以是多个
- interceptorNames:需要织入目标对象的Bean列表,配置中的顺序对应调用的顺序
- singleton:返回的代理实例是否是单实例,默认为单实例
- optimize:设置为true时,强制使用CGLib动态代理(对于singleton的代理用CGLib,其他作用域的用JDK),CGLib代理时速度慢,但是创建的代理对象运行效率高,JDK反之
- proxyTargetClass:是否对类进行代理,设置为true时使用CGLib代理
2、配置方式实现横切逻辑增强 — BeforeAdviceTest2
package com.mistra.aop.createAdvice;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;
/**
* @author Mistra-WangRui
* @create 2018/3/29 11:11
* @desc 配置方式实现横切逻辑增强
*/
public class BeforeAdviceTest2 {
@Test
public void test(){
String configPath = "com.mistra/aop/createAdvice/beans.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
Waiter waiter = (Waiter)ctx.getBean("waiter");
waiter.greetTo("古天乐");
}
}
本文只介绍了前置增强,其他的增强都大同小异。
本文这样实施增强的话是把增强织入了目标类的所有方法中,调用目标类的所有方法时都会执行增强逻辑代码。有时候希望有选择的把增强织入目标类的特定方法中,就需要使用切点进行目标类连接点的定位。
四、Spring AOP其他
1、定义切面Advisor:有选择性的织入增强到某些类的某些方法
2、自动创建代理:上文都是通过ProxyFactoryBean创建的目标类代理,Spring AOP通过BeanPostProcessor可以自动为目标类创建代理
3、无法实现增强的问题:在JDK动态代理中通过接口来实现方法拦截,必须确保要拦截的目标方法在接口中有定义。在CGLib动态代理中通过动态代理子类来实现方法拦截,必须确保要拦截的目标方法可被子类访问,也就是目标方法不能被定义为final。
五、基于@AspectJ和Schema的AOP
@AspectJ和Schema都是实现AOP的方式,@AspectJ支持编码的方式和配置的方式实现AOP,Schema就是运用
< aop:config>< /aop:config>标签配置实现AOP。难点就是@AspectJ的语法基础和其中的配置切点表达式函数吧。无论用哪种方式实现AOP,底层原理都是运用JDK或者是CGLib动态代理技术。在运行期动态生成目标类的代理类实现的。