环境
springboot:1.5
Intellij IDEA:2021.1
序言
最近有个需求,要做方法层面的权限控制。以前在公司使用的是spring security
,然后使用注解
如下:
@PreAuthorize("hasPermission('', 'user:login')")
public String helloAdmin() {
return "I am Admin";
}
但是目前这个项目,虽然引入了spring security
的依赖,但是在启动类中排出掉了;
我若开启,势必会要添加很多配置:URL放行、跨域问题等问题。
项目时间赶,没时间去弄。所以选择AOP自定义注解的方式来解决。
步骤
依赖
以下依赖的基础是,项目是springboot的项目:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
自定义注解
我自定义了一个TestAnnotation
注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TestAnnotation {
String[] values();
}
连接点
其实就是方法。类中所有的方法,都是一个一个的连接点。
当具体到某个方法时,就变成切点
了
所以连接点仅仅只是个概念。
定义切点
切点:当明确类中某个方法需要被AOP
时,那么这个方法就是切点。
切点有两种写法:
① 表达式写法
②注解写法
我们先创建一个TestAspect
类:
@Aspect
@Component
public class TestAspect {
// 这里面写切点和切面
}
表达式的写法
@Aspect
@Component
public class TestAspect {
@Pointcut("execution(* com.sgy.service.impl..*.*(..)) || execution(* com.sgy.domain..*.*(..))")
public void action() {
}
}
注解的写法
@Aspect
@Component
public class TestAspect {
@Pointcut("@annotation(com.sgy.securitydemo.annotation.TestAnnotation)")
public void action() {
//这个空方法就是为了挂切点
}
}
定义切面
切面就是通知+切点;
想实现的功能、什么时候去做,在哪里做;
@Aspect
@Component
public class TestAspect {
@Pointcut("@annotation(com.sgy.securitydemo.annotation.TestAnnotation)")
public void action() {
}
//在目标方法被调用之前执行
//目标方法:被@TestAnnotation注解的方法即为目标方法
//@annotation中的值,需要和target方法中参数名称相同(必须相同,但是名称任意)
@Before("@annotation(testAnnotation)")
public void target(JoinPoint joinPoint, TestAnnotation testAnnotation) {
System.out.println(Arrays.toString(testAnnotation.values()));
}
//写法二:
//也有这种写法:就是在@Before里面加入切点的空方法,其实没有必要。
@Before("action() && @annotation(testAnnotation)")
public void target(JoinPoint joinPoint, TestAnnotation testAnnotation) {
System.out.println(Arrays.toString(testAnnotation.values()));
}
//写法三:
@Before("action()")
public void target(JoinPoint joinPoint) {
MethodSignature sign = (MethodSignature)joinPoint.getSignature();
Method method = sign.getMethod();
TestAnnotation testAnnotation = method.getAnnotation(TestAnnotation.class);
System.out.print("打印:"+ Arrays.toString(testAnnotation.values()) +" 前置日志");
}
}
注意:
使用
@RestController
public class HelloController {
@GetMapping("/hellono")
@TestAnnotation(values = "{1,2,3}")
public String hellono() {
return "I am Admin";
}
}
执行结果:
其他概念:
引入
就是把切面用到目标类中;所以它只是个概念,具体框架已经帮我们做了。
目标
切点作用的地方。也就是具体某个方法。
织入
把切面应用到目标对象来创建新的代理对象的过程。
遇到的问题
ProceedingJoinPoint as first parameter is allowed only in @Around advices
下面参数中:ProceedingJoinPoint
会报错,因为ProceedingJoinPoint
只是给@Around
用的。
@Before("action()")
public void target(ProceedingJoinPoint joinPoint) {
MethodSignature sign = (MethodSignature)joinPoint.getSignature();
Method method = sign.getMethod();
TestAnnotation testAnnotation = method.getAnnotation(TestAnnotation.class);
System.out.print("打印:"+ Arrays.toString(testAnnotation.values()) +" 前置日志");
}
总结
- 我想用AOP实现什么样的功能;-- 通知
- 我要对哪个方法进行处理; – 切点
- 将通知和切换整合起来 – 切面
- 定义切面类(
@Aspect
标注的类)时,记得注入给spring
,所以@Component
别忘了。
参考地址: