在开始讲如何实现自定义注解之前,我们先唠唠嗑,其实我们刚开始学java的时候,如何创建一个对象,这本身就是一个难题,有的人或许会直接说直接new一个,或者通过反射机制直接创建一个类的实例对象进行对对象实例的操作。
不曾想,每个人都是这样经历过来的,不知你是否还记得在spring的xml配置文件里如何配置对象的场景?或许依然记忆犹新,或许早已抛开在脑后,等等吧。后面spring出现了通过注解的方式去注入一个实例,这或许解放了很多我这样的码农双手,坏笑。
通过上面的故事,我们一步一步走进了注解的视野里面,既然熟知注解的应用,接下来我们还是看下如何实现自己的注解吧。
在看示例程序之前,我们看下是如何自定义一个注解的,我们都知道定义一个类使用class标识符进行修饰,定义一个接口使用interface标识进行修饰,那么同样使用@interface标识进行修饰的就是自定义注解。
public @interface ValidatorString {
}
好了,我们既然知道了如何自定义一个自己的注解,接下来我们看下我们的示例程序吧。
package com.wpw.springboot;
import java.lang.annotation.*;
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidatorString {
/**
* 判断是否为空
* @return boolean值
*/
boolean isEmpty();
}
上面我们实现了一个对字符串进行判空操作的自定义注解,在看下面的程序之前,我们还是看下上面的各个元注解的含义好了。
什么叫做元注解呢?其实这个词语可以不必深究,因为就算你懂了,对你来说也没什么意思,元注解就是修饰注解的注解,是不是有点绕?坏笑。
@Documented:这个注解的含义就是可以包含在javadoc中的。
@Inherited:这个注解的含义就是允许子类继承父类的注解的。
@Target:这个注解的含义就是说这个注解的作用范围,标识这个注解可以用在什么地方。比如说我们可以作用在类上,接口上,字段上,方法上等等等。
@Retention:这个注解的含义就是说注解的保存策略,比如说注解的生效范围,有的可能在编译器生效,有的可能只存在源码级别生效,但是大部分我们都是设置为运行期生效,毕竟我们就是在程序运行期间进行使用的嘛,不知道这段话,你懂了没有。
好了,下面我们需要定义一个类来验证我们的注解是否可行。
package com.wpw.springboot;
public class User {
@ValidatorString(isEmpty = false)
private String username;
@ValidatorMax(max = 120)
@ValidatorMin(min = 1)
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
在上面的示例程序中,我们使用了我们自己的自定义注解对字符串username进行了限定,同样我们使用了下面的注解进行对age属性进行了最大最小的限定。
package com.wpw.springboot;
import java.lang.annotation.*;
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidatorMax {
/**
* 校验最大值
* @return 最大值
*/
int max();
}
package com.wpw.springboot;
import java.lang.annotation.*;
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidatorMin {
/**
* @return 返回最小值
*/
int min();
}
其实上面的是可以放到一个自定义注解里面的。由于文章都有注释,详细解析就不再说明了,你应该都会明白的。
接下来我们就是要写一个自定义解析器,对我们的自定义注解的内容进行解析。
package com.wpw.springboot;
import java.lang.reflect.Field;
import java.util.Objects;
public class ValidatorResolver {
public static <T> boolean validator(T t) throws IllegalAccessException {
Field[] fields = t.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
ValidatorString validatorString = field.getAnnotation(ValidatorString.class);
ValidatorMax validatorMax = field.getAnnotation(ValidatorMax.class);
ValidatorMin validatorMin = field.getAnnotation(ValidatorMin.class);
//判断字符是否为空
if (Objects.nonNull(validatorString) && field.getType().equals(String.class)) {
if (Objects.isNull(field.get(t)) || ((String) field.get(t)).length() <= 0) {
System.out.println(field.getName() + "不可以为空");
return false;
}
}
if ((Objects.nonNull(validatorMax) || Objects.nonNull(validatorMin)) && field.getType().equals(Integer.class) &&
Objects.isNull(field.get(t))) {
System.out.println(field.getName() + "字段不可以为空");
return false;
}
//判断该字段是否为Integer或者int类型
if (field.getType().equals(Integer.class) || field.getType() == int.class) {
if (Objects.nonNull(validatorMax) && ((int) field.get(t)) > validatorMax.max()) {
System.out.println(field.getName() + "字段大于了最大值");
return false;
}
if (Objects.nonNull(validatorMin) && ((int) field.get(t) < validatorMin.min())) {
System.out.println(field.getName() + "字段小于了最小值");
return false;
}
}
}
return true;
}
}
好了,上面的自定义注解的解析器就写完了,下面我们开始做个测试了,看下我们的自定义注解是否生效。
public static void main(String[] args) throws IllegalAccessException {
User user=new User();
user.setUsername("backCoder");
user.setAge(90);
System.out.println(ValidatorResolver.validator(user));//true
}
上面我们都是对对象user进行了合理的设置,接下来我们故意写个错误的看下是否可以达到我们想要的效果。
public static void main(String[] args) throws IllegalAccessException {
User user=new User();
user.setAge(90);
System.out.println(ValidatorResolver.validator(user));
}
当我们不设置username值时,我们看下控制台的输出语句。
username不可以为空
false
这就是我们在解析器里面输出的内容和返回的结果。
好了,今天我想要分享的内容就到这里结束了,这就是我们这次分享的如何实现自己的注解的实现和验证了,喜欢文章的欢迎关注后端Coder,点击在看,分享,转发一下。