SpringAOP如何获取⽅法参数上的注解SpringAOP获取⽅法参数上的注解
⼀、⽰例
①如下代码,⾃定义⼀个参数注解@Test,并将其使⽤到⽅法参数上,⽤于标注需要检验的参数
/**
* ⾃定义注解,⽤于参数
*/
@Target(PARAMETER)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Test{
}
/
**
* 接⼝层,使⽤使⽤@Test注解标记参数
*/
@RestController
@RequestMapping("/v1/test")
public class TestController {
@PostMapping(value = "/email", produces = "application/json")
public String send(@RequestBody @Test MailSendDTO mailSendDTO) {
//TODO 业务处理
return "SUCCESS";
}
}
②通过切⾯拦截该⽅法,从连接点获取signature,并将signature强转为MethodSignature,从⽽从MethodSignature对象可以获取拦截的⽅法对象以及⽅法参数注解
@Aspect
@Configuration
public class ValidateAop {
/**
* 切点配置,表达式,在st.controller包下,所有的类public的任意参数的⽅法
*/
@Pointcut("execution(public * st.controller.*.*(..))")
public void validate(){}
@Before("validate()")
public void doBefore(JoinPoint joinPoint){
Object[] params = Args();
if(params.length == 0){
return;
}
//获取⽅法,此处可将signature强转为MethodSignature
MethodSignature signature = (MethodSignature) Signature();
Method method = Method();
//参数注解,1维是参数,2维是注解
Annotation[][] annotations = ParameterAnnotations();
for (int i = 0; i < annotations.length; i++) {
Object param = params[i];
Annotation[] paramAnn = annotations[i];
//参数为空,直接下⼀个参数
if(param == null || paramAnn.length == 0){
continue;
}
for (Annotation annotation : paramAnn) {
//这⾥判断当前注解是否为Test.class
if(annotation.annotationType().equals(Test.class)){
//校验该参数,验证⼀次退出该注解
//TODO 校验参数
break;
}
}
}
}
}
⼆、debug
通过debug代码:
可发现连接点实际为MethodInvocationProceedingJoinPoint对象,连接点中的signature则为MethodSignatureImpl对象,是MethodInvocationProceedingJoinPoint的内部类。
三、类图及源码
MethodInvocationProceedingJoinPoint类图,顶级实现了JoinPoint(以后再使⽤切⾯的时候,可以看看其他类⾥⾯都扩展了哪些⽅法可以直接使⽤)
MethodSignatureImpl类图,顶级实现了Signature(以后再使⽤切⾯的时候,可以看看其他类⾥⾯都扩展了哪些⽅法可以直接使⽤)
⽤AOP拦截⾃定义注解并获取注解属性与上下⽂参数(基于Springboot框架)
AOP可以⽤于⽇志的设计,这样话就少不了要获取上下⽂的信息,博主在设计⽇志模块时考虑了⼀下此法,整理了⼀下如何⽤AOP来拦截你⾃定义的注解。
⾃定义注解
⾸先先⾃定义⼀个注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Axin {
/**
* 所属模块
* @return
*/
String module() default "⽇志模块";
/**
* 动作描述
* @return
*/
String desc() default "⽆动作";
}
@Documented:注解表明制作javadoc时,是否将注解信息加⼊⽂档。如果注解在声明时使⽤了@Documented,则在制作javadoc时注解信息会加⼊javadoc。
@Target:⽤来说明该注解可以被声明在那些元素之前
@Target(ElementType.TYPE) //接⼝、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //⽅法
@Target(ElementType.PARAMETER) //⽅法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
@Retention:⽤来说明该注解类的⽣命周期。
@Retention(RetentionPolicy.SOURCE) —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
@Retention(RetentionPolicy.CLASS) —— 这种类型的Annotations编译时被保留,在class⽂件中存在,但JVM将会忽略@Retention(RetentionPolicy.RUNTIME) —— 这种类型的Annotations将被JVM保留,所以他们能在运⾏时被JVM或其他使⽤反射机制的代码所读取和使⽤.
定义切⾯
/**
* @author Axin
*/
@Aspect
@Component
public class AxinAspect {
/**
* 这⾥定义了⼀个总的匹配规则,以后拦截的时候直接拦截log()⽅法即可,⽆须去重复写execution表达
式
*/
@Pointcut("@annotation(Axin)")
public void log() {
}
@Before("log()&&@annotation(axin)")
public void doBefore(JoinPoint joinPoint,Axin axin) {
System.out.println("******拦截前的逻辑******");
System.out.println("⽬标⽅法名为:" + Signature().getName());
System.out.println("⽬标⽅法所属类的简单类名:" + Signature().getDeclaringType().getSimpleName());
System.out.println("⽬标⽅法所属类的类名:" + Signature().getDeclaringTypeName());
System.out.println("⽬标⽅法声明类型:" + Signature().getModifiers()));
//获取传⼊⽬标⽅法的参数
Object[] args = Args();
for (int i = 0; i < args.length; i++) {
System.out.println("第" + (i + 1) + "个参数为:" + args[i]);
}
System.out.println("被代理的对象:" + Target());
System.out.println("代理对象⾃⼰:" + This());
System.out.println("拦截的注解的参数:");
System.out.dule());
System.out.println(axin.desc());
}
@Around("log()&&@annotation(axin)")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint,Axin axin) throws Throwable {
System.out.println("环绕通知:");
System.out.dule());
System.out.println(axin.desc());
Object result = null;
result = proceedingJoinPoint.proceed();
return result;
}
@After("log()")
public void doAfter() {
System.out.println("******拦截后的逻辑******");
}
}
匹配规则:
//匹配AOP对象的⽬标对象为指定类型的⽅法,即DemoDao的aop的代理对象
@Pointcut("this(com.hhu.DemaoDao)")
public void thisDemo() {
...
}
通知类别:
前置通知(Before advice)- 在⽬标⽅便调⽤前执⾏通知
后置通知(After advice)- 在⽬标⽅法完成后执⾏通知
返回通知(After returning advice)- 在⽬标⽅法执⾏成功后,调⽤通知
异常通知(After throwing advice)- 在⽬标⽅法抛出异常后,执⾏通知
环绕通知(Around advice)- 在⽬标⽅法调⽤前后均可执⾏⾃定义逻辑
获取上下⽂信息JoinPoint
JoinPoint对象封装了SpringAop中切⾯⽅法的信息,在切⾯⽅法中添加JoinPoint参数,就可以获取到封装了该⽅法信息的JoinPoint对象. 注意:这⽤于⾮环绕通知
⽅法名功能
Signature getSignature();获取封装了署名信息的对象,在该对象中可以获取到⽬标⽅法名,所属类的Class等信息Object[] getArgs();获取传⼊⽬标⽅法的参数对象
Object getTarget();获取被代理的对象
Object getThis();获取代理对象
⽅法使⽤模板:
public void doBefore(JoinPoint joinPoint) {
System.out.println("******拦截前的逻辑******");
System.out.println("⽬标⽅法名为:" + Signature().getName());
System.out.println("⽬标⽅法所属类的简单类名:" + Signature().getDeclaringType().getSimpleName());
System.out.println("⽬标⽅法所属类的类名:" + Signature().getDeclaringTypeName());
System.out.println("⽬标⽅法声明类型:" + Signature().getModifiers()));
//获取传⼊⽬标⽅法的参数
Object[] args = Args();
for (int i = 0; i < args.length; i++) {
System.out.println("第" + (i + 1) + "个参数为:" + args[i]);
}
System.out.println("被代理的对象:" + Target());
System.out.println("代理对象⾃⼰:" + This());
}
ProceedingJoinPoint
ProceedingJoinPoint对象是JoinPoint的⼦接⼝,该对象只⽤在@Around的切⾯⽅法中
⽅法名功能
Object proceed() throws Throwable执⾏⽬标⽅法
Object proceed(Object[] var1) throws Throwable传⼊的新的参数去执⾏⽬标⽅法
定义测试⽅法
/
/Service接⼝
public interface AxinService {
String axinRun(String arg1, User user);
}
//实现类
/**
spring怎么读取配置* @author Axin
*/
@Component
public class AxinServiceImpl implements AxinService {
@Axin(module = "print",desc = "打印")
@Override
public String axinRun(String arg1, User user) {
String res = arg1 + Name() + Age();
return res;
}
public String axinRun(String arg1, Person person) {
String res = arg1 + Name() + Age();
return res;
}
}
//控制类
/
**
* @author Axin
*/
@RestController
public class HelloController {
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论