写点什么

Java 注解系统学习与实战

作者:Java-fenn
  • 2022 年 9 月 14 日
    湖南
  • 本文字数:3786 字

    阅读完需:约 12 分钟

Java 背景为什么要再次梳理一下 java 注解,显而易见,因为重要啊。也是为研究各大类开源框架做铺垫,只有弄清楚 Java 注解相关原理,才能看懂大部分框架底层的设计。


缘起注解也叫做元数据,是 JDK1.5 版本开始引入的一个特性,用来对代码进行标记说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解修饰。其本身不包含任何业务逻辑。


一般注解大类分为三种:


JDK 自带的相关注解自定义的注解第三方的(例如相关的框架中的注解)注解三步走:定义、配置、解析


定义:定义标记配置:把标记打到需要用到的代码中解析:在编译器或运行时检测到标记,并进行特殊操作元注解什么是元注解?元注解的作用就是负责注解其他注解。元注解有以下五种:


@Retention:指定其所修饰的注解的保留策略 @Document:该注解是一个标记注解,用于指示一个注解将被文档化 @Target:用来限制注解的使用范围 @Inherited:该注解使父类的注解能被其子类继承 @Repeatable:该注解是 Java8 新增的注解,用于开发重复注解 @Retention 注解用于指定被修饰的注解可以保留多长时间,即指定 JVM 策略在哪个时间点上删除当前注解。


目前存在以下三种策略


策略值 功能描述 Retention.SOURCE 注解只在源文件中保留,在编译期间删除 Retention.CLASS 注解只在编译期间存在于.class 文件中,运行时 JVM 不可获取注解信息,该策略值也是默认值 Retention.RUNTIME 运行时 JVM 可以获取注解信息(反射),是最长注解持续期 @Document 注解 @Document 注解用于指定被修饰的注解可以被 javadoc 工具提取成文档。定义注解类时使用 @Document 注解进行修饰,则所有使用该注解修饰的程序元素的 API 文档中将会包含该注解说明。


@Target 注解 @Target 注解用来限制注解的使用范围,即指定被修饰的注解能用于哪些程序单元。标记注解方式如下:@Target({应用类型 1, 应用类型 2,...})【@Target(ElementType.FIELD)】


枚举值的介绍如下:


枚举值 功能描述 ElementType.Type 可以修饰类、接口、注解或枚举类型 ElementType.FIELD 可以修饰属性(成员变量),包括枚举常量 ElementType.METHOD 可以修饰方法 ElementType.PAPAMETER 可以修饰参数 ElementType.CONSTRUCTOR 可以修饰构造方法 ElementType.LOCAL_VARIABLE 可以修饰局部变量 ElementType.ANNOTATION_TYPE 可以修饰注解类 ElementType.PACKAGE 可以修饰包 ElementType.TYPE_PARAMETER JDK8 之后的新特性,表示该注解能写在类型变量的声明语句中(如,泛型声明)ElementType.TYPE_USE JDK8 之后的新特性,表示该注解能写在使用类型的任何语句中(例如:声明语句、泛型和强制转换语句中的类型)@Inherited 注解 @Inherited 注解指定注解具有继承性,如果某个注解使用 @Inherited 进行修饰,则该类使用该注解时,其子类将自动被修饰。


按照以上三步走的流程,咱们这里来举例子写代码说明一下:


(1)定义注解


@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic @interface InheritedExtend {String comment();int order() default 1;}(2)配置:标记打到类上


@InheritedExtend(comment ="注解继承",order = 2)public class Base {}(3)解析:获取注解并解析做测试


public class InheritedDemo extends Base{public static void main(String[] args) {//从本类中获取父类注解信息 InheritedExtend extend = InheritedDemo.class.getAnnotation(InheritedExtend.class);//输出 InheritedExtend 注解成员信息 System.out.println(extend.comment()+":"+extend.order());//打印出 InheritedDemo 是否类是否具有 @InheritedExtend 修饰 System.out.println(InheritedDemo.class.isAnnotationPresent(InheritedExtend.class));}}结果输出:


注解继承:2 true 以上结果就很好地说明了该注解的继承性质。


@Repeatable 注解 @Repeatable 注解是 Java8 新增的注解,用于开发重复注解。在 Java8 之前,同一个程序元素前只能使用一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解必须通过注解容器来实现。从 Java8 开始,允许使用多个相同的类型注解来修饰同一个元素,前提是该类型的注解是可重复的,即在定义注解时要用 @Repeatable 元注解进行修饰。


(1)定义注解


@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Repeatable(AnnolContents.class)public @interface RepeatableAnnol {String name() default "老猫";int age();}


//注解为容器,@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@interface AnnolContents {//定义 value 成员变量,该成员变量可以接受多个 @RepeatableAnnol 注解 RepeatableAnnol[] value();}(2)注解使用以及解析


@RepeatableAnnol(name = "张三",age = 12)@RepeatableAnnol(age = 23)public class RepeatableAnnolDemo {public static void main(String[] args) {RepeatableAnnol[] repeatableAnnols = RepeatableAnnolDemo.class.getDeclaredAnnotationsByType(RepeatableAnnol.class);


    for(RepeatableAnnol repeatableAnnol : repeatableAnnols){        System.out.println(repeatableAnnol.name() + "----->" + repeatableAnnol.age());    }
AnnolContents annolContents = RepeatableAnnolDemo.class.getDeclaredAnnotation(AnnolContents.class); System.out.println(annolContents);}
复制代码


}结果输出:


张三----->12 老猫----->23@com.ktdaddy.annotation.repeatable.AnnolContents(value={@com.ktdaddy.annotation.repeatable.RepeatableAnnol(name="张三", age=12), @com.ktdaddy.annotation.repeatable.RepeatableAnnol(name="老猫", age=23)})自定义注解实战应用利用注解+springAOP 实现系统日志记录,主要用于记录相关的日志到数据库,当然,老猫这里的 demo 只会到日志打印层面,至于数据库落库存储有兴趣的小伙伴可以进行扩展。


以下是 maven 依赖:


<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency>


    <dependency>          <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.79</version> </dependency></dependencies>
复制代码


注解的定义如下:


@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface OperateLog {String desc() default "";}这个地方只是定义了一个字段,当然大家也可以进行拓展。


接下来,咱们以这个注解作为切点编写相关的切面程序。具体代码如下:


@Aspect@Component@Order(0)public class OperateLogAdvice {


@Pointcut("@annotation(com.ktdaddy.annotation.OperateLog)")public void recordLog(){}
@Around("recordLog()")public Object recordLogOne(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("进来了"); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); OperateLog operateLog = methodSignature.getMethod().getAnnotation(OperateLog.class); String spELString = operateLog.desc(); //创建解析器 SpelExpressionParser parser = new SpelExpressionParser(); //获取表达式 Expression expression = parser.parseExpression(spELString); //设置解析上下文(有哪些占位符,以及每种占位符的值) EvaluationContext context = new StandardEvaluationContext(); //获取参数值 Object[] args = joinPoint.getArgs(); //获取运行时参数的名称 DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); String[] parameterNames = discoverer.getParameterNames(method); for (int i = 0; i < parameterNames.length; i++) { context.setVariable(parameterNames[i],args[i]); } //解析,获取替换后的结果 String result = expression.getValue(context).toString(); System.out.println(result); return joinPoint.proceed();}
复制代码


}关于切面这块不多做赘述,非本篇文章的重点。


接下来就可以在我们的代码层面使用相关的注解了,具体如下:


@Servicepublic class UserServiceImpl implements UserService {@OperateLog(desc = "#user.desc")public void saveUser(User user){System.out.println("测试注解...");}}关于 controller 层面就省略了,都是比较简单的。


通过上述切面以及注解解析,我们可以获取每次传参的参数内容,并且将相关的日志进行记录下来,当然这里面涉及到了 SpEL 表达式注入,相关的知识点,小伙伴们可以自行学习。


最终启动服务,并且请求之后具体的日志如下。


进来了这是测试测试注解...{"age":12,"desc":"这是测试","id":1,"name":"张三","operator":"操作人"}至此关于 Java 注解的回顾学习已经结束,之后咱们再去看一些底层代码的时候或许会轻松很多。

用户头像

Java-fenn

关注

需要Java资料或者咨询可加我v : Jimbye 2022.08.16 加入

还未添加个人简介

评论

发布
暂无评论
Java注解系统学习与实战_Java_Java-fenn_InfoQ写作社区