6.1. 简介

6.2.4.1. 前置通知(Before advice)

一个切面里使用 @Before 注解声明前置通知:

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

@Aspect

public class BeforeExample {

@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")

public void doAccessCheck() { // ... }

}

如果使用一个in-place 的切入点表达式,我们可以把上面的例子换个写法:

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

@Aspect

public class BeforeExample {

@Before("execution(* com.xyz.myapp.dao.*.*(..))")

public void doAccessCheck() { // ... }

}

6.2.4.2. 返回后通知(After returning advice)返回后通知通常在一个匹配的方法返回的时候执行。使用 @AfterReturning 注解来声明:

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.AfterReturning;

@Aspect

public class AfterReturningExample {

@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")

public void doAccessCheck() { // ... }

}

说明:你可以在同一个切面里定义多个通知,或者其他成员。我们只是在展示如何定义一个简单的通知。这些例子主要的侧重点是正在讨论的问题。有时候你需要在通知体内得到返回的值。你可以使用以 @AfterReturning 接口的形式来绑定返回值:

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.AfterReturning;

@Aspect

public class AfterReturningExample {

@AfterReturning( pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", returning="retVal")

public void doAccessCheck(Object retVal) { // ... }

}

在 returning 属性中使用的名字必须对应于通知方法内的一个参数名。 当一个方法执行返回后,返回值作为相应的参数值传入通知方法。 一个 returning 子句也限制了只能匹配到返回指定类型值的方法。 (在本例子中,返回值是 Object 类,也就是说返回任意类型都会匹配)

6.2.4.3. 抛出后通知(After throwing advice)抛出后通知在一个方法抛出异常后执行。使用 @AfterThrowing 注解来声明:

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.AfterThrowing;

@Aspect

public class AfterThrowingExample {

@AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")

public void doRecoveryActions() { // ... }

}

你通常会想要限制通知只在某种特殊的异常被抛出的时候匹配,你还希望可以在通知体内得到被抛出的异常。 使用 throwing 属性不光可以限制匹配的异常类型(如果你不想限制,请使用 Throwable 作为异常类型),还可以将抛出的异常绑定到通知的一个参数上。

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.AfterThrowing;

@Aspect

public class AfterThrowingExample {

@AfterThrowing( pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", throwing="ex")

public void doRecoveryActions(DataAccessException ex) { // ... }

}

在 throwing 属性中使用的名字必须与通知方法内的一个参数对应。 当一个方法因抛出一个异常而中止后,这个异常将会作为那个对应的参数送至通知方法。 throwing 子句也限制了只能匹配到抛出指定异常类型的方法(上面的示例为 DataAccessException)。

6.2.4.4. 后通知(After (finally) advice)不论一个方法是如何结束的,在它结束后(finally)后通知(After (finally) advice)都会运行。 使用 @After 注解来声明。这个通知必须做好处理正常返回和异常返回两种情况。通常用来释放资源。

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.After;

@Aspect

public class AfterFinallyExample {

@After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")

public void doReleaseLock() { // ... }

}

6.2.4.5. 环绕通知(Around Advice)最后一种通知是环绕通知。环绕通知在一个方法执行之前和之后执行。 它使得通知有机会既在一个方法执行之前又在执行之后运行。并且,它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。 环绕通知经常在在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。 请尽量使用最简单的满足你需求的通知。(比如如果前置通知(before advice)也可以适用的情况下不要使用环绕通知)。环绕通知使用 @Around 注解来声明。通知的第一个参数必须是 ProceedingJoinPoint 类型。 在通知体内,调用 ProceedingJoinPoint 的 proceed() 方法将会导致潜在的连接点方法执行。 proceed 方法也可能会被调用并且传入一个 Object[] 对象-该数组将作为方法执行时候的参数。当传入一个 Object[] 对象的时候,处理的方法与通过AspectJ编译器处理环绕通知略有不同。 对于使用传统AspectJ语言写的环绕通知来说,传入参数的数量必须和传递给环绕通知的参数数量匹配(不是后台的连接点接受的参数数量),并且特定顺序的传入参数代替了将要绑定给连接点的原始值(如果你看不懂不用担心)。 Spring采用的方法更加简单并且更好得和他的基于代理(proxy-based),只匹配执行的语法相适用。 如果你适用AspectJ的编译器和编织器来编译为Spring而写的@AspectJ切面和处理参数,你只需要了解这一区别即可。 有一种方法可以让你写出100%兼容Spring AOP和AspectJ的,我们将会在后续的通知参数(advice parameters)的章节中讨论它。

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.ProceedingJoinPoint;

@Aspect

public 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;

}

}

方法的调用者得到的返回值就是环绕通知返回的值。 例如:一个简单的缓存切面,如果缓存中有值,就返回该值,否则调用proceed()方法。 请注意proceed可能在通知体内部被调用一次,许多次,或者根本不被调用。

6.2.4.6. 通知参数(Advice parameters)Spring 2.0 提供了完整的通知类型 - 这意味着你可以在通知签名中声明所需的参数,(就像在以前的例子中我们看到的返回值和抛出异常一样)而不总是使用Object[]。 我们将会看到如何在通知体内访问参数和其他上下文相关的值。首先让我们看以下如何编写普通的通知以找出正在被通知的方法。

6.2.4.6.1. 访问当前的连接点任何通知方法可以将第一个参数定义为 org.aspectj.lang.JoinPoint 类型 (环绕通知需要定义为 ProceedingJoinPoint 类型的, 它是 JoinPoint 的一个子类。) JoinPoint 接口提供了一系列有用的方法, 比如 getArgs()(返回方法参数)、getThis()(返回代理对象)、getTarget()(返回目标)、getSignature()(返回正在被通知的方法相关信息)和 toString()(打印出正在被通知的方法的有用信息)。

6.2.4.6.2. 传递参数给通知(Advice)我们已经看到了如何绑定返回值或者异常(使用后置通知(after returning)和异常后通知(after throwing advice)。 为了可以在通知(adivce)体内访问参数,你可以使用 args 来绑定。 如果在一个参数表达式中应该使用类型名字的地方使用一个参数名字,那么当通知执行的时候对应的参数值将会被传递进来。 可能给出一个例子会更好理解。假使你想要通知(advise)接受某个Account对象作为第一个参数的DAO操作的执行,你想要在通知体内也能访问到account对象,你可以写如下的代码:

@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" + "args(account,..)")

public void validateAccount(Account account) { // ...}

切入点表达式的 args(account,..) 部分有两个目的: 首先它保证了只会匹配那些接受至少一个参数的方法的执行,而且传入的参数必须是 Account 类型的实例, 其次它使得可以在通知体内通过 account 参数来访问那个account参数。另外一个办法是定义一个切入点,这个切入点在匹配某个连接点的时候“提供”了一个Account对象, 然后直接从通知中访问那个命名的切入点。你可以这样写:

@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" + "args(account,..)")

private void accountDataAccessOperation(Account account) {}

@Before("accountDataAccessOperation(account)")

public void validateAccount(Account account) { // ..}如果想要知道更详细的内容,请参阅 AspectJ 编程指南。代理对象(this)、目标对象(target) 和注解(@within, @target, @annotation, @args)都可以用一种简单格式绑定。 以下的例子展示了如何使用 @Auditable 注解来匹配方法执行,并提取AuditCode。首先是 @Auditable 注解的定义:

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface Auditable { AuditCode value();}

然后是匹配 @Auditable 方法执行的通知:

@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && " + "@annotation(auditable)")

public void audit(Auditable auditable) { AuditCode code = auditable.value(); // ...}

6.2.4.6.3. 决定参数名绑定在通知上的参数依赖切入点表达式的匹配名,并借此在(通知(advice)和切入点(pointcut))的方法签名中声明参数名。 参数名 无法 通过Java反射来获取,所以Spring AOP使用如下的策略来决定参数名字:如果参数名字已经被用户明确指定,则使用指定的参数名: 通知(advice)和切入点(pointcut)注解有一个额外的"argNames"属性,该属性用来指定所注解的方法的参数名 - 这些参数名在运行时是 可以 访问的。例子如下:

@Before( value="com.xyz.lib.Pointcuts.anyPublicMethod() && " + "@annotation(auditable)", argNames="auditable")

public void audit(Auditable auditable) {

AuditCode code = auditable.value(); // ...

}

如果一个@AspectJ切面已经被AspectJ编译器(ajc)编译过了,那么就不需要再添加 argNames 参数了,因为编译器会自动完成这一工作。使用 'argNames' 属性有点不那么优雅,所以如果没有指定'argNames' 属性, Spring AOP 会寻找类的debug信息,并且尝试从本地变量表(local variable table)中来决定参数名字。 只要编译的时候使用了debug信息(至少要使用 '-g:vars' ),就可获得这些信息。

使用这个flag编译的结果是:

(1)你的代码将能够更加容易的读懂(反向工程),

(2)生成的class文件会稍许大一些(不重要的),

(3)移除不被使用的本地变量的优化功能将会失效。 换句话说,你在使用这个flag的时候不会遇到任何困难。如果不加上debug信息来编译的话,Spring AOP将会尝试推断参数的绑定。 (例如,要是只有一个变量被绑定到切入点表达式(pointcut expression)、通知方法(advice method)将会接受这个参数, 这是显而易见的)。 如果变量的绑定不明确,将会抛出一个 AmbiguousBindingException 异常。如果以上所有策略都失败了,将会抛出一个 IllegalArgumentException 异常。

6.3.3. 声明通知和@AspectJ风格一样,基于schema的风格也支持5种通知类型并且两者具有同样的语义。

6.3.3.1. 通知(Advice)Before通知在匹配方法执行前进入。在里面使用元素进行声明。 ...这里 dataAccessOperation 是一个顶级()切入点的id。 要定义内置切入点,可将 pointcut-ref 属性替换为 pointcut 属性: ...我们已经在@AspectJ风格章节中讨论过了,使用命名切入点能够明显的提高代码的可读性。Method属性标识了提供了通知的主体的方法(doAccessCheck)。这个方法必须定义在包含通知的切面元素所引用的bean中。 在一个数据访问操作执行之前(执行连接点和切入点表达式匹配),切面中的"doAccessCheck"会被调用。

6.3.3.2. 返回后通知(After returning advice)After returning通知在匹配的方法完全执行后运行。和Before通知一样,可以在里面声明。例如: ...和@AspectJ风格一样,通知主体可以接收返回值。使用returning属性来指定接收返回值的参数名: ...doAccessCheck方法必须声明一个名字叫 retVal 的参数。 参数的类型强制匹配,和先前我们在@AfterReturning中讲到的一样。例如,方法签名可以这样声明:public void doAccessCheck(Object retVal) {...

6.3.3.3. 抛出异常后通知(After throwing advice)After throwing通知在匹配方法抛出异常退出时执行。在 中使用after-throwing元素来声明: ...和@AspectJ风格一样,可以从通知体中获取抛出的异常。 使用throwing属性来指定异常的名称,用这个名称来获取异常: ...doRecoveryActions方法必须声明一个名字为 dataAccessEx 的参数。 参数的类型强制匹配,和先前我们在@AfterThrowing中讲到的一样。例如:方法签名可以如下这般声明:public void doRecoveryActions(DataAccessException dataAccessEx) {...

6.3.3.4. 后通知(After (finally) advice)After (finally)通知在匹配方法退出后执行。使用 after 元素来声明: ...

6.3.3.5. 通知Around通知是最后一种通知类型。Around通知在匹配方法运行期的“周围”执行。 它有机会在目标方法的前面和后面执行,并决定什么时候运行,怎么运行,甚至是否运行。 Around通知经常在需要在一个方法执行前或后共享状态信息,并且是线程安全的情况下使用(启动和停止一个计时器就是一个例子)。 注意选择能满足你需求的最简单的通知类型(i.e.如果简单的before通知就能做的事情绝对不要使用around通知)。Around通知使用 aop:around 元素来声明。 通知方法的第一个参数的类型必须是 ProceedingJoinPoint 类型。 在通知的主体中,调用 ProceedingJoinPoint的proceed() 方法来执行真正的方法。 proceed 方法也可能会被调用并且传入一个 Object[] 对象 - 该数组将作为方法执行时候的参数。 参见 Section 6.2.4.5, “环绕通知(Around Advice)” 中提到的一些注意点。 ...doBasicProfiling 通知的实现和@AspectJ中的例子完全一样(当然要去掉注解):public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed(); // stop stopwatch return retVal;}6.3.3.6. 通知参数Schema-based声明风格和@AspectJ支持一样,支持通知的全名形式 - 通过通知方法参数名字来匹配切入点参数。 参见 Section 6.2.4.6, “通知参数(Advice parameters)” 获取详细信息。如果你希望显式指定通知方法的参数名(而不是依靠先前提及的侦测策略),可以通过 arg-names 属性来实现。示例如下:The arg-names attribute accepts a comma-delimited list of parameter names.arg-names属性接受由逗号分割的参数名列表。

使用Spring进行面向切面编程(AOP)---讲解+代码相关推荐

  1. Spring之面向切面编程AOP(八)

    介绍&步骤 视频教程: https://www.bilibili.com/video/BV1WZ4y1P7Bp?p=121 官方笔记链接:https://pan.baidu.com/s/1dn ...

  2. 【Spring】面向切面编程AOP

    AOP基础 什么是AOP [废话解释]在软件业,AOP全称Aspect Oriented Programming 即:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AO ...

  3. 图文结合分析Spring的面向切面编程--AOP

    Spring还可以这么学–AOP 上一篇文章Spring还可以这么学–IoC(控制反转) / DI(依赖注入)理解 1. 什么是AOP? AOP(Aspect Oriented Programming ...

  4. Spring之面向切面编程AOP(三)

    上两节提到Spring的装配bean还有高级装配,这一节就是Spring的另一个核心内容-AOP AOP的基本概念 AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务 ...

  5. Spring in Action 入门之面向切面编程AOP

    注明:这篇文章一是当成学习笔记,二是给大家提供另一个快速理解学习Spring的参考.欢迎留言讨论,持续更新中~ (该部分是Spring的面向切面编程AOP) 第四章 通知Bean 在软件编程中,散布于 ...

  6. Spring(4)——面向切面编程(AOP模块)

    Spring AOP 简介 如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用. AOP 即 Aspect Orien ...

  7. Spring→面向切面编程AOP、相关概念、通知Advice类型、配置切面切入点通知、AOP相关API、AOP代理类ProxyFactoryBean、AOP注解@AspectJ

    面向切面编程AOP CGLib AOP相关概念 Advice类型 Spring实现AOP Spring配置切面aspect 配置切入点pointcut 配置通知advice 配置通知参数 调用新的父类 ...

  8. Spring(四):面向切面编程AOP

    2019独角兽企业重金招聘Python工程师标准>>> 横切关注点:分布于应用中多处的功能 面向切面编程AOP:将横切关注点与业务逻辑相分离 在使用面向切面编程时,仍在一个地方定义通 ...

  9. 服务端第三次课程:面向切面编程AOP

    3:面向切面编程AOP 1:回顾 bean的组装方式 规划的装配 component autowired sacn是在configuration底下的 Java config 使用configurat ...

  10. Spring-学习笔记08【面向切面编程AOP】

    Java后端 学习路线 笔记汇总表[黑马程序员] Spring-学习笔记01[Spring框架简介][day01] Spring-学习笔记02[程序间耦合] Spring-学习笔记03[Spring的 ...

最新文章

  1. matlab dfp法,DFP算法及Matlab程序.docx
  2. 通过rsync清除目录的shell脚本
  3. 基于网格的空间数据组织
  4. Java删除文件(delete file in java)
  5. 《Java技术》第二次作业计科1501赵健宇
  6. 非暴力拆解:小熊派NB-IoT通信扩展板
  7. win7修复计算机无法修复工具栏,WIN7多系统启动引导修复工具BCDautofix v1.3
  8. 小米平板4刷机win10或linux,小米平板2中Win10/MIUI系统互刷终极教程
  9. 3月第4周网络安全报告:境内76.2万个主机感染病毒
  10. 在微软工作有多舒服?
  11. 用Excel制作不一样的分割图表
  12. Web前端HTML使用
  13. java ssm人体健康体检信息管理系统-
  14. Tribon M3 license keygen
  15. android recovery中文下载地址,Android Recovery模式与Recovery界面的中文详细说明
  16. 信息系统项目管理:项目经理担任什么样的角色?
  17. 20200523_01_Multisim14.2+仿真+入门
  18. NSGAII快速非支配遗传算法二代学习笔记1
  19. IDC:中国人工智能及自动化市场十大预测
  20. 货运物流公司要怎么注册?注册企业需要流程

热门文章

  1. python环境配置与pytorch下载
  2. boost::spirit模块实现自定义用作容器数据的测试程序
  3. boost::python::numpy::unary_ufunc相关的测试程序
  4. boost::mpi模块实现一个简单的点类,我们可以构建、添加、比较和 连载
  5. boost::log::core用法的测试程序
  6. boost::histogram模块实现导向轴的测试程序
  7. boost::geometry::model::d3::point_xyz用法的测试程序
  8. boost::foreach模块右值 const_r的测试程序
  9. boost::contract模块实现push button的测试程序
  10. Boost:使用/type <>语法测试功能对象