Skip to main content

Spring有哪些常用注解?

作者:程序员马丁

在线博客:https://open8gu.com

note

大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。

由于相关的注解是在太多,因此本篇文章主要还是围绕spring-context 模块中 Bean 配置相关的注解、少量 spring-beansspring-tx 模块的注解展开。

而其他的注解,则按照模块和功能放到了另外的几篇文章里:

问题详解

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 将代理 InstanceFactoryget 方法,使其在调用时总是返回 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;

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

在同一个类中,可以同时有多个被注解的初始化和销毁方法,不支持组合注解,但是支持解析父类或父接口中的注解。

此外,由于是基于后处理器执行的,因此在初始化和销毁过程中,被注解方法的执行时机总是:

  1. 先执行被 @PostConstruct/ @PreDestroy 注解的回调方法;
  2. 再执行 InitializingBean / DisposableBean 接口的回调方法;
  3. 最后再执行在 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: 指定哪些异常不会导致事务回滚。

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 的组合注解是什么?