面试官没有直接问我“@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:
|
|
可以看到@Transactional 的定义里只有属性,没有任何一行业务逻辑或容器逻辑。
它只是告诉Spring容器:“这个方法/类需要开启事务,传播行为是REQUIRED,隔离级别是DEFAULT…”,真正开启/提交/回滚事务的逻辑,全在Spring的事务管理器和AOP切面里。
除此之外,Spring注解的定义比较短,还有一个原因是Spring大量使用了元注解(Meta-Annotation)。
那什么是元注解呢?
元注解就是“标记注解的注解”,可以复用已有注解的功能,不用重复写代码。
比如上面的 @Transactional,它上面有@Target、@Retention、@Inherited、@Documented这4个元注解。
怎么自定义一个Spring注解?
简单来说有以下3步:
- 定义注解
用 @interface 定义,加上JDK原生元注解(至少需要 @Target 和 @Retention(RUNTIME));
- 编写后置处理器
实现 BeanPostProcessor 或 BeanFactoryPostProcessor 接口,或者用 AOP 切面(更适合方法级别的拦截,比如权限校验、记录执行时间),处理自定义注解的逻辑;
- 注册后置处理器
把自定义后置处理器交给Spring容器管理(比如用 @Component 标记)。
举个例子@AuthCheck:
|
|