Spring中AOP实现

本文内容纲要:

- 1.什么是SpringAOP

- 2.SpringAOP框架的用途

- 3.AOP的实现方式

- 4.SpringAOP中的一些概念

- 5.SpringAOP实现

- 5.1SpringAOP中的容器实现

- 5.1.1 前置通知 before

- 5.1.5环绕通知around

- 6.Advisor

- 7.SpringAOP代理实现

-

- 7.1.8IntroductionInterceptor

- 8.自动代理实现

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 plaincopy

  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. aop:config
  7. <aop:aspect id="aspectTest" ref="myAspect">
  8. <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
  9. <aop:before method="before" pointcut-ref="myPointcut"/>
  10. </aop:aspect>
  11. </aop:config>

切面类:

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

[java] view plaincopy

  1. /*
  2. * 声明一个切面类
  3. * */
  4. public class MyAspect {
  5. public void before(){
  6. System.out.println("aspect before");
  7. }
  8. }

业务类:

[java] view plaincopy

  1. //业务类
  2. public class AspectBiz {
  3. public void biz(){
  4. System.out.println("Aspect biz");
  5. }
  6. }

测试:

[java] view plaincopy

  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 plaincopy

  1. aspect before
  2. Aspect biz

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

5.1.2后置通知after-returning

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

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

[html] view plaincopy

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

切面类:

添加afterReturning方法

[java] view plaincopy

  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 plaincopy

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

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

5.1.3异常通知after-throwing

在方法抛出异常后的通知

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

[html] view plaincopy

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

切面类:

[java] view plaincopy

  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 plaincopy

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

测试和结果:

[plain] view plaincopy

  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 plaincopy

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

切面类:

[java] view plaincopy

  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 plaincopy

  1. //业务类
  2. public class AspectBiz {
  3. public void biz(){
  4. System.out.println("Aspect biz");
  5. throw new RuntimeException();
  6. }
  7. }

测试结果:

[plain] view plaincopy

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

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

[java] view plaincopy

  1. 如果业务类如下:没有异常,
  2. public class AspectBiz {
  3. public void biz(){
  4. System.out.println("Aspect biz");
  5. }
  6. }

结果:

[plain] view plaincopy

  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 plaincopy

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

切面类:

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

[java] view plaincopy

  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 plaincopy

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

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

5.1.6通知中的参数传递

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

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

[html] view plaincopy

  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. aop:config
  7. <aop:aspect id="aspectTest" ref="myAspect">
  8. <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
  9. <aop:before method="before" pointcut-ref="myPointcut"/>
  10. <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
  11. <aop:after-throwing method="afteThrowing" pointcut-ref="myPointcut"/>
  12. <aop:after method="after" pointcut-ref="myPointcut"/>
  13. <aop:around method="aroundInit" pointcut="execution(* com.xxx.spring.aop.bean.annotation.aop.AspectBiz.init(String,int))
  14. and args(bizName,times)"/>
  15. </aop:aspect>
  16. </aop:config>

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

切面类:

[java] view plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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. classpath:hibernate.cfg.xml
  11. <bean id="txManager"
  12. class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  13. aop:config
  14. <aop:pointcut expression="excution(* com.xxx.spring.aop.bean.annotation.service.impl..(..))" id="serviceMethod" />
  15. <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
  16. </aop:config>

关于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 plaincopy

  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. advice

注意:

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

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

[html] view plaincopy

  1. com.xxx.spring.aop.bean.UserDao

value中可以写多个接口

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

如果有多个通知可以写成:

[html] view plaincopy

  1. advice

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

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

上边的通知配置

ProxyFactoryBean的配置

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

切面通知实现类

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

[java] view plaincopy

  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 plaincopy

  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 plaincopy

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

测试:

[java] view plaincopy

  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 plaincopy

  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 plaincopy

  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. com.xxx.spring.aop.bean.UserDao
  6. before

切面前置通知实现类:

实现MethodBeforeAdvice

[java] view plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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. com.xxx.spring.aop.bean.UserDao
  6. after

切面后置通知:

实现 AfterReturningAdivce接口

[java] view plaincopy

  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 plaincopy

  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 plaincopy

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

7.1.5异常通知throw

配置文件:

[html] view plaincopy

  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. com.xxx.spring.aop.bean.UserDao
  6. throws

异常通知切面:

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

[java] view plaincopy

  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 plaincopy

  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 plaincopy

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

7.1.6切入点配置pointcut

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

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

mappedNames匹配的方法名集合

配置文件:

[html] view plaincopy

  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. sa*
  6. com.xxx.spring.aop.bean.UserDao
  7. defaultAdvisor
  8. throws

如上边切入点配置:

[html] view plaincopy

  1. sa*

切面:

[html] view plaincopy

7.1.7使用匿名的代理对象

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

配置文件:

[html] view plaincopy

  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. sa*
  6. com.xxx.spring.aop.bean.UserDao
  7. defaultAdvisor
  8. throws

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 plaincopy

  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. targ*
  6. before

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

实现类:

[java] view plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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. .*deleteUser

测试:

[java] view plaincopy

  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 plaincopy

  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

本文内容总结:1.什么是SpringAOP,2.SpringAOP框架的用途,3.AOP的实现方式,4.SpringAOP中的一些概念,5.SpringAOP实现,5.1SpringAOP中的容器实现,5.1.1 前置通知 before,5.1.5环绕通知around,6.Advisor,7.SpringAOP代理实现,,7.1.8IntroductionInterceptor,8.自动代理实现,

原文链接:https://www.cnblogs.com/xuyatao/p/8485851.html

以上是 Spring中AOP实现 的全部内容, 来源链接: utcz.com/z/362961.html

回到顶部