老兄,快来一起过春天!(五)

使用 Spring AOP ,配置ApplicationContext.XML 实在是繁杂而乏味的工作。如果有多于10个切入点需要AOP代理,可以想象XML中的装配需要声明多少个切入bean,每个切入点需要4个增强,就需要40个切入点声明。
而在aop装配中,也需要把这些声明一一写到注入的list中。

因此,在大部分情况下,应该使用一些AOP框架来简化这些AOP装配。譬如:AspectJ。

AspectJ:一个扩展了java语言、面向切面的框架。它具有自身的AOP语法,更有专用的编译器生成遵守Java字节编码规范的Class文件。


新建工程:AspectJTest,结构如下:

 

dao下有接口IUserService

  1. package com.spring.dao;
  2. public interface IUserService {
  3.     
  4.     void create(String name,String pwd);
  5.     void update(String name,String pwd);
  6. }
及其实现类UserServiceImpl
  1. package com.spring.dao.impl;
  2. import com.spring.dao.IUserService;
  3. public class UserServiceImpl implements IUserService {
  4.     public void create(String name, String pwd) {
  5.         System.out.println("创建成功:" + name + "    " + pwd);
  6.     }
  7.     public void update(String name, String pwd) {
  8.         System.out.println("更新成功:" + name + "    " + pwd);
  9.     }
  10. }

先不去管aspectj包下的aopAspectj类,打开ApplicationContext.xml:
由于AspectJ需要引入AspectJ的库来解析切入点,所以在ApplicationContext.xml的DTD部份,需要引入AspectJ的命名空间:
如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.        xmlns:aop="http://www.springframework.org/schema/aop"
  5.        xsi:schemaLocation="
  6.        http://www.springframework.org/schema/beans
  7.        http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
  8.        http://www.springframework.org/schema/aop
  9.        http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
  10. >

在<beans></beans>节点最后位置输入"<a",试着利用Eclipse的智能提示看提示中是否有<aop:...>选项,如发现含有选项,可以证明AspectJ命名空间引入成功。接着
输入以下配置开启AspectJ解析。

  1. <aop:aspectj-autoproxy />

开启解析后,把dao包下的实现类作为管理bean声明:

  1. <bean id="userservice" class="com.spring.dao.impl.UserServiceImpl" />

关闭ApplicationContext.xml,在aspectj下新建类:aopAspectj.java

  1. package com.spring.aspectj;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.AfterReturning;
  4. import org.aspectj.lang.annotation.AfterThrowing;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Before;
  8. @Aspect
  9. public class aopAspectj {
  10.     
  11.     @Before("execution(* com.spring.dao.IUserService.create(..))")
  12.     public void logBefore(){
  13.         System.out.println("方法开始执行");
  14.     }
  15.     
  16.     @AfterReturning("execution(* com.spring.dao.IUserService.create(..))")
  17.     public void logAfter(){
  18.         System.out.println("方法结束执行");
  19.     }
  20.     
  21.     @Around("execution(* com.spring.dao.IUserService.create(..))")
  22.     public Object logAround(ProceedingJoinPoint pjp)throws Throwable {
  23.         System.out.println("方法正在执行" + pjp.getArgs()[0]);
  24.             return pjp.proceed();
  25.     }
  26.     @AfterThrowing(
  27.         pointcut="execution(* com.spring.dao.IUserService.create(..))",
  28.         throwing = "e"
  29.     )
  30.     public void logFailure(RuntimeException e){
  31.         System.out.println("方法执行发生错误");
  32.     }
  33. }

可以看到这个类与普通的类有所区别:多了很多注解。

AspectJ就是利用特有的自动代理bean:AnnotationAwareAspectJAutoProxyCreator对ApplicationContext.xml中所有的管理bean扫描解析,检查是否含有AspectJ约定的特殊注解。如发现注解,则根据注解的不同作出不同的装配,最后组合为AOP代理对象。
而管理bean中的所有方法一旦作出注解,则表示此方法应通过AspectJ解析转换为实现了AOP联盟规定接口的某个类。
其实可以把含有AspectJ注解的方法看作实现AOP接口的类,一个方法一个类。

如@Before:表示对应实现了MethodBeforeAdvice接口的类;
如@AfterReturning:对应实现AfterReturningAdvice接口的类;
如@Around:对应实现MethodInterceptor接口的类;
如@AfterThrowing:对应实现ThrowsAdvice接口的类;

这些注解都是AspectJ规定死的,不能改变(除非自己更改AspectJ的底层配置)。
execution(* com.spring.dao.IUserService.create(..)),执行条件使用正则来判断
execution:表示执行切入点的条件,上一句意为执行com.spring.dao.IUserService类中名为create,且参数任意(考虑重载)的方法。
注意:create(..)中的点只能是两点,不可多不可少,表示任意。

aopAspectj类完成后,需要在ApplicationContext.xml中声明成管理bean。


 

  1. <bean id="aopaspect" class="com.spring.aspectj.aopAspectj" ></bean>

在main包下新建测试类TestMain.java

  1. package com.spring.main;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. import com.spring.dao.IUserService;
  5. public class TestMain {
  6.     public static void main(String[] args){
  7.         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  8.         IUserService userservice = (IUserService) context.getBean("userservice");
  9.         try{
  10.             userservice.create("ddd""fff");
  11.         }catch(Exception e){
  12.         }
  13.     }
  14.     
  15. }

将TestMain.java做为java Application程序运行,可以看到结果:

 

方法开始执行
方法正在执行
创建成功:ddd    fff
方法结束执行

 

表明AspectJ解析了aopAspectJ类的注解,将方法自动装配为代理对象并成功运行了。

 

这时例子中的每个方法都没有拦截业务类的参数,更改业务流程。现在试着就加入参数:

  1.     @Before("execution(* com.spring.dao.IUserService.create(..)) && args(name,..)")
  2.     public void logBefore(String name){
  3.         System.out.println("方法开始执行" + name);
  4.     }
  5.     
  6.     @AfterReturning("execution(* com.spring.dao.IUserService.create(..)) && args(name,..)")
  7.     public void logAfter(String name){
  8.         System.out.println("方法结束执行" + name);
  9.     }
  10.     
  11.     @Around("execution(* com.spring.dao.IUserService.create(..))")
  12.     public Object logAround(ProceedingJoinPoint pjp)throws Throwable {
  13.         System.out.println("方法正在执行" + pjp.getArgs()[0]);
  14.         if(pjp.getArgs()[0].equals("ddd")){
  15.             return pjp.proceed();
  16.         }
  17.         throw new RuntimeException("错误了");
  18.     }
  19.     @AfterThrowing(
  20.         pointcut="execution(* com.spring.dao.IUserService.create(..)) && args(name,..)",
  21.         throwing = "e"
  22.     )
  23.     public void logFailure(RuntimeException e,String name){
  24.         System.out.println("方法执行发生错误" + name);
  25.     }

("execution(* com.spring.dao.IUserService.create(..)) && args(name,..)")
表示AspectJ在进行代理拦截时,会将参数name也拦截下来交给增强方法处理。而在增强方法logBefore的参数中,也必须对应args的参数,不可多不可少。
否则会抛出IllegalArgumentException错误。

在没有用AspectJ的AOP设计中,实现了MethodInterceptor接口的增加可以拦截连接点,改变整个应用程序的运行流程。同样的注解为@Around的方法也拥有同样效果。
public Object logAround(ProceedingJoinPoint pjp)throws Throwable
方法声明传入一个连接点对象返回对象,抛出AOP代理错误。和实现MethodInterceptor接口的方法炉出一辄。
public Object usernow(MethodInvocation invocation)throws Throwable

将TestMain.java做为java Application程序运行,可以看到结果:

 

方法开始执行ddd
方法正在执行ddd
创建成功:ddd    fff
方法结束执行ddd

 

可以看到参数被成功拦截。现在再把@Around注解的增强方法中的判断更改为:

  1. .....
  2. if(pjp.getArgs()[0].equals("fff")){
  3. .....

运行程序,结果为:

 

方法开始执行ddd
方法正在执行ddd
方法执行发生错误ddd

 

可以看到@Around注解的增强方法抛出了错误并被注解为@AfterThrowing的增强方法捕获了,程序的流程改变。可以想想如是把AOP运用在权限、日志等大范围验证记录方面,的确可以节约很多重复性工作。

 

 

现在看看aopAspectJ类的注解,一共有4个execution执行注解,AspectJ提供了一个叫@Pointcut的注解,可以将有需要的注解归纳到一起定义。
将aopAspectJ类更改如下:

  1. package com.spring.aspectj;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.AfterReturning;
  4. import org.aspectj.lang.annotation.AfterThrowing;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Before;
  8. import org.aspectj.lang.annotation.Pointcut;
  9. @Aspect
  10. public class aopAspectj {
  11.     
  12.     @Before("logApjectJ() && args(name,..)")
  13.     public void logBefore(String name){
  14.         System.out.println("方法开始执行" + name);
  15.     }
  16.     
  17.     @AfterReturning("logApjectJ() && args(name,..)")
  18.     public void logAfter(String name){
  19.         System.out.println("方法结束执行" + name);
  20.     }
  21.     
  22.     @Around("logApjectJ()")
  23.     public Object logAround(ProceedingJoinPoint pjp)throws Throwable {
  24.         System.out.println("方法正在执行" + pjp.getArgs()[0]);
  25.         if(pjp.getArgs()[0].equals("ddd")){
  26.             return pjp.proceed();
  27.         }
  28.         throw new RuntimeException("错误了");
  29.     }
  30.     @AfterThrowing(
  31.         pointcut="logApjectJ() && args(name,..)",
  32.         throwing = "e"
  33.     )
  34.     public void logFailure(RuntimeException e,String name){
  35.         System.out.println("方法执行发生错误" + name);
  36.     }
  37.     
  38.     @Pointcut("execution(* com.spring.dao.IUserService.create(..))")
  39.     public void logApjectJ(){};
  40. }

可以看到4个execution注解已经移到新增的@Pointcut注解方法中,原先注解的方法写上了@Pointcut注解方法名。这样就达到了将相同注解集中在一起定义的目的。

 

运行TestMain.java,结果与单独进行execution注解的效果没有差别。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值