Java自定义注解

简介


Java自定义注解是通过运行时靠反射获取注解。

常用于:登陆、权限拦截、日志处理,例如我们要获取某个方法的调用日志,可以通过AOP(动态代理机制)给方法添加切面,通过反射来获取方法包含的注解,如果包含日志注解,就进行日志记录。

注解相当于是一种嵌入在程序中的元数据,可以使用注解解析工具或编译器对其进行解析,也可以指定注解在编译期或运行期有效。


创建自定义注解

创建自定义注解与编写接口很相似,除了它的接口关键字前有个@符号,我们可以在注解中定义方法

package com.tao.annotations;
 
import java.lang.annotation.*;
 
@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInfo{
    String author() default "Pankaj";
    String date();
    int revision() default 1;
    String comments();
}
  • 注解方法,没有参数
  • 可设定默认值
  • 返回类型仅限于原始类型:字符串、枚举、注解,或以上构成的数组
  • 可包含四种【元注解】与之绑定
  • Documented – 表示使用该注解的元素应被javadoc或类似工具文档化
  • **Target --**表示支持注解的元素种类:可选【TYPE, METHOD, CONSTRUCTOR, FIELD】,不写则全部支持
  • **Inherited --**注解类型会被自动继承
  • **Retention --**接收RetentionPolicy参数,可选【SOURCE, CLASS, RUNTIME】,表示注解类型保留时间的长短

Java内置注解

Java提供3种内置注解 : @Override、@Deprecated、@SuppressWarnings

  • @Override : 表示正在覆盖SuperType的方法,该方法来自父类或接口
  • @Deprecated :表示该方法已被弃用,应在javadoc中提供信息,说明弃用原因以及替代方法
  • @SuppressWarnings:这个注解仅仅是告知编译器,忽略它们产生的特殊警告
    示例如下:
package com.tao.annotations;
 
import java.io.FileNotFoundException;
import java.util.*;
 
public class AnnotationExample {
 
    public static void main(String[] args) {
    }
 
    @Override
    @MethodInfo(author = "Pankaj", comments = "Main method", date = "Nov 17 2012", revision = 1)
    public String toString() {
        return "Overriden toString method";
    }
 
    @Deprecated
    @MethodInfo(comments = "deprecated method", date = "Nov 17 2012")
    public static void oldMethod() {
        System.out.println("old method, don't use it.");
    }
 
    /**
     * 在泛型中使用原始类型,会产生警告,但通过@SuppressWarnings可以抑制警告
     */
    @SuppressWarnings({ "unchecked", "deprecation" })
    public static void genericsTest() throws FileNotFoundException {
        List l = new ArrayList();
        l.add("abc");
        oldMethod();
    }
 
}

Java注解解析

我们将使用Java反射机制从一个类中解析注解,请记住,注解保持性策略应该是RUNTIME,否则它的信息在运行期无效,我们也不能从中获取任何数据。

package com.tao.annotations;
 
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
 
public class AnnotationParsing {
 
    public static void main(String[] args) {
        try {
            for (Method method : AnnotationParsing.class
                    .getClassLoader()
                    .loadClass(("com.tao.annotations.AnnotationExample"))
                    .getMethods()) {
                // checks if MethodInfo annotation is present (before "链接-解释")
                if (method
                       .isAnnotationPresent(com.tao.annotations.MethodInfo.class)) {
                    try {
                        // iterates all the annotations available in the method
                        for (Annotation anno : method.getDeclaredAnnotations()) {
                            System.out.println("Annotation in Method '"
                                    + method + "' : " + anno);
                        }
                        MethodInfo methodAnno = method
                                .getAnnotation(MethodInfo.class);
                        if (methodAnno.revision() == 1) {
                            System.out.println("Method with revision no 1 = "
                                    + method);
                        }
 
                    } catch (Throwable ex) {
                        ex.printStackTrace();
                    }
                }
            }
        } catch (SecurityException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
 
}

项目实例

应用场景:验证手机号

package com.tao.annotations;

import com.tao.IsPhoneValidHandler;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

/**
 * 验证手机号格式-注解
 */
@Target({ElementType.METHOD,ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {IsPhoneValidHandler.class})
public @interface Phone {

    String message() default "手机号格式不正确";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
package com.tao.handler;

import com.tao.annotations.Phone;
import org.springframework.util.StringUtils;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Pattern;

/**
 * 验证手机号格式-校验规则
 */
public class IsPhoneValidHandler implements ConstraintValidator<Phone,String> {

    @Override
    public void initialize(Phone constraintAnnotation) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {

        // 不验证空值
        if (StringUtils.isEmpty(value)) {
            return true;
        }

        // 参数不是空,对 手机号格式进行校验
        return isPhone(value);

    }

    /** 验证是否为手机号
     */
    public static boolean isPhone (String phone){
        String pattern  = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$";
        if (Pattern.matches(pattern, phone)) {
            return true;
        }else{
            return false;
        }
    }
}
案例解析:

1、附加在自定义注解上的三个“元注解”:

  • 目标:方法返回值、字段、注解、构造器、方法参数
  • 持久类型:运行时
  • 生成Javadoc文档
  • 附加校验规则:@Constraint(validatedBy = {IsPhoneValidHandler.class}),使用自定义的校验规则,进行参数校验,校验规则类需要继承ConstraintValidator接口,实现initialize和isValid方法