大家好,我是公众号:【java小杰要加油】,最近在项目中,发现了很多地方都用到了自定义注解,
根据自定义的注解,再去做一些个性化的操作,非常方便,今天来分享给大家
首先,让我们来声明一个注解
// 注解可以作用在哪里 @Target({ElementType.TYPE}) // 该注解的生命周期 @Retention(RetentionPolicy.RUNTIME) // 指示默认情况下,带有类型的注释将由javadoc *和类似工具来记录 @Documented // 可以继承父类注解 @Inherited // bean @Component public @interface DIYClassAnnotation { DIYEnum diyEnum(); // 年龄默认24 岁 int age() default 24; }
可以注意到,我们声明的这个注解,他自己又带着很多元注解,我们依此来解释下,对应可取的值也如下
我们一般写注解的时候。就是用图中上面那几个加粗颜色的属性和值
- 其实使用这个自定义注解,千言万语就一句话
- 先声明一个自定义的注解
- 通过反射等方式取出这个注解,再根据这个注解中自己设定的值去做一些定制化的操作
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Component public @interface DIYClassAnnotation { // 自定义枚举类型 DIYEnum diyEnum(); // 年龄默认24 岁 int age() default 24; }
看一下这个枚举类型
public enum DIYEnum { xiaoJie("小杰","打代码"), TEACHER("老师","教书"), CHEF("厨师","做饭"); private String name; private String worker; DIYEnum(String name,String worker) { this.name = name; this.worker = worker; } public String getWorker() { return worker; } public void setWorker(String worker) { this.worker = worker; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
// 注解到什么地方 属性 上 @Target({ElementType.FIELD}) // 该注解的生命周期 @Retention(RetentionPolicy.RUNTIME) // 指示默认情况下,带有类型的注释将由javadoc *和类似工具来记录 @Documented // 可以继承父类注解 @Inherited // bean @Component public @interface DIYFieldAnnotation { // 性别 String sex(); }
// 注解到什么地方 方法 上 @Target({ElementType.METHOD}) // 该注解的生命周期 @Retention(RetentionPolicy.RUNTIME) // 指示默认情况下,带有类型的注释将由javadoc *和类似工具来记录 @Documented // 可以继承父类注解 @Inherited // bean @Component public @interface DIYMethodAnnotation { // 是否校验 int verification(); // 接口名称 String interfaceName(); }
我们自定义注解定义完了,下面要开始真正使用啦
// 抽象父类 public abstract class Person { public abstract void hobby(); }
@DIYClassAnnotation(diyEnum = DIYEnum.xiaoJie,age=23 ) public class Student extends Person { @DIYFieldAnnotation(sex = "男") private String sex; @Override public void hobby() { System.out.println(DIYEnum.xiaoJie.getWorker()); } }
@DIYClassAnnotation(diyEnum = DIYEnum.TEACHER,age=46 ) public class Teacher extends Person { @DIYFieldAnnotation(sex = "女") private String sex; @Override public void hobby() { System.out.println(DIYEnum.TEACHER.getWorker()); } }
@DIYClassAnnotation(diyEnum = DIYEnum.CHEF,age=50 ) public class Chef extends Person { @DIYFieldAnnotation(sex = "男") private String sex; @Override public void hobby() { System.out.println(DIYEnum.CHEF.getWorker()); } }
再来一个注解工具类
public class DIYAnnotationUtils { public static Person getPerson(Person ...persons){ for (Person person:persons) { // 判断这个类是否有这个注解 if (person.getClass().isAnnotationPresent(DIYClassAnnotation.class)){ // 得到这个自定义的注解 DIYClassAnnotation workerAnnotation = person.getClass().getAnnotation(DIYClassAnnotation.class); // 判断这个自定义注解注解的值是否是我们想要的 if (DIYEnum.xiaoJie.getName().equals(workerAnnotation.diyEnum().getName())){ // 反射得到这个对象的属性 Field[] fields = person.getClass().getDeclaredFields(); for (Field field:fields) { // 如果这个字段有这个注解 if (field.isAnnotationPresent(DIYFieldAnnotation.class)){ // 打印出这个属性上有这个注解的值 DIYFieldAnnotation annotation = field.getAnnotation(DIYFieldAnnotation.class); System.out.println(annotation.sex()); } } return person; } } } return null; } }
最主要的就是这个工具类(用到反射),其中根据传进来的对象判断符合不符合我们的要求
(注解时的名字是不是小杰),如果符合的话,把注解在属性上的注解拿出来
public class Test { public static void main(String[] args) { Student student =new Student(); Chef chef = new Chef() ; Teacher teacher = new Teacher(); Person person = DIYAnnotationUtils.getPerson(student, chef, teacher); if (person != null){ person.hobby(); } } }
输出结果是
男 打代码
下面来看下controller
@RestController public class Controller { // 此方法需要校验 @DIYMethodAnnotation(verification = 1,interfaceName = "学生爱好接口") @RequestMapping("/verification") public String verificationMethod(String id){ new Student().hobby(); return "校验"; } // 此方法不需要校验 @DIYMethodAnnotation(verification = 0,interfaceName = "老师爱好接口") @RequestMapping("/noVerification") public String noVerificationMethod(String id){ new Teacher().hobby(); return "不校验"; } // 此方法没有注解 @RequestMapping("/noAnnotation") public String noAnnotationMethod(String id){ new Chef().hobby(); return "无注解"; } }
再看下切面类 本文注重讲解注解,这个切面类还有很多完善的地方不过不在本文范围内
@Component @Aspect public class LogAspect { // 注解的位置 @Pointcut("@annotation(com.example.demo.annotation.DIYMethodAnnotation)") public void diyPointCut(){}; @Around("diyPointCut()") public Object diyAround(ProceedingJoinPoint joinPoint){ //获得被增强的方法相关信息 MethodSignature signature = (MethodSignature)joinPoint.getSignature(); // 获得这个方法 Method method = signature.getMethod(); // 获得这个方法上面的注解 DIYMethodAnnotation diyMethodAnnotation = method.getAnnotation(DIYMethodAnnotation.class); // 根据注解自定义的一些属性去做自定义的操作 if (diyMethodAnnotation.verification() == 1){ System.out.println("当前校验的是:"+diyMethodAnnotation.interfaceName()); System.out.println("方法名称是:"+method.getName()); System.out.println("传递参数是:"+JSON.toJSONString(joinPoint.getArgs())); } System.out.println("aop 拦截器里 verification:"+diyMethodAnnotation.verification() ); try { return joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); return null; } } }
我们将项目跑起来,分别访问两个打了注解的接口
http://localhost:8081/verification?id=1 当前校验的是:学生爱好接口 方法名称是:verificationMethod 传递参数是:["1"] aop 拦截器里 verification:1 打代码
http://localhost:8081/noVerification?id=1 aop 拦截器里 verification:0 做饭
http://localhost:8081/noAnnotation?id=1 做饭
由输出结果可以得出一个结论,
综上所述,我们在日常开发中,如果对某个类/字段/方法有什么特殊的要求的话,可以使用自定义注解,再通过反射获取到此注解,再根据这个注解中自定义的值在进行我们自定义的操作
原文链接:https://mp.weixin.qq.com/s/IOOmPzmMsdtqqICkkRgEgQ
再贴一张最近火爆全网同时也对我触动很大的话
欢迎大家关注,我是【java小杰要加油】,有什么想说的想看的欢迎评论区里留言,我们下期见。