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();
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容