跳至主要內容

⑧JAVA 注解

holic-x...大约 16 分钟JAVA基础

⑧JAVA 注解

学习核心

  • 注解
    • 什么是注解?作用是什么?
    • 怎么自定义注解?

学习资料

JAVA编程思想_第5版本_中文 第23章 注解open in new window

注解概念

注解概念

​ Java中,注解是以 @ 字符开始的修饰符(例如最基础的@Override

​ 包含命名或者未命名的属性(例如@Author name="oop" date="01/01/1900"

​ 如果只有一个名为value的属性,则可以省略名称(例如@SuppressWarnings("unchecked")

​ 从本质上来说,注解是一种标签,其实质上可以视为一种特殊的注释,如果没有解析它的代码,它并不比普通注释强。

解析一个注解往往有两种形式:

  • 编译期直接的扫描 - 编译器的扫描指的是编译器在对 java 代码编译字节码的过程中会检测到某个类或者方法被一些注解修饰,这时它就会对于这些注解进行某些处理。这种情况只适用于 JDK 内置的注解类。
  • 运行期的反射 - 如果要自定义注解,Java 编译器无法识别并处理这个注解,它只能根据该注解的作用范围来选择是否编译进字节码文件。如果要处理注解,必须利用反射技术,识别该注解以及它所携带的信息,然后做相应的处理

注解分类

  • 按照运行机制分
    • 源码注解:在源码中存在,编译称class文件就不存在了
    • 编译时注解:在源码和.class文件中存在(例如JDK自带的注解,在编译时起作用)
    • 运行时注解:在程序运行阶段还起作用,甚至会影响运行逻辑的注解
  • 按照来源分
    • JDK注解
    • 第三方注解
    • 自定义注解
    • 元注解(注解的注解)

注解作用

注解有许多用途:

  • 编译器信息 - 编译器可以使用注解来检测错误或抑制警告
  • 编译时和部署时的处理 - 程序可以处理注解信息以生成代码,XML 文件等
  • 运行时处理 - 可以在运行时检查某些注解并处理

​ 作为 Java 程序员,多多少少都曾经历过被各种配置文件(xml、properties)支配的恐惧。过多的配置文件会使得项目难以维护。使用注解则可以减少配置文件或代码

注解代价

  • 注解是一种侵入式编程,存在着增加程序耦合度的问题
  • 自定义注解的处理需要在运行时通过反射技术来获取属性,一来存在性能代价,二来可能存在破坏程序封装性的情况(通过反射可以访问非public成员)
  • 注解产生的问题相对而言更加难以debug定位

注解的应用范围

注解可以应用于类、字段、方法和其他程序元素的声明。

JDK8 开始,注解的应用范围进一步扩大,以下是新的应用范围:

类实例初始化表达式:

new @Interned MyObject();

类型转换:

myString = (@NonNull String) str;

实现接口的声明:

class UnmodifiableList<T> implements
    @Readonly List<@Readonly T> {}

抛出异常声明:

void monitorTemperature()
    throws @Critical TemperatureException {}

1.Java中的常见注解

基本的 Annotation:

  • @Override: 限定某个方法,是重写父类方法, 该注解只能用于方法
  • @Deprecated: 用于表示某个程序元素(类, 方法等)已过时
  • @SuppressWarnings: 抑制编译器警告
  • @SafeVarargs(JDK7 引入)
  • @FunctionalInterface(JDK8 引入)

常见第三方注解

  • Spring:@Autowire、@Service、@Repository
  • MyBatis:@InsertProvider、@UpdateProvider、@Options

@Override

​ 一般场景用于子类继承父类重写父类方法

public class OverrideDemo {
    public static void main(String[] args) {
        Son son = new Son();
        son.fly();
    }
}

class Father{
    public void fly(){
        System.out.println("父类 fly方法");
    }
}

class Son extends Father{
    @Override
    public void fly(){
        System.out.println("子类 fly方法");
    }
}

@Override

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

结合源码记忆@Override的作用

  • 只能作用于方法(看源码的@Target属性,@Target是修饰注解的注解成为元注解)
  • @Override表示显示指定重写的父类(从编译层验证),如果父类没有要重写的方法就会报错
  • 如果子类和父类满足重写规则,就算不加@Orerride仍然构成重写

@Deprecated

​ @Deprecated: 用于表示某个程序元素(类, 方法等)已过时。可以修饰方法、类、字段、包、参数等信息,一般在版本升级过渡时使用

​ 例如此处标记A类以过期,此时如果在方法中使用A则会提示如下(表示A已经过期),但是A类还是可以正常使用的

public class DeprecatedDemo {

    public static void main(String[] args) {
        A a = new A();
        a.test();
    }
}

// 使用@Deprecated标识这个类已经过期
@Deprecated
class A{
    public void test(){
        System.out.println("testing");
    }
}

image-20240521194326638

@SafeVarargs

@SafeVarargs 在 JDK7 中引入。@SafeVarargs的作用是:告诉编译器,在可变长参数中的泛型是类型安全的。可变长参数是使用数组存储的,而数组和泛型不能很好的混合使用。

简单的说,数组元素的数据类型在编译和运行时都是确定的,而泛型的数据类型只有在运行时才能确定下来。因此,当把一个泛型存储到数组中时,编译器在编译阶段无法确认数据类型是否匹配,因此会给出警告信息;即如果泛型的真实数据类型无法和参数数组的类型匹配,会导致 ClassCastException 异常。

@SafeVarargs 注解使用范围:

  • @SafeVarargs 注解可以用于构造方法。
  • @SafeVarargs 注解可以用于 staticfinal 方法。

@SafeVarargs 示例:

public class SafeVarargsAnnotationDemo {

    /**
     * 此方法实际上并不安全,不使用此注解,编译时会告警
     */
    @SafeVarargs
    static void wrongMethod(List<String>... stringLists) {
        Object[] array = stringLists;
        List<Integer> tmpList = Arrays.asList(42);
        array[0] = tmpList; // 语法错误,但是编译不告警
        String s = stringLists[0].get(0); // 运行时报 ClassCastException
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");

        List<String> list2 = new ArrayList<>();
        list.add("1");
        list.add("2");

        wrongMethod(list, list2);
    }
}

image-20240522083015672

@SuppressWarnings

@SuppressWarnings: 抑制编译器警告

类型说明
unchecked忽略没有检查的警告
rawtypes忽略没有指定泛型的警告(传参时没有指定泛型的警告错误)
unused忽略没有使用某个变量的警告错误

@SuppressWarnings 可以修饰的程序元素为,查看@Target 源码

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

@FunctionalInterface

@FunctionalInterface 在 JDK8 引入。

@FunctionalInterface用于指示被修饰的接口是函数式接口

需要注意的是,如果一个接口符合"函数式接口"定义,不加 @FunctionalInterface 也没关系;但如果编写的不是函数式接口,却使用 @FunctionInterface,那么编译器会报错。

什么是函数式接口?

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。

函数式接口的特点:

  • 接口有且只能有个一个抽象方法(抽象方法只有方法定义,没有方法体)。
  • 不能在接口中覆写 Object 类中的 public 方法(写了编译器也会报错)。
  • 允许有 default 实现方法。
public class FunctionalInterfaceAnnotationDemo {

    @FunctionalInterface
    public interface Func1<T> {
        void printMessage(T message);
    }

    /**
     * @FunctionalInterface 修饰的接口中定义两个抽象方法,编译时会报错
     * @param <T>
     */
    /*@FunctionalInterface
    public interface Func2<T> {
        void printMessage(T message);
        void printMessage2(T message);
    }*/

    public static void main(String[] args) {
        Func1 func1 = message -> System.out.println(message);
        func1.printMessage("Hello");
        func1.printMessage(100);
    }
}

2.自定义注解

元注解

JDK 的元 Annotation 用于修饰其他 Annotation,元注解种类(在看代码的时候可以关注)

  • Retention:指定注解的作用范围(生命周期: SOURCE、CLASS、RUNTIME)

  • Target:指定注解可以在哪些地方使用(指定注解可以修饰的元素类型)

  • Documented:指定该注解是否会在 javadoc 体现

  • Inherited:子类会继承父类注解(如果注解类型声明存在该元注解,则注解所修饰类的所有子类都是继承此注解)

  • Repeatable:表示注解可以重复使用

  • @Retention:RetentionPolicy

    • RetentionPolicy.SOURCE:标记的注解仅在源文件中有效,编译器会忽略
    • RetentionPolicy.CLASS:标记的注解在 class 文件中有效,JVM 会忽略
    • RetentionPolicy.RUNTIME:标记的注解在运行时有效
  • @Target:ElementType

    • ElementType.ANNOTATION_TYPE - 标记的注解可以应用于注解类型
    • ElementType.CONSTRUCTOR - 标记的注解可以应用于构造函数
    • ElementType.FIELD - 标记的注解可以应用于字段或属性
    • ElementType.LOCAL_VARIABLE - 标记的注解可以应用于局部变量
    • ElementType.METHOD - 标记的注解可以应用于方法。
    • ElementType.PACKAGE - 标记的注解可以应用于包声明
    • ElementType.PARAMETER - 标记的注解可以应用于方法的参数
    • ElementType.TYPE - 标记的注解可以应用于类的任何元素
  • @Documented:指定该注解是否会在 javadoc 体现(默认情况下注释不包括在Javadoc中)

  • @Inherited:表示注解类型可以被继承(默认情况下不是这样)

  • @Repeatable:表示注解可以重复使用(参考Spring的@Scheduled

语法要求

​ 使用 @interface 自定义注解时,自动继承了 java.lang.annotation.Annotation 接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface 用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过 default 来声明参数的默认值。

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface Description{

    String desc();

    String author();

    int age() default 18;

}

// 基于上述注解其使用规则参考如下
@Description(desc = "用户",author = "holic-x",age = 18)
class User{
    private String name;
}

​ idea中JAVADOC的生成(可以指定工程、单个文件)=》指定输出目录,生成文件

image-20240521200612478

​ 注解定义只是一种声明,更多的是需要借助反射去解析注解进而扩展代码

/**
 * 自定义注解@Description
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface Description{

    String desc();

    String author();

    int age() default 18;

}

@Description(desc = "用户类",author = "holic-x",age = 18)
class User{
    private String name;


    @Description(desc = "getName方法",author = "noob",age = 33)
    public String getName() {
        return name;
    }
}

// 定义注解解析类(通过反射机制)
class ParseDescriptionAnno{

    // 获取类上的注解
     public static void getClassAnno(){
         // 1.使用类加载器加载类
         Class clazz = User.class;
         // 2.找到类上面的注解
         boolean isExistAnno = clazz.isAnnotationPresent(Description.class);
         // 3.获取到注解示例
         if(isExistAnno){
             Description d = (Description) clazz.getAnnotation(Description.class);
             System.out.println(d.desc());
         }
     }

     // 获取方法上的注解
    public static void getMethodAnno(){
         Method[] methods = User.class.getMethods();
         for(Method method : methods){
             if(method.isAnnotationPresent(Description.class)){
                 Description d = (Description) method.getAnnotation(Description.class);
                 System.out.println(d.desc());
             }
         }
    }

    // 获取方法上的注解(另一种方式获取)
    public static void getMethodAnnoByOther(){
        Method[] methods = User.class.getMethods();
        for(Method method : methods){
            // 获取方法上的所有注解
            Annotation[] annotations = method.getDeclaredAnnotations();
            for(Annotation annotation : annotations){
                if(annotation instanceof Description){
                    Description d = (Description) annotation;
                    System.out.println(d.desc());
                }
            }
        }
    }

    public static void main(String[] args) {
        getClassAnno();
        getMethodAnno();
        getMethodAnnoByOther();
    }

}

// output
用户类
getName方法
getName方法

​ 可以通过调整元注解来验证元注解的功能,例如将@Retention(RetentionPolicy.RUNTIME)的作用域调整为SOURCE级别再运行,就会发现控制台不打印任何内容,是因为此时注解的作用域被限定为源码级别,当编译为class则该注解就没了

3.注解实战

(1)仿hibernate实现SQL解析

需求分析

用户表:用户ID、用户名、昵称、年龄、性别、所在城市、邮箱、手机号

实现:方便地对每个字段或者字段的组合条件进行检索,并打印出sql,使用方式要足够简单

基础框架内容参考如下,需要借助注解结合查询条件构建SQL语句

// 查询条件
class Filter{
    private int userId;
    private String userName;
    private String nickName;
    private int age;
    private int sex;
    private String city;
    private String email;
    private String phone;

    public void setEmail(String email) {
        this.email = email;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

public class CustomSQLAnnotationDemo {

    public static void main(String[] args) {
        Filter f1 = new Filter();
        f1.setUserId("1");// 查询id为10的用户


        Filter f2 = new Filter();
        f2.setUserName("lucy");// 模糊查询用户名为lucy的用户


        Filter f3 = new Filter();
        f1.setEmail("lucy@163.com,xiaobai@126.com");// 查询邮箱为任意一个的用户信息

        // 根据上述条件,生成相应的sql语句
        String sql1 = query(f1);
        String sql2 = query(f2);
        String sql3 = query(f3);
        System.out.println(sql1);
        System.out.println(sql2);
        System.out.println(sql3);

    }

    private static String query(Filter f1) {
        return null;
    }
}

构建思路:

​ 针对Filter(它既是一个对应着数据表的实体、又可以作为查询条件),参考常见的ORM框架,此处设定两个注解(@Table、@Column)分别将Filter与数据表的内容进行映射

​ 注解构建完成,通过反射解析Filter类对象,获取到对象上所有注解信息以及对象信息,随后再根据这些信息进行SQL封装(在构建SQL的时候可以理清思路,先获取到拼接SQL所需要的参数信息,然后再一步步结合语境优化不同场景下的SQL处理)

​ 例如一开始可能是获取到注解信息和对应对象的字段和值信息,然后拼接成一条完整的SQL语句

# 初版
select * from user where 1=1 
and userId=1 and userName=xx and email=xxx@qq.com,xxx@163.com

# V1:针对不同类型的字段相应做SQL转化处理(例如字符串需要‘’拼接)
- 程序上则可通过instanceof判断字段的不同数据类型,再进行转化处理
select * from user where 1=1 
and userId=1 and userName='xx' and email='xxx@qq.com,xxx@163.com'

# V2:再进一步优化,针对email这种类型的字段,传入String字符串需要解析为in操作
- 程序上根据fieldValue是否包括指定分隔符(例如此处为,)进行判断,如果包含则将其结构进行拆分然后依次拼接为集合检索
- 这种拆分方式逻辑上是可行,但是场景分析来看存在缺陷,不能够单一的认为存在指定分隔符就将其解析为集合检索,会存在异议。而是应该考虑更加完善的机制去约束输入输出
select * from user where 1=1 
and userId=1 and userName='xx' and email in ('lucy@163.com','xiaobai@126.com')

参考实践样例

// 自定义注解@Table

import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

// 自定义注解@Table
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String value();
}


// 自定义注解@Cloumn
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Column{
    String value();
}

// 查询条件
@Table("user")
class Filter{
    @Column("user_id")
    private int userId;
    @Column("user_name")
    private String userName;
    @Column("nick_name")
    private String nickName;
    @Column("age")
    private int age;
    @Column("sex")
    private int sex;
    @Column("city")
    private String city;
    @Column("email")
    private String email;
    @Column("phone")
    private String phone;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

public class CustomSQLAnnotationDemo {

    public static void main(String[] args) throws Exception {
        Filter f1 = new Filter();
        f1.setUserId(1);// 查询id为10的用户

        Filter f2 = new Filter();
        f2.setUserName("lucy");// 模糊查询用户名为lucy的用户

        Filter f3 = new Filter();
        f1.setEmail("lucy@163.com,xiaobai@126.com");// 查询邮箱为任意一个的用户信息

        // 根据上述条件,生成相应的sql语句
        String sql1 = query(f1);
        String sql2 = query(f2);
        String sql3 = query(f3);
        System.out.println(sql1);
        System.out.println(sql2);
        System.out.println(sql3);

    }

    /**
     * query 方法:解析注解生成sql语句
     * @param f
     * @return
     */
    private static String query(Filter f) throws Exception {
        // 定义字符串对象封装sql
        StringBuilder sb = new StringBuilder();
        String tableName = "";

        // 获取到class
         Class c = f.getClass();
         // 根据class获取到相关注解
        boolean isExistTable = c.isAnnotationPresent(Table.class);
        if(isExistTable){
            // 获取到tableName
            Table tableAnno = (Table)c.getAnnotation(Table.class);
            tableName = tableAnno.value();
        }
        // 拼接检索语句
        sb.append("select * from " + tableName).append(" where 1=1 ");

        // 获取所有字段上的注解内容
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            boolean isExistFieldAnno = field.isAnnotationPresent(Column.class);
            if(isExistFieldAnno){
                Column columnAnno = (Column)field.getAnnotation(Column.class);
                // 通过反射获取到字段值(因为方法名有规律,可以通过字段名获取到方法名)
                String fieldName = field.getName();
                String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                Method getMethod = c.getDeclaredMethod(getMethodName,null);
                // 调用方法获取到字段值
                 Object fieldValue = getMethod.invoke(f,null);
                 // 处理不同字段类型转换、以及null字段值处理(String类型为null则不拼接、Integer类型为0也不拼接)
                if(null == fieldValue || (fieldValue instanceof Integer && (Integer)fieldValue==0)){
                    continue;
                }

                if(fieldValue instanceof String){
                    if(((String) fieldValue).contains(",")){
                        // 对于String类型的列表数据(通过,进行分割),将其解析为in形式
                        sb.append("and ").append(fieldName).append(" in (");
                        String[] strs = fieldValue.toString().split(",");
                        for (String str : strs) {
                            sb.append("'" + str + "',");
                        }
                        // 删除最后一个逗号(,)
                        sb.deleteCharAt(sb.length()-1);
                        sb.append(")");
                    }else{
                        // 如果是String类型,则需要拼接‘’
                        sb.append("and " + fieldName + "='" + fieldValue + "' ");
                    }
                }else if(fieldValue instanceof Integer){
                    // 如果是Integer类型,则直接拼接
                    sb.append("and " + fieldName + "=" + fieldValue + " ");
                }
            }
        }
        return sb.toString();
    }
}

(2)自定义参数校验器

需求分析

​ 自定义参数校验器:RegexValid注解、RegexValidUtil注解解析器(注解处理器)、RegexValidDemo使用自定义注解

  • RegexValid注解:指定接收value、policy参数(其中Policy是枚举类型)
// 定义自定义注解RegexValid
@Documented
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@interface RegexValid{

    enum Policy {
        // @formatter:off
        EMPTY(null),
        DATE("^(?:(?!0000)[0-9]{4}([-/.]?)(?:(?:0?[1-9]|1[0-2])\\1(?:0?[1-9]|1[0-9]|2[0-8])|(?:0?[13-9]|1[0-2])\\1"
                + "(?:29|30)|(?:0?[13578]|1[02])\\1(?:31))|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|"
                + "(?:0[48]|[2468][048]|[13579][26])00)([-/.]?)0?2\\2(?:29))$"),
        MAIL("^[A-Za-z0-9](([_\\.\\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\\.\\-]?[a-zA-Z0-9]+)*)\\.([A-Za-z]{2,})$");
        // @formatter:on

        private String policy;

        Policy(String policy) {
            this.policy = policy;
        }

        public String getPolicy() {
            return policy;
        }
    }

    String value() default "";
    Policy policy() default Policy.EMPTY;

}
  • RegexValidUtil注解解析器(注解处理器):解析对应作用于范围的注解信息,随后进行校验
    • 例如此处注解是作用于字段(Field),用于校验参数是否符合约定
// 定义注解解析器(解析使用了该注解的内容)
class RegexValidUtil{
    public static boolean check(Object obj) throws Exception {
        boolean result = true;
        StringBuilder sb = new StringBuilder();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 判断成员是否被 @RegexValid 注解所修饰
            if (field.isAnnotationPresent(RegexValid.class)) {
                RegexValid valid = field.getAnnotation(RegexValid.class);

                // 如果 value 为空字符串,说明没有注入自定义正则表达式,改用 policy 属性
                String value = valid.value();
                if ("".equals(value)) {
                    RegexValid.Policy policy = valid.policy();
                    value = policy.getPolicy();
                }

                // 通过设置 setAccessible(true) 来访问私有成员
                field.setAccessible(true);
                Object fieldObj = null;
                try {
                    fieldObj = field.get(obj);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                if (fieldObj == null) {
                    sb.append("\n")
                            .append(String.format("%s 类中的 %s 字段不能为空!", obj.getClass().getName(), field.getName()));
                    result = false;
                } else {
                    if (fieldObj instanceof String) {
                        String text = (String) fieldObj;
                        Pattern p = Pattern.compile(value);
                        Matcher m = p.matcher(text);
                        result = m.matches();
                        if (!result) {
                            sb.append("\n").append(String.format("%s 不是合法的 %s !", text, field.getName()));
                        }
                    } else {
                        sb.append("\n").append(
                                String.format("%s 类中的 %s 字段不是字符串类型,不能使用此注解校验!", obj.getClass().getName(), field.getName()));
                        result = false;
                    }
                }
            }
        }

        if (sb.length() > 0) {
            throw new Exception(sb.toString());
        }
        return result;
    }
}
  • 自定义Person类,使用@RegexValid注解进行校验
// 定义类使用自定义注解校验参数
class Person{

    private String name;
    @RegexValid(policy = RegexValid.Policy.DATE)
    private String date;
    @RegexValid(policy = RegexValid.Policy.MAIL)
    private String mail;
    @RegexValid("^((\\+)?86\\s*)?((13[0-9])|(15([0-3]|[5-9]))|(18[0,2,5-9]))\\d{8}$")
    private String phone;

    public Person(String name, String date, String mail, String phone) {
        this.name = name;
        this.date = date;
        this.mail = mail;
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "User{" + "name='" + name + '\'' + ", date='" + date + '\'' + ", mail='" + mail + '\'' + ", phone='"
                + phone + '\'' + '}';
    }

}
  • main方法测试(验证自定义注解解析器(处理器))
public class RegexValidDemo {

    // 方法上使用自定义注解RegexValid对日期进行校验
     static void printDate(@RegexValid(policy = RegexValid.Policy.DATE) String date){
         System.out.println(date);
     }

    public static void main(String[] args) throws Exception {
        Person p1 = new Person("Tom", "1990-01-31", "xxx@163.com", "18612341234");
        Person p2 = new Person("Jack", "2019-02-29", "sadhgs", "183xxxxxxxx");
        if (RegexValidUtil.check(p1)) {
            System.out.println(p1 + "正则校验通过");
        }
        if (RegexValidUtil.check(p2)) {
            System.out.println(p2 + "正则校验通过");
        }
    }
}
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3