Spring AOP(4)

xiaoxiao2023-03-20  27

在第三节里面,完满讲了使用@AspectJ注解实现Spring AOP,它需要运行在Java5以上的版本中,对于Java1.4之前的版本,我们也想使用Spring AOP,那么怎么办呢? 一种是像1,2节里面讲的那样,定义Advice实现MethodBeforeAdvice、MethodAfterAdvice、ThrowsAdvice、MethodInterceptor接口之一,然后包装在Advisor中,最后使用BeanPostProcessor(如BeanNameAutoProxyCreator、DefaultAdvisorAutoProxyCreator)创建业务Bean的代理对象。显然这种方式很繁琐。 第二种选择是使用Spring的<aop:...>命名空间,这是在2.0版本以后引入的。它可以运行在Java1.4基础上。本节主要介绍使用<aop:...>基于xml配置实现Spring AOP。 1、定义业务Bean package spring.aop;public class UserService { public void getUser(int id){ System.out.println("the user id: "+id); }} 2、定义切面 public class UserAspect { //业务方法执行前会执行此操作 public void before() { System.out.println("before advice"); } //业务方法正常执行结束后会执行此操作 public void afterReturn() { System.out.println("after-returning advice"); } //相当于finally,无论业务方法是否产生异常,执行后都会执行此操作 public void after() { System.out.println("after advice"); }} 这里的切面就是一个简单的POJO,不需要实现任何接口,不需要继承任何类。里面的方法就是一个Advice(包括before、after、after-throwing、after-return、around五种类型),而Pointcut在xml配置。 3、配置xml <bean id="userService" class="spring.aop.UserService"/><bean id="aspect" class="spring.aop.UserAspect"/><aop:config> <aop:pointcut id="pointcut" expression="execution(* spring.aop.UserService.* (..))"/> <aop:aspect ref="aspect"> <aop:before pointcut-ref="pointcut" method="before"/> <aop:after-returning pointcut-ref="pointcut" method="afterReturn" > <aop:after pointcut-ref="pointcut" method="after"> </aop:aspect></aop:config> 测试代码: ApplicationContext con=new ClassPathXmlApplicationContext("applicationContext.xml");UserService us=(UserService)con.getBean("userService");us.getUser(12); 输出结果: before advicethe user id: 12after-returning adviceafter advice 4、环绕通知 public Object around(ProceedingJoinPoint pjp) throws Throwable{ try{ System.out.println("around advice: before"); Object obj=pjp.proceed();//必须调用此方法,否则后续处理终断 System.out.println("around advice: after-returning"); return obj; }catch(Exception e){ throw e; }} 对应的xml配置: <aop:aspect ref="aspect"> <aop:around pointcut-ref="pointcut" method="around"/></aop:aspect> 环绕通知使我们有机会在方法执行的前后都作出相应的处理,功能最为强大,但必须包含一个处理连接点参数ProceedingJoinPoint pjp,并在与处理(before)之后调用pjp.proceed(),忘记次调用会带来莫名其妙的结果,所以能使用其他Advice进行处理的,尽量使用其他更简单的Advice。 5、异常 public void exception(Exception e) { //记录异常 System.out.println("exception ["+e+"]");} 这里参数Exception e是必须的。在xml配置中也必须指明此参数:throwing="e" 对应的xml配置: <aop:aspect ref="aspect"> <aop:after-throwing pointcut-ref="pointcut" method="exception" throwing="e"/></aop:aspect> 6、使用带参数的Advice 上面的例子中除了around和after-throwing含有参数,且around中的参数不需要xml中配置,其他的Advice都是无参数的,要想使用带有自定义参数的Advice,怎么办呢?此时就需要重新配置Pointcut了: 例子,一个带参数的before Advice: 定义业务Bean: public class UserService { public void login(User user){ System.out.println("user id: "+u.getId()); }} 定义before Advice: public void before(User user) { System.out.println("user "+u.getName()+"try to login...");} 在xml中配置Pointcut: <aop:config> <aop:pointcut id="pointcut" expression="execution(* spring.aop.UserService.* (User)) and args(u)"/> <aop:aspect ref="aspect"> <aop:before pointcut-ref="pointcut" method="before" arg-names="u"/> </aop:aspect></aop:config> 关键在于这一行:expression="execution(* spring.aop.UserService.* (User)) and args(u)",指明了切入点为spring.aop.UserService的任何方法,[b][color=red]并且[/color][/b]此方法含有一个类型的User的参数,参数名为u(可以与业务Bean中的参数名不一样,实际上是它的一个别名),<aop:before../>中arg-names就是引用的这个别名(可以与Advice中的参数名不一样)。 [color=red]注意: 其他Advice不能共享此Pointcut,除非Advice中的参数与此Pointcut中的参数一致。[/color] 有人会问,如果Advice中只使用业务Bean方法的[color=red][b]部分参数[/b][/color],该如何做呢? 答案是:依然利用Pointcut配置。 Demo: 定义业务Bean: public class UserService { public void login(String name,String psw){ System.out.println("login..."); }} 定义before Advice: public void before(String n) { System.out.println("user "+n+" try to login...");} 在xml中配置Pointcut: <aop:config> <aop:pointcut id="pointcut" expression="execution(* spring.aop.UserService.* (String,..)) and args(n,..)"/> <aop:aspect ref="aspect"> <aop:before pointcut-ref="pointcut" method="before" arg-names="n"/> </aop:aspect></aop:config> (String,..)声明切入点至少含有一个String类型的参数,显然可以匹配UserService中的login(String name,String psw); args(n,..)声明给login(String name,String psw)的第一个参数起了个别名“n”传递给Advice,如果<aop:before...>中arg-names不是“n”,将抛出异常。 7、Advice的顺序 1)一般情况下,before、after-throwing、after的执行顺序是一定的,即: before-->after-throwing-->after 而before与around中的proceed()方法调用之前的处理则是按照谁配在前谁先处理的原则,after与around中的proceed()方法调用之后的处理也是如此; 当异常抛出时,after-returning操作不会被处理,而after-throwing、after依次被处理。 2)如果是基于注解的方式,在测试中发现是按照如下顺序执行增强的: before advicearound advice: beforelogin...around advice: after-returningafter-returning adviceafter advice
转载请注明原文地址: https://www.6miu.com/read-4986772.html

最新回复(0)