一、自定义注解怎么回事?
代码如下(示例):
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
String Fid();
}
@Target 注解声明注解可使用的范围,如果需要声明多个范围,可以使用{ }括起来
@Retention 中文是保留的意思,和字面意思差不多,就是在程序执行的过程中,保留的位置RetentionPolicy保留策略,大家在使用的时候会发现有三个值,CLASS,SOURCE,RUNTIME,具体是在编译时候保留存在,在CLASS文件中存在,在JVM运行时还存在。这个一般都会使用RUNTIME,你也不想让你的注解夭折吧
@Documented 这注解存在感很低,会生成java doc文档注释,不知道这个具体用法…
OK 这样你就自定义了一个注解,但是这样有啥用,随便去标记一下嘛,那岂不是很傻叉,还有里面的这个String Fid(),写着干嘛?
二、使用这个注解
1.作用情况
代码如下(示例):
@ResponseBody
@GetMapping(value ="/test")
@MyAnnotation(Fid = “test”)
public User getUserData(@RequestBody User user){
System.out.println(user.getUname());
return userService.getUserById(user.getId());
}
这里我在想,我自己写了个注解(还不错哦),但是说实话,这个注解在给我提示说(有那个大病),你要说他有什么作用,就是刚刚那个Fid 字段告诉我,这是个test,其实这个自定义的字段不写也是没有什么问题的。
如果就这样去使用了这个注解,那确实有点浪费了,要具体了解他如何使用,这时候就要了解到我们的Aop了,既然这个注解在之前说到是作用在方法上,那我们在方法上加了这个注解之后不就是可以通过Aop来对这个操作了。
//切点
@Pointcut("execution(* com.lwy.madmod.learn.service.UserServiceImp.*(Integer))")
public void pointCut(){}
@Pointcut("@annotation(MyAnnotation)")
public void anpCut(){}
关于execution表达式这个在这里大家应该不陌生了,这里提一下**"@annotation(MyAnnotation)",他是我们通过切入该注解作用方法的,在 @annotation() 里面我们写的也是该注解的位置,只不过企业里面一般是把自定义的注解和对应的切面**写在同一个包的文件下面,可以直接找到。
2.搭配切面具体使用
代码如下(示例):maven依赖,springboot下只需要下面这个就行,如果是spring的话,可以看spring下的相关依赖,切记。
第一个依赖只是提供了@Before、@Aspect等注解使用,但是不会生成代理织入,这样是没有效果的。
<dependency>
<groupId>repository.org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
使用@Aspect注解定义之后,通过@Component加入道IOC容器里面,SpringBoot下会自动扫描配置,不用担心
@Aspect
@Component
public class AspectDemo {
//切点
@Pointcut("execution(* com.lwy.madmod.learn.service.UserServiceImp.*(Integer))")
public void pointCut(){}
@Pointcut("@annotation(MyAnnotation)")
public void anpCut(){}
@Before("pointCut()")
public void toBefore(){
System.out.println("This is a before cut");
}
@Around("anpCut()")
public Object toAround(ProceedingJoinPoint joinPoint) throws Exception {
Object[] args = joinPoint.getArgs();
Class<?> aClass = args[0].getClass();
Field ids = aClass.getDeclaredField("id");
ids.setAccessible(true);
System.out.println(ids.get(args[0]));
User usr = new User(2,"2","123");
args[0] = usr;
Object proceed = null;
try {
proceed = joinPoint.proceed(args);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return proceed;
}
附上一张从大佬哪里搞过来的图,哈哈哈具体可以看大佬
具体的处理,我这里讲一下 @Around 这个很厉害的东西
ProceedingJoinPoint joinPoint是得到连接点的对象,我们通过@Around一般是在方法放行前做一定的校验
这其中可以通过反射得到具体的对象的类型,对刚刚的String Fid 那个字段还有印象嘛,我们可以通过规定方法上的Fid值去做对应的校验,每个Fid对应能够告诉我们这个方法对应什么,不然出现了多个同一注解,不好区分。继续说一下@Around,如果我们不执行joinPoint.proceed(),这个方法,我们的Aop是不会放行的,这个时候程序也还没有走完,既然他还可以控制放行,我们是不是还有想法?那就是他也可以改变我们的传入进来的值,上面一个是我使用new 对象的方式去改值的。
@Around("anpCut()")
public Object toAround(ProceedingJoinPoint joinPoint) throws Exception {
Object[] args = joinPoint.getArgs();
Class<?> aClass = args[0].getClass();
Field ids = aClass.getDeclaredField("id");
ids.setAccessible(true);
ids.set(args[0],2);
Object proceed = null;
try {
proceed = joinPoint.proceed(args);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return proceed;
}
建议使用第二种,Tips:就是我们在joinPoint.proceed(args),这个args是一个Object集合,对应里面的Object一定要和你前端接口传入的是一样的类型。
@ResponseBody
@GetMapping(value ="/test")
@MyAnnotation()
public User getUserData(@RequestBody User user){
System.out.println(user.getUname());
return userService.getUserById(user.getId());
}
不然会报转化异常的。
总结
要把反射注解和框架里面的知识联系起来使用。