Spring AOP深度详解 综合

本文详细解析了 Spring AOP 的核心概念,包括静态 AOP 和动态 AOP 的区别,关键术语如 Jointpoint、Advice 和 Pointcut 的含义,以及如何在实际项目中运用这些概念来实现日志记录、异常监控等功能。

 

Spring AOP 的关键概念

  1. 静态AOP:通过修改应用程序实际的字节码来完成织入过程,从而根据需求修改和扩展代码,由于最终结果就是普通的Java字节码,因此性能较高。但是注意其缺点是,如果想作任何的修改,即使只是加入一个新的JointPoint,都必须重新编译整个程序。

  2. 动态AOP:织入过程是在运行时动态进行的。具体的实现有若干种,在Spring中是为所有目标对象创建代理(但不能为final类生成代理),以便Advice可以按需要被使用。虽然其性能比不上静态的AOP,但是对应用的修改不必重新编译主程序的代码。

  3. 建议首选Spring AOP,只在功能达不到需求的时候,或者在程序性能调优时发现性能较差时才改用AspectJ。在Spring AOP中,第一部份是AOP core,它提供了完全解耦的,纯编程式的AOP功能;第二部份则是为让我们能在程序中更简便地使用AOP而给出的一组框架服务。Spring AOP仅是所有AOP特性的一个子集,但涵盖了最常用的90%的功能。

  4. Jointpoint:一个程序执行过程中的特定点。可以是方法的调用,方法执行过程本身,类的初始化,对象的实例化等。

  5. Advice:在某一特定的jointpoint运行的代码。可以分为before advice, after advice等。

  6. Pointcut:用来定义某一个advice应该何时执行的一组jointpoint。比如一个典型的pointcut就是对某一个类的所有方法调用的集合,通常我们会组建复杂的pointcut来控制advice什么时候执行。

  7. Aspect: advice和pointcut的组合。这个组合定义了一段程序中应该包括的逻辑以及何时应该执行该逻辑。

  8. Weaving:将aspect真正加入程序代码的过程。这里可以分为编译时织入和运行时织入。

  9. Target: 如果一个对象的执行过程受到某个AOP操作的修改,那么它就是一个目标对象。

  10. Introduction:通过引入,我们可以在一个对象中加入新的方法或字段,以改变它的结构,你可以使用引入来让任何对象实现一个特定的接口,而不需要对这个对象的类型显式地实现这个接口。

  11. Spring内部有两种实现代理的方法:JDK动态代理和CGLIB代理,且CGLIB代理的性能较优。Spring AOP只支持方法级别的jointPoint。

  12. 接口Advisor代表一个Aspect,但这个接口不建议直接使用,而是使用其子接口,来创建各种不同的aspect。

  13. ProxyFactory的addAddvice方法,这个方法内部会将提供的advice包装在一个DefaultPointCutAdvisor对象里面,它也是PointCutAdvisor的标准实现,将其配置为包含所有默认方法调用的pointcut。如果想对创建的Advisor有更多的控制,或者要向代理中添加一个introduction,那么可以自行创建Advisor并调用addAdvisor方法。同时,ProxyFactory也提供了removeAdvice,removeAdvisor等方法来删除之前加入的advisor。

  14. 前置advice是最有用的advice之一,它可以修改传递给目标方法的参数,也可以抛出一个异常来阻止目标方法的执行。

  15. 后置advice是在方法调用完成后才执行的。我们没有办法改变已经执行的方法的参数,只能读取而不能修改目标方法的执行路径或阻止目标方法的执行,更重要的是也无法修改目标方法的返回值。不过,还是可以抛出一个异常让调用者看到的是异常而不是返回值。

  16. 包围通知在功能上综合了前置通知和后置通知,并且可以修改方法的返回值,或者阻止方法的执行。即将目标方法的实现完全更换成新的代码。Spring的很多功能,如远程代理支持和事务管理,都是由拦截器完成的。同时,拦截器也是剖析程序运行的好办法。

  17. 抛出通知和后置通知一样,是在jointPoint之后运行的,不过抛出通知只在方法抛出一个异常时才执行。此外,抛出通知对程序运行本身不能做任何改变。我们可以做的只是改变抛出异常的类型,比如需要使用一个API时,它的异常设计非常糟糕,那就可以抛出通知来通知该API中所有的类,这样就能重新设计异常继承的结构,从而使之更易管理并且具备自描述性。同时,也可以用抛出通知来提供一个集中的错误日志,以此减少散布在程序各处的记录错误日志代码的数量。

    要注意的是 ThrowsAdvice只是一个标志接口,里面的方法是通过反射来调用 的,其实现类必须以下面的形式来实现方法:

void afterThrowing([Method,args,target,] ThrowableSubclass);

其中前三个参数是可选的。特别地,对于抛出的每一个异常,Spring会选择签名与抛 出的异常最匹配的一个方法,如果抛出的的Advice定义了两个afterThrowing方法, 而且它们的异常类型一样,但分别有1个和4个参数,那Spring会运行含有4个参数 的afterThorwing方法。

注意,如果一个throws-advice方法本身抛出一个exception,它会覆盖原始的异常, 即改变抛出给用户的异常,改写后的一般是一个RuntimeException,因为这与任何方 法签名都是兼容的。然而,如果一个throws-advice方法抛出一个checked Exception, 那么它必须匹配目标方法抛出的异常,所以这在一定程度上与目标方法的签名是耦合 的。永远不要抛出一个非声明的与目标方法签名不兼容的checked exception。

 

 

 

 

 

 

 

需明确的几个概念:
                                                  
1、通知(Advice):用于告知系统将有哪些新的行为。
2、切入点(Pointcut):定义了通知应该在应用到那些连接点
3、目标对象(Target):被通知的对象。
4、 代理(Proxy):将通知应用到目标对象后创建的对象。

Spring有两种代理创建方式:

     1.如果目标对象实现了一个或多个接口暴露的方法,Spring将使用JDK的java.lang.reflect.Proxy创建代理。这个类让Spring动态产生一个新的类,它实现了所需的接口,织入了通知,并且代理目标的所有请求。(这篇主要介绍这个方式)

2.如果目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类。在创建这个子类的时候,Spring将通知织入,并且将对目标对象的调用委托给这个子类。

 下面以一个实例说明Spring AOP的基本开发方法:

一.创建通知

Spring连接点模型是建立在方法拦截上,这意味着你编写的Spring通知会在方法调用周围的各个地方织入系统中。

图1

    TestAopServiceAdvice实现了接口MethodBeforeAdvice(前置通知),并实现它的惟一的方法before,这个类就可以在调用目标对象前被调用。同样的还有AfterReturningAdvice(后置通知),MethodInterceptor(环绕通知),异常通知(ThrowsAdvice),引入通知等。

    在这个方法中我们输出了一个字符串TestAopServiceAdvice,用于验证这个方法是否在目标对象前调用了。

注意:我们无法改变before方法中的参数args和target中的值,args中存的是原来要传入目标对象的变量,target即指目标对象。

 二.配置Spring XML配置文件

要在Spring中实现AOP,一般情况下需要配置4个bean:

1.目标对象(target)

2.通知(advice)

3.切入点(pointcut)

4.代理(proxy)

图2

切入点又分为静态切入点和动态切入点
1、  静态切入点的意思是通知总是被执行,也是最常用的一种切入点。
2、   动态切入点根据运行时方法的参数值决定通知是否被执行。

在图2中,定义了使用了一个Spring提供的静态切入点

    NameMatchMethodPointAdvisor,它保证了当被调用的方法的名字与给出的映射名字相匹配的时候,这个切入点才匹配。

   Spring提供的另一个静态切入点为RegexpMethodPointcutAdvisor,让你可以利用正则表达式来定义切入点.

 使用ProxyBeanFactory可以创建一个被通知的类,即代理对象。它的最常用的个控制行为的属性是:
1、proxyInterfaces:代理应该实现的接口列表
2、interceptorNames:需要应用到目标对象上的通知Bean的名字。可以是拦截器、Advisor或其他通知类的名字。

注:在用容器的getBean方法时,应该是getBean(代理类的名字),而不是getBean(目标对象的名字),否则AOP无法工作。

的概念

简单地说,AOP就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理(Transactions),日志管理(logging, tracing, profiling and monitoring 记录跟踪 优化 校准),权限控制(Authentication),同步(Synchronization), 封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

实质上,AOP只是OOP的一种补充或某种改进,它转换了编程的范式和视角,关注了一直以来被OOP忽略或者说未能解决好的角落,使开发人员可以更好地将本不该彼此纠缠在一起的责任(如银行业务和事务处理)分离开来。通过面向切面的编程,可以将程序的责任分开,对象与切面互不干扰。面向切面的模块并非显式地为对象所调用,而是通过或注入或截取的方式,去获得被封装的对象内部方法间的消息,然后做出相应地处理。也许面向切面的模式破坏了对象的封装,却正其如此,方才能降低模块与模块之间的耦合度。同样地,通过对方面的封装,将这些通用的功能从不同的类中分离出来,使不同的模块都能共享同样的方面,这也极大地减少了重复代码。

Spring AOP核心技术还是动态代理,Spring的代理实现方式有两种,默认使用CGLIB,也可以选择使用JDK的代理,这一切对开发人员来说是透明的,可以不关心这些。

 

的基本术语

Spring为例:

1.面(Aspect): 一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。

2.连接点(Joinpoint): 在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。 在Spring AOP中,一个连接点 总是 代表一个方法的执行。 通过声明一个 org.aspectj.lang.JoinPoint 类型的参数可以使通知(Advice)的主体部分获得连接点信息。

3.知(Advice): 在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”“before”“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。

4.切入点(Pointcut 匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。

5.引入(Introduction (也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。 Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。 例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。

6.目标对象(Target Object 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。

7.AOP代理(AOP Proxy AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 注意:Spring 2.0最新引入的基于模式(schema-based)风格和@AspectJ注解风格的切面声明,对于使用这些风格的用户来说,代理的创建是透明的。

8.织入(Weaving 把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。

通知的类型:

1.前置通知(Before advice 在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

2.返回后通知(After returning advice 在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。

3.抛出异常后通知(After throwing advice 在方法抛出异常退出时执行的通知。

4.后通知(After (finally) advice 当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

5.环绕通知(Around Advice 包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

 

 

 



 

 

 

 

 


 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值