精通Spring+4.x++企业开发与实践之SpringAOP基础

AOP概述

AOP的简称"Aspect Oriented Programing"的简称———面向切面编程。

AOP术语

连接点

一个类或者一段程序代码用于一些具有边界性质的特性的特定点。这些代码的特定点就被成为"连接点"。Spring只支持方法的连接点,即仅能在方法调用前,方法调用后,方法抛出异常时及方法调用后这些程序执行点织入增强。连接点由两个信息确定: 1.用方法表示的程序执行点 2.用相对于位置表示方位。 例如: demo.foo()方法执行器的连接点,执行点未demo.foo(),方位未改方法执行前的位置。Spring使用切点对执行点进行定位,而方位装载增强类型中定义。

切点(Pointcut)

每个程序类都用于多个连系欸但,如果一个用于两个方法的类,这两个方法都是连接点,连接点是客观存在的事务。AOP通过"切点"定位特定的连接点。切点和连接点是一对多的关系(就像数据库查询条件和记录的关系)。在Spring中,切点使用org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则系欸性能引起负责系欸性能切点所设定的查询条件,找到对应的连接点。连接点是方法执行前,执行后等包括方位信息的具体程序执行点,而切点值定位到某个方法上,所以如果希望定位到具体的连接点上,还需要提供方位信息.

增强(Advice)

增强是织入目标类连接点上的一段程序代码,结合执行段的方位信息和切点信息,就可以找到特定的连接。真正的增强及包括用于添加到目标连接点上的一段执行逻辑,由包含用于定位连接点的方位信息,所以Spring所提供的增强接口的都是带方位名的。所以Spring所提供的增强接口都是带方位名的,例如BeforeAdvice,AfterReturningAdvice,ThrowsAdvice等。只有结合了切点和增强,才能确定特定的连接点并实施增强逻辑。

目标对象(Target)

增强逻辑的织入目标类。如果没有AOP,那么目标业务需要自己实现所有的逻辑,在AOP的帮助下,那些非横切的逻辑的程序逻辑,而性能监视和事务管理等这些横切的逻辑则可以使用AOP的动态织入特定的连接点上。

引介(Introduction)

引介是一种特护的增强,它为类添加一些属性和方法。这样,几时一个业务类原本没有实现某个接口,通过AOP的引介功能,也可以动态地位改业务类添加接口地实现逻辑,让业务类成为这个接口地实现类。

织入(Weaving)

织入是将增强添加到慕白哦类地具体连接点上。AOP就像一台织布机,将目标类,曾倩或者引介天衣无缝地编织到一起。AOP有三种织入方式: 1.编译期织入,这个要求使用特殊地Java编译器。 2.类装载期织入,这个要求使用特殊地类装器。 动态代理,在运行期位目标类添加增强生产子类地方式。 Spring采用了动态代理地织入,而AspectJ采用编译期织入和类装载期织入。

代理(Proxy)

一个类被AOP织入增强之后,就产生了一个结果类,它是融合了袁磊和曾倩逻辑地代理类。根据不同地代理方式,代理可能是ihe袁磊具有相同接口地类,也可能是原类地子类。,所以可以采用于调用原类相同地方式调用代理类。

切面(Aspect)

切面有切点和曾倩(引介)组成,它既包括横切逻辑地定义,也包括了连接点地定义。Spring AOP就是负责实施切面地框架,它将切面所定义地横切逻辑织入切面所指定的连接点中。

SpringAOP涉及的java基础知识

JDK动态代理

JDK的动态代理主题要涉及java.lang.reflect包下面的java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。其中,java.lang.reflect.InvocationHandler是一个接口,可以i通过实现接口定义很且逻辑,并通过反射机制调用目标类代码,动态地将横切逻辑和业务逻辑编制在一起。Proxy利用了InvocationHandler动态创建一个符合某一接口地实例. 例子: 实现InvocationHandler

public class JdkProxy implements InvocationHandler {
	private Object target;

	public JdkProxy(Object target) {
		this.target = target;
	}

	[@Override](https://my.oschina.net/u/1162528)
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		PerformanceMonitor.begin(target.getClass().getName()+"."+method.getName());
		Object invoke = method.invoke(target, args);
		PerformanceMonitor.end();
		return invoke;
	}
}

测试代码: System.out.println("-------------------使用jdk动态代理------------------"); JdkProxy jdkProxy = new JdkProxy(forumService); ForumService instance = (ForumService) Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{ForumService.class}, jdkProxy); instance.removeForum(1); 执行结果:

CGLib动态代理

使用JDK创建地代理有一个限制,它只能位接口创建一个代理实例,这一点可以从Proxy地接口方法public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)中看出来。第二个参数就是接口需要代理实例实现的接口列表。CGLib采用了底层的字节码技术,可以位一个类创建一个子类,在子类中采用方法拦击的技术拦截所以父类方法调用并顺势织入很且逻辑。下面采用CGLib技术编写一个可以位任何类创建织入性能键事横切逻辑代理对象的代理创建器。

实例:

public class CglibProxy implements MethodInterceptor {
	Enhancer enhancer = new Enhancer();

	public Object getProxy(Class clazz) {
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		return enhancer.create();
	}

	[@Override](https://my.oschina.net/u/1162528)
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
	  PerformanceMonitor.begin(o.getClass().getName()+"."+method.getName());
		Object invoke = methodProxy.invokeSuper(o, objects);
		PerformanceMonitor.end();
		return invoke;
	}
}

用户通过getProxy(Class clazz)为一个类创建代理对象,该对象扩展clazz实现代理,在这个代理对象中,织入性能监视得横切逻辑。intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)是CGLib定义的intercept接口方法,它拦截了所以目标类方法的调用。o:目标类实例,method:目标了方法的反射对象,args:方法的动态入参,proxy:代理实例。

测试代码:

    System.out.println("-------------------使用CGLib动态代理------------------");
    CglibProxy cglibProxy = new CglibProxy();
    ForumServiceImpl forumService1 = (ForumServiceImpl)cglibProxy.getProxy(ForumServiceImpl.class);
    forumService1.removeForum(1);

执行结果:

AOP联盟

AOP联盟是众多开源开源AOP项目的联合组织,该组织的目的是为了制定一套规范的AOP标准,定义标准的AOP接口。以便遵守标准的具体实现可以互相调用。

SpringAOP的增强类型

创建增强

Spring只支持方法的增强,增强既包含横切逻辑,又包括部分链接点的信息。

增强类型

AOP联盟为增强定义了Advice接口,Sring支持5种类型的增强,增强的接口集成关系图如下:

1.前置增强:org.springframework.aop.BeforeAdvice是为扩展而用,org.springframework.aop.MethodBeforeAdvice是目前可用的前置增强。

2.后置增强:org.springframework.aop.AfterReturningAdvice代表后置增强,表示在目标方法执行后实施增强。

3.环绕增强:org.aopalliance.intercept.MethodInterceptor代表环绕增强,表示在目标方法的执行前后实施增强。

4.异常抛出增强:org.springframework.aop.ThrowsAdvice代表抛出异常增强,表示在目标类种添加一些新的方法和属性。

5.引介增强:org.springframework.aop.IntroductionInterceptor代表引介增强,表示在目标类种添加一些新的方法和属性。

前置增强

MethodBeforeAdvice是BeforeAdvice前置的增强接口子类,BeforeAdvice存在的意义是为了后续的扩展。 void before(Method method, Object[] args, Object target) throws Throwable是MethodBeforeAdvice接口的唯一方法,method是目标放啊,args目标方法的参数,target为目标类实例。该方法发生异常的时候将阻止目标类方法执行。

实例: Waiter.java普通服务员

public interface Waiter {
void greetTo(String name);
void serveTo(String name);
}

PoliteWaiter.java礼貌地服务员

public class PoliteWaiter implements Waiter {
	[@Override](https://my.oschina.net/u/1162528)
	public void greetTo(String name) {
		System.out.println("greet to "+name+"....");
	}

	[@Override](https://my.oschina.net/u/1162528)
	public void serveTo(String name) {
		System.out.println("serve to "+name+"....");

	}
}

GreetBeforeAdvice.java 前置增强

public class GreetBeforeAdvice implements MethodBeforeAdvice {
	[@Override](https://my.oschina.net/u/1162528)
	public void before(Method method, Object[] objects, Object o) throws Throwable {
		String clientName = (String)objects[0];
		System.out.println("How are you! Mr."+clientName);
	}
}

测试代码:

    Waiter target = new PoliteWaiter();
    BeforeAdvice advice = new GreetBeforeAdvice();
    //Spring提供的代理工厂
    ProxyFactory proxyFactory = new ProxyFactory();
    //设置代理目标
    proxyFactory.setTarget(target);
    proxyFactory.addAdvice(advice);
    Waiter proxy = (Waiter) proxyFactory.getProxy();
    proxy.greetTo("John");
    proxy.serveTo("jack");

执行结果:

ProxyFactory 在上面的例子中我们使用了ProxyFactory,ProxyFactory实际上使用的是JDK和CGLib动态代理技术将增强应用到目标类中。 ![] CglibAopProxy使用的是CGLib动态代理技术,JdkDynamicAopProxy使用的是JDK动态代理技术创建代理。如果通过ProxyFactory的setInterfaces(Class[] interfaces)方法指定目标接口进行代理,就是要JdkDynamicAopProxy,如果针对的是类的代理,就使用CglibAopProxy,可以使用ProxyFactory的setOptimize(true)方法让ProxyFactory启动u欧化代理的方式,此时针对接口的代理也是使用CglibAopProxy。

    //使用CGLib代理的方式
    proxyFactory.setOptimize(true);

    //使用的jdk代理的方式  proxyFactory.setInterfaces(target.getClass().getInterfaces());

使用配置文件的方式:

	<bean id="greetingAdvice" class="com.flexible.beforeadvice.GreetBeforeAdvice"></bean>
	<bean id="target" class="com.flexible.beforeadvice.PoliteWaiter"></bean>

	<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
	p:proxyInterfaces="com.flexible.beforeadvice.Waiter"
	p:interceptorNames="greetingAdvice"
	p:target-ref="target">
	</bean>

测试代码:

	@Test
	public void testMethod1(){
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("waiter");
		waiter.greetTo("zhangsgan ");
		waiter.serveTo("李四");
	}

ProxyFactoryBean是FactoryBean接口的实现类,它负责实例化一个Bean。ProxyFactoryBean负责为其他的Bean创建代理实例,它在内部使用ProxyFactory来完成这项工作。ProxyFactoryBean的几个常用的可配置属性。

1.target:代理的目标对象。

2.proxyInterfaces:代理所需要实现的接口,可以是多个接口。该属性还有一个别名属性interfaces

3.interceptorNames:需要织入目标对象的Bean列表,采用Bean的名曾指定,这些Bean必须实现了org.springframework.cglib.proxy.MethodInterceptor接口或者org.springframework.aop.Advisor的Bean,配置中的顺序的应调用的顺序。

4.sigleton:返回的代理是否是单实例,默认为单实例。

5.optimize:默认是false,使用的是jdk动态代理创建代理,如果设置true,强制使用CGLib动态代理,对于singleton代理,推荐使用CGLib。

6.proxyTargetClass:是否对类进行代理(而不是对接口进行代理)。设置为true时,使用的CGLib动态代理。

后置增强

后置增强在目标类方法调用后执行。

GreetAfterAdvice.java

public class GreetAfterAdvice implements AfterReturningAdvice {

	@Override
	public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
		System.out.println("please enjoy yourself....");
	}
}

beans.xml

	<bean id="target2" class="com.flexible.afteradvice.PoliteWaiter"></bean>
	<bean id="greetingAdvice2" class="com.flexible.afteradvice.GreetBeforeAdvice"></bean>
	<bean id="greetingAfterAdvice" class="com.flexible.afteradvice.GreetAfterAdvice"></bean>

	<bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean"
		  p:proxyInterfaces="com.flexible.afteradvice.Waiter"
		  p:interceptorNames="greetingAdvice2,greetingAfterAdvice"
		  p:optimize="true"
		  p:target-ref="target2">
	</bean>

测试代码:

	@Test
	public void testMethod1(){
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("waiter2");
		waiter.greetTo("zhangsgan ");
		waiter.serveTo("李四");
	}

执行结果:

How are you! Mr.zhangsgan greet to zhangsgan .... please enjoy yourself.... How are you! Mr.李四 serve to 李四.... please enjoy yourself....

环绕增强

环绕增强允许在目标类方法调用前后织入横切逻辑,综合了前置和后置增强功能。

例子: GreetingInterceptor.java

public class GreetingInterceptor implements MethodInterceptor {
	@Override
	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
		Object[] args =methodInvocation.getArguments();
		String clientNme = (String) args[0];
		System.out.println("How are you Mr."+clientNme+"....");
		Object proceed = methodInvocation.proceed();
		System.out.println("Please enjoy yourself....");
		return proceed;
	}
}

bean.xml

	<bean id="target3" class="com.flexible.aroundadvice.PoliteWaiter"></bean>
	<bean id="greetingAdvice3" class="com.flexible.aroundadvice.GreetingInterceptor"></bean>

	<bean id="waiter3" class="org.springframework.aop.framework.ProxyFactoryBean"
		  p:proxyInterfaces="com.flexible.aroundadvice.Waiter"
		  p:interceptorNames="greetingAdvice3"
		  p:target-ref="target3">
	</bean>

测试代码:

	@Test
	public void testMethod1(){
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("waiter3");
		waiter.greetTo("zhangsgan ");
		waiter.serveTo("李四");
	}

测试结果

How are you Mr.zhangsgan ....
greet to zhangsgan ....
Please enjoy yourself....
How are you Mr.李四....
serve to 李四....
Please enjoy yourself....

异常抛出增强

异常抛出增强最适合的应用场景时事务管理,当参与事务的某个DAO发生异常时,事务管理器就必须回滚事务。要定义异常增强需要实现业务ThrowsAdvice增强,该接口只是一个标签接口,在运行期间,Spring使用反射机制自行判断,必须使用下面的形式定义方法

public void afterThrowing(Method method,Object[] args,Object target,Exception ex){

PayService.java 业务类 /** * 模拟业务的类,支付的时候抛出异常 */ public class PayService {

	public void payMony(String from, double money, String to) {
	//TODO -- TODO some thing
		throw new RuntimeException("测试抛出运行时异常....");
	}
}

beans.xml 配置

<bean id="transactionManager" class="com.flexible.throwadvice.TransactionManager"></bean>
<bean id="payTarget" class="com.flexible.throwadvice.PayService"></bean>

<bean id="ins" class="org.springframework.aop.framework.ProxyFactoryBean"
      p:interceptorNames="transactionManager"
      p:target-ref="payTarget"
      p:proxyTargetClass="true">
</bean>

测试代码:

@Test
public void testMethod1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
    PayService payService = (PayService) context.getBean("ins");
    payService.payMony("1",100.00,"2");
}

测试结果:


method:payMony 抛出异常:测试抛出运行时异常.... 成功回滚事务

引介增强

引介增强时一种比较特殊的增强类型,它不是在目标方法增强,而是为目标类创建新的方法和属性,所以介增强的连接点是类级别的。而非方法级别的。通过引介增强,可以为目标类添加一个接口的实现,即原来的目标类的为实现某个接口,通过引介增强可以为目标了常见实现某个接口的代理。Spring 定义了引介增强接口IntroductionInterceptor,该接口没有定义任何方法,Spring为该接口提供了DelegatingIntroductionInterceptor实现类。一般情况下,通过扩展该实现类定义自己引介增强类。

创建切面

如果我们需要有选择地织入目标类地某些特定地方法中,就需要使用切点进行目标连接点的定位。描述连系欸但是进行AOP编程最主要的工作。增强的提供了连系欸但方法为信息,切点进一步的描述了织入哪些类的哪些方法上。 Spirng通过org.springframework.aop.Pointcut接口秒速和切点,其中org.springframework.aop.Pointcut是由ClassFilter和MethodMatcher构成,通过ClassFilter定位到某些特定类上,通过MethodMatcher定位到某些特定方法上。这样org.springframework.aop.Pointcut就能够定位某些类的某些方法上的能力。org.springframework.aop.Pointcut的关系结构图如下:

ClassFilter只定义了一个方法matcher(Class clazz),其参数代表一个被检测类,改方法判别被u检测的类是否匹配过滤条件。 Spring支持两种匹配器:

1.静态匹配器(仅需要匹配一次,对方法签名进行匹配)

2.动态匹配器(因为可能每次入参都不一样,需要每次调用都检测,影响性能,一般不常使用),方法匹配器的类型由isRuntime()方法返回值决定,false表示静态匹配器,true表示动态匹配器。

切点的类型

1.静态方法切点:org.springframework.aop.support.StaticMethodMatcherPointcut是静态方法切点的抽象类,默认情况下它匹配所以的类。org.springframework.aop.support.StaticMethodMatcherPointcut的子类有:org.springframework.aop.support.NameMatchMethodPointcut和org.springframework.aop.support.AbstractRegexpMethodPointcut,NameMatchMethodPointcut提供简单字符串匹配签名,AbstractRegexpMethodPointcut使用正则表达式匹配放啊签名。

2.动态方法切点:org.springframework.aop.support.DynamicMethodMatcherPointcut是动态方法切点的抽象基类,默认情况下匹配所有类。

3.注解切点:org.springframework.aop.support.annotation.AnnotationMatchingPointcut实现类表示注解其欸但。使用AnnotationMatchingPointcut支持在BEAN中直接通过Java5.0注解标签定义的切点。

4.表达式切点:org.springframework.aop.support.ExpressionPointcut接口主要为了支持AspectJ切点表达式语法而定义的接口。

5.流程切点:org.springframework.aop.support.ControlFlowPointcut实现类表示控制流程切点。ControlFlowPointcut是一种特殊的的切点,它根据程序执行堆栈信息的查看目标方法是否由某一个方法直接或者见解的发起调用,以此判断是否为匹配的连接点。

6.复合切点:org.springframework.aop.support.ComposablePointcut实现类是为了创建多个切点而提供的方便操作类。它所有的方法都返回ComposablePointcut类,这样就可以使用链接表达式对切点进行操作:Pointcut pc = new ComposablePointcut().union(classFilter).intersetion(methodMatcher).intersetion(pointcut).

切面的类型

由于增强既包含横切代码,又包含部分连接点的部分信息(方法前,方法后主方为信息),可以进通过增强类生成一个切面。但是切点仅代表目标类连系欸但那的部分信息(类和方法的定位),所有仅有切点无法制作出一个切面,必须结合增强才能制作出切面。Spring使用org.springframework.aop.Advisor接口比埃是切面的概念,一个切面同时包含横切代码和连接点信息。切面可以分为三类:

1.Advisor:代表一般切面,仅包含一个Advice。因为Advice包含了横切代码和连接点信息,所有Advice本身就是一个简单的切面,它代表的横切的连接点是所有目标类的所有方法,因为这个横切面很宽泛,一般情况下不使用。

2.PointcutAdvisor:代表具有切点的切面,包含Advice和Pointcut两个类,这样就可以通过类,方法名及方法方位等信息灵活的定义切面的链接点,提供更具使用行的切面。

PointcutAdvisor主要有6个具体的实现类。 1.DefaultPointcutdvisor:最常用的切面类型,它可以通过任意Pointcut和Advice定义一个切面,唯一不支持的是引介的切面类型,一般可以通过扩展改类实现自定义的的切面。

     2.NameMatchMethodPointcutAdvisor:通过类可以定义按方法名定义切点的切面。

     3.RegexpMethodPointcutAdvisor:对于按正则表达式匹配方法名进行切点定义的切面,可以通过扩展改实现类进行操作。其内部是通过jdkRegexpMethodPoint构造出正则表达式方法名称切点。

     4.StaticMethodMatcherPointcutAdvisor:静态方法匹配切点定义的切面,默认情况下匹配所有的目标类。

    5.AspectJExpressionPointcutAdvisor:用于AspectJ切点表达式定义切点的切面。

    6.AspectJPointcutAdvisor:用于AspectJ语法定义切点的切切面。

3.IntroductionAdvisor:代表引介切面。引介切面是对应引介增强的特殊的切面,它应用于类层面上,所有切点使用ClassFilter进行定义。

静态普通方法名匹配切面

StaticMethodMatcherPointAdvisor代表一个静态方法匹配切面,它通过StaticMethodMatcherPointcut来定义切点,并通过类过滤和方法名来匹配所定义的切点。 例子: Waiter.java

public class Waiter {
void greetTo(String name){
	System.out.println("Waiter greet to "+name+"...");
}
void serveTo(String name){
	System.out.println("waiter sering "+name+"...");
}
}

Seller.java

public class Seller {
public void greetTo(String name){
	System.out.println("Waiter greet to "+name+"...");
}
}

GreetBeforeAdvice.java(前置增强)

public class GreetBeforeAdvice implements MethodBeforeAdvice {
	@Override
	public void before(Method method, Object[] objects, Object o) throws Throwable {
		String clientName = (String)objects[0];
		System.out.println("How are you! Mr."+clientName);
	}
}

GreetingAdvisor.java(切面匹配,这里限制只能匹配Waiter的greetTo())

public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor {
	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		return "greetTo".equals(method.getName());
	}
	public ClassFilter getClassFilter() {
		return new ClassFilter() {
			@Override
			public boolean matches(Class<?> clazz) {
				return Waiter.class.isAssignableFrom(clazz);
			}
		};
	}
}

beans.xml

<!--普通静态方法切面匹配-->

<bean id="waiterTarget" class="com.flexible.advisormatch.staticmethodadvisormatch.Waiter"></bean>
<bean id="sellerTarget" class="com.flexible.advisormatch.staticmethodadvisormatch.Seller"></bean>

<bean id="advisormatchGreetingAdvice"
      class="com.flexible.advisormatch.staticmethodadvisormatch.GreetBeforeAdvice"></bean>

<bean id="greetAdvisor" class="com.flexible.advisormatch.staticmethodadvisormatch.GreetingAdvisor"
      p:advice-ref="advisormatchGreetingAdvice"></bean>  <!--这里注入一个前置切面-->
<!--通过一个父类定义一个公共的配置信息-->
<bean id="parent" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
      p:interceptorNames="greetAdvisor"
      p:proxyTargetClass="true"
></bean>
<bean id="advisormatchWaiter" parent="parent" p:target-ref="waiterTarget"></bean>
<bean id="advisormatchSeller" parent="parent" p:target-ref="sellerTarget"></bean>

测试代码:

    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
    Waiter waiter = (Waiter) context.getBean("advisormatchWaiter");
    Seller seller = (Seller) context.getBean("advisormatchSeller");
    waiter.greetTo("zhangsan");
    waiter.serveTo("zhangsan");
    seller.greetTo("李四");

执行结果:

静态正则表达式方法匹配切面

RegexpMethodPointcutAdvisor是正则表达式方法匹配的切面实现类,改类已经是功能齐全的实现类,可以直接拿来使用。

例子: 与上一个例子差别主要再配置文件,beans.xml配置文件的内容如下:

<bean id="waiterTarget2" class="com.flexible.advisormatch.regexpadvisormatch.Waiter"></bean>
<bean id="sellerTarget2" class="com.flexible.advisormatch.regexpadvisormatch.Seller"></bean>

<bean id="advisormatchGreetingAdvice2"
      class="com.flexible.advisormatch.regexpadvisormatch.GreetBeforeAdvice"></bean>

<bean id="greetAdvisor2" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
      p:advice-ref="advisormatchGreetingAdvice2">
    <property name="patterns">
        <list>
            <value>.*greet.*</value>
        </list>
    </property>
</bean>
<!--通过一个父类定义一个公共的配置信息-->
<bean id="parent2" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
      p:interceptorNames="greetAdvisor2"
      p:proxyTargetClass="true"
></bean>
<bean id="advisormatchWaiter2" parent="parent2" p:target-ref="waiterTarget2"></bean>
<bean id="advisormatchSeller2" parent="parent2" p:target-ref="sellerTarget2"></bean>

测试执行结果:

动态切面

DynamicMethodMatcherPointcut抽象类,是Spring用于创建动态切面你的抽象类,改出响雷默认匹配所有的类和方法,因此默认需要扩展该类编写符合要求的动态的切点。 例子: GreetingDynamicPointcut.java

public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut {
	private static List<String> specialClientList = new ArrayList<>();
	static {
		specialClientList.add("John");
		specialClientList.add("Tom");
	}
	//    对类进行静态切点检查
	@Override
	public ClassFilter getClassFilter() {
		return new ClassFilter() {
			@Override
			public boolean matches(Class<?> clazz) {
				System.out.println("调用getClassFilter()对" + clazz.getName() + "做静态检查.");
				return Waiter.class.isAssignableFrom(clazz);
			}
		};
	}
	//    对方法进行静态切点检测
	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		return super.matches(method, targetClass);
	}

	// 对方法进行动态切点检测
	@Override
	public boolean matches(Method method, Class<?> targetClass, Object... args) {
		System.out.println("调用matches(Method method, Class<?> targetClass, Object... args)"
				+ targetClass.getName() + "." + method.getName()+"做动态检测");
		String clientName = (String) args[0];
		return specialClientList.contains(clientName);
	}
}

beans.xml

	<bean id="waiterTarget3" class="com.flexible.advisormatch.dynamicmatch.Waiter"></bean>
	<bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="pointcut">
			<bean class="com.flexible.advisormatch.dynamicmatch.GreetingDynamicPointcut"></bean>
		</property>
		<property name="advice">
			<bean class="com.flexible.advisormatch.dynamicmatch.GreetBeforeAdvice"></bean>
		</property>
	</bean>
	<bean id="waiter4" class="org.springframework.aop.framework.ProxyFactoryBean"
		  p:interceptorNames="dynamicAdvisor"
		  p:target-ref="waiterTarget3"
		  p:proxyTargetClass="true"></bean>

测试代码:

		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("waiter4");
		waiter.greetTo("John");
		waiter.greetTo("John");
		waiter.serveTo("zhangsan");
		waiter.serveTo("zhangsan");

执行结果:

从执行的结果可以看出,Spring会创建代理织入切面时,对目标类中的所有方法进行了静态切点检查;生成了的植入切面的代理对象后,第一次调用代理类的每一个方法都会进行一次静态的检查,如果本次检查就能从候选者列表中将该方法排除,则以后对该方法的调用就不再执行静态检查;对于静态匹配的方法,后续的调用该方法都会进行动态的检查。

流程切面

Spring 的流程切面由DefaultPointcutAdvisor和ControlPointcut是实现。流程切点代表由某个方法直接或者简洁的发起调用的其他方法。

例子: WaiterDelegate.java

public class WaiterDelegate {

	private Waiter waiter;

	/**
	 * 该方法发起调用的其他方法都织入GreetBeforeAdvice增强,
	 * 要完成该功能就需要使用到流程切面。
	 *
	 * @param clientName
	 */
	public void service(String clientName) {
		waiter.greetTo("service" + clientName);
		waiter.serveTo("service" + clientName);
	}

	public void setWaiter(Waiter waiter) {
		this.waiter = waiter;
	}
}

beans.xml

	<!--流程切面-->
	<bean id="controlFlowGreetingAdvice" class="com.flexible.advisormatch.controflowmatch.GreetBeforeAdvice"></bean>

	<bean id="controlFlowWaiterTarget" class="com.flexible.advisormatch.controflowmatch.Waiter"></bean>

	<bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
		<constructor-arg type="java.lang.Class"
						 value="com.flexible.advisormatch.controflowmatch.WaiterDelegate"></constructor-arg>
		<constructor-arg type="java.lang.String" value="service"></constructor-arg>
	</bean>

	<bean id="controlFlowAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
		  p:pointcut-ref="controlFlowPointcut"
		  p:advice-ref="controlFlowGreetingAdvice">
	</bean>

	<bean id="controlFlowWaiter1" class="org.springframework.aop.framework.ProxyFactoryBean"
		  p:interceptorNames="controlFlowAdvisor"
		  p:target-ref="controlFlowWaiterTarget"
		  p:proxyTargetClass="true">
	</bean>

测试代码:

		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("controlFlowWaiter1");
		WaiterDelegate delegate = new WaiterDelegate();
		delegate.setWaiter(waiter);
		waiter.greetTo("zhangsan");
		waiter.serveTo("zhangsan");
		delegate.service("zhangsan");

执行结果:

复合切点切面

如果一个切点难以描述目标连接点的信息,比如再前面流程切面的例子中,在上面的例子中,如果调用service(String clientName)就会发现该方法调用的两个方法都会织入增强,如果只希望增强其中一个,那么这个切点就是符合切点。

例子:

GreetingComposablePointcut.java

public class GreetingComposablePointcut {
	public Pointcut getIntersectionPointcut(){
		ComposablePointcut cp = new ComposablePointcut();
		Pointcut pt1 = new ControlFlowPointcut(WaiterDelegate.class,"service");
		NameMatchMethodPointcut pt2 = new NameMatchMethodPointcut();
		pt2.addMethodName("greetTo");
		return cp.intersection(pt1).intersection((Pointcut)pt2);
	}
}

beans.xml

<!--复合切点-->
	<bean id="composableGreetingAdvice" class="com.flexible.advisormatch.composablepointcutmatch.GreetBeforeAdvice"></bean>
	<bean id="gcp" class="com.flexible.advisormatch.composablepointcutmatch.GreetingComposablePointcut"></bean>
	<bean id="composableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
	p:pointcut="#{gcp.intersectionPointcut}"
	p:advice-ref="composableGreetingAdvice"
	></bean>
	<bean id="composableWaiterTarget" class="com.flexible.advisormatch.composablepointcutmatch.Waiter"></bean>
	<bean id="composableProxyWaiter" class="org.springframework.aop.framework.ProxyFactoryBean"
	p:interceptorNames="composableAdvisor"
	p:target-ref="composableWaiterTarget"
	p:proxyTargetClass="true"></bean>

测试代码:

		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("composableProxyWaiter");
		WaiterDelegate delegate = new WaiterDelegate();
		delegate.setWaiter(waiter);
		waiter.serveTo("zhansagn");
		waiter.greetTo("zhangsan");
		delegate.service("zhangsgan");

执行结果:

自动创建代理

之前的例子都是通过ProxyFactoryBean创建织入切面的代理,每个需要被代理的Bean都需要配置,这样就会很麻烦,因此Spring提供了自动代理的机制,让容器自动生成代理,把开发从繁琐的配置工作中解放出来。而这些都是Spring内部使用BeanPostProcessor自动完成这项工作。

BaeanPostProcessor实现类的介绍

基于BeanPostProcessor自动代理创建器的实现类有三类: 1.BeanNameAutoProxyCreator:基于Bean配置名规则的自动代理创建器,允许为一组特定的配置名的Bean自动创建代理实例的代理创建器。

例子:

beans.xml

<!--BeanNameAutoProxyCreator自动创建代理类-->
<bean id="autoWaiter" class="com.flexible.autoprxycreator.Waiter"></bean>
<bean id="autoSeller" class="com.flexible.autoprxycreator.Seller"></bean>
<bean id="autoGreetAdvice" class="com.flexible.autoprxycreator.GreetingBeforeAdvice"></bean>

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
//这里可以配置多个需要代理的baean
p:beanNames="autoWaiter,autoSeller"
p:interceptorNames="autoGreetAdvice"
//p:optimize="true"需要配置,实现使用CGLib生成代理
p:optimize="true"></bean>

测试代码:

		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("autoWaiter");
		Seller seller = (Seller) context.getBean("autoSeller");
		waiter.greetTo("John");
		seller.greetTo("Tom");

执行结果:

com.flexible.autoprxycreator.Waiter.greetTo How are you!Mr.John. waiter greet to John... com.flexible.autoprxycreator.Seller.greetTo How are you!Mr.Tom. seller greet to Tom...

2.DefaultAdvisorAutoProxyCreator:基于Advisor匹配机制的自动代理创建器,他会对容器中的所有的Advisor进行扫描,自动将这些切面应用到匹配的Bean中(为目标Bean创建代理实例)

beans.xml配置

	<bean id="autoWaiter2" class="com.flexible.autoprxycreator.defaultautoproxycreator.Waiter"></bean>
	<bean id="autoSeller2" class="com.flexible.autoprxycreator.defaultautoproxycreator.Seller"></bean>
	<bean id="autoGreetAdvice2" class="com.flexible.autoprxycreator.defaultautoproxycreator.GreetingBeforeAdvice"></bean>
	<bean id="regexpMethodPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
		  p:advice-ref="autoGreetAdvice2">
		<property name="patterns">
			<list>
				<value>.*greet.*</value>
			</list>
		</property>
	</bean>
	<!--会开启扫描所有的切面 这个地需要配置使用CGLib动态代理-->
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"  p:proxyTargetClass="true"></bean>

测试代码:

		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:autoproxycreator/beans");
		Waiter waiter = (Waiter) context.getBean("autoWaiter2");
		Seller seller = (Seller) context.getBean("autoSeller2");
		waiter.greetTo("John");
		seller.greetTo("Tom");

执行结果:

com.flexible.autoprxycreator.defaultautoproxycreator.Waiter.greetTo How are you!Mr.John. waiter greet to John... com.flexible.autoprxycreator.defaultautoproxycreator.Seller.greetTo How are you!Mr.Tom. seller greet to Tom...

3.AnnotationAwareAspectJAutoProxyCreator:基于Bean中AspectJ注解标签的自动代理创建器,为包含AspectJ注解的Bean自动创建代理实例。

Spring AOP的切面类型

通过自动代理技术创建切面

猜你喜欢

转载自my.oschina.net/u/3474937/blog/2878513
今日推荐