Spring 知识 -- Spring @Bean 和 @Component

本文最后更新于:4 个月前

我的理解

当学习 Spring 框架的时候,我们会使用 Java configuration + annotation 的方法配置依赖,例如使用 @Component 注解在 Service,dao,mapper 等类, 这样来注册给 IoC 容器,让它来管理这个 Bean 并且自动装配所需要的依赖

但是有个问题,@Component 也是用于 Bean 上,那么 @Bean 注解又是什么呢?

首先我们要知道 @Bean 在什么情况下会用到:

对于我们自己手动写的类,可能直接在类上加入 @Component 注解就可以直接让 Spring IoC 容器来管理对象的产生了

但是,对于需要用到的第三方的组件,这种情况下,除非将它的开源的代码重新整合到项目里面,否则我没有办法在它的 jar 包中的 class 文件反编译后的 Java 文件的类上加入 @Component 注解,但是这种整合源码的方法肯定是不可能的

而如果不加入 @Component ,我们自己去 new 这个对象也可以,这个时候就需要我们自己去管理这个对象,相当于做了一部分 IoC 容器做到事情,这样还是不太好

而且,如果这个对象还需要一些依赖,有些依赖甚至还需要从 Spring IoC 容器管理的依赖去取,自己管理的方法就不行了

也就是说,我们需要自己完成这个第三方对象的创建,但是,虽然创建我只需要 new Class() 就可以了,但是我还需要把这个创建好的对象交给 Spring IoC 容器来管理

此时,我们就可以在配置类上加上 @Configuration 注解,然后在创建对象的方法上面加上 @Bean 注解 (并且这个方法返回这个对象),这样就把我们手动生成的对象交给 Spring 的 IoC 容器进行管理了,如果这个类还需要依赖,只要你把所需要的依赖作为参数声明到方法上, Spring IoC 容器就可以将依赖自动传给这个方法,进而把依赖注入给这个类


官方文档

The central artifacts in Spring’s new Java-configuration support are @Configuration-annotated classes and @Bean-annotated methods.

Spring 的新 Java Configuration支持中的主要构件是 @Configuration 注释的类和 @Bean 注释的方法。

这句话意思是,@Configuration 和 @Bean 是 Spring 的 Java Configuration 编码方式中提供支持的

The @Bean annotation is used to indicate that a method instantiates, configures, and initializes a new object to be managed by the Spring IoC container. For those familiar with Spring’s XML configuration, the @Bean annotation plays the same role as the element. You can use @Bean-annotated methods with any Spring @Component. However, they are most often used with @Configuration beans.

@Bean批注用于指示方法实例化,配置和初始化要由Spring IoC容器管理的新对象。对于那些熟悉Spring的 XML配置的人来说,@ Bean注释与元素具有相同的作用。您可以将@Bean注释的方法与任何Spring @Component一起使用。但是,它们最常与@Configuration bean一起使用。

这段话是说,@Bean 可以等同于使用 Spring schema 编码方式中的 xml 中的 <bean>

Annotating a class with @Configuration indicates that its primary purpose is as a source of bean definitions. Furthermore, @Configuration classes let inter-bean dependencies be defined by calling other @Bean methods in the same class.

用@Configuration注释类表示该类的主要目的是作为Bean定义的来源。此外,@Configuration类允许通过调用同一类中的其他@Bean方法来定义Bean间的依赖关系。

Full @Configuration vs “lite” @Bean mode?

When @Bean methods are declared within classes that are not annotated with @Configuration, they are referred to as being processed in a “lite” mode. Bean methods declared in a @Component or even in a plain old class are considered to be “lite”, with a different primary purpose of the containing class and a @Bean method being a sort of bonus there. For example, service components may expose management views to the container through an additional @Bean method on each applicable component class. In such scenarios, @Bean methods are a general-purpose factory method mechanism.

如果在未使用@Configuration注释的类中声明@Bean方法,则将它们称为以“精简”模式进行处理。在@Component或甚至在 POJO 中声明的Bean方法被认为是“精简版”,其中包含类具有不同的主要用途,而@Bean方法在那里具有一定的优势。例如,Service 组件中可以通过每个适用组件类上的其他@Bean方法将管理视图公开给容器。在这种情况下,@ Bean方法是一种通用的工厂方法机制。

Unlike full @Configuration, lite @Bean methods cannot declare inter-bean dependencies. Instead, they operate on their containing component’s internal state and, optionally, on arguments that they may declare. Such a @Bean method should therefore not invoke other @Bean methods. Each such method is literally only a factory method for a particular bean reference, without any special runtime semantics. The positive side-effect here is that no CGLIB subclassing has to be applied at runtime, so there are no limitations in terms of class design (that is, the containing class may be final and so forth).

与完整的@Configuration不同,lite @Bean方法无法声明Bean之间的依赖关系。取而代之的是,它们在其包含组件的内部状态上进行操作,并且还可以根据可能声明的自变量进行操作。因此,一个@Bean方法不应调用其他@Bean方法。每个此类方法实际上只是针对特定bean引用的工厂方法,而没有任何特殊的运行时语义。这里的积极副作用是,不必在运行时应用CGLIB子类,因此在类设计方面没有任何限制(即,包含类可以是最终类,依此类推)。

In common scenarios, @Bean methods are to be declared within @Configuration classes, ensuring that “full” mode is always used and that cross-method references therefore get redirected to the container’s lifecycle management. This prevents the same @Bean method from accidentally being invoked through a regular Java call, which helps to reduce subtle bugs that can be hard to track down when operating in “lite” mode.

在常见情况下,@ Bean方法将在@Configuration类中声明,以确保始终使用“完全”模式,因此跨方法引用将重定向到容器的生命周期管理。这样可以防止通过常规Java调用意外地调用同一@Bean方法,这有助于减少在“精简”模式下运行时难以跟踪的细微错误。

@Bean 使用

You can use the @Bean annotation in a @Configuration-annotated or in a @Component-annotated class.

你可以在 @Configuration 注释的类或 @Component 注释的类中使用@Bean注释。

例如

@Configuration
public class AppConfig {

    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

However, this limits the visibility for advance type prediction to the specified interface type (TransferService). Then, with the full type (TransferServiceImpl) known to the container only once, the affected singleton bean has been instantiated. Non-lazy singleton beans get instantiated according to their declaration order, so you may see different type matching results depending on when another component tries to match by a non-declared type (such as @Autowired TransferServiceImpl, which resolves only once the transferService bean has been instantiated).

但是,这将高级类型推断的功能的可见性限制为指定的接口类型(TransferService)。然后,仅使容器明白完整类型(TransferServiceImpl)一次,就可以实例化受影响的单例bean。非懒惰单例bean根据其声明顺序实例化,因此您可能会看到不同的类型匹配结果,具体取决于另一个组件何时尝试通过未声明的类型进行匹配(例如@Autowired TransferServiceImpl,仅当transferService bean具有被实例化)。

If you consistently refer to your types by a declared service interface, your @Bean return types may safely join that design decision. However, for components that implement several interfaces or for components potentially referred to by their implementation type, it is safer to declare the most specific return type possible (at least as specific as required by the injection points that refer to your bean).

如果您通过声明的服务接口一致地引用同一类型,则@Bean返回类型可以安全地加入该设计决策。但是,对于实现多个接口的组件或由其实现类型潜在引用的组件,声明可能的最具体的返回类型(至少与引用您的bean的注入点所要求的具体类型一样)更为安全。

A @Bean-annotated method can have an arbitrary number of parameters that describe the dependencies required to build that bean. For instance, if our TransferService requires an AccountRepository, we can materialize that dependency with a method parameter, as the following example shows:

@Bean注释的方法可以具有任意数量的参数,这些参数描述构建该bean所需的依赖关系。例如,如果我们的TransferService需要一个AccountRepository,则可以使用方法参数来实现该依赖关系,如以下示例所示:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration 的使用

@Configuration is a class-level annotation indicating that an object is a source of bean definitions. @Configuration classes declare beans through public @Bean annotated methods. Calls to @Bean methods on @Configuration classes can also be used to define inter-bean dependencies.

@Configuration是类级别的注释,指示对象是Bean定义的源。@Configuration类通过公共@Bean注释方法声明bean。对@Configuration类的@Bean方法的调用也可以用于定义Bean之间的依赖关系。

This method of declaring inter-bean dependencies works only when the @Bean method is declared within a @Configuration class. You cannot declare inter-bean dependencies by using plain @Component classes.

仅当在@Configuration类中声明@Bean方法时,此声明bean间依赖性的方法才有效。您不能使用普通的@Component类声明Bean间的依赖关系。