Spring中AOP实现

Easter79
• 阅读 788

1.什么是SpringAOP

什么是aop:Aspect Oriented Programming的缩写,面向切面编程,通过预编译和动态代理实现程序功能的
统一维护的一种技术
主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等

2.SpringAOP框架的用途

提供了声明的企业服务,特别是EJB的替代服务的声明
允许用户控制自己的方面,以完成OOP和AOP的互补使用

OOP:模拟真实的世界,一切皆是对象

3.AOP的实现方式

下边这两种Spring都是支持的

3.1预编译
-AspectJ  完整的面向切面编程解决方案--》spring不是完整的解决方案,不过spring提供比较好的实现方式,当然spring是同时也是支持这种方式的,这也是一种常用的方式

3.2运行期间动态代理(JDK动态代理,CGLib动态代理)
-SpringAop,JbossAop

Spring的AOP使用纯java实现,无需特殊的编译过程,不需要控制类的加载器层次,目前只支持方法的执行的连接点(通知Spring Bean某个方法执行)
不是为了提供完整AOP实现;而是侧重于一种AOP于IOC容器之间的整合,SpringAOP不会AspectJ(完整的AOP解决方案)竞争

Spring没有使用AspectJ的时候,也可以通过如下方式实现AOP

Spring AOP 默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者集合)都可以被代理
Spring AOP 中也可以使用CGLIB代理(如果一个业务对象没有实现一个接口)
有接口的:使用JDK的动态里
无接口的:使用CGLIB代理

4.SpringAOP中的一些概念

在实际使用SpringAOP之前,了解他的概念是必不可少的一个过程,

SpringAOP主要围绕以下概念展开:

切面(Aspect)一个关注点的模块化,这个关注点可能会横切多个对象
连接点(Joinpoint)程序执行过程中某个特定的连接点
通知(Advice) 在切面的某个特的连接点上执行的动作
切入点(Pointcut)匹配连接的断言,在Aop中通知和一个切入点表达式关联
引入(Intruduction) 在不修改类代码的前提下,为类添加新的方法和属性
目标对象(Target Object) 被一个或者多个切面所通知的对象
Aop代理(AOP Proxy) AOP框架创建的对象,用来实现切面契约(aspect contract)(包括方法执行等)
织入(Weaving)把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,氛围:编译时织入,类加载时织入,执行时织入

通知类型Advice:

前置通知(before advice) 在某个连接点(jion point)之前执行的通知,但不能阻止连接点前的执行(除非抛出一个异常)
返回后通知(after returning advice)在某个连接点(jion point)正常执行完后执行通知
抛出异常通知(after throwing advice) 在方法异常退出时执行的通知
后通知(after(finally) advice)在方法抛出异常退出时候的执行通知(不管正常返回还是异常退出)
环绕通知(around advice) 包围一个连接点(jion point)的通知

切入点Pointcut:SpringAOP占时仅仅支持方法的连接点

例如定义切入点表达式 execution(* com.sample.service.impl..*.*(..))
execution()是最常用的切点函数,其语法如下所示:
 整个表达式可以分为五个部分:
 1、execution(): 表达式主体。
 2、第一个*号:表示返回类型,*号表示所有的类型。
 3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
 4、第二个*号:表示类名,*号表示所有的类。
 5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
 
execution(public * *(..)) 切入点为执行所有的public方式时
execution(* set*(..)) 切入点执行所有的set开始的方法时
execution(* com.xyz.service.Account.*(..)) 切入点执行Account类的所有方法时
execution(* com.xyz.service.*.*(..))切入点执行com.xyz.service包下的所有方法时
execution(* com.xyz.service..*.*(..)) 切入点执行com.xyz.service包以及其子包的所有的方法时

上边这种方式aspectj和springaop通用的,其他方式可以自己查找资料

推荐:

http://blog.csdn.net/abcd898989/article/details/50809321

http://blog.csdn.net/peng658890/article/details/7223046

5.SpringAOP实现

上边的一些概念,看过后可能还是不懂,可以自行查阅资料,下边我也会说明

我们在Java项目开发中,AOP常见的配置有如下3种:

a.基于SpringAOP容器的实现,使用AspectJ(Spring封装了AspectJ的使用),这是一种比较常用的方式,可以很容易看清代码结构

b.运行期间的动态代理实现,这是一种比较老的实现方式,比较繁琐

c.注解实现,这种方式开发起来简单,但是不利于后期维护,比如说很难找出你所有使用了SpringAOP注解的地方

这里我主要介绍第一种,和第二种的使用

5.1SpringAOP中的容器实现

5.1.1 前置通知 before

某个需要切入的方法之前执行切面中的方法

Spring所有的切面通知都必须放在一个aop:config内(可以配置多个aop:config元素),每一个aop:config可以包含pointcut,adviso
和aspect元素(注意这些元素的出现是由顺序的)

配置文件: spring-aop-schema-advice.xml

声明了切面类,当切入点中匹配到了类名包含BIZ字符串的类时,选取面类中的一个方法,在选取某种Adivice通知,切入该方法的执行过程

其中:aop:aspect 中id的值任意(表意性强)  ref的值为切面类的id值

aop:pointcut 中expression写切入点表达式,说明那个方法可能需要做切入点

aop:before 中method的值为切面中的方法名说明切入那个方法,pointcut-ref的值为切入点的id值

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"

  2.  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  3.  xmlns:aop="http://www.springframework.org/schema/aop"

  4.  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

  5.  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"\>

  6.     

  7.     <bean id="myAspect" class="com.xxx.spring.aop.bean.annotation.aop.MyAspect"></bean>

  8.     

  9.     <bean id="aspectBiz" class="com.xxx.spring.aop.bean.annotation.aop.AspectBiz"></bean>

  10.     

  11.     aop:config\

  12.     

  13.         <aop:aspect id="aspectTest" ref="myAspect">

  14.         

  15.             <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>

  16.             

  17.             <aop:before method="before" pointcut-ref="myPointcut"/>

  18.         </aop:aspect>

  19.     </aop:config>

  20. </beans>

切面类:

切面类中的某个方法,一般是用于切入业务类中的某个方法在某种状态时切入

[java] view plain copy

  1. /*

  2. * 声明一个切面类

  3. * */

  4. public class MyAspect {

  5.     public void before(){

  6.         System.out.println("aspect before");

  7.     }  

  8. }

  

业务类:

[java] view plain copy

  1. //业务类

  2. public class AspectBiz {

  3.     public void biz(){

  4.         System.out.println("Aspect biz");

  5.     }  

  6. }

  

测试:

[java] view plain copy

  1. @Test
  2. public void aspectBefore(){
  3.     ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/xxx/spring/chap4/spring-aop-schema-advice.xml");
  4.     AspectBiz aspectBiz = (AspectBiz) ac.getBean("aspectBiz");
  5.     aspectBiz.biz();  
  6. }

  

结果:

[plain] view plain copy

  1. aspect before  
  2. Aspect biz

  

通过结果可以看出,前置通知在业务类AspectBiz的方法biz执行之前执行了before方法

5.1.2后置通知after-returning

某个需要切入的方法执行完成之后执行切面中指定的方法

配置文件: spring-aop-schema-advice.xml

[html] view plain copy

  1. aop:config\
  2.     
  3.         <aop:aspect id="aspectTest" ref="myAspect">
  4.         
  5.             <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
  6.             
  7.             <aop:before method="before" pointcut-ref="myPointcut"/>
  8.             
  9.             <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
  10.         </aop:aspect>
  11. </aop:config>

切面类:

添加afterReturning方法

[java] view plain copy

  1. /*

  2. * 声明一个切面类

  3. * */

  4. public class MyAspect {

  5.     public void before(){

  6.         System.out.println("aspect before");

  7.     }  

  8.     public void afterReturning(){

  9.         System.out.println("aspect afterReturning");

  10.     }  

  11. }

  

结果:

[plain] view plain copy

  1. aspect before  
  2. Aspect biz  
  3. aspect afterReturning

  

结果可以看出,后置通知会在业务方法的执行之后

5.1.3异常通知after-throwing

在方法抛出异常后的通知

配置文件: spring-aop-schema-advice.xml

[html] view plain copy

  1. aop:config\
  2.     <aop:aspect id="aspectTest" ref="myAspect">
  3.     
  4.         <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
  5.         
  6.         <aop:before method="before" pointcut-ref="myPointcut"/>
  7.         
  8.         <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
  9.         
  10.         <aop:after-throwing method="afteThrowing" pointcut-ref="myPointcut"/>
  11.     </aop:aspect>
  12. </aop:config>

切面类:

[java] view plain copy

  1. /*

  2. * 声明一个切面类

  3. * */

  4. public class MyAspect {

  5.     public void before(){

  6.         System.out.println("aspect before");

  7.     }  

  8.     public void afterReturning(){

  9.         System.out.println("aspect afterReturning");

  10.     }  

  11.     public void afteThrowing(){

  12.         System.out.println("aspect afteThrowing");

  13.     }  

  14. }

  

业务类:

修改为如下:

[java] view plain copy

  1. public void biz(){
  2.     System.out.println("Aspect biz");
  3.     throw new RuntimeException(); //出现异常的时候afteThrowing才会执行
  4. }

  

测试和结果:

[plain] view plain copy

  1. aspect before  
  2. Aspect biz  
  3. aspect afteThrowing   
  4. 上边的结果由于抛出异常后,不会正常的返回所有没有aspect afterReturning输出

  

5.1.4最终通知after(finally) advice

不管方法是否会抛出异常都会通知,就像try...catch..finallly

配置文件: spring-aop-schema-advice.xml

[html] view plain copy

  1. aop:config\
  2.     <aop:aspect id="aspectTest" ref="myAspect">
  3.     
  4.         <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
  5.         
  6.         <aop:before method="before" pointcut-ref="myPointcut"/>
  7.         
  8.         <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
  9.         
  10.         <aop:after-throwing method="afteThrowing" pointcut-ref="myPointcut"/>
  11.         
  12.         <aop:after method="after" pointcut-ref="myPointcut"/>
  13.     </aop:aspect>
  14. </aop:config>

切面类:

[java] view plain copy

  1. /*

  2. * 声明一个切面类

  3. * */

  4. public class MyAspect {

  5.     public void before(){

  6.         System.out.println("aspect before");

  7.     }  

  8.     public void afterReturning(){

  9.         System.out.println("aspect afterReturning");

  10.     }  

  11.     public void afteThrowing(){

  12.         System.out.println("aspect afteThrowing");

  13.     }  

  14.     public void after(){

  15.         System.out.println("aspect after(finally)");

  16.     }  

  17. }

  

业务类:

[html] view plain copy

  1. //业务类  

  2. public class AspectBiz {  

  3.     public void biz(){  

  4.         System.out.println("Aspect biz");  

  5.         throw new RuntimeException();  

  6.     }  

  7. }

  

测试结果:

[plain] view plain copy

  1. aspect before  
  2. Aspect biz  
  3. aspect afteThrowing  
  4. aspect after(finally)

  

从结果中可以看出,抛出异常后,切面类中的after方法还是可以执行

[java] view plain copy

  1. 如果业务类如下:没有异常,  

  2. public class AspectBiz {

  3.     public void biz(){

  4.         System.out.println("Aspect biz");

  5.     }  

  6. }

  

结果:

[plain] view plain copy

  1. aspect before  
  2. Aspect biz  
  3. aspect after(finally)

  

由以上结果可以看到,不管怎么样after方法还是会执行,很像try...catch..finally

5.1.5环绕通知around

在方法的执行前后执行通知

配置文件: spring-aop-schema-advice.xml

[html] view plain copy

  1. aop:config\
  2.     <aop:aspect id="aspectTest" ref="myAspect">
  3.     
  4.         <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
  5.         
  6.         <aop:before method="before" pointcut-ref="myPointcut"/>
  7.         
  8.         <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
  9.         
  10.         <aop:after-throwing method="afteThrowing" pointcut-ref="myPointcut"/>
  11.         
  12.         <aop:after method="after" pointcut-ref="myPointcut"/>
  13.         
  14.         <aop:around method="around" pointcut-ref="myPointcut"/>
  15.     </aop:aspect>
  16. </aop:config>

切面类:

可以看出,环绕方法的第一参数必须四ProceedingJoinPoint类型

[java] view plain copy

  1. /*

  2. * 声明一个切面类

  3. * */

  4. public class MyAspect {

  5.     public void before(){

  6.         System.out.println("aspect before");

  7.     }  

  8.     public void afterReturning(){

  9.         System.out.println("aspect afterReturning");

  10.     }  

  11.     public void afteThrowing(){

  12.         System.out.println("aspect afteThrowing");

  13.     }  

  14.     public void after(){

  15.         System.out.println("aspect after(finally)");

  16.     }  

  17.     public void around(ProceedingJoinPoint joinPoint){

  18.         Object object = null;

  19.         try{

  20.             System.out.println("aspect around 1"); //方法执行前

  21.             object = joinPoint.proceed();  //代表业务方法的执行

  22.             System.out.println("aspect around 1"); //方法执行后

  23.         }catch(Throwable e){

  24.             e.printStackTrace();  

  25.         }  

  26.     }  

  27. }

  

测试结果:

[plain] view plain copy

  1. aspect around 1  
  2. Aspect biz  
  3. aspect around 1

  

从结果可以看出,joinPoint.proceed其实就是执行业务方法,我们可以在其之前做和之后做一些操作

5.1.6通知中的参数传递

这里我们列举环绕通知中的参数参数传递,其他通知也是同样的方式。

配置文件: spring-aop-schema-advice.xml

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"

  2.  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  3.  xmlns:aop="http://www.springframework.org/schema/aop"

  4.  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

  5.  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"\>

  6.     

  7.     <bean id="myAspect" class="com.xxx.spring.aop.bean.annotation.aop.MyAspect"></bean>

  8.     

  9.     <bean id="aspectBiz" class="com.xxx.spring.aop.bean.annotation.aop.AspectBiz"></bean>

  10.     

  11.     aop:config\

  12.     

  13.         <aop:aspect id="aspectTest" ref="myAspect">

  14.         

  15.             <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>

  16.             

  17.             <aop:before method="before" pointcut-ref="myPointcut"/>

  18.             

  19.             <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>

  20.             

  21.             <aop:after-throwing method="afteThrowing" pointcut-ref="myPointcut"/>

  22.             

  23.             <aop:after method="after" pointcut-ref="myPointcut"/>

  24.             

  25.             

  26.             

  27.             <aop:around method="aroundInit" pointcut="execution(* com.xxx.spring.aop.bean.annotation.aop.AspectBiz.init(String,int))

  28.             and args(bizName,times)"/>

  29.         </aop:aspect>

  30.     </aop:config>

  31. </beans>

其中,通知中也可以单独指定的pointcut,切入点中,我们单独指定了init方法,且指定了方法的参数类型,和方法参数的名字,其中方法参数名字,可以更实际的方法参数名字不同,比如说init(String biz,int t) 都是可以匹配到的,不过为了维护方便,最好还是都一样。

切面类:

[java] view plain copy

  1. /*

  2. * 声明一个切面类

  3. * */

  4. public class MyAspect {

  5.     public void before(){

  6.         System.out.println("aspect before");

  7.     }  

  8.     public void afterReturning(){

  9.         System.out.println("aspect afterReturning");

  10.     }  

  11.     public void afteThrowing(){

  12.         System.out.println("aspect afteThrowing");

  13.     }  

  14.     public void after(){

  15.         System.out.println("aspect after(finally)");

  16.     }  

  17.     public void around(ProceedingJoinPoint joinPoint){

  18.         Object object = null;

  19.         try{

  20.             System.out.println("aspect around 1"); //方法执行前

  21.             object = joinPoint.proceed();  //代表业务方法的执行

  22.             System.out.println("aspect around 2"); //方法执行后

  23.         }catch(Throwable e){

  24.             e.printStackTrace();  

  25.         }  

  26.     }  

  27.     //AOP中参数的传递

  28.     public void aroundInit(ProceedingJoinPoint joinPoint,String bizName,int times){

  29.         System.out.println(bizName+"--"+times);

  30.         Object object = null;

  31.         try{

  32.             System.out.println("aspect around 1"); //方法执行前

  33.             object = joinPoint.proceed();  //代表业务方法的执行

  34.             System.out.println("aspect around 1"); //方法执行后

  35.         }catch(Throwable e){

  36.             e.printStackTrace();  

  37.         }  

  38.     }  

  39. }

  

业务类:

[java] view plain copy

  1. public class AspectBiz {

  2.     public void biz(){

  3.         System.out.println("Aspect biz");

  4.     }  

  5.     public void init(String bizName,int times){

  6.         System.out.println("aspectBiz init:"+bizName+"  "+times);

  7.     }  

  8. }

  

测试:

[java] view plain copy

  1. @Test
  2. public void aspectAround(){
  3.     ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/xxx/spring/chap4/spring-aop-schema-advice.xml");
  4.     AspectBiz aspectBiz = (AspectBiz) ac.getBean("aspectBiz");
  5.     aspectBiz.init("init", 3);
  6. }

  

测试结果:

[plain] view plain copy

  1. aspect before  
  2. init--3  
  3. aspect around 1  
  4. aspectBiz init:init  3  
  5. aspect around 1  
  6. aspect after(finally)  
  7. aspect afterReturning  
  8. AfterClass 标注的方法 会最后执行

  

可以看到,参数比顺利的传送过去

6.Advisor

Advisor就像一个小的自包含,只有一个advice切面通过一个bean标识,并且必须实现一个advice接口,同时advisor也可以很好的利用aspectJ的切入点表达式Spring通过配置文件中 aop:advisor元素支持advisor实际使用中,大多数情况下它会和transactional advice配合使用,用于事务控制

配置文件使用案例:

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"

  2.     xmlns:context="http://www.springframework.org/schema/context"

  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"

  4.     xmlns:aop="http://www.springframework.org/schema/aop"

  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans

  6.      http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  

  7.  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"\>

  8.     <bean name="sessionFactory"

  9.         class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  10.         <property name="configLocation">

  11.             

  12.             <value>classpath:hibernate.cfg.xml</value>

  13.         </property>

  14.     </bean>

  15.     

  16.     <bean id="txManager"

  17.         class="org.springframework.orm.hibernate3.HibernateTransactionManager">

  18.         <property name="sessionFactory">

  19.             

  20.         </property>

  21.     </bean>

  22.     <!-- advisor配置 配置事务处理的Bean,定义切面(advice)   

  23.         由于getxxx,queryxxx,findxxx不涉及事务,可以设置read-only为true  

  24.     -->

  25.     <tx:advice id="txAdvice" transaction-manager="txManager">

  26.         tx:attributes\

  27.             <tx:method name="get*" read-only="true" />

  28.             <tx:method name="query*" read-only="true" />

  29.             <tx:method name="find*" read-only="true" />

  30.             <tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception" />

  31.             <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" />

  32.             <tx:method name="del*" propagation="REQUIRED" rollback-for="java.lang.Exception" />

  33.             <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception" />

  34.             <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />

  35.         </tx:attributes>

  36.     </tx:advice>

  37.     

  38.     aop:config\

  39.     

  40.         <aop:pointcut expression="excution(* com.xxx.spring.aop.bean.annotation.service.impl..(..))" id="serviceMethod" />

  41.         <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"  />

  42.     </aop:config>

  43. </beans>

关于spring的事务控制将会在另外一篇文章中介绍。

7.SpringAOP代理实现

这种实现方式,主要使用动态代理技术。 有接口的:使用JDK的动态里,无接口的:使用CGLIB代理

这种方式不是很常用

7.1.1ProxyFactoryBean

1.使用SpringAOP代理最关键的是使用org.springframework.aop.framework.ProxyFactoryBean,可以完全控制切入点和通知advice以及他们的顺序

2.使用ProxyFacotryBean或者其他的IOC相关类来创建AOP代理的最重要的好处就是通知切入点可以由IOC来管理

3.被代理的类没有实现任何接口,使用CGLIB代理,否者使用JDK代理

4.通过设置ProxyTargetClass为true可以强制使用CGLIB,(无论是否实现接口)

5.如果目标类实现了一个或者多个接口,那么创建代理的类型将依赖于ProxyFactoryBean的配置

6.如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或者多个全限定接口名,基于JDK的代理被创建

7.如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现类一个或多个接口,那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理

7.1.2使用代理实现环绕通知around

配置文件:

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans
  4. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"\>
  5.     
  6.     <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
  7.     
  8.     <bean name="advice" class="com.xxx.spring.aop.bean.AdviceTest"></bean>
  9.     
  10.     <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  11.         <!--proxyInterfaces 如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现类一个或多个接口,  
  12.         那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理 -->
  13.         <property name="proxyInterfaces" value="com.xxx.spring.aop.bean.UserDao"></property>
  14.         
  15.         <property name="target" ref="target"></property>
  16.         
  17.         <property name="interceptorNames">
  18.         <list>
  19.             <value>advice</value>
  20.         </list>
  21.     </property>
  22.     </bean>
  23. </beans>

注意:

上边的可以不用写,Spring的ProxyFactoryBean将会自动检测到

如果被代理的对象target实现了多个接口可以按照如下配置:

[html] view plain copy

  1. <property name="interfaces">
  2.         <array>
  3.             <value>com.xxx.spring.aop.bean.UserDao</value>
  4.         </array>
  5. </property>

value中可以写多个接口

同样:通知interceptorNames如果只用一个通知可以写成
    如果有多个通知可以写成:

[html] view plain copy

  1. <property name="interceptorNames">
  2.         <list>
  3.             <value>advice</value>
  4.         </list>
  5.     </property>

但是 通知是不可以省略的,同时推荐结合的写法

上边的委托类即使普通的实现类:即ProxyFactoryBean被代理的对象

上边的通知配置

ProxyFactoryBean的配置

需要配置被代理的目标对象,通知,目标类实现的接口(可以省略,被ProxyFactoryBean自动识别 )

切面通知实现类

实现MethodInterceptor就可以实现环绕通知,我们可以在方法的目标对象类的方法执行前后加入处理逻辑

[java] view plain copy

  1. import java.util.Date;

  2. import org.aopalliance.intercept.MethodInterceptor;

  3. import org.aopalliance.intercept.MethodInvocation;

  4. public class AdviceTest implements MethodInterceptor{

  5.     @Override

  6.     public Object invoke(MethodInvocation method) throws Throwable {

  7.         System.out.println("方法开始执行"+new Date());

  8.         method.proceed();  

  9.         System.out.println("方法执行完毕"+new Date());

  10.         return null;

  11.     }  

  12. }

  

目标类:

[java] view plain copy

  1. //委托类

  2. public class UserDaoImpl implements UserDao {

  3.     @Override

  4.     public void saveUser() {

  5.         System.out.println("保存用户");

  6.     }  

  7.     @Override

  8.     public void deleteUser() {

  9.         System.out.println("删除用户");

  10.     }  

  11.     @Override

  12.     public void updateUser() {

  13.         System.out.println("更新用户");

  14.     }  

  15. }

  

接口:

[java] view plain copy

  1. public interface UserDao {
  2.     public abstract void saveUser();
  3.     public abstract void deleteUser();
  4.     public abstract void updateUser();
  5. }

  

测试:

[java] view plain copy

  1. @Test
  2. public void advice(){
  3.     BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/advice.xml");
  4.     UserDao userDao = factory.getBean("proxy", UserDao.class);
  5.     userDao.saveUser();  
  6.     userDao.deleteUser();  
  7.     userDao.updateUser();  
  8. }

  

测试结果:

[plain] view plain copy

  1. 方法开始执行Sun Sep 11 21:02:12 CST 2016  
  2. 保存用户  
  3. 方法执行完毕Sun Sep 11 21:02:12 CST 2016  
  4. 方法开始执行Sun Sep 11 21:02:12 CST 2016  
  5. 删除用户  
  6. 方法执行完毕Sun Sep 11 21:02:12 CST 2016  
  7. 方法开始执行Sun Sep 11 21:02:12 CST 2016  
  8. 更新用户  
  9. 方法执行完毕Sun Sep 11 21:02:12 CST 2016

  

7.1.3使用动态代理实现前置通知before

配置文件:

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans
  4. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"\>
  5.     
  6.     <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
  7.     
  8.     <bean name="before" class="com.xxx.spring.aop.bean.BeforeTest"></bean>
  9.     
  10.     <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  11.         
  12.         <property name="target" ref="target"></property>
  13.         
  14.         <property name="interfaces">
  15.             <array>
  16.                 <value>com.xxx.spring.aop.bean.UserDao</value>
  17.             </array>
  18.         </property>
  19.         
  20.         <property name="interceptorNames">
  21.             <array>
  22.                 <value>before</value>
  23.             </array>
  24.         </property>
  25.     </bean>
  26. </beans>

切面前置通知实现类:

实现MethodBeforeAdvice

[java] view plain copy

  1. import java.lang.reflect.Method;

  2. import org.springframework.aop.MethodBeforeAdvice;

  3. public class BeforeTest implements MethodBeforeAdvice{

  4.     @Override

  5.     public void before(Method method, Object[] obj, Object object)

  6.             throws Throwable {

  7.         System.out.println("version 1.0 author tom "+method.getName()+" is execute");

  8.     }  

  9. }

  

测试:

[java] view plain copy

  1. @Test
  2. public void before(){
  3.     BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/before.xml");
  4.     UserDao userDao = factory.getBean("proxy", UserDao.class);
  5.     userDao.saveUser();  
  6.     userDao.deleteUser();  
  7.     userDao.updateUser();  
  8. }

  

结果:

[plain] view plain copy

  1. version 1.0 author tom saveUser is execute  
  2. 保存用户  
  3. version 1.0 author tom deleteUser is execute  
  4. 删除用户  
  5. version 1.0 author tom updateUser is execute  
  6. 更新用户

  

7.1.4后置通知afterReturning

配置文件:

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans
  4. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"\>
  5.     
  6.     <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
  7.     
  8.     <bean name="after" class="com.xxx.spring.aop.bean.AfterTest"></bean>
  9.     
  10.     <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  11.         
  12.         <property name="target" ref="target"></property>
  13.         
  14.         <property name="interfaces">
  15.             <array>
  16.                 <value>com.xxx.spring.aop.bean.UserDao</value>
  17.             </array>
  18.         </property>
  19.         
  20.         <property name="interceptorNames">
  21.             <array>
  22.                 <value>after</value>
  23.             </array>
  24.         </property>
  25.     </bean>
  26. </beans>

切面后置通知:

实现 AfterReturningAdivce接口

[java] view plain copy

  1. import java.lang.reflect.Method;

  2. import org.springframework.aop.AfterReturningAdvice;

  3. public class AfterTest  implements AfterReturningAdvice {

  4.     @Override

  5.     public void afterReturning(Object arg0, Method arg1, Object[] arg2,

  6.             Object arg3) throws Throwable {

  7.         System.out.println(arg1.getName()+" is over!");

  8.     }  

  9. }

  

测试:

[java] view plain copy

  1. @Test
  2. public void after(){
  3.     BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/after.xml");
  4.     UserDao userDao = factory.getBean("proxy", UserDao.class);
  5.     userDao.saveUser();  
  6.     userDao.deleteUser();  
  7.     userDao.updateUser();  
  8. }

  

测试结果:

[plain] view plain copy

  1. 保存用户  
  2. saveUser is over!  
  3. 删除用户  
  4. deleteUser is over!  
  5. 更新用户  
  6. updateUser is over!

  

7.1.5异常通知throw

配置文件:

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans
  4. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"\>
  5.     
  6.     <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
  7.     
  8.     <bean name="throws" class="com.xxx.spring.aop.bean.ThrowsAdiviceTest"></bean>
  9.     
  10.     <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  11.         
  12.         <property name="target" ref="target"></property>
  13.         
  14.         <property name="interfaces">
  15.             <array>
  16.                 <value>com.xxx.spring.aop.bean.UserDao</value>
  17.             </array>
  18.         </property>
  19.         
  20.         <property name="interceptorNames">
  21.             <list>
  22.                 <value>throws</value>
  23.             </list>
  24.         </property>
  25.     </bean>
  26. </beans>

异常通知切面:

参数中必须有Throwable的子类,前边的参数 afterThrowing([Method, args, target], subclassOfThrowable)[]号中的参数可选

[java] view plain copy

  1. package com.briup.spring.aop.bean;

  2. import java.lang.reflect.Method;

  3. import org.springframework.aop.ThrowsAdvice;

  4. public class ThrowsAdiviceTest implements ThrowsAdvice{

  5.     public void afterThrowing(Method method,Object[] args,Object target,Exception ex)throws Throwable{//Throwable subclass

  6.         System.out.println("afterThrowing 2 ...."+method.getName()+"   "+ target.getClass().getName());

  7.     }  

  8. }

  

测试:

[java] view plain copy

  1. @Test
  2. public void throwTest(){
  3.     BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/throw.xml");
  4.     UserDao userDao = factory.getBean("proxy", UserDao.class);
  5.     userDao.saveUser();  
  6.     userDao.deleteUser();  
  7.     userDao.updateUser();  
  8. }

  

结果:

[plain] view plain copy

  1. 保存用户  
  2. afterThrowing 2 ....saveUser   com.xxx.spring.aop.bean.UserDaoImpl

  

7.1.6切入点配置pointcut

使用代理方式也可以配置切入点

NameMatchMethodPointcut,根据方法的名字进行匹配

mappedNames匹配的方法名集合

配置文件:

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans
  4. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"\>
  5.     
  6.     <bean name="target" class="com.briup.spring.aop.bean.UserDaoImpl"></bean>
  7.     
  8.     <bean name="advice" class="com.xxx.spring.aop.bean.AdviceTest"></bean>
  9.     <bean name="throws" class="com.xxx.spring.aop.bean.ThrowsAdiviceTest"></bean>
  10.     
  11.     <bean name="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
  12.         <property name="mappedNames">
  13.             <list>
  14.                 <value>sa*</value>
  15.             </list>
  16.         </property>
  17.     </bean>
  18.     
  19.     <bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
  20.         <property name="advice" ref="throws"></property>
  21.         <property name="pointcut" ref="pointcutBean"></property>
  22.     </bean>
  23.     
  24.     <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  25.         
  26.         <property name="target" ref="target"></property>
  27.         
  28.         <property name="interfaces">
  29.             <array>
  30.                 <value>com.xxx.spring.aop.bean.UserDao</value>
  31.             </array>
  32.         </property>
  33.         
  34.         <property name="interceptorNames">
  35.             <list>
  36.                 <value>defaultAdvisor</value>
  37.                 <value>throws</value>
  38.             </list>
  39.         </property>
  40.     </bean>
  41. </beans>

如上边切入点配置:

[html] view plain copy

  1.   <bean name="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
  2.     <property name="mappedNames">
  3.         <list>
  4.             <value>sa*</value>
  5.         </list>
  6.     </property>
  7.   </bean>

切面:

[html] view plain copy

  1. <bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
  2.     <property name="advice" ref="advice"></property>
  3.     <property name="pointcut" ref="pointcutBean"></property>
  4. </bean>

7.1.7使用匿名的代理对象

使用匿名的代理对象可以将bean的配置到代理里边,这样就不用为target目标对象配置单独的对象,这样可以直接避免目标对象

配置文件:

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans
  4. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"\>
  5.     
  6.     <bean name="advice" class="com.xxx.spring.aop.bean.AdviceTest"></bean>
  7.     <bean name="throws" class="com.xxx.spring.aop.bean.ThrowsAdiviceTest"></bean>
  8.     
  9.     <bean name="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
  10.         <property name="mappedNames">
  11.             <list>
  12.                 <value>sa*</value>
  13.             </list>
  14.         </property>
  15.     </bean>
  16.     
  17.     <bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
  18.         <property name="advice" ref="advice"></property>
  19.         <property name="pointcut" ref="pointcutBean"></property>
  20.     </bean>
  21.     
  22.     <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  23.         
  24.         <property name="target">
  25.         <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
  26.         </property>
  27.         
  28.         <property name="interfaces">
  29.             <array>
  30.                 <value>com.xxx.spring.aop.bean.UserDao</value>
  31.             </array>
  32.         </property>
  33.         
  34.         <property name="interceptorNames">
  35.             <list>
  36.                 <value>defaultAdvisor</value>
  37.                 <value>throws</value>
  38.             </list>
  39.         </property>
  40.     </bean>
  41. </beans>

7.1.8IntroductionInterceptor

Introduction是个特别的Advice,可以在不修改代码的基础上添加一些方法,可以参见

http://www.iteedu.com/webtech/j2ee/springdiary/35.php

http://go12345.iteye.com/blog/352745

8.自动代理实现

8.1.BeanNameAutoProxyCreator

Spring允许使用自动代理的bean定义,他可以自动代理选定bean,这样我么就不用为代理对象声明接口,或者没有实现接口的时候,使用CGLIB代理

配置文件:

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans
  4. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"\>
  5.     
  6.     <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
  7.     
  8.     <bean name="before" class="com.xxx.spring.aop.bean.BeforeTest"></bean>
  9.     
  10.      <bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  11.         
  12.         <property name="beanNames">
  13.             <list>
  14.                 <value>targ*</value>
  15.             </list>
  16.         </property>
  17.         
  18.         <property name="interceptorNames">
  19.             <list>
  20.                 <value>before</value>
  21.             </list>
  22.         </property>
  23.     </bean>
  24. </beans>

通过,自动代理,proxy会自动帮我们代理所有以targ开头的目标委托类

实现类:

[java] view plain copy

  1. //委托类

  2. public class UserDaoImpl implements UserDao {

  3.     @Override

  4.     public void saveUser(){

  5.         System.out.println("保存用户");

  6.     }  

  7.     @Override

  8.     public void deleteUser() {

  9.         System.out.println("删除用户");

  10.     }  

  11.     @Override

  12.     public void updateUser() {

  13.         System.out.println("更新用户");

  14.     }  

  15. }

  

测试:

[java] view plain copy

  1. @Test
  2. public void autoAdvisor(){
  3.     BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/autoAdvisor.xml");
  4.     UserDao userDao = factory.getBean("target", UserDao.class);//autoAdvisor只能通过委托类的名字来拿
  5.     userDao.saveUser();  
  6.     userDao.deleteUser();  
  7.     userDao.updateUser();  
  8. }

  

结果:

[plain] view plain copy

  1. version 1.0 author tom saveUser is execute  
  2. 保存用户  
  3. version 1.0 author tom deleteUser is execute  
  4. 删除用户  
  5. version 1.0 author tom updateUser is execute  
  6. 更新用户

  

8.2DefaultAdvisorAutoProxyCreator

使用DefaultAdvisorAutoProxyCreator我们可以不用显示的指定advisor的bean定义 

[html] view plain copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"

  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  3. xsi:schemaLocation="http://www.springframework.org/schema/beans

  4. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"\>

  5.     

  6.     <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>

  7.     

  8.     <bean name="before" class="com.xxx.spring.aop.bean.BeforeTest"></bean>

  9.     

  10.     

  11.     <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

  12.         

  13.         <property name="advice" ref="before"></property>

  14.         

  15.         <property name="patterns">

  16.             <list>

  17.                 <value>.*deleteUser</value>

  18.             </list>

  19.         </property>

  20.     </bean>

  21.     

  22.     <bean name="proxy"  class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>

  23. </beans>

测试:

[java] view plain copy

  1. @Test
  2. public void autoProxyByName(){
  3.     BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/autoProxyByName.xml");
  4.     UserDao userDao = factory.getBean("target", UserDao.class);
  5.     userDao.saveUser();  
  6.     userDao.deleteUser();  
  7.     userDao.updateUser();  
  8. }

  

结果:

[java] view plain copy

  1. 保存用户  
  2. version 1.0 author tom deleteUser is execute
  3. 删除用户  
  4. 更新用户

  

参考文章:

http://blog.csdn.net/abcd898989/article/details/50809321

http://blog.csdn.net/peng658890/article/details/7223046

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
带我的粉丝们一起揭秘spring aop底层原理及实现
实在是不知道写什么了,博主变low了呀。springaop使得我们的aop开发工作变得简单,这是众所周知的今天还是带我的粉丝们一起揭秘springaop底层原理及实现吧哈哈哈哈AOP面向切面编程:主要是通过切面类来提高代码的复用,降低业务代码的耦合性,从而提高开发效率。主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。AOP实现原理
Wesley13 Wesley13
3年前
AOP相关概念
1.AOP(面向切面编程)在软件业,AOP为AspectOrientedProgramming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,在软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型.利用AOP
Easter79 Easter79
3年前
Spring的AOP逐层深入——AOP的基本原理(六)
什么是AOP    AOP(AspectOrientedProgramming),意思是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP基于IoC基础,是对OOP(ObjectOrientedProgramming,面向对象)的延续。同时,AOP实际是GOF设计模式的延续,设计模式孜孜不倦
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k