AOP设计的初衷
- DRY:Don’t Repeat Yourself,减少重复代码;
- SoC:Separation of Concerns,关注分离;
- 水平分离:展示层 --> 服务层 --> 持久层
- 垂直分离:模块之间分离;
- 切面分离:功能性需求与非功能性需求分离;
使用AOP的好处
- 集中处理某一关注点/横切逻辑
- 可以很方便的添加/删除关注点
- 侵入性少,增强代码可读性以及可维护性
AOP应用场景
- 权限控制;
- 缓存控制;
- 事务控制;
- 审计日志;
- 性能监控;
- 分布式追踪;
- 异常处理;
AOP术语介绍
- 通知(Advice):简单的说就是你想要的功能,预先定义好这些功能,然后应用在你想要用的地方;
- 连接点(JoinPoint):spring允许你使用通知(Advice)的地方,通常可以是:每个方法调用的前后、抛出异常时等;
- 切入点(PointCut):切入点是建立在连接点基础上的,例如有十个方法,每个方法的调用前后等都是连接点,但你只想在某个方法的调用前织入通知,那么就可以使用切点来定义这个方法。通过切点对连接点进行定义,从而筛选出你想要织入通知的连接点;
- 切面(Aspect):切面是通知和切点的结合。通知定义了什么时候干什么事,切点定义了在哪干,通知和切点共同组成了完整的切面;
- 引入(Introduction):允许我们向现有的类中添加新的方法。结合上面的几个术语,其实就是将切面应用到具体的目标类中;
- 目标(Target):即目标类,也就是被通知的对象,通常是真正的业务逻辑。目标类可以在毫不知情的情况下被织入切面,而自己专注于业务本身的逻辑;
- 代理(Proxy):AOP机制的实现就是通过代理完成的;
- 织入(Weaving):把切面应用到目标对象时创建新的代理对象的过程,有三种织入方法:编译期织入,装载期织入和运行时织入;
AOP注解介绍
- @Aspect :标注在类上,表示当前类是一个切面类;
- @PointCut:切入点;
- @Before:前置通知,即在某个连接点之前执行通知;
- @AfterReturning:后置通知,即在某个连接点正常完成后执行通知,通常在一个匹配的方法返回的时候执行;
- @AfterThrowing:异常通知,即在方法抛出异常退出时执行通知;
- @After:最终通知,即某个连接点退出时执行通知;
- @Around:环绕通知,它是最强大也是最麻烦的通知,它可以在方法调用前后完成自定义的行为,它可以自己选择是否继续执行连接点或者直接返回或者抛出异常来结束执行;
切面表达式
切面表达式主要由三部分组成:
- designators(指示器):execution()等;
- wildcards(通配符): * … +
- operators(操作符):&& || !
- *:匹配任意数量的字符;
- +:匹配指定类及其子类;
- …:一般用于匹配任意数的子包或者参数;
- &&:与;
- ||:或
- !:非
- execution()
表达式结构:
execution(<修饰符>? <返回类型> <包名>?<方法名>(<参数>)异常?)
execution(
modifier-pattern?
returnType-pattern
package-pattern?
methodName-pattern(args-pattern)
throwException-pattern?
)
execution(*[修饰符]? *[返回值] *[包路径]?*[方法名](..[参数])throw *[异常类]?)
- 修饰符匹配(modifier-pattern)
//匹配所有public修饰的方法
@Pointcut("execution(public * **(..))")
public void exTest1(){}
- 返回值匹配(returnType-pattern)
//匹配所有String或者void返回值的方法
@Pointcut("execution(String||void **(..))")
public void exTest2(){}
- 包路径匹配(package-pattern)
//com包下的所有类的方法
@Pointcut("execution(* com.*.*(..))")
public void exTest3(){}
//如果不使用..匹配到了类级别的名字,需要类.方法名.............
//com包下的所有子包的所有类的方法
@Pointcut("execution(* com..*(..))")
public void exTest4(){}
//com包下的所有子包的ProductService类的方法
@Pointcut("execution(* com..ProductService.*(..))")
public void exTest5(){}
- 方法名匹配(methodName-pattern)
//匹配所有test名字开头的方法
@Pointcut("execution(* test*(..))")
public void exTest6(){}
//匹配所有包含test名字的方法
@Pointcut("execution(* *test*(..))")
public void exTest7(){}
- 参数匹配(args-pattern)
//匹配所有参数列表的方法
@Pointcut("execution(* *(..))")
public void exTest8(){}
//匹配无参数列表的方法
@Pointcut("execution(* *())")
public void exTest9(){}
- 异常匹配(throwsException-pattern)
//匹配所有抛过异常的方法
@Pointcut("execution(* *()throws *)")
public void exTest10(){}
//只匹配所有抛出空指针异常的方法
@Pointcut("execution(* *()throws NullPointerException)")
public void exTest11(){}
- @target()
//匹配所有使用了AspectAnnotation注解的类的所有方法(要求注解的RetentionPolicy的级别为RUNTIME)
@Pointcut("@target(com.tiglle.manage.AspectAnnotation)")
public void targetMatch(){}
- @args()
//匹配所有使用了AspectAnnotation注解为参数的方法
@Pointcut("@args(com.tiglle.manage.AspectAnnotation)")
public void argsMatch(){}
- @winth()
//匹配所有使用了AspectAnnotation注解的类的所有方法(要求注解的RetentionPolicy的级别为CLASS)
@Pointcut("@within(com.tiglle.manage.AspectAnnotation)")
public void withinMatch(){}
- @annotation()
//方法注解匹配,匹配所有带AspectAnnotation注解的方法
@Pointcut("@annotation(com.tiglle.manage.annotation.AspectAnnotation)")
public void test(){
}
- within()
- 如果传的是全类名(包名.类名):匹配此类下所有的方法
//匹配TestService类中的所有方法
@Pointcut("within(com.tiglle.service.TestService)")
public voud test(){}
- 如果传的时包名:匹配此包下所有类的方法
//匹配com/tiglle/包下所有包和子包中的类中的所有方法
@Pointcut("within(com.tiglle..*)")
public voud test(){}
- this()
//匹配代理对象和普通对象及其所有子类的方法
@Pointcut("this(com.tiglle.manage.service.ProductService)")
public void thisMatch(){
}
- bean()
//根据spring容器的bean的名称(id)匹配,(不匹配子类)
@Pointcut("bean(productService)")
public void beanMatch(){
}
- target()
//匹配目标对象和普通对象及其所有子类的方法
@Pointcut("target(com.tiglle.manage.service.ProductService)")
public void targetMatch(){
}
- args()
//匹配spring容器所有此参数类型和列表的方法(String,Long)
@Pointcut("args(String,Long)")
public void argsMatch(){}
//匹配spring容器所有此参数类型和列表的方法(第一个为Long,后面随意)
@Pointcut("args(Long,..)")
public void argsMatch2(){}