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方法