不曾想,每个人都是这样经历过来的,不知你是否还记得在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
这就是我们在解析器里面输出的内容和返回的结果。