1、在XML配置文件中配置切面、切入点、增强处理。spring-1.5之前只能使用XML Schema方式配置切面、切入点、增强处理。
spring配置文件中,所有的切面、切入点、增强处理都必须定义在<aop:config.../>元素内部。<beans.../>元素可以包含多个<aop:config.../>元素,一个<aop:config>可以包含pointcut、advisor、aspect元素,且三个元素必须按照此顺序来定义。其中aspect下可以包含多个子元素。
使用<aop:config.../>方式进行配置时,可能与spring的自动代理方式相冲突,例如使用<aop:aspectj-autoproxy>或类似方式显示启用了自动代理,则可能导致出现问题(比如有些增强处理没有被织入)。所以要么全使用<aop:config.../>的配置方式,要么全使用自动代理方式,不要混合使用。
2、配置切面
使用<aop:aspect.../>定义,bean可以是Java bean也可以是容器bean(即可以给bean注入依赖),如果切面bean是容器bean,<aop:aspect.../>使用ref属性引用。使用<aop:aspect.../>可以指定的属性:
1》id:定义该切面的标识名。
2》ref:用于将ref引用的容器bean转换成切面bean。
3》order:指定该切面bean的优先级,该属性的作用与前面@AspectJ中的@Order注解、Ordered接口的作用完全一样,order属性值越小,该切面对应的优先级越高。
3、配置增强处理
使用XML配置增强处理需要以下元素:
<aop:before.../>:配置Before增强处理。
<aop:after.../> :配置After增强处理。
<aop:after-returning.../>:配置AfterReturning增强处理。
<aop:after-throwing.../>:配置AfterThrowing增强处理。
<aop:around.../>:配置Around增强处理。
这些元素都不支持使用子元素,但可以指定以下属性:
pointcut:指定一个切入点表达式,spring将在匹配该表达式的连接点时织入该增强处理。
pointcut-ref:该属性指定一个已经存在的切入点名称,通常pointcut、pointcut-ref两个属性只是用其中之一。
method:该属性指定一个方法名,指定将切面bean的该方法转换为增强处理。
throwing:该属性只对<after-throwing.../>元素有效,用于指定一个形参名,AfterThrowing增强处理方法可通过该形参访问目标方法所抛出的异常。
returning:该属性只对<after-returning.../>元素有效,用于指定一个形参名,AfterReturning增强处理方法可通过该形参访问目标方法的返回值。
当定义切入点表达式时,XML配置方式完全支持excution、within、args、this、target、bean等切入点指示符。
要组合切入点表达式时,使用的是and(相当于&&)、or(相当于||)、not(相当于!)
举个例子:
HelloImpl.java
package com.lfy.impl; import com.lfy.bean.Hello; /** * 被增强的目标类 * @author lfy * */ public class HelloImpl implements Hello { @Override public void foo() { System.out.println("执行Hello组件的foo()方法"); } @Override public void addUser(String name, String pass) { System.out.println("执行Hello组件的addUser()添加用户: "+name); } @Override public int addGroup(String groupName, int groupMemberNumber) { System.out.println("执行Hello组件的addGroup()添加群组: "+groupName); if(groupMemberNumber<0||groupMemberNumber>100) { throw new IllegalArgumentException("群组成员数在0~100之间"); } return 0; } @Override public int fourAdviceGetParamer(String param) { System.out.println("执行Hello组件的fourAdviceGetParamer()方法,param:"+param); return 5; } }
FourAdviceGetParamerAspect.java
package com.lfy.aspect; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; /** * Around、Before、After、AfterReturning四种增强处理<br> * 访问目标方法最简单的做法是定义增强处理方法时将第一个参数定义为JoinPoint类型, * 当该增强处理方法被调用时,该JoinPoint参数就代表了织入增强处理的连接点。JoinPoint * 提供了如下的常用访问方法:<br> * 1>Objeact[] getArgs():返回目标方法的参数<br> * 2>Signature getSignature():返回目标方法的相关信息<br> * 3>Object getTarget():返回被增强处理的对象<br> * 4>Object getThis():返回AOP框架为目标对象生成的代理对象。<br> * 在这些方法中,只有Around增强处理中可以对目标方法的参数进行修改!<br> * @author lfy * * 切入点组合连接符有&&、||、! */ public class FourAdviceGetParamerAspect { public Object processTx(ProceedingJoinPoint jp) throws Throwable { System.out.println("FourAdviceGetParamerAspect.Around执行目标方法之前,模拟开始事务..."); //获取目标方法原始的调用参数 Object[] args=jp.getArgs(); if(args!=null && args.length>0 && args[0].getClass() == String.class) { //修改目标方法调用参数的第一个参数 args[0]="[增强的前缀]"+args[0]; } //改变后的参数去执行目标方法,并保存目标方法执行后的返回值 Object rvt=jp.proceed(args); System.out.println("FourAdviceGetParamerAspect.Around执行目标方法之后,模拟结束事务..."); System.out.println("<---------------->"); //如果rvt的类型是Integer,将rvt改为它的平方 if(rvt!=null&&rvt instanceof Integer) { rvt=(Integer)rvt*(Integer)rvt; } return rvt; } public void authority(JoinPoint jp) { System.out.println("FourAdviceGetParamerAspect.Before模拟执行权限检查"); //返回被织入增强的目标方法 System.out.println("被织入的目标方法是: "+jp.getSignature().getName()); System.out.println("被织入的目标方法的参数: "+Arrays.toString(jp.getArgs())); System.out.println("被织入增强处理的目标对象为: "+jp.getTarget()); System.out.println("<---------------->"); } public void release(JoinPoint jp) { System.out.println("FourAdviceGetParamerAspect.After模拟方法结束后的释放资源..."); //返回被织入增强的目标方法 System.out.println("被织入的目标方法是: "+jp.getSignature().getName()); System.out.println("被织入的目标方法的参数: "+Arrays.toString(jp.getArgs())); System.out.println("被织入增强处理的目标对象为: "+jp.getTarget()); System.out.println("<---------------->"); } public void log(JoinPoint jp,Object rvt) { System.out.println("FourAdviceGetParamerAspect.AfterReturning获取目标方法返回值: "+rvt); System.out.println("FourAdviceGetParamerAspect.AfterReturning模拟记录日志功能..."); //返回被织入增强的目标方法 System.out.println("被织入的目标方法是: "+jp.getSignature().getName()); System.out.println("被织入的目标方法的参数: "+Arrays.toString(jp.getArgs())); System.out.println("被织入增强处理的目标对象为: "+jp.getTarget()); System.out.println("<---------------->"); } }
beans.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- spring配置文件的根元素,使用spring-beans-4.0.xsd语义约束 --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> <aop:config> <!-- 配置全局切入点,设置在<aop:config>下是全局切入点标签, 可以被多个切面共享。设置在<aop:aspect>下是某个切面的 切入点标签 ,表示该切入点只能在该切面中有效。 --> <aop:pointcut id="myPointcut0" expression="execution(int com.lfy.impl.HelloImpl.fourAdviceGetParamer(..))"/> <!-- 将fourAdviceGetParamerBean转换成切面bean,切面 bean的名称为fourAdviceGetParamerAspect,指定该 切面的优先级为2 --> <aop:aspect id="fourAdviceGetParamerAspect" ref="fourAdviceGetParamerBean" order="2"> <!-- 直接指定切入点表达式 --> <aop:around pointcut="execution(int com.lfy.impl.HelloImpl.fourAdviceGetParamer(..))" method="processTx"/> <aop:before pointcut-ref="myPointcut0" method="authority"/> <!-- 定义一个After增强处理,直接指定切入点,以切面bean(即:fourAdviceGetParamerAspect)中的release()方法作为增强处理方法 --> <aop:after pointcut-ref="myPointcut0" method="release"/> <aop:after-returning pointcut-ref="myPointcut0" method="log" returning="rvt"/> </aop:aspect> </aop:config> <bean id="fourAdviceGetParamerBean" class="com.lfy.aspect.FourAdviceGetParamerAspect"/> <bean id="hello" class="com.lfy.impl.HelloImpl"/> <bean id="world" class="com.lfy.impl.WorldImpl"/> </beans>
SpringAOPTest.java
package com.lfy.main; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lfy.bean.Hello; import com.lfy.bean.World; /** * 2、基于XML Schema方式 * @author lfy * 未登记的知识点: * 1>指定增强处理的优先级,@Order注解及Orderd接口 * 2>切入点指示符 */ public class SpringAOPTest { public static void main(String[] args) { ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); Hello hello=ctx.getBean("hello", Hello.class); System.out.println("修改后的目标方法的返回值:"+hello.fourAdviceGetParamer("唐僧")); } }
运行结果:
4、配置切入点
XML配置方式也可以通过定义切入点来重复使用切入点表达式。spring提供了<aop:pointcut.../>元素定义切入点。当把<aop:pointcut.../>元素作为<aop:config.../>元素的子元素定义时,表明该切入点可被多个切面共享;当把<aop:pointcut.../>元素作为<aop:aspect.../>的子元素定义时,表明该切入点只能在该切面中有效。
配置<aop:pointcut.../>元素通常需要指定的属性有:
id:指定该切入点的标识名。
expression:指定该切入点关联的切入点表达式。
<aop:pointcut id="myPointcut0" expression="execution(int com.lfy.impl.HelloImpl.fourAdviceGetParamer(..))"/>
如果已经使用了注解定义了某个切入点,也可以给expression指定该已有的切入点。(但不推荐注解和XML Schema混合使用,我们在第十七篇讲过,容易出错。)
<aop:pointcut id="myPointcut0" expression="com.lfy.aspect.PointcutUtil.myPointcut()"/>