【面试真题拆解】Spring中的注解

面试官没有直接问我"@Autowired和@Resource的区别"这种题,而是:

先让我举几个Spring注解的例子,再问"Spring注解定义为什么都挺短,不会有大量代码",直接考察你对注解本质和Spring AOP/IOC底层的理解。

举几个Spring注解的例子

1. IOC依赖注入类注解

注解 作用 区别
@Autowired Spring原生注解,按类型(byType) 自动注入依赖 优先byType,找不到唯一Bean时,会尝试按变量名(byName) 匹配;可以配合 @Qualifier 指定Bean名称;可以用 required=false 允许依赖为空
@Qualifier 配合 @Autowired 使用,强制按名称(byName) 注入依赖 当容器中有多个同类型Bean时,必须用它指定具体用哪个
@Resource 名称(byName) 优先注入,找不到再按类型(byType) 匹配 JSR-250标准,跨框架兼容性好;可以用 name 属性显式指定Bean名称

2. IOC组件注册类注解

注解 作用
@Component 通用组件注册注解,把任意类标记为Spring容器的Bean
@Controller @Component 的子类,专门标记Web控制器层的Bean
@Service @Component 的子类,专门标记业务逻辑层的Bean
@Configuration @Component 的子类,专门标记Java配置类,替代XML配置文件
@Repository @Component 的子类,专门标记数据访问层的 Bean;还能把数据库的异常(如 SQLException)转换成 Spring 的 DataAccessException(非受检异常)

3. AOP切面类注解

横切关注点就是"和业务逻辑无关,但很多地方都要用的功能",比如日志、事务、权限校验、性能监控这些。

注解 作用
@Aspect 把类标记为切面类
@Pointcut 定义切入点表达式,告诉Spring"哪些方法需要被增强"
@Before 前置通知 :在目标方法执行之前执行增强逻辑
@After 后置通知 :在目标方法执行之后(不管有没有异常)执行增强逻辑
@AfterReturning 返回通知 :在目标方法正常返回之后执行增强逻辑
@AfterThrowing 异常通知 :在目标方法抛出异常之后执行增强逻辑
@Around 环绕通知 :最强大的通知,可以完全控制目标方法的执行(比如不执行目标方法、修改参数、修改返回值)

Lombok的常用注解

Lombok是一种代码简化工具,通过注解自动生成代码。

虽然不是Spring原生注解,但Lombok的注解是Java开发中经常会用到的。

注解 作用
@Slf4j 自动生成 SLF4J日志对象
@Data 自动生成 getter/setter、toString、equals、hashCode、无参构造器
@NoArgsConstructor 自动生成 无参构造器
@AllArgsConstructor 自动生成 全参构造器

Spring 注解定义为什么通常都挺短,不会有大量代码?

面试官这么问主要是想考察你对注解本质和Spring容器底层的理解。

先明确一下元数据(Metadata) 的定义,元数据就是"描述数据的数据",本身不是数据的内容,只是给数据打标签、加属性。

根本原因在于注解只是元数据标记,本身不包含任何逻辑。

举个Spring注解的例子,比如 @Transactional

java 复制代码
// @Transactional的核心定义(简化版,去掉了很多元注解和属性)
@Target({ElementType.METHOD, ElementType.TYPE}) // 元注解:标记这个注解能用在方法/类上
@Retention(RetentionPolicy.RUNTIME) // 元注解:标记这个注解保留到运行时
@Inherited // 元注解:标记这个注解可以被子类继承
@Documented // 元注解:标记这个注解会被生成到JavaDoc里
public @interface Transactional {
    // 只有属性,没有任何方法/逻辑!
    String value() default ""; // 事务管理器的名称
    Propagation propagation() default Propagation.REQUIRED; // 传播行为
    Isolation isolation() default Isolation.DEFAULT; // 隔离级别
    int timeout() default -1; // 超时时间
    boolean readOnly() default false; // 是否只读
}

可以看到@Transactional 的定义里只有属性,没有任何一行业务逻辑或容器逻辑。

它只是告诉Spring容器:"这个方法/类需要开启事务,传播行为是REQUIRED,隔离级别是DEFAULT...",真正开启/提交/回滚事务的逻辑,全在Spring的事务管理器和AOP切面里。

除此之外,Spring注解的定义比较短,还有一个原因是Spring大量使用了元注解(Meta-Annotation)。

那什么是元注解呢?

元注解就是"标记注解的注解",可以复用已有注解的功能,不用重复写代码。

比如上面的 @Transactional,它上面有@Target@Retention@Inherited@Documented这4个元注解。

怎么自定义一个Spring注解?

简单来说有以下3步:

  1. 定义注解

@interface 定义,加上JDK原生元注解(至少需要 @Target@Retention(RUNTIME));

  1. 编写后置处理器

实现 BeanPostProcessorBeanFactoryPostProcessor 接口,或者用 AOP 切面(更适合方法级别的拦截,比如权限校验、记录执行时间),处理自定义注解的逻辑;

  1. 注册后置处理器

把自定义后置处理器交给Spring容器管理(比如用 @Component 标记)。

举个例子@AuthCheck:

java 复制代码
@Target(ElementType.METHOD)
//规定这个标签能贴在哪里,ElementType.METHOD 意思是"只能贴在方法上面"。
@Retention(RetentionPolicy.RUNTIME)
//规定这个标签的有效期,RUNTIME的意思是"程序运行的时候,这个标签依然存在"
public @interface AuthCheck {

    /**
     * @interface 不是接口(interface),而是专门用来定义注解的语法。
     * String mustRole():表示用这个标签时,
     * 你可以写上一个角色名,比如 @AuthCheck(mustRole = "admin")。
     * default "":如果你只写了 @AuthCheck
     * 而没填角色,那默认值就是一个空字符串 ""
     * (表示不需要特定角色,只要登录就行,具体看你怎么实现业务逻辑)。
     *
     */
    String mustRole() default "";
}
相关推荐
不吃香菜学java2 小时前
苍穹外卖-新增菜品需求分析
java·spring boot·spring·tomcat·maven·ssm
2401_894241922 小时前
实战:用OpenCV和Python进行人脸识别
jvm·数据库·python
MrZhangBaby2 小时前
SQL-leetcode—3482. 分析组织层级
数据库·sql·leetcode
阿蒙Amon2 小时前
C#常用类库-详解SqlSugar
开发语言·数据库·c#
m0_662577972 小时前
使用Pandas进行数据分析:从数据清洗到可视化
jvm·数据库·python
档案宝档案管理2 小时前
档案宝|开箱即用,打破档案管理“复杂魔咒”
数据库·人工智能·档案·档案管理
MekoLi292 小时前
Arthas 安装与使用全流程教程
后端·面试
小朋友,你是否有很多问号?2 小时前
java udf 实现经纬度匹配pg数据库public.geometry地理位置
数据库
@insist1232 小时前
软件设计师-数据库核心:事务 ACID 特性、并发控制与备份恢复技术全解
数据库·oracle·软考·软件设计师·软件水平考试