面试必问五分钟搞懂Java常用注解以及原理

Posted 苍山有雪,剑有霜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试必问五分钟搞懂Java常用注解以及原理相关的知识,希望对你有一定的参考价值。

注解是什么?

Java 注解是Java5添加的,用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解可以用于这一目的。

注解是一种元数据,可以将它理解为一种特殊的注释,它为我们在代码中添加信息提供了一种形式化的方法,它用于帮助我们更快捷的写代码。

简单来说,注解主要有四个部分的作用

  • 生成文档:即将元数据生成为Javadoc文档;
  • 编译检查:编译器在编译阶段会对代码进行检查,例如@override注解会提示编译器查看其是否重写了父类的方法;
  • 编译动态处理:主要是用作动态生成代码,例如一些帮助类、方法,通过注解实现自动生成;
  • 运行动态处理:典型的例子是使用反射来注入实例;

Java提供了三种注解:自带的标准注解(见下)、元注解(指修饰注解的注解,是最基准的注解)、自定义注解(用户可以自定义注解,如@Retention、@Target、@Inherited、@Documented、@Repeatable 等)

在实际的项目开发中见到的注解一般是来自Java自带的注解、元注解、Spring等框架的注解(如@Resource、@Bean等,lombok的@data、@Slf4j等)。

常见注解

一些Java及Spring等常见的注解如下:

  1. @Deprecated – 所标注内容不再被建议使用;
  2. @Override – 只能标注方法,表示该方法覆盖父类中的方法;
  3. @SuppressWarnings – 所标注内容产生的警告,编译器会对这些警告保持静默;
  4. @interface用于定义一个注解
  5. @Documented --将所标注内容包含到javadoc中;
  6. @Inherited – 只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性,跟类的继承形式同意;
  7. @Retention – 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性,它表示注解存在阶段是保留在源码(编译期),字节码(类加载)或者运行期(JVM中运行),关于注解声明周期的概念可以看看下图:

img

一般来说,如果retention设置为source和class,那么就需要被继承并实现,因为加载到JVM时,这两者的注解就被抹除了,也就失去了效果。(lombok的@Data注解就是在这个过程中,加入了get/set方法)

  1. @Target – 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性,它是通过枚举类类来表示注解的作用范围,如作用于TYPE(接口、类、枚举、注解)、FIELD(字段、枚举常量)、METHOD(方法)、PARAMETER(方法参数)、ANNOTATION_TYPE(注解)、CONSTRUCTOR(构造函数)、LOCAL_VARIABLE(局部变量)、PACKAGE(包)等
  2. @Repeatable-表明该注解所修饰的注解可以同时作用一个对象多次,但每次的含义可以不同;

下面是Spring的一些重要注解:

  1. @Configuration

标注在类上,相当于把该类作为spring的xml配置文件中的,用来配置spring容器(上下文)。

  1. @Bean

标注在方法上(返回某个实例的方法),等价于spring的xml配置文件中的,作用为:注册bean对象。@Bean注解默认作用域为单例singleton作用域。

  1. @ComponentScan

标注在类上,用于对Component进行扫描;

  1. @WishlyConfiguration

组合注解,可以替代注解2-3;

上述1-4,是配置类的相关注解;

  1. @Component

一般是在类上,表明其是一个组件,告知Spring该类会创建Bean;

  1. @Repository

表明该对象是数据访问层(DAO)使用的;

  1. @Service

表明该对象是业务逻辑层(SERVICE)使用的;

  1. @Controller

这是控制器的声明,表明该对象在展现层使用;

以上5-8都是有关声明bean的注解;

  1. @Resource:自动依赖注入

  2. @Autowired:自动依赖注入;

注:两者的用法很相似,都是用作依赖注入,但是Resource是默认按照Name注入,而Autowired是按照Type注入的

  1. @Inject:手动依赖注入;

这里补充一下Spring启动容器与组件之间的具体过程:

  • 在启动spring的时候,首先要启动容器
  • 启动spring容器时,会默认寻找容器扫描范围内的可加载bean,然后查找哪些bean上的属性和方法上有@Resource注解
  • 找到@Resource注解后,判断@Resource注解括号中的name属性是否为空,如果为空:看spring容器中的bean的id与@Resource要注解的那个变量属性名是否相同,如相同,匹配成功;如果不相同,看spring容器中bean的id对应的类型是否与@Resource要注解的那个变量属性对应的类型是否相等,若相等,匹配成功,若不相等,匹配失败;
  • 如果@Resource注解括号中的name属性不为空,看name的属性值和容器中的bean的id名是否相等,如相等,则匹配成功;如不相等,则匹配失败。

其他一些注解如下:

  1. @profile

指定类或方法在特定的 Profile 环境生效,任何@Component或@Configuration注解的类都可以使用@Profile注解。在使用DI来依赖注入的时候,能够根据@profile标明的环境,将注入符合当前运行环境的相应的bean。

  1. @Data :注在类上,提供类的get、set、equals、hashCode、canEqual、toString方法

  2. @AllArgsConstructor :注在类上,提供类的全参构造

  3. @NoArgsConstructor :注在类上,提供类的无参构造

  4. @Setter : 注在属性上,提供 set 方法;

  5. @Getter :注在属性上,提供 get 方法

  6. @EqualsAndHashCode :注在类上,提供对应的 equals 和 hashCode 方法;

  7. @Log4j/@Slf4j : 注在类上,提供对应的 Logger 对象,变量名为 log

  8. @After: 表明该方法在其他方法执行之后执行;

  9. @Before: 表明该方法在其他方法执行之前执行;

  10. @Around: 表明该方法在其他方法执行之前和之后都执行;

自定义注解?

上述的这些常见的注解可以减少代码量,使得代码结构更加简洁和清晰,但是光用这些注解是不够的,有时在业务场景需要或者保持代码结构清晰等情况下,就需要使用自定义注解了。

举个例子,一般项目中都要设置日志系统,或是监控告警系统,就需要上传或者提供一些QPS、RT等,如果将这些代码直接写在项目代码中,那么就会使得项目代码被污染,而且,在项目中会大量存在这些重复代码,这个时候自定义注解就有用咯!

如何解?那就是利用APO切面编程以及反射的思想!将AOP相关的配置信息写入注解里,就可以实现优雅地解决这个问题了。

反射是为了获取Java运行时的相关信息,AOP切面编程是为了能够在Spring的AOP中中直接判断某一个方法是否带有自定义的注解——如果有,那么自定义注解就会将相关的监控逻辑加载在相关的方法上。

有关这个案例,可以详细看一下这篇博客结尾的代码

参考资料

常见注解

Java自定义注解

Java3y写的注解,很棒

以上是关于面试必问五分钟搞懂Java常用注解以及原理的主要内容,如果未能解决你的问题,请参考以下文章

面试官:@Transactional 注解是如何实现的?面试必问!

『图解Java并发』面试必问的CAS原理你会了吗?

大厂高级工程师面试必问系列:Java动态代理机制和实现原理详解

面试必问:布隆过滤器的原理以及使用场景

Android 大厂高级面试必问36题以及算法合集(附:2022年Android 中高级面试题汇总以及面试题解析)

面试必问之JVM原理