SpringApplication 类提供了一种方便的方法来引导从 main() 方法启动的 Spring 应用程序。在许多情况下,可以使用静态 SpringApplication.run 方法,如以下示例所示:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@SpringBootApplication
@SpringBootApplication
封装了@Configuration
、@EnableAutoConfiguration
和@ComponentScan
注解及其默认属性。
@EnableAutoConfiguration
@EnableAutoConfiguration
告诉 Spring Boot 根据您添加的 jar 依赖项“猜测”您想要如何配置 Spring。例如,如果添加了spring-boot-starter-web
依赖,则还会添加 Tomcat 和 Spring MVC,因此自动配置假定您正在开发 Web 应用程序并相应地设置 Spring。
请注意,我们必须将此注释与 @Configuration 一起使用:
@Configuration
@EnableAutoConfiguration
class VehicleFactoryConfig {}
声明@EnableAutoConfiguration
注解的类的包被视为默认包。因此,我们应该始终在根包中应用 @EnableAutoConfiguration
注解,以便可以==检查每个子包和类==。
此外,@EnableAutoConfiguration注释提供了两个参数来手动排除任何参数:
@EnableAutoConfiguration(exclude={JdbcTemplateAutoConfiguration.class})
我们可以使用 exceptName 来定义要从自动配置中排除的类名的完全限定列表:
@EnableAutoConfiguration(excludeName = {"org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration"})
@ComponentScan
在开发应用程序时,我们需要告诉 Spring 框架寻找 Spring 管理的组件。@ComponentScan
使 Spring 能够扫描配置、控制器、服务和我们定义的其他组件等内容。
特别是,@ComponentScan
注解与@Configuration
注解一起使用来指定Spring扫描组件的包:
或者,Spring也可以从指定的包开始扫描,我们可以使用basePackageClasses()
或basePackages()
来定义。如果没有指定包,那么它会将声明 @ComponentScan
注解的类的包视为起始包:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Configuration
@ComponentScan(basePackages = {"com.example.componentscanautoconfigure.healthcare", "com.example.componentscanautoconfigure.employee"}, basePackageClasses = MyClass.class)
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Spring 搜索指定的包及其所有子包以查找使用 @Configuration
注解的类。此外,Configuration
类可以包含@Bean
注释,它将方法注册为 Spring 应用程序上下文中的bean。之后,@ComponentScan
注释可以自动检测此类bean:
@Configuration
public class Hospital {
@Bean
public Doctor getDoctor() {
return new Doctor();
}
}
此外,@ComponentScan 注解还可以扫描、检测和注册使用 @Component
、@Controller
、@Service
和 @Repository
注解的类的 Bean。
例如,我们可以创建一个 Employee
类作为组件,可以通过 @ComponentScan
注解进行扫描:
@Component("employee")
public class Employee {
// ...
}
@Configuration
@Configuration
是一个类级注释,指示对象是 bean 定义的源。 @Configuration
类通过@Bean
注解的方法声明bean。对 @Configuration
类上的 @Bean
方法的调用也可用于定义 bean 间的依赖关系。
当 Bean 之间存在依赖关系时,表达该依赖关系就像让一个 Bean 方法调用另一个 Bean 方法一样简单,如以下示例所示:
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
return new BeanOne(beanTwo());
}
@Bean
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
在以上的示例中,beanOne 通过构造函数注入接收对 beanTwo 的引用。
注意
这种声明 Bean 间依赖关系的方法仅在@Configuration
类中声明@Bean
方法时才有效。您不能使用普通的@Component
类来声明 bean 间的依赖关系。
考虑以下示例,其中显示 @Bean 注解的方法被调用两次:
@Configuration
public class AppConfig {
@Bean
public ClientService clientService1() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientService clientService2() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientDao clientDao() {
return new ClientDaoImpl();
}
}
clientDao()
已在 clientService1()
中调用一次,并在 clientService2()
中调用一次。由于此方法创建 ClientDaoImpl
的新实例并返回它,因此您通常期望有两个实例(每个服务一个)。这肯定是有问题的:==在 Spring 中,实例化的 beans 默认情况下具有单例范围(singleton scope)==。这就是神奇之处:所有 @Configuration
类在启动时都使用 CGLIB 进行子类化。在子类中,子方法在调用父方法并创建新实例之前,首先检查容器中是否有任何缓存(作用域)bean。
注意
根据 bean 的范围,行为可能会有所不同。我们这里讨论的是单例。没有必要将 CGLIB 添加到类路径中,因为 CGLIB 类在 org.springframework.cglib 包下重新打包,并直接包含在 spring-core JAR 中。
TIP
由于 CGLIB 在启动时动态添加功能,因此存在一些限制。特别是,配置类不能是最终的。但是,配置类上允许使用任何构造函数,包括使用@Autowired
或用于默认注入的单个非默认构造函数声明。如果您希望避免任何 CGLIB 施加的限制,请考虑在非
@Configuration
类上声明您的@Bean
方法(例如,在普通的@Component
类上)或通过使用@Configuration(proxyBeanMethods = false)
注解您的配置类。@Bean
方法之间的跨方法调用不会被拦截,因此您必须完全依赖构造函数或方法级别的依赖注入。
@Bean
@Bean
注解用于指示一个方法实例化、配置和初始化一个由Spring IoC容器管理的新对象。
使用@Configuration
注释一个类表明它的主要目的是作为bean定义的来源。此外,@Configuration
类允许通过调用同一类中的其他 @Bean
方法来定义 bean 之间的依赖关系。最简单的 @Configuration
类如下所示:
@Configuration
public class AppConfig {
@Bean
public MyServiceImpl myService() {
return new MyServiceImpl();
}
}
前面的 AppConfig 类相当于以下 Spring <beans/>
XML:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
完整的 @Configuration 与“精简”@Bean 模式?
当 @Bean 方法在未使用 @Configuration 注释的类中声明时,它们被称为以“lite”模式处理。在未使用 @Configuration 注释的 bean 上声明的 Bean 方法被认为是“lite”,包含类的主要目的不同,而 @Bean 方法是一种额外的好处。例如,服务组件可以通过每个适用组件类上的附加 @Bean 方法向容器公开管理视图。在这种场景下,@Bean方法是一种通用的工厂方法机制。
与完整的 @Configuration 不同,精简的 @Bean 方法无法声明 bean 间的依赖关系。相反,它们对包含组件的内部状态进行操作,并且可以选择对它们可能声明的参数进行操作。因此,这样的 @Bean 方法不应调用其他 @Bean 方法。每个这样的方法实际上只是特定 bean 引用的工厂方法,没有任何特殊的运行时语义。这里的积极副作用是,在运行时不必应用 CGLIB 子类化,因此在类设计方面没有限制(即,包含的类可以是最终类,等等)。
在常见场景中,@Bean 方法将在 @Configuration 类中声明,确保始终使用“完整”模式,并且跨方法引用因此被重定向到容器的生命周期管理。这可以防止通过常规 Java 调用意外调用相同的 @Bean 方法,这有助于减少在“精简”模式下操作时难以追踪的细微错误。
您可以指定使用 @Bean 注释定义的 bean 应该具有特定的范围。您可以使用 Bean 作用域部分中指定的任何标准作用域。
默认范围是singleton
,但您可以使用 @Scope
注释覆盖它,如以下示例所示:
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
下表描述了支持的范围:
Scope | 描述 |
---|---|
singleton | 默认)将单个 bean 定义范围限定为每个 Spring IoC 容器的单个对象实例。 |
prototype | 将单个 bean 定义的范围限定为任意数量的对象实例。 |
request | 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。仅在 Web 感知的 Spring ApplicationContext 上下文中有效。 |
session | 将单个 bean 定义的范围限定为 HTTP Session 的生命周期。仅在 Web 感知的 Spring ApplicationContext 上下文中有效。 |
application | 将单个 bean 定义的范围限定为 ServletContext 的生命周期。仅在 Web 感知的 Spring ApplicationContext 上下文中有效。 |
websocket | 将单个 bean 定义的范围限定为 WebSocket 的生命周期。仅在 Web 感知的 Spring ApplicationContext 上下文中有效。 |
默认情况下,配置类使用 @Bean 方法的名称作为结果 bean 的名称。但是,可以使用 name 属性覆盖此功能,如以下示例所示:
@Configuration
public class AppConfig {
@Bean("myThing")
public Thing thing() {
return new Thing();
}
}
正如命名 Bean 中所讨论的,有时需要为单个 bean 指定多个名称,也称为 bean 别名。为此,@Bean 注释的 name 属性接受一个 String 数组。以下示例显示如何为 Bean 设置多个别名:
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
有时,提供 Bean 的更详细的文本描述会很有帮助。当出于监视目的公开 Bean(可能通过 JMX)时,这尤其有用。
要向@Bean添加描述,可以使用@Description注释,如以下示例所示:
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}