像平时用到的 springboot、mybatis 等框架提供了许多的注解,免去了许多配置文件的繁琐工作,大大简便了开发,Java 提供了自定义注解的功能,这里就先展示简单的例子。
1、注解的作用
注解可以看作是一种特殊的标记,可以用在方法、类、参数和包上,程序在编译或者运行时可以检测到这些标记而进行一些特殊的处理,例如标注在方法上可以实现接口权限的校验。
使用场景:自定义注解+拦截器或者 AOP。
声明方式:通过关键字 @interface 声明为注解,例子如下:
@Target({ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidateUnionRepeat {
/**
* 异常信息
*/
String msg() default "";
/**
* 需验证属性数组
*/
String[] validateFields() default {};
/**
* 用于判断注解是否生效(默认true生效)
*/
boolean effective() default true;
}
2、注解的元素类型
主要有@Target,@Retention,@Document,@Inherited 用来修饰注解。
2.1、@Target
表明该注解可以应用的java元素类型。
public enum ElementType {
//类,接口,枚举类
TYPE,
//成员变量,枚举常量
FIELD,
//方法
METHOD,
//形式参数
PARAMETER,
//构造方法
CONSTRUCTOR,
//局部变量
LOCAL_VARIABLE,
//注解
ANNOTATION_TYPE,
//包
PACKAGE,
//类型参数
TYPE_PARAMETER,
//类型使用
TYPE_USE
}
2.2、@Retention
表明该注解的生命周期。
public enum RetentionPolicy {
//源文件保留
SOURCE,
//编译期保留,默认值
CLASS,
//运行期保留,可通过反射去获取注解信息
RUNTIME
}
取值 描述 作用范围 使用场景
取值 | 描述 | 作用范围 | 使用场景 |
---|---|---|---|
RetentionPolicy.SOURCE | 表示注解只保留在源文件,当java文件编译成class文件,就会消失 | 源文件 | 只是做一些检查性的操作,,比如 @Override 和 @SuppressWarnings |
RetentionPolicy.CLASS | 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期 | class文件(默认) | 要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife) |
RetentionPolicy.RUNTIME | 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在 | 运行时也存在 | 需要在运行时去动态获取注解信息 |
2.3 @Documented
表明该注解标记的元素可以被Javadoc 或类似的工具文档化。
2.4、@Inherited
表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解
举例
/**
* 多字段联合验证(加于类上)
*
*/
@Target({ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidateUnionRepeat {
/**
* 异常信息
*/
String msg() default "";
/**
* 需验证属性数组
*/
String[] validateFields() default {};
/**
* 用于判断注解是否生效(默认true生效)
*/
boolean effective() default true;
}
@RequestMapping(value = "/add", method = RequestMethod.POST)
public RespMsg<?> add(@RequestBody @Valid MonDeptConfigForm monDeptConfigForm) {
CheckOnlyUtil.checkOnlyFuncForInsert(monDeptConfigForm, monDeptConfigService, MonDeptConfigDto.class, null);
try {
return RespMsg.success();
} catch (Exception e) {
throw new CdtBaseException(e.getMessage(), e);
}
}
private static void validateClass(Class<?> beanClass, Object formBean,
Class dtoClass, IBaseService baseService, String[] idFields, Object[] ids) throws Exception {
//查询条件Dto对象
Object dtoBean = null;
//查询条件form对象
Object formBeanParam = null;
boolean result = false;
String repeatMsg = "";
long num = 0;
boolean isNull = false;
//判断是否关联属性重复限制
if (beanClass.isAnnotationPresent(ValidateUnionRepeat.class)) {
ValidateUnionRepeat unionRepeat = beanClass.getAnnotation(ValidateUnionRepeat.class);
if (!unionRepeat.effective()) {
return;
}
String[] validateFields = unionRepeat.validateFields();
if (ArrayUtils.isEmpty(validateFields)) {
log.warn("警告:验证字段为空,无法校验!");
return;
}
formBeanParam = beanClass.newInstance();
//循环获取需要查重的属性
for (String fieldName : validateFields) {
Field field = null;
try {
field = beanClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
log.warn("警告:formBean验证字段【{}】不存在!", fieldName);
throw new IllegalArgumentException("警告:formBean验证字段【" + fieldName + "】不存在!");
}
try {
dtoClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
log.warn("警告:dtoBean验证字段【{}】不存在!", fieldName);
throw new IllegalArgumentException("警告:dtoBean验证字段【" + fieldName + "】不存在!");
}
field.setAccessible(true);
Object fieldValue = field.get(formBean);
if (Objects.isNull(fieldValue) || (fieldValue instanceof String && StringUtil.isEmpty((String) fieldValue))) {
log.info("提示:formBean验证字段【{}】值为空,无需验证!", field.getName());
isNull = true;
break;
}
field.set(formBeanParam, fieldValue);
}
if (!isNull) {
dtoBean = JSON.parseObject(JSON.toJSONString(formBeanParam), dtoClass);
num = baseService.selectCount(dtoBean);
result = duplicateJudgment(num, idFields, ids, dtoBean, baseService);
if (!result) {
resultCode.set(false);
repeatMsg = unionRepeat.msg();
throw new CdtBissException(repeatMsg);
}
}
}
}