Spring有哪些常用注解?
作者:程序员马丁
在线博客:https://open8gu.com
大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。
由于相关的注解是在太多,因此本篇文章主要还是围绕spring-context
模块中 Bean 配置相关的注解、少量 spring-beans
和 spring-tx
模块的注解展开。
而其他的注解,则按照模块和功能放到了另外的几篇文章里:
- ✅ Spring Web 有哪些常用注解?:主要介绍
spring-web
模块的注解。 - ✅ Spring 依赖注入有哪些常用注解?:主要介绍
spring-beans
模块中依赖注入相关的注解。 - ✅ Spring 配置上下文有哪些常用注解?:主要介绍
spring-context
模块中,与配置文件相关的一些注解。
问题详解
1. Bean 配置
1.1. @Component
用于标记在类上,表示当前类的对象是一个 Bean:
@Component("foo") // 声明一个 Foo 类型的 Bean,其 beanName 为 "foo"
public class Foo {
}
它可能是我们最常见到的注解,基于 Spring 的组合注解机制,几乎所有兼具“声明一个特定类型的对象作为被 Spring 容器管理的 Bean”功能的注解几乎都由它组合而来(即使用其作为元注解),比如 @Configuration
、@Controller
、@Service
、@Repository
……等等 。
关于组合注解的,请参见 ✅ Spring 的组合注解是什么?
当项目启动时,Spring 将扫描特定包路径下直接或间接被 @Component
注解的类,然后为其生成 BeanDefinition
,并在容器初始化完成后根据 BeanDefinition
创建对应的实例。
beanName
对于容器中的 Bean,Spring 将会根据 BeanNameGenerator
为 Bean 生成名称。
默认使用的 AnnotationBeanNameGenerator
将使用 @Component#value
指定的值作为 beanName,若未指定,则默认使用小写开头的短类名作为 beanName(com.xyz.FooServiceImpl -> fooServiceImpl
)
1.2. @ManagedBean & @Named
@ManagedBean
和 @Named
都是 JSR330 规定的注解,前者等价于无法指定 beanName 的 @Component
,而后者完全等价于 @Component
:
@Named("a") // == @Component("a")
public class A {}
@ManagedBean // == @Component
public class B {}
1.3. @Configuration & @Bean
@Configuration
用于声明一个配置类,其内部带有 @Bean
注解的工厂方法返回的对象会被作为 Bean:
@Configuration // 声明一个配置类,其本身也视为一个 Bean
public class ExampleConfiguration {
@Bean("foo") // 声明一个 Foo 类型的 bean,其 beanName 为 "foo"
public Foo foo() {
return new Foo();
}
}
相比起 @Component
,它更便于集中化管理 Bean,并且支持将项目引入的其他库中的类声明为 Bean。
它基于 ConfigurationClassPostProcessor
后处理实现。
1.3.1. @Bean
@Bean
注解用于注解在工厂方法上,标明该方法返回的对象需要作为一个 Bean 交由 Spring 管理。
@Bean
提供了一些有用的属性:
value
/name
:指定 beanName,此处可以同时配置多个 beanName,从第二个开始都作为别名。autowireCandidate
:声明的 Bean 是否需要依赖注入。initMethod
/destroyMethod
:Bean 的初始化和销毁方法的名称,可以用于绑定一个无参方法作为回调方法。
1.3.2. Full 模式和 Lite 模式
@Bean
注解一般比较多配合 @Configuration
使用,不过实际上它在 @Component
中也能生效。在 Spring 中,这种配置方式称为 Lite 模式,而通过 @Configuration
的配置模式称为 Full 模式。
两者的主要区别参见: ✅ @Configuration 和@Component 有什么区别?
1.4. @Lookup
@Lookup
注解用于将特定方法变为一个从 Spring 容器获取特定类型 Bean 的工厂方法。
比如:
@Component
public Instance { } // 在容器中声明一个类型为 Instance 的 Bean
@Component
public class InstanceFactory {
@Lookup // 指定该方法返回一个类型为 Instance 的 Bean
public Instance get() {
return null; // 返回任何值都行,因此实际上方法体中的任何逻辑都不执行
}
}
@Component
public class Example {
@Autowired
private InstanceFactory factory;
@Autowired
private Instance instance;
public void run() {
Assert.isTrue(instance == factory.get()); // 两者相同
}
}
如上图所示,Spring 将代理 InstanceFactory
的 get
方法,使其在调用时总是返回 Spring 容器中的 Instance
类型的 Bean。
这不是一个常用的注解,它一般用在需要重复获得 scope
指定为 prototype
的单例 Bean 的场景,等价于:
@Component
public class Example {
@Autowired
private ApplicationContext context;
@Autowired
private Instance instance;
public void run() {
Assert.isTrue(instance == context.getBean(Instance.class));
}
}
1.5. @Scope
@Scope
注解用于在带有@Component
的类或带有 @Bean
注解的工厂方法上指定 Bean 的作用域。
Spring 默认支持两种作用域:
- 单例
singleton
:对应常量ConfigurableBeanFactory.SCOPE_SINGLETON
; - 原型(多例)
prototype
:对应常量ConfigurableBeanFactory.SCOPE_PROTOTYPE
;
在 SpringWeb 中,又额外支持三种作用域:
- 请求
request
:对应WebApplicationContext.SCOPE_REQUEST
,另有对应组合注解@RequestScope
; - 会话
session
:对应WebApplicationContext.SCOPE_SESSION
,另有对应组合注解@RequestSession
; - 全局
application
:对应WebApplicationContext.SCOPE_APPLICATION
,另有组合注解@RequestApplication
;
默认的 Bean 都是单例的,有必要也可以改成多例:
@Scope("prototype") // 声明为单例
@Component
public class Foo { }
关于作用域,请参见 ✅ Bean 的作用域是什么?有哪些作用域?
1.6. @Lazy
@Lazy
注解有三种用法:
- 用于在带有
@Configuration
的类上,指定所有工厂方法对应的 Bean 都是否懒加载。 - 用于在带有
@Component
的类或带有@Bean
注解的工厂方法上指定 Bean 是否懒加载。 - 用在带有
@Inject
/@Autowired
注解的属性上,实现延迟注入。
再总结一下,就是两种作用:
- 懒加载:延迟单例 Bean 的加载时机,在不被其他非懒加载 Bean 依赖的情况下,不在
AbstractApplicationContext.refresh
执行过程中随其他单例 Bean 提前实例化,而是等到被从 Spring 容器获取时再初始化; - 延迟注入:延迟依赖注入的 Bean 的加载时机,即先注入一个空的代理对象,等真正调用的时候再在代理对象中从容器加载并获取 Bean;
- 关于 Bean 的加载时机,请参见:✅ Bean 的生命周期?
- 关于懒加载与延迟注入的概念,请参见:✅ Spring 的懒加载和延迟注入是什么?两者有何区别?
1.7. @Order & @Priority
@Order
注解用于在带有@Component
的类或带有 @Bean
注解的工厂方法上指定 Bean 的执行顺序。
注意,这里说的是执行顺序而不是加载顺序,仅指诸如批量获取 Bean 时的排序,或者 SpringAOP 中切面(更准确点说,是 Advisor
)的执行顺序,又或者是 SpringMVC 中的拦截器的执行顺序。
比如:
@Order(2)
@Component
public class Foo1 implements Foo {}
@Order(1) // 值越小越靠前
@Component
public class Foo2 implements Foo {}
@Component
public class Example {
@Autowired // 批量注入 Bean 时,根据 @Order 注解的排序值排序
private Collection<Foo> foos; // = [foo2, foo1]
}
除 @Order
注解外,也可以用 @Priority
注解(由 javax
包提供),或者通过实现 Ordered
接口指定排序值,排序规则如下:
- 排序值值越小,则顺序越先。
- 先看
@Order
,再看@Priority
,最后再看Ordered
接口;
此外,和 Spring 的其他注解一样,此处的 @Order
和 @Priority
同样支持组合注解,具体内容可以参见 Spring 的AnnotationAwareOrderComparator
,它是最常用的比较器。
1.8. @Primary
@Primary
** 注解用于指定在依赖注入时,当存在多个匹配的同类型 Bean 时要优先注入哪一个 Bean。**
当我们根据类型进行依赖注入时,如果只需要注入一个,但是同类型的 Bean 却又多个,那么就会报错,此时处理使用 @Qualifier
注解显式指定 beanName 外,我们也可以为其中一个 Bean 添加 @Primary
注解,令 Spring 在这种情况下优先使用这个 Bean:
@Primary
@Component
public class Foo1 implements Foo {}
@Component
public class Foo2 implements Foo {}
@Component
public class Example {
@Autowired // 优先使用 Foo1
private Foo foos; // = foo1
}
此外,如果此时发现有多个同类型的 Bean 都有 @Primary
注解,那么还是会报错。
1.9. @DependsOn
@DependsOn
注解用于显示指定 Bean 之间的依赖关系,从而使特定的 Bean 在当前 Bean 之前优先加载。
有时候,我们会希望某个 Bean 在另一些 Bean 之前加载,尤其是在某些配置类需要按特定顺序加载的场景。此时,除了通过依赖注入显式的指定它们的依赖关系外,还可以通过 @DependsOn
显式的指定某个 Bean 需要在当前 Bean 之前加载。
比如:
@DependsOn("configuration2") // configuration2 需要在 configuration1 之前加载
@Configuration("configuration1")
public class Configuration1 {}
@Configuration("configuration2")
public class Configuration2 {}
1.10. @PostConstruct & @PreDestroy
这两种注解都是 JSR250 引入的生命周期注解,被 @PostConstruct
注解的方法会在 Bean 初始化后调用,而被 @PreDestroy
注解的方法会在 Bean 销毁后调用。
public class Foo {
@PostConstruct
public void init() {
// Bean 初始化后调用
}
@PreDestroy
public void destory() {
// Bean 销毁时调用
}
}
它都通过 CommonAnnotationBeanPostProcessor
这个注解处理器进行处理,这个处理器的父类有着更开门见山的名字 InitDestroyAnnotationBeanPostProcessor
。
在同一个类中,可以同时有多个被注解的初始化和销毁方法,不支持组合注解,但是支持解析父类或父接口中的注解。
此外,由于是基于后处理器执行的,因此在初始化和销毁过程中,被注解方法的执行时机总是:
- 先执行被
@PostConstruct
/@PreDestroy
注解的回调方法; - 再执行
InitializingBean
/DisposableBean
接口的回调方法; - 最后再执行在 XML 配置文件/
@Bean
注解中指定的初始化和销毁方法;
关于初始化/销毁方法的执行时机,请参见:✅ Bean 的生命周期?
最后,这几个注解从 JDK9 开始将不像 JDK8 那样可以直接使用,引用官网文档的话:
与
@Resource
一样,@PostConstruct
和@PreDestroy
注解类型在 JDK 6 到 8 中是标准 Java 库的一部分。然而,整个javax.annotation
包在 JDK 9 中从核心 Java 模块中分离出来,最终在 JDK 11 中被删除。从 Jakarta EE 9 开始,该包现在住在jakarta.annotation
中。如果需要,现在需要通过 Maven 中心获得jakarta.annotation-api
工件,只需像其他库一样添加到应用程序的classpath
中即可。
2. 事务 @Transactional
@Transactional
用来在方法上开启事务,方法执行中所有的事务操作要么全部成功执行,要么全部回滚。
比如:
@Service
public class Service {
@Autowired
private Repository repository;
@Transactional(rollbackFor = Exception.class)
public void performTransactionalOperation() {
// 如果任何一个保存操作失败,整个事务将回滚
repository.save(entity1);
repository.save(entity2);
}
}
你也可以在类上添加 @Transactional
注解,这意味着类中的所有公共方法都将应用同样的事务配置,除非又显式在方法上使用 @Transactional
注解覆盖。
另外,基于 Spring 的组合注解,@Transactional
也可以作为其他注解的元注解,或者加在接口或接口的抽象方法上。
@Transactional
注解还提供了一些有用的属性::
isolation
: 指定事务的隔离级别,默认是 Isolation.DEFAULT。propagation
: 指定事务的传播行为,默认是 Propagation.REQUIRED。readOnly
: 指定事务是否为只读,如果为只读,可以优化事务处理,默认是false
。rollbackFor
: 指定哪些异常会导致事务回滚。noRollbackFor
: 指定哪些异常不会导致事务回滚。
- 关于
@Transactional
的原理,参见:Spring 声明式事务的工作原理?- 关于声明式事务失效的场景,参见:Spring 声明式事务失效的场景?
- 关于事务的传播行为,参见:Spring 事务的传播行为?
3. 事件
3.1. @EventListener
@EventListener
注解用于将方法注册为 Spring 中的事件监听器,当发布相关事件时,监听器方法将被调用。
比如:
@Component
public class MyEventListener {
@EventListener(condition = "#event.message != null")
public void handleEvent(MyCustomEvent event) {
// 处理 MyCustomEvent 类型的事件
System.out.println("Received event: " + event.getMessage());
}
}
@Component
public class Example {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void publishCustomEvent(String message) {
// 创建并发布 MyCustomEvent 事件
MyCustomEvent event = new MyCustomEvent(this, message);
eventPublisher.publishEvent(event);
}
}
关于声明式事件监听器,请参见:什么是声明式监听器?
3.2. @TransactionalEventListener
@TransactionalEventListener
是 @EventListener
的扩展注解,相比起 @EventListener
,它仅在事务进入某个特定阶段时才真正响应事件。
比如:
@Component
public class MyTransactionalEventListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleEvent(MyCustomEvent event) {
// 处理 MyCustomEvent 类型的事件,仅在事务成功提交后触发
System.out.println("Received transactional event: " + event.getMessage());
}
}
@Component
public class Example {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public void publishCustomEvent(String message) {
// 创建并发布 MyCustomEvent 事件
MyCustomEvent event = new MyCustomEvent(this, message);
eventPublisher.publishEvent(event);
}
}
此外,它本质上可以看做 @EventListener
与 @Transactional
的组合注解,关于组合注解,请参见:✅ Spring 的组合注解是什么?