SpringBoot 自定义注解结合AOP的简单使用
前一篇关于redis缓存的博客让我对注解起了兴趣,原本在培训班的时候觉得AOP的课好无聊,而且一直以来都没怎么用AOP,觉得一点用都没有。当然现在明白过去的无知还不晚。而且现在感觉自己会写注解之后逼格高了一个档次。
首先要讲的内容来只上一篇Redis缓存数组管理。
AOP切面编程首先要引入依赖
<!-- 切面编程 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
然后创建一个切面类aspect。这个类大致包含如下内容:数个切入点和数个通知方法,当然你也可以把切入点嵌入通知中。
如下
@Aspect @Component public class MyAspect { /* *定义一个切入点,指定切入的类型和位置。 *类型: execution: 切入方法中,可以使用通配符来切入多个方法 @annotation: 切入注解中,不可以使用通配符指定切入什么注解。对方法注解和类注解有效,对参数注解无效 其他的请看别人总结的:https://blog.****.net/sunlihuo/article/details/52701548 */ //这里我们指定了切入点为MyAnnotaion注解. @Pointcut("@annotation(com.dingguan.template.annotation.MyAnnotaion)") private void value(){} /* * 定义通知。 * 通知有5中方式: * @Before: 前置通知, 在方法执行之前执行 * @After: 后置通知, 在方法执行之后执行 。 * @AfterRunning: 返回通知, 在方法返回结果之后执行 * @AfterThrowing: 异常通知, 在方法抛出异常之后 * @Around: 环绕通知, 围绕着方法执行 * 然后括号里选择切入点 * 也可以直接将切入点写在这里,例如: * @Around("@annotation(com.dingguan.template.annotation.MyAnnotaion)") * 这样就可以不用写上面的切入点了。 * 参数 ProceedingJoinPoint 继承自JoinPoint, * 可以用其获得切入方法的注解、注解的参数、执行返回结果等等 * 我们可以从其获得需要的值后进行加工, * 如果仍需要执行原方法,则调用joinPoint.proceed(),这个方法可以拿到返回体。 * 这个方法的返回体会替换掉原方法的返回体 */ @Around("value()") public Object set(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //获得该方法的全部注解 Annotation[][] annotations = signature.getMethod().getParameterAnnotations(); //获得该方法的指定注解,获得注解之后调用类似于 参数名() 这样的方法就可以得到注解的参数 MyAnnotaion annotaion = signature.getMethod().getAnnotation(MyAnnotaion.class); //调用下面这个会执行原方法,并传回返回体,我们可以加工返回体后再返回给方法调用者 Object object = joinPoint.proceed(); return object; } }
我们大致了解切面类之后就创建注解。
注解有四个元注解,如下:
[email protected] //加了这个会把注解加入到javadoc中
[email protected](RetentionPolicy.RUNTIME) //这里指定注解的保留策略,如果不太了解就用RUNTIME
[email protected](ElementType.ANNOTATION_TYPE) //这里指定注解的作用目标,枚举类值的含义如下
ElementType.TYPE 接口、类、枚举、注解
ElementType.FIELD 字段、枚举的常量
ElementType.METHOD 方法
ElementType.PARAMETER 方法参数
ElementType.CONSTRUCTOR 构造函数
ElementType.LOCAL_VARIABLE 局部变量
ElementType.ANNOTATION_TYPE 注解
ElementType.PACKAGE 包
[email protected]:说明子类可以继承父类中的该注解
注解使用@interface 来定义,它和接口一样,是不能有实现方法的。
而且注解的参数以 public/private 数据类型(八大数据类型的引用类型不能用)参数名() [default 默认值] 的形式定义;
@Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyAnnotaion { public long value() default 20L; String str(); //如果没有给默认值,那么使用注解时必须给值 }
然后我们在需要的地方使用注解,就可以对其进行各种AOP通知了,也可以利用注解的参数做一些操作。
举例可以看上篇Redis数组管理。
AOP是在方法上去通知的。我们若是要对方法参数进行加工呢?例如验证参数合法性、把参数转换成指定类型等操作,
例如@ReuestBody和@Valid。这时AOP不适用了。我百度了很多博客,参考了下面两篇博客总结出了方法:
https://blog.****.net/a18716374124/article/details/79208990
https://blog.****.net/potatobeancox55555/article/details/80588507
目前我只能是处理controller下的方法的参数,其他非controller类的处理我还没找到,如果找到了会更新。
首先我们先定义一个函数参数处理器HandlerMethodArgumentResolver,并将其向spring注册
@Component public class MyArgumentResolve implements HandlerMethodArgumentResolver { public MyArgumentResolve() { } @Override public boolean supportsParameter(MethodParameter parameter) { //System.out.println(parameter.hasParameterAnnotation(MyAnnotaion.class)); return parameter.hasParameterAnnotation(MyAnnotaion.class); } @SuppressWarnings("unchecked") @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { //controller 中的参数使用下面的方法获得 HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); Object str = request.getParameter("str");//这个str应该是你的参数名 System.out.println(str); return "yeye";//返回你处理后的内容 } }
然后我们向spring注册,记得把Myannotaion的作业类型改成ElementType.PARAMETER,然后我还去掉了全部参数
@Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Autowired MyArgumentResolve myArgumentResolve; @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(myArgumentResolve); } }
这个注册类你还可以做很多功能,比如添加转换器Convert之类的。
最后一个测试controller:
@RestController @Api(description = "测试专用接口") @RequestMapping("/test") public class Test { @GetMapping("/test") @ApiOperation("/c") @ApiImplicitParam(name="str",paramType = "query",dataType = "string") public ResultBean test2(@MyAnnotaion String str){//如果你是复制我上面的注解肯定报错了,因为没有指定默认值也没有给值 System.out.println(str); return new ResultBean(200,res);//ResultBean是我自己写的一个返回方法体 } }
通过swagger我进行了请求,然后就把我输入的bb改成了yeye,测试通过!