Spring面向方面编程(AOP)
2016/6/19 12:51:27 seventeenWen
AOP总结
虽然可以通过PreformanceHandler实现横切逻辑的动态织入,但还是有不足之处:
- 目标类的所有方法都添加了横切逻辑,而有时,并不是所有的方法都需要横切逻辑。
- 通过硬编码的方式织入了横切逻辑的织入点,在业务方法开始前和结束后织入代码。
- 需要手工编写代理对象的创建过程,为不同类创建代理是,需要编写不同的代码。
Spring的AOP便解决了上面的三点,
- 通过Pointcut(切点)指定了在哪些类的哪些方法上织入横切逻辑。
- 通过Advice(增强)描述横切逻辑的方法和具体织入的点(方法前,方法后,方法的两端)。
- 通过Advisor(切面)将Pointcut和Advice两者组装起来。
Spring的Advice(增强)
- 前置增强(org.springframework.aop.BeforeAdvice):表示在方法前增强。
BeeforeAdvice接口只定义了一个方法
void before(Method method, Object[] args, Object target) throws Throwable;
三个参数:
- method:目标类的方法
- args:目标类传入的参数
- obj:目标类的对象
Spring的ProxyFactory
代理工厂(ProxyFactory)
Spring源码(org.springframework.aop.framework.AopProxy)中定义类这么个接口:
public interface AopProxy {
Object getProxy();
Object getProxy(ClassLoader classLoader);
}
它有两个实现类JdkDynamicAopProxy和CglibAopProxy,一个是使用JDK动态代理技术,一个是使用Cglib代理技术实现的。如果通过ProxyFactory的setInterfaces指定接口进行代理则使用JDK代理,如果针对类进行代理则使用CGLib技术
Spring中的配置
<bean id="ServeAdvice" class="com.spring.proxy.test.ServeAdviceBefpreAdvice">
<bean id="target" class="com.spring.proxy.test.target">
<bean id="obj" class="org.springframework.aop.framework.ProxyFactoryBean"
P:proxyinterfaces="com.spring.proxy.test.interface"//指定需要代理的接口
P:interceptorNames="adviceMethod"//指定使用的增强方法
p:target-ref="target"//指定对哪个Bean进行增强
其他属性:
- target:代理的目标对象
- proxyInterfaces:代理需要实现的接口
- interceptorNames:需要植入目标对象的Bean列表,这些Bean必须实现了org.aopalliance.intercept.MethodInterceptor或org.springframework.aop.Advisor,配置中的顺序代表调用的顺序
- singleton:返回来的对象是否为单例
- optimeize:设置为true时,强制使用CGLib代理
- proxyTargetClass:是否对类进行代理,设置为true时,使用CGLib代理
- 后置增强(org.springframework.aop.AfterReturningAdvice):表示在方法后增强。
AfterReturningAdvice接口只定义了一个方法
void afterReturning(Method method, Object[] args, Object target) throws Throwable;
\ - 环绕增强(org.springframework.aop.MethodInterceptor):表示方法环绕增强(即在方法前后调用),
Object invoke(MethidInvocation invocation) throws Throwable
异常抛出增强(org.springframework.aop.ThrowsAdvice) :表示异常抛出增强,ThrowsAdvice接口没有任何方法,它是一个标识接口,我们必须以
void afterThrowing(Methid method,Onject[] args,Object target,Throwable);
这么定义异常的抛出,方法名必须是afterThrowing,前两个参数可选要么都填,要么都不填,最后Throwable参数必须添加,并且是Throwable或它的子类。
- 引介增强(org.springframework.aop.support.DelegatingIntriductionInterceptor):它不是在目标方法周围织入增强,而是为目标类创建新的方法和属性,所以引介增强的连接点是类级别的,而非方法级别的,即即使目标类没有实现某个接口,同过引介增强可以为目标类创建实现某接口的代理。通过扩展DelegatingIntroductionInterceptor实现类来定义自己的引介增强类。
Spring的切面
如果我们想要有选择的织入到目标类的某些方法中,就需要使用切点进行连接点的定位。
Spring通过(org.springframework.Pointcut)接口描述切点,接口有两个方法:
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
ClassFilter中定义了一个方法:
boolean matches(Class<?> clazz);
clazz代表一个被检测类,这个方法判断是否匹配过滤条件。Spring支持两种匹配器,一种静态方法匹配器,运行期间只会匹配一次,根据方法的名称和入参的顺序匹配,一种动态方法匹配器。会在运行期间检查方法的值。每次调用方法都会匹配一次。
MethodMatcher isRuntime();
方法返回true,代表动态方法,返回false,代表静态方法。
切点类型
- 静态方法切点(org.springframework.aop.support.StaticMethodMatcherPointcut):默认匹配所有的类,有两个子类NameMethodPointcut:(简单的根据字符串进行匹配)和AbstractRegexpMethodPointcut(正则表达式进行匹配)
- 动态方法切点(org.springframework.aop.support.DynamicMethodMatcherPointcut):默认匹配所有类,
- 流程切点(org.springframework.aop.support.ControlFlowPointcut):它的实现类表示控制流程切点,
- 复合切点(org.springframework.aop.support.ComposablePointcut):它的实现类是为了创建多个切点的方便提供类
切面类型
Spring中,切面有三种:
1. Advisor:只代表一个切面,是个抽象的接口,代表切面的概念。
2. PointcutAdvisor:代表具有切点的切面,它包含两个类Advice和Pointcut
3. IntroductionAdvisor:代表引介切面,它是应用在类的层面上
静态方法切点Demo
public class WaiterImp implements Waiter{
public void greetTo(String name){
System.out.println("Waiter greet to "+name+"....");
}
public void serveTo(String name){
System.out.println("waiter serving to "+name+"....");
}
}
public class SellerImp implements Seller{
public void greetTo(String name){
System.out.println("seller greet to "+name+"...");
}
}
//定义切面
public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor{
private static final long serialVersionUID = 1L;
/* (non-Javadoc)
*
* 匹配方法名称为serveTo的方法
* @see org.springframework.aop.MethodMatcher#matches(java.lang.reflect.Method, java.lang.Class)
*/
public boolean matches(Method method, Class<?> targetClass) {
return "serveTo".equals(method.getName());
}
public ClassFilter getClassFilter(){
return new ClassFilter(){
/* (non-Javadoc)
*
* 类是Waiter的子类或实现类的匹配
*
* @see org.springframework.aop.ClassFilter#matches(java.lang.Class)
*/
public boolean matches(Class<?> clazz) {
return Waiter.class.isAssignableFrom(clazz);
}
};
}
}
//定义增强
public class GreetBeforeAdvice implements MethodBeforeAdvice{
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"."+method.getName());
String clientName=(String)args[0];
System.out.println("How are you"+clientName+".");
}
}
beans.xml配置
<bean id="waiterTarget" class="com.spring.advisor.WaiterImp"/>
<bean id="sellerTarget" class="com.spring.advisor.SellerImp"/>
<bean id="greetingAdvice class="com.spring.advisor.GreetBeforeAdvice" />
<bean id="greetingAdvisor" class="com.spring.advisor.GreetingAdvisor"
p:advice-ref="greetingAdvice"/>
<bean id="parent" abstract="true"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="greetingAdvisor"
p:proxyTargetClass="true"/>
<bean id="waiter" parent="parent" p:target-ref="waiterTarget"/>
<bean id="seller" parent="parent" p:target-ref="sellerTarget"/>
测试类Demo
public class Run {
@Test
public void RunTest(){
ApplicationContext ctx =new ClassPathXmlApplicationContext("beans.xml");
WaiterImp waiter =(WaiterImp) ctx.getBean("waiter");
SellerImp seller =(SellerImp) ctx.getBean("seller");
waiter.greetTo("seventeenWen");
waiter.serveTo("seventeenWen");
seller.greetTo("seventeenWen");
}
}
结果:
Waiter greet to seventeenWen....
com.spring.advisor.WaiterImp.serveTo
How are youseventeenWen.
waiter serving to seventeenWen....
seller greet to seventeenWen...