Spring之AOP

1.AOP术语:
 
● 连接点(Joinpoint)
一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就称为“连接点”。
Spring中仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。
连接点是程序中客观存在的事物。
 
●切点(Pointcut)
AOP通过切点定位连接点。
连接点相当于数据库中的记录,而切点相当于查询条件。
 
●增强(Advice)
增强是织入到目标类连接点上的一段程序代码。
 
●目标对象(Target)
增强逻辑的织入目标类。
 
●引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法。
 
●织入(Weaving)
织入是将增强添加到目标类具体连接点上的过程。
 
●代理(Proxy)
一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。
代理类既可以是和原类具有相同接口的类,也可以是原类的子类。
 
●切面(Aspect)
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义。
 
AOP的工作重心在于如何将增强应用于目标对象的连接点上,这里首先包括两个工作:第一,如何通过增强和切点定位到连接点上;第二,如何在增强中编写切面的代码。
 
 
 
基础知识
 
Spring使用两种代理机制:一种是基于JDK的动态代理;一种是基于CGLib的动态代理。
 
1.JDK动态代理
 
JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。而Proxy利用InvocationHandler动态创建符合某一接口的实例,生成目标类的代理对象。
 
ForumService.java:
publicinterfaceForumService {
publicvoidremoveTopic(inttopicId);
publicvoidremoveForum(intforumId);
}
 
ForumServiceImpl.java:移除了横切代码
publicclassForumServiceImplimplementsForumService {
 
@Override
publicvoidremoveTopic(inttopicId) {
System.out.println("模拟删除Topic记录:"+ topicId);
try{
Thread.currentThread().sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
 
@Override
publicvoidremoveForum(intforumId) {
System.out.println("模拟删除ForumId记录:"+ forumId);
try{
Thread.currentThread().sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
 
}
 
PerformanceHandler.java:
publicclassPerformanceHandlerimplementsInvocationHandler{
 
privateObjecttarget;//目标对象
 
publicPerformanceHandler(Object target){
this.target= target;
}
 
/**
*@paramproxy 最终生成的代理实例,一般不会用到
*@parammethod 被代理的目标实例的某个具体的方法,通过它可以发起目标实例方法的反射调用
*@paramargs 传给代理实例某个具体方法的入参数组
*/
publicObjectinvoke(Object proxy, Method method, Object[] args)
throwsThrowable {
PerformanceMonitor.begin(target.getClass().getName() +"."+ method.getName());
Object obj = method.invoke(target, args);//通过反射调用target实例的目标方法
PerformanceMonitor.end();
returnobj;
}
 
}
 
TestForumService.java:测试JDK动态代理
publicclassTestForumService {
publicstaticvoidmain(String[] args) {
//创建希望被代理的目标业务类
ForumService target =newForumServiceImpl();
//将目标业务类和横切代码纺织在一起
PerformanceHandler performanceHandler =newPerformanceHandler(target);
//根据编织了目标业务类逻辑和横切逻辑的InvocationHandler实例创建代理实例
ForumService proxy =
(ForumService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), performanceHandler);
proxy.removeForum(12);
proxy.removeTopic(1024);
}
}
 
Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler)方法说明:第一个入参为类加载器;第二个入参是代理实例需要实现的一组接口;第三个入参是整合了业务逻辑和横切逻辑的“编织器”对象。
 
2.CGLib动态代理
 
CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,度顺势织入横切逻辑。
 
CGLibProxy.java
publicclassCGLibProxyimplementsMethodInterceptor {
 
privateEnhancerenhancer=newEnhancer();
 
publicObject getProxy(Classclazz){
enhancer.setSuperclass(clazz);//设置需要创建子类的类
enhancer.setCallback(this);
returnenhancer.create();//通过字节码技术动态创建子类的实例
}
//拦截父类所有方法的调用
@Override
publicObject intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3)throwsThrowable {
PerformanceMonitor.begin(arg0.getClass().getName() +"."+ arg1.getName());
Object result = arg3.invokeSuper(arg0, arg2);//通过代理类调用父类中的方法
PerformanceMonitor.end();
returnresult;
}
}
 
TestForumService2.java:测试CGLib动态代理
publicclassTestForumService2 {
 
publicstaticvoidmain(String[] args) {
CGLibProxy proxy =newCGLibProxy();
//通过动态生成子类的方式创建代理类
ForumServiceImpl forumService = (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(16);
forumService.removeTopic(1024);
}
}
 
 
 
创建增强类
 
1.增强分类
按照增强在目标类方法的连接点位置,可以分为以下几类:
● 前置增强:org.springframework.aop.MethodBeforeAdvice,表示在目标方法执行前实施增强。
● 后置增强:org.springframework.aop.AfterReturningAdvice,表示在目标方法执行后实施增强。
● 环绕增强:org.aopalliance.intercept.MethodInterceptor,表示在目标方法执行前后实施增强。
● 异常增强:org.springframework.aop.ThrowsAdvice,表示在目标方法抛出异常后后实施增强。
● 引介增强:org.springframework.aop.IntroductionInterceptor,表示在目标类中添加一些新的方法和属性。
 
2.前置增强
 
Waiter.java:
publicinterfaceWaiter {
publicvoidgreetTo(String name);
publicvoidserveTo(String name);
}
 
NaiveWaiter.java:
publicclassNaiveWaiterimplementsWaiter {
@Override
publicvoidgreetTo(String name) {
System.out.println("greet to "+ name);
}
@Override
publicvoidserveTo(String name) {
System.out.println("serve to "+ name);
}
}
 
GreetingBeforeAdvice.java:
publicclassGreetingBeforeAdviceimplementsMethodBeforeAdvice {
/**
*@paramarg0 目标类的方法
*@paramarg1 目标类的入参
*@paramarg2 目标类实例
*/
@Override
publicvoidbefore(Method arg0, Object[] arg1, Object arg2)
throwsThrowable {
String clientName = (String)arg1[0];
System.out.println("Welcome to you, Mr. "+ clientName);
}
}
 
TestBeforeAdvice.java:
publicclassTestBeforeAdvice {
publicstaticvoidmain(String[] args) {
//目标实例
Waiter waiter =newNaiveWaiter();
//增强
BeforeAdvice advice =newGreetingBeforeAdvice();
//Spring提供的代理工厂
ProxyFactory proxyFactory =newProxyFactory();
//设置代理目标
proxyFactory.setTarget(waiter);
//为代理目标添加增强
proxyFactory.addAdvice(advice);
//生成代理实例
Waiter proxy = (Waiter)proxyFactory.getProxy();
proxy.greetTo("TEST");
proxy.serveTo("test");
}
}
 
输出结果:
Welcome to you, Mr. TEST
greet to TEST
Welcome to you, Mr. test
serve to test
 
解剖ProxyFactory
ProxyFactory内部使用JDK代理或CGLib代理技术,将增强应用到目标类中。
Spring定义了org.springframework.aop.framework.AopProxy接口,并提供了Cglib2AopProxy和JdkDynamicAopProxy两个final类型的实现类。如果通过ProxyFactory的setInterface(Class[] interfaces)指定针对接口进行代理,ProxyFactory就使用JdkDynamicAopProxy;如果是针对类的代理,则使用Cglib2AopProxy。此外,还可以通过ProxyFactory的setOptimize(true)方法,让ProxyFactory启动优化代理方式,这样,针对接口的代理也会使用Cglib2AopProxy。值得注意的一点是,使用CGLib代理时,必须引入CGLib类库。
 
在Spring中配置
Beans-Advice.xml:通过ProxyFactoryBean配置代理
<beanid="greetingAdvice"class="AspectJ.GreetingBeforeAdvice"/>
<beanid="target"class="AspectJ.NaiveWaiter"/>
<beanid="waiter"class="org.springframework.aop.framework.ProxyFactoryBean">
<propertyname="proxyInterfaces">
<list>
<value>AspectJ.Waiter</value>
</list>
</property>
<propertyname="interceptorNames">
<list>
<value>greetingAdvice</value>
</list>
</property>
<propertyname="target"ref="target"/>
</bean>
TestBeforeAdvice_02.java:
publicclassTestBeforeAdvice_02{
publicstaticvoidmain(String[] args) {
String configPath ="AspectJ/Beans-Advice.xml";
ApplicationContext ctx =newClassPathXmlApplicationContext(configPath);
Waiter waiter = (Waiter)ctx.getBean("waiter");
waiter.greetTo("test");
waiter.serveTo("TEST");
}
}
ProxyFactoryBean的几个常用的可配置属性:
● target:代理的目标对象
● proxyInterfaces:代理所要实现的接口
● interceptorNames:需要植入目标对象的增强Bean列表,采用Bean的名称指定。
● singleton:返回的代理是否是单实例,默认为单实例。
● optimize:设置为true时,强制作用CGLib代理。对于singleton的代理,推荐使用CGLib,对于其它作用域类
型的代理,最好使用JDK代理。
● proxyTargetClass:是否对类进行代理(而不是对接口进行代理),设置为true时,使用CGLib代理。
 
3.后置代理
Beans-AfterReturningAdvice.xml:
<beanid="greetingAdvice"class="AspectJ.GreetingAfterAdvice"/>
<beanid="target"class="AspectJ.NaiveWaiter"/>
<beanid="waiter"class="org.springframework.aop.framework.ProxyFactoryBean">
<propertyname="proxyInterfaces"value="AspectJ.Waiter"/>
<propertyname="interceptorNames"value="greetingAdvice"/>
<propertyname="target"ref="target"/>
</bean>
备注:interceptorNames是String[]类型,它接收增强Bean的名称而非增强Bean的实例。这是因为,ProxyFactoryBean内部在生成代理时,需要使用增强的类,而非增强的实例,即重用增强的横切逻辑代码,所以可以说增强是类级别的。
 
4.环绕增强
Beans-MethodInterceptor.xml
<beanid="greetingAround"class="AspectJ.GreetingInterceptor"/>
<beanid="target"class="AspectJ.NaiveWaiter"/>
<beanid="waiter"class="org.springframework.aop.framework.ProxyFactoryBean">
<propertyname="proxyInterfaces"value="AspectJ.Waiter"/>
<propertyname="interceptorNames">
<list>
<idreflocal="greetingAround"/>
</list>
</property>
<propertyname="target"ref="target"/>
</bean>
备注:对于属性是字符数组类型且数组元素是Bean名称的配置,最好使用<idref local="xxx" />标签,这样在一般的IDE环境下编缉Spring配置文件时,IDE会即时发现配置错误并给出报警。
 
5.异常抛出增强
异常抛出增强是适合应用的场景是事务管理。
TransactionManager.java:
publicclassTransactionManagerimplementsThrowsAdvice {
publicvoidafterThrowing(Method method,Object[] args, Object target,Exception ex){
System.out.println("-----------------");
System.out.println("method:"+ method.getName());
System.out.println("抛出异常:"+ ex.getMessage());
System.out.println("成功回滚.");
}
}
ThrowsAdvice异常抛出增强接口没有定义任何方法,它是一个标识接口。
void afterThrowing(Method method,Object[] args, Object target,Exception ex);
方法名必须是afterThrowing,前三个参数Method method,Object[] args, Object target是可选的(要么三个入参提供,要么不提供),最后一个入参是Throwable或其子类。
可以在同一个异常抛出增强中定义多个afterThrowing(),当目标方法抛出异常时,Spring会自动选用最匹配的增强方法。
备注:标识属性是没有任何方法和属性的接口,标识接口不对实现类有任何语义上的要求,仅仅表明它的实现类属于一个特定的类型。
 
6.引介增强
引介增强是一种比较特殊的增强类型,它不是在目标方法周围织入增强,而是为目标类创建新的方法和属性,所以引介增强的连接点是类级别的,而非方法级别的。通过引介增强,可以为目标类创建实现某接口的代理。
引介增强的配置与一般的配置有较大的区别:首先,需要指定引介增强所实现的接口;其次,由于只能通过为目标类创建子类的方式生成引介增强的代理,所以必须将proxyTargetClass设置为true。
 

 
创建切面
 
增强提供了连接点方位信息:如织入到方法前面、后面等,而切点进一步描述了织入到哪些类的哪些方法上。
Spring通过org.springframework.aop.Pointcut接口描述切点,Pointcut由ClassFilter和MethodMatcher构成,它通过ClassFilter定位到某些特定类上,通过MethodMatcher定位到某些特定的方法上。
Spring支持两种方法匹配器:静态方法匹配器和动态方法匹配器。所谓静态方法匹配器,它仅对方法名签名(包括方法名和入参类型及顺序)进行匹配;而动态方法匹配器,会在运行期检查方法入参的值。静态匹配仅会判别一次,而动态匹配因为每次调用方法的入参都可能不一样,所以每次都必须判断。一般情况下,动态匹配不常用。方法匹配器的类型由isRuntime()返回值决定,返回false表示是静态方法匹配器,返回true表示是动态方法匹配器。
 
1.切点类型
Spring提供了六种类型切点:
●静态方法切点:org.springframework.aop.support.StaticMethodMatcherPointcut是静态方法切点的抽象基类,默认情况下它匹配所有的类。StaticMethodMatcherPointcut包括NameMatchMethodPointcut和AbstractRegexpMethodPointcut两个子类,NameMatchMethodPointcut提供简单字符串匹配方法签名,AbstractRegexpMethodPointcut使用正则表达式匹配方法签名。
●动态方法切点:org.springframework.aop.support.DynamicMethodMatcherPointcut是动态方法的抽象基类,默认情况下它匹配所有的类。
●注解切点:org.springframework.aop.support.annotation.AnnotationMatchingPointcut实现类表示注解切点。使用AnnotationMatchingPointcut支持在Bean中直接通过JDK 5.0注解标签定义切点。
●表达式切点:org.springframework.aop.support.ExpressionPointcut接口主要是为了支持AspectJ切点表达式语法而定义的接口。
●流程切点:org.springframework.aop.support.ControlFlowPointcut实现类表示控制流程切点。ControlFlowPointcut是一种特殊的切点,它根据程序执行堆栈的信息查看目标方法是否由某一个方法直接或间接调用,以此判断是否为匹配的连接点。
●复合切点:org.springframework.aop.support.ComposablePointcut实现类是为创建多个切点而提供的方便操作类。它所有的方法都返回ComposablePointcut类。
 
2.切面类型
切面的分类:
●Advisor:代表一般切面,它仅包含一个Advice。这个切面太宽泛,一般不会直接使用。
●PointcutAdvisor:代表具有切点的切面,它包含Advice和Pointcut两个类。
●IntroductionAdvisor:代表引介切面。引介切面是对应引介增强的特殊的切面,它应用于类层面上。
 
PointcutAdvisor主要有6个具体的实现类:
●DefaultPointcutAdvisor:最常用的切面类型,它可以通过任意Pointcutt Advice定义一个切面,唯一不支持的是引介切面类型,一般可以通过扩展该类实现自定义的切面。
●NameMatchMethodPointcutAdvisor:通过该类可以定义按方法名定义切点的切面。
●RegexpMethodPointcutAdvisor:对于按正则表达式匹配方法名进行切点定义的切面,可以通过扩展该实现类进行操作。
●StaticMethodMatcherPointcutAdvisor:静态方法匹配器切点定义的切面,默认情况下,匹配所有的目标类。
●AspectJExpressionPointcutAdvisor:用于AspectJ切点表达式定义切点的切面。
●AspectJPointcutAdvisor:用于AspectJ语法定义的切面。
 
3.静态普通方法名匹配切面
Waiter.java:
publicclassWaiter {
publicvoidgreetTo(String name){
System.out.println("waiter greet to "+ name);
}
publicvoidserveTo(String name){
System.out.println("waiter serving "+ name);
}
}
Seller.java:
publicclassSeller {
publicvoidgreetTo(String name){
System.out.println("seller greet to "+ name);
}
}
GreetingBeforeAdvice.java
publicclassGreetingBeforeAdviceimplementsMethodBeforeAdvice {
@Override
publicvoidbefore(Method arg0, Object[] arg1, Object arg2)
throwsThrowable {
System.out.println(arg2.getClass().getName() +"."+ arg0.getName());//输出切点
String clientName = (String)arg1[0];
System.out.println("How are you ! Mr."+ clientName);
}
}
GreetingAdvisor.java
publicclassGreetingAdvisorextendsStaticMethodMatcherPointcutAdvisor {
@Override
publicbooleanmatches(Method arg0, Class<?> arg1) {
return"greetTo".equals(arg0.getName());
}
//默认情况下,该切面匹配所有的类,通过覆盖getClassFilter()方法,让它仅匹配Waiter类及其子类
@Override
publicClassFilter getClassFilter() {
returnnewClassFilter(){
@Override
publicbooleanmatches(Class<?> arg0) {
returnWaiter.class.isAssignableFrom(arg0);
}
};
}
}
Beans-StaticMethodMatcherPointcutAdvisor.xml
<beanid="waiterTarget"class="Advisor.Waiter"/>
<beanid="sellerTarget"class="Advisor.Seller"/>
<beanid="greetingBeforeAdvice"class="Advisor.GreetingBeforeAdvice"/>
<beanid="greetingAdvisor"class="Advisor.GreetingAdvisor">
<propertyname="advice"ref="greetingBeforeAdvice"/>
</bean>
<!-- 通过一个父Bean定义公共的配置信息 -->
<beanid="parent"abstract="true"class="org.springframework.aop.framework.ProxyFactoryBean">
<propertyname="interceptorNames"value="greetingAdvisor"/>
<propertyname="proxyTargetClass"value="true"/>
</bean>
<!-- waiter代理 -->
<beanid="waiter"parent="parent">
<propertyname="target"ref="waiterTarget"/>
</bean>
<!-- seller代理 -->
<beanid="seller"parent="parent">
<propertyname="target"ref="sellerTarget"/>
</bean>
StaticMethodMatcherPointcutAdvisor除了Advice属性外,还可以定义如下两个属性:
● classFilter:类匹配过滤器
● order:切面织入的顺序,该属性用于定义Ordered接口表示的顺序。
TestGreetingAdvisor.java
publicclassTestGreetingAdvisor {
publicstaticvoidmain(String[] args) {
String configPath ="Advisor/Beans-StaticMethodMatcherPointcutAdvisor.xml";
ApplicationContext ctx =newClassPathXmlApplicationContext(configPath);
Waiter waiter = (Waiter)ctx.getBean("waiter");
Seller seller = (Seller)ctx.getBean("seller");
waiter.greetTo("Test");
waiter.serveTo("Test");
seller.greetTo("Test");
}
}
输出结果:
Advisor.Waiter.greetTo
How are you ! Mr.Test
waiter greet to Test
waiter serving Test
seller greet to Test
 
4.静态正则表达式方法匹配切面
RegexpMethodPointcutAdvisor是正则表达式方法匹配的切面实现类,该类已经是功能齐全的实现类,一般情况下,无需扩展该类。
配置示例:
<bean id="regexpAdvisor" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="advice" ref="greetingAdvice" />
<property name="patterns">
<list><value>.*greet.*</value></list>
</property>
</bean>
 
5.动态切面
使用DynamicMethodMatcherPointcut和DefaultPointcutAdvisor来完成动态切面。
Spring采用这样的机制:在创建代理时,对目标类的每个连接点使用静态切点检查,如果仅通过静态切点检查就可以知道连接点是不可匹配的,则在运行时就不再进行动态检查了;如果静态切点检查是匹配的,在运行时才进行动态切点检查。
GreetingDynamicAdvisor.java:
publicclassGreetingDynamicAdvisorextendsDynamicMethodMatcherPointcut {
privatestaticList<String>specialClientList=newArrayList<String>();
static{
specialClientList.add("wgc");
}
//对类进行动态切点检索
@Override
publicClassFilter getClassFilter() {
returnnewClassFilter(){
@Override
publicbooleanmatches(Class<?> arg0) {
System.out.println("调用getClassFilter()对 "+ arg0.getName() +" 做静态检索!");
returnWaiter.class.isAssignableFrom(arg0);
}
};
}
//对方法进行静态方法检查
@Override
publicbooleanmatches(Method method, Class<?> targetClass) {
System.out.println("调用matches(method,targetClass)对 "+ targetClass.getName() +"."+ method.getName() +" 做静态检索!");
return"greetTo".equals(method.getName());
}
//对方法进行动态切点检索
@Override
publicbooleanmatches(Method arg0, Class<?> arg1, Object[] arg2) {
System.out.println("调用matches(arg0,arg1,arg2)对 "+ arg1.getName() +"."+ arg0.getName() +" 做动态检索!");
String clientName = (String)arg2[0];
returnspecialClientList.contains(clientName);
}
}
Beans-DefaultPointcutAdvisor.xml:
<beanid="waiterTarget"class="Advisor.Waiter"/>
<beanid="dynamicAdvisor"class="org.springframework.aop.support.DefaultPointcutAdvisor">
<propertyname="pointcut">
<beanclass="Advisor.GreetingDynamicAdvisor"/>
</property>
<propertyname="advice">
<beanclass="Advisor.GreetingBeforeAdvice"/>
</property>
</bean>
<beanid="waiter"class="org.springframework.aop.framework.ProxyFactoryBean">
<propertyname="interceptorNames"value="dynamicAdvisor"/>
<propertyname="target"ref="waiterTarget"/>
<propertyname="proxyTargetClass"value="true"/>
</bean>
Spring会在创建代理织入切面时,对目标类所有方法进行静态切点方法检查;在生成织入切面代理对象后,第一次调用代理类的每一个方法都会进行一次静态检查,如果本次检查就能从候选者中将该方法排除,以后对该方法的调用 就不再执行静态切点检查;对于那些在静态检查匹配的方法,在后续调用该方法时,将执行动态切点检查的操作。
动态代理是运行时动态产生的代理。在Spring中,不管是静态切面还是动态切面都是通过动态代理技术实现的。所谓静态切面是指在生成代理对象时,就确定增强是否需要织入到目标类连接点上,而动态切面是指必须在运行期根据方法入参的值来判断是否需要织入到目标类连接点上。
 
6.流程切面
Spring的流程切面由DefaultPointcutAdvisor和ControlFlowPointcut实现。流程切点代表某个方法直接或间接发起调用的其它方法。
WaiterDelegate.java:
publicclassWaiterDelegate{
privateWaiterwaiter;
publicvoidsetWaiter(Waiter waiter){
this.waiter= waiter;
}
//所有由WaiterDelegate#service方法发起调用的其他方法都织入GreetingBeforeAdvice
publicvoidservice(String clientName){
waiter.greetTo(clientName);
waiter.serveTo(clientName);
}
}
Beans-ControlFlowAdvisor.xml:
<beanid="waiterTarget"class="Advisor.Waiter"/>
<beanid="greetingAdvice"class="Advisor.GreetingBeforeAdvice"/>
<beanid="controlFlowPointcut"class="org.springframework.aop.support.ControlFlowPointcut">
<!-- 指定流程切点的类 -->
<constructor-argtype="java.lang.Class"value="Advisor.WaiterDelegate"/>
<!-- 指定流程切点的方法 -->
<constructor-argtype="java.lang.String"value="service"/>
</bean>
<beanid="controlFlowAdvisor"class="org.springframework.aop.support.DefaultPointcutAdvisor">
<propertyname="pointcut"ref="controlFlowPointcut"/>
<propertyname="advice"ref="greetingAdvice"/>
</bean>
<beanid="waiter"class="org.springframework.aop.framework.ProxyFactoryBean">
<propertyname="interceptorNames"value="controlFlowAdvisor"/>
<propertyname="target"ref="waiterTarget"/>
<propertyname="proxyTargetClass"value="true"/>
</bean>
TestControlFlowAdvisor.java:测试类
publicclassTestControlFlowAdvisor{
publicstaticvoidmain(String[] args) {
String configPath ="Advisor/Beans-ControlFlowAdvisor.xml";
ApplicationContext ctx =newClassPathXmlApplicationContext(configPath);
Waiter waiter = (Waiter)ctx.getBean("waiter");
waiter.greetTo("wgc");
WaiterDelegate waiterDelegate =newWaiterDelegate();
waiterDelegate.setWaiter(waiter);
waiterDelegate.service("WGC");
}
}
输出结果:
waiter greet to wgc
Advisor.Waiter.greetTo
How are you ! Mr.WGC
waiter greet to WGC
Advisor.Waiter.serveTo
How are you ! Mr.WGC
waiter serving WGC
 
7.复合切点切面
使用时机:一个切点无法描述目标连接点的信息。
ComposablePointcut可以将多个切点以并集或交集的方式组合起来,提供了切点之间复合运算的可能。
ComposablePointcut的构造函数:
●ComposablePointcut():构造一个匹配所有类所有方法的复合切点
●ComposablePointcut(ClassFilter classFilter):构造一个匹配特定类所有方法的复合切点
●ComposablePointcut(MethodMatcher methodMathcer):构造一个匹配所有类特定方法的复合切点
●ComposablePointcut(ClassFilter classFilter,MethodMatcher methodMathcer):构造一个匹配特定类特定方法的复合切点
ComposablePointcut提供了三个交集运算的方法:
●ComposablePointcut intersection(ClassFilter filter):将复合切点和一个ClassFilter对象进行交集运算,得到一个结果复合切点
●ComposablePointcut intersection(MethodMatcher mm):将复合切点和一个MethodMatcher对象进行交集运算,得到一个结果复合切点
●ComposablePointcut intersection(Pointcut other):将复合切点和一个切点对象进行交集运算,得到一个结果复合切点
ComposablePointcut提供了两个并集运算的方法:
●ComposablePointcutunion(ClassFilter filter):将复合切点和一个ClassFilter对象进行并集运算,得到一个结果复合切点
●ComposablePointcutunion(MethodMatchermm):将复合切点和一个MethodMatcher对象进行并集运算,得到一个结果复合切点
org.springframework.aop.support.Pointcuts提供了两个非常好用的静态方法:
● Pointcut intersection(Pointcut a, Pointcut b):对两个切点进行交集运算,返回一个结果切点。
● Pointcut union(Pointcut a, Pointcut b):对两个切点进行并集运算,返回一个结果切点。
 
8.引介切面
作用:引介切面是引介增强的封装器,通过引介切面,可以更容易地为现有对象添加任何接口的实现。
IntroductionAdvisor接口同时继承了Advisor和IntroductionInfo接口,IntroductionInfo描述了目标类需要实现的新接口。
IntroductionAdvisor有两个实现类,分别是:DefaultIntroductionAdvisor(常用)和DeclareParentsAdvisor。
DefaultIntroductionAdvisor的三个构造方法:
●DefaultIntroductionAdvisor(Advice advice):通过一个增强创建的引介切面,引介切面将为目标对象新增增强对象中所有接口的实现。
●DefaultIntroductionAdvisor(DynamicIntroductionAdvice advice, Class clazz):通过一个增强和一个指定的接口类创建引介切面,仅为目标对象新增clazz接口的实现。
●DefaultIntroductionAdvisor(Advice advice, IntroductionInfo introductionInfo):通过一个增强和一个IntroductionInfo对象创建一个引介切面,目标对象需要实现哪些接口,由introductionInfo对象的getInterfaces()表示。

 
 
自动创建代理
 
在内部,Spring使用BeanPostProcessor让窗口自动生成代理。基于BeanPostProcessor的自动代理创建器的实现类,将根据一些规则在容器实例化Bean时为匹配的Bean生成代理实例。
代理创建器可以分为三类:
● 基于Bean配置名规则的自动代理生成器:允许为一组特定配置名的Bean自动创建代理实例的代理创建器,实现类为BeanNameAutoProxyCreator
● 基于Advisor匹配机制的自动代理创建器它会对容器中的所有Advisor进行扫描,自动将这些切面应用到匹配的Bean中,实现类是DefaultAdvisorAutoProxyCreator
● 基于Bean中AspectJ注解标签的自动代理生成器:为包含AspectJ注解的Bean自动创建代理实例。
 
1.BeanNameAutoProxyCreator
Bean-BeanNameAutoProxyCreator.xml
<beanid="waiter"class="Advisor.Waiter"/>
<beanid="seller"class="Advisor.Seller"/>
<beanid="greetingAdvice"class="Advisor.GreetingBeforeAdvice"/>
<beanclass="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 由于只有一个Bean名称,所以可以直接使用属性进行配置,可以通过list子元素设定
多个Bean名称,或通过逗号、空格、分号的方式设定多个bean名称 -->
<propertyname="beanNames"value="*er"/><!-- 指定一组需要自动代理的Bean名称 -->
<!-- 指定一个或多个增强 -->
<propertyname="interceptorNames"value="greetingAdvice"/>
<!-- 为true时强制使用CGLib动态代理技术 -->
<propertyname="optimize"value="true"/>
</bean>
 
2.DefaultAdvisorAutoproxyCreator
Beans-DefaultAdvisorAutoProxyCreator.xml
<beanid="waiter"class="Advisor.Waiter"/>
<beanid="seller"class="Advisor.Seller"/>
<beanid="greetingAdvice"class="Advisor.GreetingBeforeAdvice"/>
<beanid="regexpAdvisor"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<propertyname="patterns"value=".*greet.*"/>
<propertyname="advice"ref="greetingAdvice"/>
</bean>
<beanclass="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值