很多人都认为别人的东西总好过自己的东西,这或许是想要拥有所有东西的理由。 —— satey 《网络》
由一个小例子引出今天的课题:
校验用户信息,后端对接收的对象的信息例如用户名和密码做一个指定的校验。
代码如下:
User类
指定用户名和密码设定要求
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
/**
* 用户名 不能为空 长度不能大于6
*/
private String username;
/**
* 密码 不能为空 长度不能低于5 大于13
*/
private String password;
}
然后我们编写校验逻辑
/**
* 模拟接收(username)用户名 (passwoord)密码 验证用户信息时候符合要求
*/
public static boolean saveUser(User user) {
//User为空返回false
if (user == null) {
System.out.println("失败:不可为空");
return false;
}
String username = user.getUsername();
String password = user.getPassword();
if (username == null || "".equals(username)) {
System.out.println("{用户名可空校验}失败:不可为空");
return false;
} else {
System.out.println("{用户名可空校验}通过!!!");
if (username.length() > 6) {
System.out.println("{用户名长度校验}失败:长度不能超过6");
return false;
} else {
System.out.println("{用户名长度校验}通过!!!");
}
}
if (password == null || "".equals(password)) {
System.out.println("{密码可空校验}失败:不可为空");
return false;
} else {
System.out.println("{密码可空校验}通过");
if (password.length() < 5 || password.length() > 13) {
System.out.println("{密码长度校验}失败:长度不能低于5,不能超过13");
return false;
} else {
System.out.println("{用户名长度校验}通过!!!");
}
}
return true;
}
测试类调用
public static void main(String[] args) {
System.out.println(saveUser(new User("MRyan", "123456")));
System.out.println(saveUser(new User("", "123456")));
System.out.println(saveUser(new User("MRyan", "1234566666666666")));
System.out.println(saveUser(new User("MRyan", "12")));
}
输出log查看结果
{用户名可空校验}通过!!!
{用户名长度校验}通过!!!
{密码可空校验}通过
{用户名长度校验}通过!!!
true
{用户名可空校验}失败:不可为空
false
{用户名可空校验}通过!!!
{用户名长度校验}通过!!!
{密码可空校验}通过
{密码长度校验}失败:长度不能低于5,不能超过13
false
{用户名可空校验}通过!!!
{用户名长度校验}通过!!!
{密码可空校验}通过
{密码长度校验}失败:长度不能低于5,不能超过13
false
Process finished with exit code 0
实现了这个需求,但是我们会发现,校验融入了业务逻辑中,如果想要修改校验需求那就需要改动业务代码,这样做显然不是我们想要的。
我们可以用Java注解的方式来完成校验。
什么是Java注解
1.注解的定义:
Java文件叫做Annotation,用@interface表示。
2.元注解:
@interface上面按需要注解上一些东西,包括@Retention、@Target、@Document、@Inherited四种。
3.注解的保留策略:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
@Retention(RetentionPolicy.SOURCE) // 注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
4.注解的作用目标:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
@Target(ElementType.TYPE) // 接口、类、枚举、注解
@Target(ElementType.FIELD) // 字段、枚举的常量
@Target(ElementType.METHOD) // 方法
@Target(ElementType.PARAMETER) // 方法参数
@Target(ElementType.CONSTRUCTOR) // 构造函数
@Target(ElementType.LOCAL_VARIABLE) // 局部变量
@Target(ElementType.ANNOTATION_TYPE) // 注解
@Target(ElementType.PACKAGE) // 包
5.注解包含在javadoc中:
@Documented
6.注解可以被继承:
@Inherited
了解完基础知识我们就可以来实践了
实践
将上面的代码进行修改
添加一个UserValidate注解类
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface UserValidate {
/**
* 用户名长度最大值
*/
public int username_maxLength() default 10;
/**
* 密码长度最小值
*/
public int password_minLength() default 2;
/**
* 密码长度最大值
*/
public int password_maxLength() default 20;
/**
* 是否支持为空
*/
public boolean isNotNull() default false;
}
User对象类,在变量上方添加注解
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
/**
* 用户名 不能为空 长度不能大于6
*/
@UserValidate(isNotNull = true, username_maxLength = 6)
private String username;
/**
* 密码 不能为空 长度不能低于5 大于13
*/
@UserValidate(isNotNull = true, password_minLength = 5, password_maxLength = 13)
private String password;
}
利用反射获取到注解修饰的属性,并做相应的校验逻辑
public class UserCheck {
public static boolean check(User user) {
if (user == null) {
System.out.println("失败:不可为空");
return false;
}
// 获取User类的所有属性(如果使用getFields,就无法获取到private的属性)
Field[] declaredFields = User.class.getDeclaredFields();
for (Field declaredField : declaredFields) {
UserValidate userValidate = declaredField.getAnnotation(UserValidate.class);
if (declaredField.getName().equals("username")) {
if (user.getUsername() == null || "".equals(user.getUsername())) {
if (userValidate.isNotNull()) {
System.out.println("{用户名可空校验}失败:不可为空");
return false;
} else {
System.out.println("{用户名可空校验}通过!!!");
}
}
if (user.getUsername().length() > userValidate.username_maxLength()) {
System.out.println("{用户名长度校验}失败");
return false;
} else {
System.out.println("{用户名长度校验}通过!!!");
}
}
if (declaredField.getName().equals("password")) {
if (user.getPassword() == null || "".equals(user.getPassword())) {
if (userValidate.isNotNull()) {
System.out.println("{密码可空校验}失败:不可为空");
return false;
} else {
System.out.println("{密码可空校验}通过!!!");
}
}
if (user.getPassword().length() < userValidate.password_minLength() || user.getPassword().length() > userValidate.username_maxLength()) {
System.out.println("{密码长度校验}失败");
return false;
} else {
System.out.println("{密码长度校验}通过!!!");
}
}
}
return true;
}
}
最后测试类
public class Main {
public static void main(String[] args) {
System.out.println(UserCheck.check(new User("MRyan", "123456")));
System.out.println(UserCheck.check(new User("", "123456")));
System.out.println(UserCheck.check(new User("MRyan", "1234566666666666")));
System.out.println(UserCheck.check(new User("MRyan", "12")));
}
}
输出结果
{用户名长度校验}通过!!!
{密码长度校验}通过!!!
true
{用户名可空校验}失败:不可为空
false
{用户名长度校验}通过!!!
{密码长度校验}失败
false
{用户名长度校验}通过!!!
{密码长度校验}失败
false
Process finished with exit code 0
我们会发现使用注解确实会更方便,利用注解也可以实现一些其它的功能,Spring中就包含了大量的注解。