连接点joinpoint:可以织入代码的位置,方法前后,出异常时等位置
切点pointcut:用PointCut接口进行描述,切点功能上类似一个正则表达式,描述了哪些类的哪些方法符合条件,具体不到方法前还是方法后等连接点
增强advice:织入到连接点上的方法,定位了大致连接点(方法前,方法后,异常时等)和要织入的代码
目标对象target:要织入的代码的目标类
引介introduction:为目标类动态添加属性和方法,在spring中一般是为目标类动态的添加接口,使目标类成为某接口的实现类
织入weaving:将增强advice添加到目标类的具体连接点上的过程,有三种形式:编译器织入(aspectJ),类装载期织入(aspectJ),动态织入(spring)
代理proxy:融合了原类和增强逻辑的代理类
切面aspect:由切点和增强/引介组成
五种增强类型:
前置增强:方法执行前,用BeforeAdvice的子接口MethodBeforeAdvice
后置增强:AfterReturningAdvice
环绕增强:MethodInterceptor
异常抛出增强:ThrowsAdvice
引介增强:IntroductionInterceptor
org.springframework.aop.framework.ProxyFactoryBean是负责为其他Bean创建代理对象的类,其中的几个属性:
target:要代理的目标类对象
proxyInterfaces,别名interfaces:代理要实现的接口,可以是多个
interceptorNames:要织入的增强或引介bean列表,必须实现MethodInterceptor或Advisor
singleton:返回的代理是否单例
optimize:值为true就强制使用cglib,对单例的代理推荐cglib,其他推荐jdk代理,因为cglib创建慢运行起来快,jdk代理相反
proxyTargetClass:是否对类进行代理(而不是对接口),true的话使用cglib,设置proxyTargetClass=true就不需要设置optimize了
前置增强
public class GreetingBeforeAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object obj) throws Throwable { String clientName = (String)args[0]; System.out.println("How are you!Mr."+clientName+"."); } }
<bean id="target" class="com.baobaotao.advice.NaiveWaiter" /> <bean id="greetingBefore" class="com.baobaotao.advice.GreetingBeforeAdvice" /> <bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean" p:proxyInterfaces="com.baobaotao.advice.Waiter" p:target-ref="target" p:interceptorNames="greetingAdvice"/>
后置增强
public class GreetingAfterAdvice implements AfterReturningAdvice { public void afterReturning(Object returnObj, Method method, Object[] args, Object obj) throws Throwable { System.out.println("Please enjoy yourself!"); } }
<bean id="target" class="com.baobaotao.advice.NaiveWaiter" /> <bean id="greetingBefore" class="com.baobaotao.advice.GreetingBeforeAdvice" /> <bean id="greetingAfter" class="com.baobaotao.advice.GreetingAfterAdvice" /> <bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean" p:proxyInterfaces="com.baobaotao.advice.Waiter" p:target-ref="target" p:interceptorNames="greetingBefore,greetingAfter"/>
前置增强和后置增强都用到了
环绕增强
public class GreetingInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { Object[] args = invocation.getArguments(); String clientName = (String)args[0]; System.out.println("How are you!Mr."+clientName+"."); Object obj = invocation.proceed(); System.out.println("Please enjoy yourself!"); return obj; } }
配置文件同上
异常抛出增强
public class TransactionManager implements ThrowsAdvice { public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable { System.out.println("-----------"); System.out.println("method:" + method.getName()); System.out.println("抛出异常:" + ex.getMessage()); System.out.println("成功回滚事务。"); } }配置文件同上
引介增强,不是在方法周围织入增强,而是为目标类添加新的属性和方法,是类级别的,在spring中引介增强通过实现IntroductionInterceptor来实现,不过一般为了简单我们使用IntroductionInterceptor的实现类DelegatingIntroductionInterceptor
package introduce2; //要代理的目标类 public class Animal { public void eat(String food){ System.out.println("eat-"+food); } }
package introduce2; //代理类要实现的接口 public interface Moveable { void move(); }
package introduce2; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.DelegatingIntroductionInterceptor; //引介增强类 public class MyAdvice extends DelegatingIntroductionInterceptor implements Moveable{ @Override public void move() { System.out.println("advice-move"); } @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("invoke-start"); Object result=super.invoke(mi); System.out.println("invoke-end"); return result; } }
<bean id="target" class="introduce2.Animal" /> <bean id="introductionObj" class="introduce2.MyAdvice" /> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean" p:interfaces="introduce2.Moveable" p:target-ref="target" p:interceptorNames="introductionObj" p:proxyTargetClass="true" /><!-- 由于一定要通过创建子类来实现,所以要用cglib,否则出错 -->
public static void main(String[] args) { ApplicationContext ctx=new ClassPathXmlApplicationContext("introduce2/beans.xml"); Object obj=ctx.getBean("proxy"); Animal animal=(Animal)obj; animal.eat("青菜"); Moveable mo=(Moveable)obj; mo.move(); }
结果:
invoke-start
eat-青菜
invoke-end
invoke-start
advice-move
invoke-end
上面提到切面aspect由切点和增强/引介组成
从上面的例子可以看出,增强提供了连接点的方位信息,织入到方法前还是后等信息,如果要定位到哪些类的"哪些方法"上就要用到切点了。
org.springframework.aop.Pointcut描述切点,由ClassFilter和MethodMatcher组成,spring支持静态方法匹配器(匹配方法签名<方法名和参数列表>,只需要判断一次)和动态方法匹配器(可以判断方法中参数的值,每次调用方法都会判断,不常用)
切点类型 参考http://blog.csdn.net/a569433273/article/details/7800863 6种:
静态方法切点 org.springframework.aop.support.StaticMethodMatcherPointcut 默认匹配所有的类。包括两个子类:NameMatchMethodPointcut简单字符串匹配方法签名;AbstractRegexpMethodPointcut使用正则表达式匹配方法签名
动态方法切点 org.springframework.aop.support.DynamicMethodMatcherPointcut 默认匹配所有的类。2.0下已经过时,可以使用DefaultPointcutAdvisor和DynamicMethodMatcherPointcut动态方法匹配器替代
注解切点 org.springframework.aop.support.annotation.AnnotationMatchingPointcut 支持在Bean中直接通过注解标签定义的切点
表达式切点 0org.springframework.aop.support.ExpressionPointcut 支持AspectJ切点表达式而定义的切点
流程切点 org.springframework.aop.support.ControlFlowPointcut 根据程序执行堆栈的信息查看目标方法是否由某一个方法直接和间接发起调用,以此判断是否为匹配的连接点
复合切点 org.springframework.aop.support.ComposablePointcut 实现类是为创建多个切点而提供的方便操作类。它所有的方法都返回ComposablePointcut,这样,就可以使用连接表达式对切点进行操作,如:Pointcut pc = new ComposablePointcut().union(classFilter).intersection(methodMatcher).intersection(pointcut)
切面类型(3种):
一般切面Advisor 仅仅包含一个Advice
具有切点的切面PointcutAdvisor,包含Advice和Pointcut两个类
引介切面IntroductionAdvisor,使用ClassFilter进行定义
具有切点的切面PointcutAdvisor的六个实现类:看http://book.51cto.com/art/200908/147223.htm中的PointcutAdvisor家族
1. DefaultPointcutAdvisor
DefaultPointcutAdvisor是PointcutAdvisor的"大弟子",是最通用的PointcutAdvisor实现。除了不能为其指定Introduction类型的Advice之外,剩下的任何类型的Pointcut、任何类型的Advice都可以通过DefaultPointcutAdvisor来使用。我们可以在构造DefaultPointcutAdvisor的时候,就明确指定属于当前DefaultPointcutAdvisor实例的Pointcut和Advice,也可以在DefaultPointcut- Advisor实例构造完成后,再通过setPointcut以及setAdvice方法设置相应的Pointcut和Advice
<bean id="pointcut" class="..."> ...</bean> <bean id="advice" class="..."> ...</bean> <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut" ref="pointcut"/> <property name="advice" ref="advice"/> </bean>
<bean id="target" class="..." /> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean" p:interceptorNames="advisor" p:target-ref="target" p:proxyTargetClass="true" />
pointcut可以是上面定义的6中类型,advice可以是上面5种中的前四种(无引介类型)
如果这里的pointcut是DynamicMethodMatcherPointcut,那么就创建了一个动态切面,这个pointcut即可进行静态检查,也可以进行动态检查,我们可以在静态检查中看那些连接点是不匹配的,动态检查中就不用检查这个了
package com.baobaotao.advisor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.springframework.aop.ClassFilter; import org.springframework.aop.support.DynamicMethodMatcherPointcut; public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut { private static List<String> specialClientList = new ArrayList<String>(); static { specialClientList.add("John"); specialClientList.add("Tom"); } public ClassFilter getClassFilter() { return new ClassFilter() { public boolean matches(Class clazz) { System.out.println("调用getClassFilter()对"+clazz.getName()+"做静态检查."); return Waiter.class.isAssignableFrom(clazz); } }; } public boolean matches(Method method, Class clazz) { System.out.println("调用matches(method,clazz)对"+clazz.getName()+"."+method.getName()+"做静态检查."); return "greetTo".equals(method.getName()); } public boolean matches(Method method, Class clazz, Object[] args) { System.out.println("调用matches(method,clazz,args)对"+clazz.getName()+"."+method.getName()+"做动态检查."); String clientName = (String) args[0]; return specialClientList.contains(clientName); } }
<bean id="waiterTarget" class="com.baobaotao.advisor.Waiter" /> <bean id="sellerTarget" class="com.baobaotao.advisor.Seller" /> <!-- 动态切面 --> <bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut"> <bean class="com.baobaotao.advisor.GreetingDynamicPointcut" /> </property> <property name="advice"> <bean class="com.baobaotao.advisor.GreetingBeforeAdvice" /> </property> </bean> <bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean" p:interceptorNames="dynamicAdvisor" p:target-ref="waiterTarget" p:proxyTargetClass="true" />
public static void main(String[] args) { ApplicationContext ctx=new ClassPathXmlApplicationContext("com/baobaotao/advisor/beans.xml"); Object obj=ctx.getBean("waiter2"); System.out.println("*******************"); Waiter waiter=(Waiter)obj; waiter.greetTo("ken");System.out.println("-----------------------"); waiter.greetTo("Tom");System.out.println("-----------------------"); waiter.greetTo("ken"); }
运行结果如下:
调用getClassFilter()对com.baobaotao.advisor.Waiter做静态检查.
调用matches(method,clazz)对com.baobaotao.advisor.Waiter.greetTo做静态检查.
调用getClassFilter()对com.baobaotao.advisor.Waiter做静态检查.
调用matches(method,clazz)对com.baobaotao.advisor.Waiter.serveTo做静态检查.
调用getClassFilter()对com.baobaotao.advisor.Waiter做静态检查.
调用matches(method,clazz)对com.baobaotao.advisor.Waiter.clone做静态检查.
调用getClassFilter()对com.baobaotao.advisor.Waiter做静态检查.
调用matches(method,clazz)对com.baobaotao.advisor.Waiter.toString做静态检查.
*******************
调用getClassFilter()对com.baobaotao.advisor.Waiter做静态检查.
调用matches(method,clazz)对com.baobaotao.advisor.Waiter.greetTo做静态检查.
调用matches(method,clazz,args)对com.baobaotao.advisor.Waiter.greetTo做动态检查.
waiter greet to ken...
----------------
调用matches(method,clazz,args)对com.baobaotao.advisor.Waiter.greetTo做动态检查.
com.baobaotao.advisor.Waiter.greetTo
How are you!Mr.Tom.
waiter greet to Tom...
----------------
调用matches(method,clazz,args)对com.baobaotao.advisor.Waiter.greetTo做动态检查.
waiter greet to ken...
如果和ControlFowPointcut结合使用就叫流程切面,这里不详述了
2. NameMatchMethodPointcutAdvisor
NameMatchMethodPointcutAdvisor是细化后的DefaultPointcutAdvisor,它限定了自身可以使用的Pointcut类型为NameMatchMethodPointcut,并且外部不可更改。不过,对于使用的Advice来说,除了Introduction,其他任何类型的Advice都可以使用。
NameMatchMethodPointcutAdvisor内部持有一个NameMatchMethodPointcut类型的Pointcut实例。当通过NameMatchMethodPointcutAdvisor公开的setMappedName和setMappedNames方法设置将被拦截的方法名称的时候,实际上是在操作NameMatchMethodPointcutAdvisor所持有的这个NameMatchMethodPointcut实例。
<bean id="advice" class="..."> ... </bean> <bean id="advisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice"> <ref bean="advice"/> </property> <property name="mappedNames"> <list> <value>method1</value> ... </list> </property> </bean>
3. RegexpMethodPointcutAdvisor
与NameMatchMethodPointcutAdvisor类似,RegexpMethodPointcutAdvisor也限定了自身可以使用的Pointcut的类型,即只能通过正则表达式为其设置相应的Pointcut。
RegexpMethodPointcutAdvisor自身内部持有一个AbstractRegexpMethodPointcut的实例。AbstractRegexpMethodPointcut有两个实现类,即Perl5RegexpMethodPointcut和JdkRegexpMethodPointcut。默认情况下,RegexpMethodPointcutAdvisor会使用JdkRegexp- MethodPointcut。如果要强制使用Perl5RegexpMethodPointcut,那么可以通过RegexpMethod- PointcutAdvisor的setPerl5(boolean)达成所愿。
RegexpMethodPointcutAdvisor提供了许多构造方法,我们可以在构造时就指定Pointcut的正则表达式匹配模式以及相应的Advice,也可以构造完成之后再指定,在使用上与其他的Advisor实现并无太多差别。
<bean id="advice" class="..."> ... </bean> <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="pattern"> <value>cn\.spring21\..*\.methodNamePattern</value> </property> <property name="advice"> <ref bean="advice"/> </property> <property name="perl5"> <value>false</value> </property> </bean>
4. DefaultBeanFactoryPointcutAdvisor
DefaultBeanFactoryPointcutAdvisor是使用比较少的一个Advisor实现,因为自身绑定到了BeanFactory,所以要使用DefaultBeanFactoryPointcutAdvisor,我们的应用一定要绑定到Spring的IoC容器了。而且通常情况下,DefaultPointcutAdvisor已经完全可以满足需求。
DefaultBeanFactoryPointcutAdvisor的作用是,我们可以通过容器中的Advice注册的beanName来关联对应的Advice。只有当对应的Pointcut匹配成功之后,才去实例化对应的Advice,减少了容器启动初期Advisor和Advice之间的耦合性。
<bean id="advice" class="..."> </bean> <bean id="pointcut" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedName" value="doSth"/> </bean> <bean id="advisor" class="org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor"> <property name="pointcut" ref="pointcut"/> <property name="adviceBeanName" value="advice"/> </bean>
注意,对应advice的配置属性名称为"adviceBeanName",而它的值就对应advice的beanName。除了这一点,与DefaultPointcutAdvisor使用一致。
5.StaticMethodMatcherPointcutAdvisor静态普通方法名匹配切面
import java.lang.reflect.Method; import org.springframework.aop.ClassFilter; import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor { @Override public boolean matches(Method method, Class clazz) { return "greetTo".equals(method.getName()); } @Override public ClassFilter getClassFilter(){ return new ClassFilter(){ public boolean matches(Class clazz){ return Waiter.class.isAssignableFrom(clazz); } }; } }
<bean id="waiterTarget" class="com.baobaotao.advisor.Waiter" /> <bean id="sellerTarget" class="com.baobaotao.advisor.Seller" /> <bean id="greetingAdvice" class="com.baobaotao.advisor.GreetingBeforeAdvice" /> <bean id="greetingAdvisor" class="com.baobaotao.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" />
ClassFilter是类过滤器,这里表示只过滤Waiter类
6.AspectJExpressionPointcutAdvisor 可以定义aspectJ表达式
<bean id="audienceAdvisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor"> <property name="advice" ref="audienceAdvice" /> <property name="expression" value="execution(* Performer+.perform(..))" /> </bean>
对应的pointcut是org.springframework.aop.aspectj.AspectJExpressionPointcut
<bean id="performancePointcut" class="org.springframework.aop.aspectj.AspectJExpressionPointcut"> <property name="expression" value="execution(* Performer+.perform(..))" /> </bean>
7.AspectJPointcutAdvisor
public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) //Create a new AspectJPointcutAdvisor for the given advice
AbstractAspectJAdvice的子类:AspectJAfterAdvice, AspectJAfterReturningAdvice, AspectJAfterThrowingAdvice, AspectJAroundAdvice, AspectJMethodBeforeAdvice
这些advisor的实现类,都可以在pointcut中找到对应的类,因为他们本来就是通过扩张对应pointcut的实现类并实现PointcutAdvisor接口而产生的。
关于使用@Aspect和aop空间参考博客@Aspect 和 aop命名空间