在实际的开发中,我们大量使用了注解,无论是spring、还是本身jdk提供的,注解都是围绕一个java程序员的开发生活,所以本篇主要介绍注解相关的概念、理论、实践。
定义注解
注解和异常非常相似,都可以自定义,但是我们自定义异常的场景比较多,但是注解就比较少。
overrider是本身jdk提供的,表示当前方法被覆盖的描述。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
我们自定义一个限流注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {public enum TimeUnit {SECOND,MINUTE,HOUR,DAY,MONTH}String apiName();int limitCount();TimeUnit timeUnit() default TimeUnit.SECOND}
target
target用来描述注解的使用范围,
public enum ElementType {TYPE, // 类型 类,接口,枚举FIELD, // 用于成员变量METHOD, // 用于方法PARAMETER, // 用于参数CONSTRUCTOR, // 用于构造方法LOCAL_VARIABLE, // 局部变量ANNOTATION_TYPE, PACKAGE, // 用于包TYPE_PARAMETER,TYPE_USE
}
一般主要常用的就是针对类、方法、成员变量。实际上注解只是一个标识作用。可以通过反射访问的代码元素,我们都可以通过注解标识。如果不使用target标识使用范围,默认就是做任何范围。
Retention
描述注解的可见范围,生命周期。
public enum RetentionPolicy {SOURCE, // 源码中可见CLASS, RUNTIME
}
SOURCE代表仅在源码中可见,当编译器将源代码编译为字节码后,注解信息将被丢弃。不过编译器可以在可见范围内查找,比如override 查找其父类是否有对应的方法,没有就编译错误。class标识在字节码范围。runtime标识在运行时期。
源码->编译->运行时。
Documented
标识在java doc中进行输出
interface
class 代表类 interface 代表接口、enum代表枚举类、@interface代表注解。
标记注解
标记注解,其实就是使用不同的注解,放在方法或者类上。
@RateLimit(apiName = "/user/info",limitCount = 1000,timeUnit = RateLimit.TimeUnit.SECOND)public void getUserInfo() { }
读取注解
定义注解、标记注解,还需要进行读取注解,也就是说需要通过响应逻辑的代码处理。对于java内建注解,编译器和JVM都可以对其进行读取和处理,比如override注解,编译器在编译代码时,会读取所有标记了@override的方法,并且检查父类中是否有同名方法。没有就编译报错。
对于自定义注解,需要我们开发相应的读取和处理逻辑,如何来读取代码中的注解信息。就需要使用上一节课中的反射语法。反射其实是作用于代码运行时。
注解应用
最常见的就是 替换配置文件,
spring中配置文件 一般通过xml进行定义,我们可以使用注解替代xml配置
那么spring容器时如何使用注解的?
在程序启动的时候,spring ioc容器利用反射获取到appConfig配置, 发现包含@configuration注解,便确定这个类时一个配置类。通过反射获取到对应方法的bean对象,并创建对应的对象,存储到一个大map中,key 为beanName value为对象,就可以通过getBean获取对象。
而平时常见的@service、@controller、@respository 都是同样的方式。
应用场景
自定义注解 csv文件
比如在实际的开发中,我们需要对一个对象的字段进行写入到csv文件中,但是有一些字段不想被写入。比如用户的基本信息(ID、name、age、phone、idcardnum、住址信息等),比如针对phone、和idcardnum 需要我们进行脱敏处理。或者忽略不写。 我们可以开发一个自定义注解。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface CsvIgnore {
}
@CsvIgnoreprivate String phone;
通过反射的方式在公共的csv文件操作逻辑进行忽略。这样其实就做到了透明化。使用者其实不用关心。
CsvIgnore ignoreProperty = field.getAnnotation(CsvIgnore.class);if (!Objects.isNull(ignoreProperty)) {csvIgnoreSet.add(field.getName());}
自定义主从切换
在实际的开发中,我们可能有多个DB要切换操作,比如读取风控数据库、后台数据库、用户数据库等,如何在同一个请求中自定义获取数据源。我们可以通过注解的方式。通过AOP的方式
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceHolder {DataSource value() default DataSource.SALVE;
}
@Around("@annotation(dataSourceHolder)")public Object enhance(ProceedingJoinPoint joinPoint, DataSourceHolder dataSourceHolder) {if (dataSourceHolder.value() == DataSource.MASTER) {DynamicDataSourceHolder.setDataSourceTypeMaster();} else {DynamicDataSourceHolder.setDataSourceTypeSlave();}Object proceed = null;try {proceed = joinPoint.proceed();} catch (Throwable throwable) {throw new RuntimeException(throwable);} finally {DynamicDataSourceHolder.clearDataSourceType();}return proceed;}
小结
好了本篇主要详细介绍了java注解的相关使用,原理,关于如何获取注解,需要等下一篇的关于反射的讲解。以后写注解就知道为什么加一个@service就可以被扫描成bean对象使用,以及如何定义注解在 自己的项目中使用。