【spring】spring reference doc 4.3.1 研读<三> Spring aop
来源:互联网 发布:千里眼软件是什么 编辑:程序博客网 时间:2024/06/05 17:34
1. Spring 的 AOP
1.1 介绍
面向切面编程(Aspect Oriented Programming) ,是对面向对象编程(OOP)的补充。 OOP 是 面向 类 , AOP 是面向 切面。
AOP 在 Spring 中的作用 :
★ 提供 声明式的企业服务 ,尤其是 替代 EJB 的声明式服务 。最重要的比如 声明式事物
★ 用户可以实现自定义的 切面 ,使用 AOP 补充 OOP
1.1.1 AOP 概念
★ Aspect : 切面 ,横切多个类的一个模块化关注点 。 通常通过有规律的类或者类的注解 @Aspect 来实现 切面
★ Join Point : 程序执行过程中的一个连接点 ,例如方法的执行 , 异常的处理 。
★ Advice : 通知 。 切面在指定的一个连接点采取的动作。 通常包括 around(环绕),before(前置),after(后置)通知 ,作为一个拦截器提供通知 。
★ Pointcut : 切点 。 匹配 Join Point 的 断言 。通知 是和 切点表达式相关联的 并且在通过切点 匹配的任意连接点执行。
★ Introduction :
★ Target object :被一个或者多个切面通知的对象 。因为 Spring AOP 是通过使用运行时代理来实现的 ,所以这个对象永远是 一个被代理的对象。
★ AOP Proxy : AOP 代理 , 为了实现 切面规定而被 AOP 框架创建的对象 。 在 Spring 框架中, AOP 是一个 JDK 动态代理 或者 CGLIB 代理 ,可手动配置。
★ Weaing : 编织 ,将 切面和其他应用类型或者对象 连接起来创建 一个被通知的对象 。这个过程在编译时期(例如使用 AspectJ 编译器实现) ,加载时期或者运行时期可以完成。 Spring AOP 像其他纯 Java 的 AOP 框架 一样,在运行时期进行编织 。
通知类型 :
★ Before advice : 在 Join Point 之前执行的通知 ,但是没有能力阻止 Join Point 的执行(除非抛出异常)
★ After returning advice : 在 连接点正常完成之后执行的通知 :例如 ,如果方法没有抛出异常正常返回后执行该通知。
★ After throwing advice : 如果一个方法因为抛出异常 退出,执行 通知
★ After (finally) advice : 不管连接点 退出与否都会执行的通知 (正常或者异常返回)
★ Around advice : 环绕 一个方法调用的连接点的通知 。能够在方法调用之前和方法调用之后自定义行为 ,而且还负责是否继续执行 join point ,还是返回自定义的值或者是抛出异常 。都可以自定义。
1.1.2 Spring AOP 的 功能和目标
Spring AOP 是 用纯 Java 实现的,适合在 Servlet 容器 / Application server 使用 。
Spring AOP 的 目标不仅仅是 实现 AOP ,而是为 AOP 实现和 Spring Ioc 提供紧密集成 ,从而解决企业级应用中的一些常见问题。
Spring AOP 需要集成 AspectJ
1.1.3 AOP 代理
Spring AOP 默认使用 标准的 JDK 动态代理 作为 AOP 的代理 ,使 任意接口(或者一组接口)被代理 。
Spring AOP 同时也可以使用 CGLIB 代理 ,相对于 JDK 代理的接口,代理类更有必要 。当 业务对象 没有实现接口 时 会默认使用 CGLIB 代理 。因为编程到接口而不是类是一个很好的变成习惯,因此 类通常 会实现一个或者多个接口 。 当然也可以强制使用 CGLIB 代理 。
1.2 @AspectJ 支持
1.2.1 开启 @AspectJ 支持
需要 aspectj 和 aspectjweaver 两个 jar 包 ,开启可通过以下两种方式 开启
使用 java 配置开启 使用 @EnableAspectJAutoProxy:
@Configuration@EnableAspectJAutoProxypublic class AppConfig {}
使用 XML 配置开启:
<aop:aspectj-autoproxy/>
需要加入 schema
1.2.2 声明一个 切面
定义类并在类上加上注解@Aspect
package org.xyz;import org.aspectj.lang.annotation.Aspect;@Aspectpublic class NotVeryUsefulAspect {}
配置到 Spring 的配置文件(或者扫描相关包):
<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect"> <!-- configure properties of aspect here as normal --></bean>
1.2.3 声明 一个切点
@Pointcut("execution(* transfer(..))")// the pointcut expressionprivate void anyOldTransfer() {}// the pointcut signature
支持的 切入点指示符 (PCD)
★ execution : 匹配方法执行连接点
★ within :
@Pointcut("within(com.peptalk.controller.*)") public void controllerPoint(){ }
★ this:
★ target:
★ args:
★ @target:
★ @args:
★ @within:
★ @annotation:
@Pointcut("@annotation(com.vastio.aop.OperationLog)") // 匹配标注该注解的连接点 public void methodCachePointcut() { }
切点表达式的组合 (&& 、 || 、 !): (XML 中使用 and 、 or 、 not 组合 多个切点表达式)
@Pointcut("execution(public * *(..))")private void anyPublicOperation() {}@Pointcut("within(com.xyz.someapp.trading..*)")private void inTrading() {}@Pointcut("anyPublicOperation() && inTrading()")private void tradingOperation() {}
一些表达式的栗子 :
execution(public * *(..)) // 匹配任意 public 类型方法的执行execution(* set*(..)) // 匹配 任意以 set开头的方法的执行execution(* com.xyz.service.AccountService.*(..)) // 匹配 AccountService 类中的任意方法的执行execution(* com.xyz.service.*.*(..)) // 匹配 service 包中任意方法的执行execution(* com.xyz.service..*.*(..)) // 匹配 service 包及其子包中 的任意方法的执行within(com.xyz.service.*) // service包内执行的任意连接点(spring aop 中的方法的执行)within(com.xyz.service..*) // service及其子包中的连接点this(com.xyz.service.AccountService) //实现 AccountService 接口的 代理的任意连接点target(com.xyz.service.AccountService) //实现 AccountService接口的目标对象的任意连接点args(java.io.Serializable) //一个参数并且在运行传递的序列化的参数的任意连接点@target(org.springframework.transaction.annotation.Transactional) //目标对象有 @Transactional注解的任意连接点@within(org.springframework.transaction.annotation.Transactional)@annotation(org.springframework.transaction.annotation.Transactional)@args(com.xyz.security.Classified) 一个参数并且运行时传递的标有 @Classified的标注bean(tradeService)bean(*Service) spring bean 的名称 以 Service 结尾的任意连接点
1.2.4 声明式的通知
★ Before advce : @Before
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class BeforeExample { @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") //在连接点之前执行 public void doAccessCheck() { // ... }}
XML 对应
<aop:aspect id="beforeExample" ref="aBean"> <aop:before pointcut-ref="dataAccessOperation" method="doAccessCheck"/> ...</aop:aspect>
★ After returning advice : @AfterReturning 返回值可以作为参数
returning 属性的名称必须要和 通知方法里面的参数名称对应,才能传递
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterReturning;@Aspectpublic class AfterReturningExample { @AfterReturning( pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", returning="retVal") //在连接点之后执行,可以将返回值作为参数 public void doAccessCheck(Object retVal) { // ... }}XML 中对应
<aop:aspect id="afterReturningExample" ref="aBean"> <aop:after-returning pointcut-ref="dataAccessOperation" returning="retVal" method="doAccessCheck"/> ...</aop:aspect>
★ After throwing advice : @AfterThrowing 异常可以作为参数
throwing 属性的 名称 必须和通知方法里面的参数的 名称对应 ,才能传递
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterThrowing;@Aspectpublic class AfterThrowingExample { @AfterThrowing( pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", throwing="ex") // 抛出异常后执行 public void doRecoveryActions(DataAccessException ex) { // ... }}XML 中对应
<aop:aspect id="afterThrowingExample" ref="aBean"> <aop:after-throwing pointcut-ref="dataAccessOperation" throwing="dataAccessEx" method="doRecoveryActions"/> ...</aop:aspect>
★ After (finally) advice : @After
匹配的方法是否顺利执行 通知都会执行。 所以 该通知 必须 处理 正常和 异常 两种情况 ,通常用作资源的释放 。
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.After;@Aspectpublic class AfterFinallyExample { @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doReleaseLock() { // ... }}XML 中对应
<aop:aspect id="afterFinallyExample" ref="aBean"> <aop:after pointcut-ref="dataAccessOperation" method="doReleaseLock"/> ...</aop:aspect>
★ Around advice :@Around ProceedingJoinPoint 可以作为参数 ,JoinPoint 的 子类
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.ProceedingJoinPoint;@Aspectpublic class AroundExample { @Around("com.xyz.myapp.SystemArchitecture.businessService()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch 连接点之前执行的代码 Object retVal = pjp.proceed(); // stop stopwatch 连接点之后执行的代码 return retVal; }}
XML中对应
<aop:aspect id="aroundExample" ref="aBean"> <aop:around pointcut-ref="businessService" method="doBasicProfiling"/> ...</aop:aspect>
取得当前的连接点 JoinPoint
每个通知方法都 可以声明 JoinPoint 类型的参数作为 第一个参数 , 环绕 通知 声明的是 JoinPoint 的 子类 ProceedingJoinPoint
JoinPoint 提供的方法 :
Object [] getArgs(); //返回方法参数Object getThis(); // 返回代理对象Signature getSignature(); // 返回被通知的方法的描述Object getTarget(); // 返回目标对象
传递参数给 advice
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")public void validateAccount(Account account) { // ...}
或者
@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")private void accountDataAccessOperation(Account account) {}@Before("accountDataAccessOperation(account)")public void validateAccount(Account account) { // ...}
Advice parameters and generics
generic type
public interface Sample<T> { void sampleGenericMethod(T param); void sampleGenericCollectionMethod(Collection<T> param);}
可以这样使用
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")public void beforeSampleMethod(MyType param) { // Advice implementation}
不可以这样使用
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")public void beforeSampleMethod(Collection<MyType> param) { // Advice implementation}
确定参数的名称
参数名称通过 java 反射 不能获取,所以使用如下的策略 :通过可选的 argNames 属性可以指定参数名称和注解名称
如果第一个参数为 JoinPoint 可以不用加入
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)", argNames="bean,auditable")public void audit(Object bean, Auditable auditable) { AuditCode code = auditable.value(); // ... use code and bean}
1.3 基于 schema 的 aop 支持
1.3.1 定义切面
<aop:config> <aop:aspect id="myAspect" ref="aBean"> ... </aop:aspect></aop:config><bean id="aBean" class="..."> ...</bean>
1.3.2 声明切点
<aop:config> <aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..))"/></aop:config>
整体
<aop:config> <aop:aspect id="myAspect" ref="aBean"> <aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..)) && this(service)"/> <aop:before pointcut-ref="businessService" method="monitor"/> ... </aop:aspect></aop:config>
1.6 代理机制
JDK 动态代理 或者 CGLIB 代理 :如果目标对象实现了至少一个 接口 那么默认使用 JDK 的动态代理 ,被目标对象实现的所有接口都会被代理 ;如果目标对象没有实现任何接口 ,那么则会默认使用 CGLIB 代理 。
以下几个需要考虑的问题:
★ 被 final 修饰的方法 不能被通知 ,因为他们不能被重写 。
★ Spring 3.2 之后 , CGLIB jar 包已经整合到 spring 的 核心包中 。意味着 基于 CGLIB 代理 可以像 JDK 动态代理那样 一直存在 。
★ Spring 4.0 之后,被代理的对象的构造函数不会再被调用两次 。
强制使用 CGLIB 的 XML 配置 :
<aop:config proxy-target-class="true"> <!-- other beans defined here... --></aop:config>
或者
<aop:aspectj-autoproxy proxy-target-class="true"/>
基于注解:
@EnableAspectJAutoProxy(proxyTargetClass = true)public class AppConfig {}
<一下内容暂略------->
<补充 : Spring AOP 拦截 controller
正常我们使用 Spring AOP 横切 service 层很容易实现 ,而在横切 controller 层时却不起作用 。因为 service 层 是由 Spring 负责 扫描管理的 ,而 controller 层是由 Spring mvc 负责扫描管理的 ,如果要保证在横切 controller 层 时 也能够实现 aop 功能 ,就必须保证 Spring AOP 的 注解由 Spring mvc 负责扫描 ,即 开启 Spring aop 的配置要配置在 Spring mvc 的配置文件中 。
- 【spring】spring reference doc 4.3.1 研读<三> Spring aop
- 【spring】Spring reference doc 4.3.1 研读 <四> Transaction Management
- Spring(三、Spring AOP)
- Spring之三 AOP
- (三)Spring AOP
- Spring框架 AOP(三)
- Spring(三) AOP
- Spring(三)AOP
- Spring源码研读
- Spring Security Reference-1
- spring系列(三):切面编程(aop)1
- 【spring】spring reference doc4.3.1 研读 <一>元数据配置以及Environment
- Spring AOP-1 Spring AOP入门
- spring aop 2.0 编程(三)
- spring学习之AOP(三)
- Spring(三)——AOP
- Spring入门介绍-AOP(三)
- spring aop入门(序列三)
- Linux 解压命令
- zip伪加密
- Uva 11489(博弈论)
- UVA297Quadtrees
- 将字符串中的空格替换为%20
- 【spring】spring reference doc 4.3.1 研读<三> Spring aop
- iOS多线程的初步研究(九)-- dispatch源
- jquery ajax
- 三极管的工作原理(详细、通俗易懂、图文并茂)
- 什么才是正确的javascript数组检测方式
- oracle 的绑定变量
- Java|今天起,别再扯订阅和回调函数
- Broadcast广播
- 第三章:Java基础程序设计(中)