AOP
一、 概念
1. 面向切面编程是一种编程范式,不是编程语言
常见的编程范式:
- 面向过程编程
- 面向对象编程(OOP)
- 函数式编程
- 事件驱动编程
- 面向切面编程
2. 是OOP的补充,不是替代
3. AOP的初衷
DRY
处理日志,权限,监控等公有和业务无关的逻辑,解决重复编写相同的逻辑。
SoC:
- 水平分离:展示层–>服务层–>持久层
- 垂直分离:OOP将公有的逻辑通过继承和组合的方式进行抽离。
- 切面分离:将功能性需求和非功能性需求分离。(日志,权限,监控等)
4. AOP的好处
- 集中处理某一关注点/横切逻辑(修改,删除,添加)。
- 侵入性少:不修改业务代码,增加业务代码非业务功能。增强代码的可读性,可维护性。
5. AOP的应用场景(非业务需求)
- 权限控制
- 缓存控制
- 事务控制
- 审计日志
- 性能监控
- 分布式追踪
- 异常处理等
二、AOP使用
Experssion
匹配注解
声明一个注解
1
2
3
4@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Login {
}声明一个切面类,在切面类中编写pointCut,Advice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SecurityAspect {
AuthService authService;
"@annotation(AdminOnly)") (
public void adminOnly(){
}
"adminOnly()") (
public void check(){
authService.checkAccess();
}
}在需要使用aop的方法上使用注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class ProductServiceAop {
AuthService authService;
public void insert(Product product){
//business
System.out.println("insert product");
}
public void view(Long productId){
//business
System.out.println("view product"+productId);
}
匹配包/类
- 匹配类[ProductService中所有的方法(公有)]
1
"within(org.study.service.ProductService)") (
- 匹配包[org.study包及子包下所有类方法]
1
"within(org.study..*)") (
匹配对象
匹配AOP对象的目标对象为指定类型的方法
1
"this(org.study.DemoDao)) (
匹配实现IDao接口的目标对象(而不是aop代理后的对象)的方法
1
2"target(org.study.IDao)") (
public void targetDemo(){}匹配所有以Service结尾的bean里头的方法
1
2"bean(*Service)") (
public void beanDemo(){}
匹配参数
匹配任何以find开头,且只有一个Long参数的方法
1
2"execution(**..find*(Long))") (
public void argsDemo1(){}
匹配任何只有一个Long参数方法
1
2"args(Long)") (
public void argsDemo2(){}
匹配任何以find开头且第一个参数为Long类型的方法
1
"execution(**..find*(Long,..))") (
匹配第一个参数为Long类型的方法
1
"args(Long,..)") (
匹配注解
匹配方法标注有Login注解的方法
1
2"annotation(org.study.security.Login)") (
public void Login(){};
匹配标注有Beta的类底下的方法,要求annotation的RetentionPolicy级别为CLASS
1
2"@within(com.google.common.annotations.Beta)") (
public void annoWithinDemo(){}
匹配标注有Respository的类底下的方法,要求annotation的RetentionPolicy级别RUNTIME
1
2"@Target(org.springframework.stereotype.Repository)") (
public void annoTargetDemo(){}
匹配传入参数类标注有Repository注解的方法
1
2"@args(org.springframework.stereotype.Repository)") (
public void annoArgsDemo(){}
execution
格式
1
2
3
4
5
6
7execution(
modifier-pattern //修饰符
ret-type-pattern //返回值
declaring-type-pattern //描述baoming
name-pattern // 描述方法参数
throws-pattern //匹配方法异常
)切点
1
2"execution(public * org.study.service.*Service*.*(..))") (
public void match(){}获取参数
1
2
3
4"match() && args(name)") (
public void before(String name){
System.out.println(name);
}获取放回值
1
2
3
4"match()", returning="result") (pointcut=
public void afterReturn(String result){
System.out.println(result);
}获取异常
1
2
3
4"match()",throwing="e") (pointcut=
public void AfterThrowing(Exception e){
e.printStackTrace();
}
Advice
织入代码的时机
@Before
前置通知:在方法前执行
@After(finally)
后置通知:不管执行成功,还是失败,都执行
@AfterReturning
放回通知,成功执行之后
@AfterThrowing
异常通知,抛出异常之后
@Around
环绕通知
1
2
3
4
5
6"execution(public * org.study.service.*Service*.*(..))") (
public void match(){}
"match()") (
public void arround(){
System.out.println(result);
}