Spring之AOP面向切面编程
AOP(Aspect-Oriented Programming)面向切面编程,以前在做Android开发的时候,就有了解到,客户端请求网络判定网络状态,动态权限检查,从切面统一处理,就会解决很多麻烦事儿,代码高度解耦。原来其实都是从spring框架来的,看看Spring 中的AOP是什么样的。
前言
- 面向切面编程是一种编程范式,不是编程语言;
- 是一种解决特殊问题的方式,不能解决所有的问题
- 是面向对象编程OOP(封装、继承、堕胎)的一种补充
AOP好处
- DRY(Dont Repeat Yourself): 不要重复代码,高度解耦
- SOC(Separation of Contents): 分离内容
- 水平分离:展示层-》服务层-》持久层
- 垂直分离:模块的划分
- 切面分离:切面编程是将功能性需求和非功能性需求分离出来
- 集中处理某一特定的逻辑
- 可侵入性少,可以增加代码的可读性和可维护性
使用场景
例如:权限控制,缓存控制,事务控制,审计日志,性能监控,异常处理,分布式追踪等基本上是非功能性的需求,下面的栗子就用一个权限校验来对比切面校验和普通校验。
初识Aspect(普通校验和切面校验)
当然首先是maven导入aspectjr的包。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>普通权限校验的实现方式,在每次需要校验的前面加上固定的方法checkAccess()来校验用户是否合法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void adminInsert() {
CurrentUserHolder.set("admin");
productService.insertWithOnlyCheck(1l);
System.out.println("--------------Done---------------");
}
public void insertWithOnlyCheck(long id){
authService.checkAccess();
System.out.println("ProductService insert --- " + id);
}
public void checkAccess() {
String user = CurrentUserHolder.get();
if (!user.equals("admin")) {
throw new RuntimeException("user is null or wrong");
}
}最终输出
1
2ProductService insert --- 1
-------------Done------------切面校验
1
2
3
4
5
6
public void admiAnnotInsert() {
CurrentUserHolder.set("Rocka");
productService.insert(1l);
System.out.println("------------Done--------------");
}带有自定义的AdminOnly注解
1
2
3
4
5
6
7
8
9
public void delete(long id){
System.out.println("ProductService only delete --- " + id);
}
(RetentionPolicy.RUNTIME)
(ElementType.METHOD)
public AdminOnly {
}@Aspect 注解自定义切面校验用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SecurityAspect {
AuthService authService;
//参数表达式,想切哪些东西,拦截标注有AdminOnly的注解
"@annotation(AdminOnly)") (
public void adminOnly(){
System.out.println("check start");
}
//在执行之前,执行一段共有逻辑
"adminOnly()") (
public void check(){
System.out.println("adminOnly annotation check before");
authService.checkAccess();
}
}
切面表达式
within表达式
匹配包(拦截匹配类里的所有方法)
@Pointcut(“within(com.rocka.service.ProductService)”)
匹配类型(拦截包及子包下所有类的方法)
@Pointcut(“within(com.rocka..*)”)
参数匹配
匹配任何以find开头且只有一个Long参数的方法
@Pointcut(“execution( ..find*(Long))”)
匹配只有一个Long参数的方法
@Pointcut(“args(Long)”)
匹配任何以find开头而且第一个参数为Long的方法
@Pointcut(“execution( ..find*(Long,..))”)
匹配第一个参数为Long型的方法
@Pointcut(“args(Long,..)”)
注解匹配
主要有匹配注解方法级别,类级别,参数级别的方法
匹配方法标注有AdminOnly注解的方法
@Pointcut(“@annotation(com.rocka.security.AdminOnly)”)
匹配标注有bean类底下的方法,要求的annotation的RetentionPolicy的级别为CLASS
@Pointcut(“@within(com.google.common.annotations.Beta)”)
匹配标注有bean类底下的方法,要求的annotation的RetentionPolicy的级别为CLASS
@Pointcut(“@target(com.springframework.stereotype.Repository)”)
匹配传入的参数类标注有Repository注解的方法
@Pointcut(“@target(com.springframework.stereotype.Repository)”)
execution表达式
@Pointcut(“execution(public com.rocka.service.Service.*(..))”)
execution里面的表达式当做一个方法来看。
advice注解
- @Before,前置通知
- @After,后置通知,方法执行之后
- @AfterReturning,返回通知,成功执行之后
- @AfterThrowing,异常通知,抛出异常之后
- @Around,环绕通知,可以替代上面所有的通知