前一篇关于redis缓存的博客让我对注解起了兴趣,原本在培训班的时候觉得AOP的课好无聊,而且一直以来都没怎么用AOP,觉得一点用都没有。当然现在明白过去的无知还不晚。而且现在感觉自己会写注解之后逼格高了一个档次。
首先要讲的内容来只上一篇Redis缓存数组管理。
AOP切面编程首先要引入依赖
<!-- 切面编程 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
然后创建一个切面类aspect。这个类大致包含如下内容:数个切入点和数个通知方法,当然你也可以把切入点嵌入通知中。
如下
@Aspect
@Component
public class MyAspect {
/*
*定义一个切入点,指定切入的类型和位置。
*类型:
execution: 切入方法中,可以使用通配符来切入多个方法
@annotation: 切入注解中,不可以使用通配符指定切入什么注解。对方法注解和类注解有效,对参数注解无效
其他的请看别人总结的:https://blog.csdn.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.csdn.net/a18716374124/article/details/79208990
https://blog.csdn.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,测试通过!