Spring Boot 核心注解

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();
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容