aop学习笔记之基于@Aspect的AOP实现:http://blog.csdn.net/zyb2017/article/details/79420172
本篇代码下载地址:https://download.csdn.net/download/zyb2017/10277492
前言
在写上一篇的时候在想Aop的适用场景,那Aop到底在做怎样一件事情呢?
一 Pointcut Express
Aop的使用我理解主要分为两大部分PointcutExpress(切面表达式)和Advice,前者表明了切入点,后者描述了切入时机
看到这里我们就了解了,Aop就好像一个插队的过程,PointcutExpress描述了要插到哪里,Advice则指出了插到这个人的前面去搞事情还是到他后面去搞事情
Pointcut Express又由designators(指示器),wildcards(通配符),operators(运算符)组成
二 wildcards
运算符我们就不说了,先说wildcards这个通配符,包含三种如下:
符号 | 作用 |
---|---|
* | 匹配任意数量字符 |
+ | 匹配自定类及其子类 |
.. | 匹配任意数的子包或参数 |
例如:com.TranisentBa.Demo.Service.*
三 designators
指示器的分类有很多种,之前看过一个视频按照匹配的目标分类如下:
3.1 within
开始测试之前先描述下环境,一个ProductService中的delete方法,应用Aop在该方法执行前做一次校验,如果不是管理员,则抛出异常。启动入口如下:
//处理异常
@Test(expected = Exception.class)
public void annoDeletetTrst() {
//正常管理员为admin
CurrentUserHolder.set("tom");
productService.delete(1L);
}
/**within匹配类**/
@Pointcut("within(com.march.aop.service.ProductService)")
public void matchWinth(){}
@Before("matchWinth()")
public void before(){
System.out.println("###within ");
authService.checkAccess();
}
3.2 this
this匹配的是经过aop代理之后的类 通过introduction动态添加的方法也可以匹配到
/**this**/
@Pointcut("this(com.march.aop.service.ProductService)")
public void matchThis(){}
@Before("matchThis()")
public void beforeThis(){
System.out.println("###this ");
authService.checkAccess();
}
3.3 target
target匹配的是原目标对象而非代理对象 通过introduction添加的方法不能匹配到 若一个类继承了被匹配的接口 则该类也会被拦截
/**target**/
@Pointcut("target(com.march.aop.service.ProductService)")
public void matchTarget(){}
@Before("matchTarget()")
public void beforeTarget(){
System.out.println("###target ");
authService.checkAccess();
}
3.4 bean
bean匹配的是spring托管的bean实例
/**Bean**/
@Pointcut("bean(authService)")
public void matchBean(){}
@Before("matchBean()")
public void beforeBean(){
System.out.println("###bean");
authService.checkAccess();
}
3.5 args
@Pointcut("args(String,..))")
public void matchArgs(){}
@Before("matchArgs()")
public void beforeArgs(){
System.out.println("###Args");
authService.checkAccess();
}
3.6 annotation
@annotation为方法级别,@within和@target匹配的为类级别,@args为参数级别的注解,以下以@annotation为例:
/**切入点为带有AdminOnly注解的方法 当程序中该注解并没有被应用时 也就是说不存在该切入点时 程序会报错 **/
@Pointcut("@annotation(AdminOnly)")
public void adminOnly(){
}
/**在adminOnly方法执行前 做checkAccess的校验**/
@Before("adminOnly()")
public void check(){
authService.checkAccess();
}
3.7 execution
execution中必要的部分有两个返回值和方法命名(参数) 例子如下:
/**拦截public 返回值为任意 位于Service包下 类名为Service结尾 其中的任意方法 任意参数 **/
// @Pointcut("execution(public * com.march.aop.service.ProductService.*(Long) throws java.lang.IllegalAccessError)")
@Pointcut("execution(public * com.march.aop.service.ProductService.*(Long))")
public void marchConditicon(){}
@Before("marchConditicon()")
public void before(){
authService.checkAccess();
}
四 advice
4.1 五中advice
注解 | 意义 |
---|---|
@Before | 在切面方法执行前织入 |
@After(finally) | 在切面方法执行后织入 |
@AfterReturning | 在切面方法成功执行后织入 |
@AfterThrowing | 在切面方法异常后织入 |
@Around | 环绕织入 |
4.2 AfterReturning
@Before和@After同上面的方法区别不大 ,这里说下带有返回值的如何捕获。
ProductService:
/**该方法带有一个String类型的返回值**/
public String delete(Long id){
System.out.println("delete product");
return "delete " + id + " success";
}
AfterReturningConfig:
/**this切入PorductService中的方法**/
@Pointcut("this(com.march.aop.service.ProductService)")
public void matchThis(){}
/**在该方法正确执行后 获取其返回值并打印**/
@AfterReturning(value = "matchThis()",returning = "result")
public void beforeThis(Object result){
System.out.println("###this result: "+ result);
// authService.checkAccess();
}
执行结果:
这里可以看到打印的返回值为 delete 1 success
五 总结
PointcutExpress和Advice可以帮我们准确定位到切面,并能正确的将代码织入,可以使用@AfterThrowing来对抛出的异常进行处理、记录,@Around是一个很全面的注解,它包含了@before和@After。就像Aop的意义一样,善用这些可以让我们在一些特定的情况更加简洁清晰的实现需求。