Spring(二)Spring AOP配置详解

来源:互联网 发布:淘宝无线端收藏链接 编辑:程序博客网 时间:2024/04/20 07:15

一、Spring AOP

        面向切面编程(AOP),是软件编程思想发展到一定阶段的产物,是对面向对象编程(OOP)的有益补充。AOP一般适用于具有横切逻辑的场合,如访问控制、事务管理、性能监测等。
        面向切面编程,简单的说,就是在不改变源程序的基础上为代码段增加新的功能,对代码进行增强处理,它的设计思想来源于代理设计模式。
        了解之后需要知道以下概念:
        1.增强类型(Advice,有的翻译为通知,个人感觉不太好理解):
                前置增强(通知),在在原方法执行之前进行处理,同理还有后置增强、环绕增强、异常抛出增强、最终增强等。
        2.连接点(Joinpoint):
                程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。
        3.切点(Pointcut):
                每个类都拥有多个连接点:类的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

下面编写一个HelloWorld:
需求:
在Student类中有一个说话的say方法,需要使用AOP对此方法进行增强处理。

[java] view plain copy
  1. package com.wzj.entity;  
  2.   
  3. public class Student {  
  4.     private int id;  
  5.     private String name;  
  6.       
  7.     //省略get、set方法  
  8.   
  9.     public void say(){  
  10.         System.out.println("大家好,我的名字叫:"+getName());  
  11.     }  
  12. }  

二、基于注解的方式实现spring aop增强

步骤:
1.创建普通的java项目,加入jar包(在上文中有jar包的下载地址),创建好spring-config.xml文件,另外还要导入AspectJ框架所需jar包:aspectjweaver.jar、aopalliance.jar,可以百度下载
2.创建一个增强类如:AdviceUtil
(1)@Aspect注解——将类声明为切面

[java] view plain copy
  1. @Aspect  
  2. public class AdviceUtil {     
  3. }  
(2)@Before注解——声明为前置增强方法
[java] view plain copy
  1. /** 
  2.  * 使用@Before注解将方法声明为前置增强方法 
  3.  * 参数中需要加入:匹配切点的表达式execution(public void com.wzj.entity.Student.say()) 
  4.  * 匹配切点的表达式:execution(修饰符  返回值  方法全名(..)) 
  5.  * 可以使用“*”和“..”通配符,* 表示匹配所有,.. 用在方法参数中,表示匹配任意个数、类型的参数 
  6.  */  
  7. @Before("execution(public void com.wzj.entity.Student.say()))")  
  8. public void before(){  
  9.     System.out.println("----->前置增强操作");  
  10. }  
(3)@AfterReturning注解——声明后置增强方法
[java] view plain copy
  1. @AfterReturning("execution(public void com.wzj.entity.Student.say())")  
  2. public void behind(){  
  3.     System.out.println("----->后置增强操作");  
  4. }  
【为了获取到代理对象以及方法的相关信息,可以再增强方法中声明一个JoinPoint类型参数,spring会自动注入该参数】
a-->getTarget():获取到代理对象
b-->getSignature:获取到目标方法,其中包含一些信息
c-->getArgs:获取到目标方法的参数列表

注意:对于后置增强还可以获取到目标方法的返回值,如:
[java] view plain copy
  1. /** 
  2.  * 1.在方法中声明一个目标方法的返回值参数 
  3.  * 2.在AfterReturning注解中通过returning属性指定参数名称 
  4.  * 3.spring会自动注入参数 
  5.  * @param jp 
  6.  * @param returnValue 
  7.  */  
  8. @AfterReturning(pointcut="execution(public * com.wzj.entity.Student.say())",returning="returnValue")  
  9. public void behind(JoinPoint jp,String returnValue){  
  10.     System.out.println("目标方法返回值:"+returnValue);  
  11. }  
(4)@AfterThrowing注解——声明异常抛出增强方法
[java] view plain copy
  1. /** 
  2.  * 1.使用AfterThrowing注解将方法声明为异常抛出增强 
  3.  * 2.可以在方法中定义对应异常类型的参数,不过需要通过注解的throwing属性指定参数名 
  4.  * 3.spring会自动注入异常类型实例 
  5.  * 4.异常增强方法可以有多个 
  6.  * @param ex 
  7.  */  
  8. @AfterThrowing(pointcut="execution(public * com.wzj.entity.Student.say())",throwing="ex")  
  9. public void catchException(ArithmeticException ex){  
  10.     System.out.println("异常增强捕获到:"+ex.getMessage());  
  11. }  
(5)@Around注解——声明环绕增强方法
[java] view plain copy
  1. /** 
  2.  * 1.通过Around注解将方法声明为环绕增强 
  3.  * 2.可以定义ProceedingJoinPoint类型的参数获取到连接点信息 
  4.  * 3.ProceedingJpintPoint是JoinPoint的子接口,用法相同 
  5.  * 4.注意:环绕增强不会自动调用目标方法,所以必须手动在方法中使用proceed()方法调用目标方法 
  6.  * 从而达到对连接点的完全控制 
  7.  * @param pj 
  8.  * @throws Throwable  
  9.  */  
  10. @Around("execution(public * com.wzj.entity.Student.say())")  
  11. public void around(ProceedingJoinPoint pj) throws Throwable{  
  12.     System.out.println("前面的处理...");  
  13.     pj.proceed();//调用目标方法  
  14.     System.out.println("后面的处理...");  
  15. }  
(6)@After注解——声明最终增强方法
        我们知道,如果在目标方法中抛出了异常,就不会执行后续的后置增强。在AspectJ中为我们提供了一种最终增强,无论是否出了异常都会执行,类似于try-catch中的finally块,一般用于释放资源
[java] view plain copy
  1. /** 
  2.  * 使用After注解,其他用法相同 
  3.  */  
  4. @After("execution(public * com.wzj.entity.Student.say())")  
  5. public void after(){  
  6.     System.out.println("----->最终增强操作");  
  7. }  
完整的AdviceUtil类:
[java] view plain copy
  1. package com.wzj.entity;  
  2.   
  3. import org.aspectj.lang.JoinPoint;  
  4. import org.aspectj.lang.ProceedingJoinPoint;  
  5. import org.aspectj.lang.annotation.After;  
  6. import org.aspectj.lang.annotation.AfterReturning;  
  7. import org.aspectj.lang.annotation.AfterThrowing;  
  8. import org.aspectj.lang.annotation.Around;  
  9. import org.aspectj.lang.annotation.Aspect;  
  10. import org.aspectj.lang.annotation.Before;  
  11.   
  12. /** 
  13.  * 使用@Aspect将此类定义为一个切面 
  14.  */  
  15. @Aspect  
  16. public class AdviceUtil {  
  17.       
  18.     /** 
  19.      * 匹配切点的表达式:execution(修饰符  返回值  方法全名(..)) 
  20.      * 可以使用“*”和“..”通配符,* 表示匹配所有,.. 用在方法参数中,表示匹配任意个数、类型的参数 
  21.      */  
  22.     @Before("execution(void com.wzj.entity.Student.say())")  
  23.     public void before(){  
  24.         System.out.println("----->前置增强操作");  
  25.     }  
  26.       
  27.     @AfterReturning(pointcut="execution(public * com.wzj.entity.Student.say())",returning="returnValue")  
  28.     public void behind(JoinPoint jp,String returnValue){  
  29.         System.out.println("----->后置增强操作");  
  30.     }  
  31.       
  32.     @AfterThrowing(pointcut="execution(public * com.wzj.entity.Student.say())",throwing="ex")  
  33.     public void catchException(ArithmeticException ex){  
  34.         System.out.println("异常增强捕获到:"+ex.getMessage());  
  35.     }  
  36.       
  37.     @Around("execution(public * com.wzj.entity.Student.say())")  
  38.     public void around(ProceedingJoinPoint pj) throws Throwable{  
  39.         System.out.println("前面的处理...");  
  40.         pj.proceed();//调用目标方法  
  41.         System.out.println("后面的处理...");  
  42.     }  
  43.       
  44.     @After("execution(public * com.wzj.entity.Student.say())")  
  45.     public void after(){  
  46.         System.out.println("----->最终增强操作");  
  47.     }     
  48. }  
3.在spring-config.xml文件中进行配置
[html] view plain copy
  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:p="http://www.springframework.org/schema/p"  
  5.     xmlns:util="http://www.springframework.org/schema/util"   
  6.     xmlns:aop="http://www.springframework.org/schema/aop"  
  7.     xsi:schemaLocation="  
  8.     http://www.springframework.org/schema/beans  
  9.     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
  10.     http://www.springframework.org/schema/util  
  11.     http://www.springframework.org/schema/util/spring-util-4.0.xsd  
  12.     http://www.springframework.org/schema/aop  
  13.     http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">  
  14.       
  15.     <!-- Student类的实例bean -->  
  16.     <bean id="student" class="com.wzj.entity.Student" p:name="张三"/>  
  17.     <!-- 切面的实例bean -->  
  18.     <bean class="com.wzj.entity.AdviceUtil"></bean>  
  19.     <!-- 使用注解方式需增加AspectJ的支持 -->  
  20.     <aop:aspectj-autoproxy/>  
  21. </beans>  
4.main方法:
[java] view plain copy
  1. public static void main(String[] args) {  
  2.     //通过ApplicationContext接口的实现类实例化Spring上下文对象  
  3.     ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");  
  4.     //通过getBean()方法来获取到Student类的对象  
  5.     Student student=(Student)context.getBean("student");  
  6.     student.say();  
  7. }  

三、使用xml文件配置spring aop增强

1.前置增强:

(1)创建AdviceBefore类,实现MethodBefore接口及其before方法:
[java] view plain copy
  1. public class AdviceBefore implements MethodBeforeAdvice{  
  2.   
  3.     @Override  
  4.     public void before(Method arg0, Object[] arg1, Object arg2)  
  5.             throws Throwable {  
  6.         System.out.println("前置增强的一些处理");  
  7.     }  
  8.   
  9. }  

(2)配置xml文件:
需要导入aop的命名空间,配置(beans跟标签省略):
[html] view plain copy
  1. <!-- Student类的实例bean -->  
  2. <bean id="student" class="com.wzj.entity.Student" p:name="张三"/>  
  3. <!-- 前置增强类的实例 -->  
  4. <bean id="adviceBefore" class="com.wzj.advice.AdviceBefore"></bean>  
  5. <!-- 配置切面 -->  
  6. <aop:config>  
  7.     <!-- 配置切点 -->  
  8.     <aop:pointcut expression="execution(* com.wzj.entity.Student.say())" id="pt"/>  
  9.     <!--   
  10.         将切点和具体的前置增强类实例粘合  
  11.         1.advice-ref:增强类实例的引用  
  12.         2.pointcut-ref:切入点的引用  
  13.      -->  
  14.     <aop:advisor advice-ref="adviceBefore" pointcut-ref="pt"/>  
  15. </aop:config>  

2.后置增强:

(1)创建AdviceBehind类,实现AfterReturningAdvice接口中的afterReturning接口
(2)配置:

[html] view plain copy
  1. <!-- 后置增强类的实例 -->  
  2. <bean id="adviceBehind" class="com.wzj.advice.AdviceBehind"></bean>  
  3. <aop:config>标签中添加:  
  4. <aop:advisor advice-ref="adviceBehind" pointcut-ref="pt"/>  

3.异常抛出增强:

1)创建类,实现ThrowsAdvice接口,该接口中并没有提供方法,需要自己定义
        注意,方法的定义需要遵守下面的方法签名:

[java] view plain copy
  1. void afterThrowing([Method method,Object[] arguments,Object target,]Throwable ex)  
a.-->方法名必须是afterThrowing
b.-->前三个参数要么都有,要么都没有,不能单个出现
c.-->Throwable类型的表示异常的类型
(2)配置:
[html] view plain copy
  1. <!-- 异常类型的实例 -->  
  2. <bean id="adviceException" class="com.wzj.util.AdviceException"></bean>  
  3. <aop:config>标签中添加:  
  4. <aop:advisor advice-ref="adviceException" pointcut-ref="pt"/>  

4.环绕增强:

         实现MethodInterceptor接口中的invoke方法,参数类型为MethodInvocation,调用参数的proceed()方法即可调用目标方法,其他配置上同。

四、基于schema配置定义切面

我们还可以在spring配置文件中通过aop命名空间将一个普通的JavaBean中的方法 声明为增强类型
1.创建一个普通的类,如:AdviceUtil

[java] view plain copy
  1. package com.wzj.entity.schema;  
  2.   
  3. import org.aspectj.lang.JoinPoint;  
  4. import org.aspectj.lang.ProceedingJoinPoint;  
  5.   
  6. public class AdviceUtil {  
  7.       
  8.     public void before(){  
  9.         System.out.println("----->前置增强操作");  
  10.     }  
  11.     public void behind(JoinPoint jp,String returnValue){  
  12.         System.out.println("----->后置增强操作");  
  13.     }  
  14.       
  15.     public void catchException(ArithmeticException ex){  
  16.         System.out.println("捕获到异常:"+ex.getMessage());  
  17.     }  
  18.       
  19.     public void around(ProceedingJoinPoint pj) throws Throwable{  
  20.         System.out.println("前面的处理...");  
  21.         pj.proceed();//调用目标方法  
  22.         System.out.println("后面的处理...");  
  23.     }  
  24.       
  25.     public void after(){  
  26.         System.out.println("----->最终增强操作");  
  27.     }  
  28.       
  29. }  
2.配置xml文件:
[html] view plain copy
  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:p="http://www.springframework.org/schema/p"  
  5.     xmlns:util="http://www.springframework.org/schema/util"   
  6.     xmlns:aop="http://www.springframework.org/schema/aop"  
  7.     xsi:schemaLocation="  
  8.     http://www.springframework.org/schema/beans  
  9.     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
  10.     http://www.springframework.org/schema/util  
  11.     http://www.springframework.org/schema/util/spring-util-4.0.xsd  
  12.     http://www.springframework.org/schema/aop  
  13.     http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">  
  14.     <!-- Student类的实例bean -->  
  15.     <bean id="student" class="com.wzj.entity.Student" p:name="张三"/>  
  16.     <!-- 含有增强方法的Bean -->  
  17.     <bean id="advice" class="com.wzj.schema.AdviceUtil"></bean>  
  18.     <!-- 配置切面 -->  
  19.     <aop:config>  
  20.         <!-- 定义切入点 -->  
  21.         <aop:pointcut expression="execution(* com.wzj.entity.Student.say())" id="pt"/>  
  22.         <!-- 引用包含增强方法的Bean -->  
  23.         <aop:aspect ref="advice">  
  24.             <!-- 将其中的before方法定义为增强方法,并引用切点,下同 -->  
  25.             <aop:before method="before" pointcut-ref="pt"/>  
  26.             <!-- 如果有需要,可以使用returning属性向增强方法中注入返回值 -->  
  27.             <aop:after-returning method="behind" pointcut="pt" returning="returnValue"/>  
  28.             <aop:after-throwing method="catchException" pointcut-ref="pt"/>  
  29.             <aop:around method="around" pointcut-ref="pt"/>  
  30.             <aop:after method="after" pointcut-ref="pt"/>  
  31.         </aop:aspect>  
  32.     </aop:config>  
  33. </beans>  

五、配置建议:

        通过接口实现增强处理是较低版本的spring aop的做法,如果在一个使用较低版本的spring aop的项目上进行升级,可以考虑使用<aop:advisor>复用已经存在的增强类; 如果项目采用JDK5.0以上版本,可以使用注解的形式,减少配置工作量; 如果不想使用注解或者项目的JDK版本较低,可以使用aop:aspect配合javabean 的方式。
原创粉丝点击