Spring Boot非常简单容易上手,它隐藏了很多内容而不需要你去关心。但对于一个好的开发人员也许希望知道Spring Boot自动配置背后到底发生了什么?

Spring Boot并不属于一种新的技术,只不过Spring Boot的启动器帮我们配置了若干个被Spring管理的bean,当我们的项目依赖这些jar并启动Spring应用时,Spring的Container容器已经把jar包下的对象加以创建及管理了。

简而言之,Spring Boot自动配置代表了一种基于类路径上存在的依赖关系自动配置Spring应用程序的方法。还可以通过定义消除自动配置类中包含的某些bean。这些可以使开发更快更容易。

1. 通过启动类创建Spring Boot应用

创建Spring Boot应用非常简单,只要创建一个包含main的启动类即可。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;@SpringBootApplication
public class App
{public static void main(String[] args){ApplicationContext ctx = SpringApplication.run(App.class, args);}
}

上面这个类被称为Spring Boot应用的启动类,它通过一个java的main()方法来引导和启动一个Spring应用。它通常包含了以下内容:

  • 创建一个Spring ApplicationContext实例。
  • 接收命令行参数并将其转为Spring属性。
  • 按照配置加载所有Spring Bean。可以根据项目需求进行其他操作。

2. @SpringBootApplication注解

这个注解其实是一个应用了3个注解的快捷方式。

2.1 @SpringBootConfiguration

@SpringBootConfiguration是在Spring Boot2中出现的一个新的注解。之前我们都是使用的 @Configuration注解,可以用 @Configuration来替换它,2个都是实现同样的功能。

它表示该类是一个配置类,应该对其进行扫描,以获得进一步的配置和bean定义。

2.2 @EnableAutoConfiguration

此注解用于启用Spring Application Context的自动配置,尝试猜测和配置您可能需要的bean。自动配置类通常基于您的类路径以及您定义的bean来应用。

自动配置尝试尽可能智能,并在您定义更多自己的配置时进行后退。您始终可以使用两种方法来手动排除任何您不想应用的配置:

  • 使用excludeName()
  • 使用spring.autoconfigure.exclude属性文件中的属性。

2.3 @ComponentScan

此注解提供了与Spring XML context:component-scan元素并行的支持。

无论是basePackageClasses()或basePackages()可以定义特定的软件包进行扫描。如果未定义特定包,则将从声明此注解的类的包进行扫描。

3.自定义自动配置

要创建自定义自动配置,我们需要创建一个注释为@Configuration的类并注册它。

让我们为MySQL数据源创建自定义配置:

@Configuration
public class MySQLAutoconfiguration {//...
}

下一个必须的步骤是通过在标准文件资源/ META-INF / spring.factories中的属性org.springframework.boot.autoconfigure.EnableAutoConfiguration下添加类的名称,将类注册为自动配置候选者:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.peterwanghao.samples.springboot.autoconfiguration.MySQLAutoconfiguration

如果我们希望我们的自动配置类优先于其他自动配置候选者,我们可以添加@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)注解。

自动配置是使用标有@Conditional注解的类和bean设计的,以便可以替换自动配置或其特定部分。

请注意,只有当应用程序中未定义自动配置的bean时,自动配置才有效。如果您定义了bean,那么将覆盖默认值。

3.1 基于类的条件注解

Class conditions允许我们指定使用@ConditionalOnClass注解指定的类,或者使用@ConditionalOnMissingClass注解来指定不存在于 classpath 上的类。

让我们指定只有存在类DataSource的情况下才会加载MySQLConfiguration,在这种情况下我们可以假设应用程序将使用数据库:

@Configuration
@ConditionalOnClass(DataSource.class)
public class MySQLAutoconfiguration {//...
}

3.2 基于Bean的条件注解

如果我们只想在指定的bean存在的情况下包含bean,我们可以使用@ConditionalOnBean和@ConditionalOnMissingBean注解。

举例说明,让我们将一个entityManagerFactory bean 添加到我们的配置类中,并指定如果存在一个名为dataSource的bean 并且尚未定义一个名为entityManagerFactory的 bean,我们就创建这个bean :

@Bean@ConditionalOnBean(name = "dataSource")@ConditionalOnMissingBeanpublic LocalContainerEntityManagerFactoryBean entityManagerFactory() {final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();em.setDataSource(dataSource());em.setPackagesToScan("com.peterwanghao.samples.springboot.autoconfiguration.example");em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());if (additionalProperties() != null) {em.setJpaProperties(additionalProperties());}return em;}

让我们配置一个只在尚未定义类型为JpaTransactionManager的bean时才会加载的transactionManager bean :

@Bean@ConditionalOnMissingBean(type = "JpaTransactionManager")JpaTransactionManager transactionManager(final EntityManagerFactory entityManagerFactory) {final JpaTransactionManager transactionManager = new JpaTransactionManager();transactionManager.setEntityManagerFactory(entityManagerFactory);return transactionManager;}

3.3 基于属性的条件注解

@ConditionalOnProperty注解用于指定是否配置将基于Spring环境属性的存在和值被加载。

首先,让我们为配置添加一个属性源文件,以确定从哪里读取属性:

@PropertySource("classpath:mysql.properties")
public class MySQLAutoconfiguration {//...
}

我们可以配置主DataSource bean,它将用于创建与数据库的连接,只有在存在名为usemysql的属性时才会加载它。

我们可以使用属性havingValue来指定必须匹配的usemysql属性的某些值。

如果usemysql属性设置为local,让我们使用默认值定义dataSource bean,该默认值连接到名为myDb的本地数据库:

@Bean@ConditionalOnProperty(name = "usemysql", havingValue = "local")@ConditionalOnMissingBeanpublic DataSource dataSource() {final DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=true&&serverTimezone=GMT%2B8");dataSource.setUsername("root");dataSource.setPassword("123456");return dataSource;}

如果usemysql属性设置为自定义,则数据源 bean将使用自定义属性值的数据库URL,用户和密码进行配置:

@Bean(name = "dataSource")@ConditionalOnProperty(name = "usemysql", havingValue = "custom")@ConditionalOnMissingBeanpublic DataSource dataSource2() {final DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl(env.getProperty("mysql.url"));dataSource.setUsername(env.getProperty("mysql.user") != null ? env.getProperty("mysql.user") : "");dataSource.setPassword(env.getProperty("mysql.pass") != null ? env.getProperty("mysql.pass") : "");return dataSource;}

该mysql.properties文件将包含usemysql属性:

usemysql=local

如果使用MySQLAutoconfiguration的应用程序希望覆盖默认属性,则它需要做的就是为mysql.properties文件中的mysql.url,mysql.user和mysql.pass属性添加不同的值以及添加usemysql = custom行。

3.4 基于资源的条件注解

添加@ConditionalOnResource注解意味着仅在存在指定资源时才加载配置。

让我们定义一个名为additionalProperties()的方法,该方法将返回一个Properties对象,该对象包含entityManagerFactory bean 使用的特定于Hibernate的属性,仅当存在资源文件mysql.properties时:

@ConditionalOnResource(resources = "classpath:mysql.properties")@Conditional(HibernateCondition.class)final Properties additionalProperties() {final Properties hibernateProperties = new Properties();hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("mysql-hibernate.hbm2ddl.auto"));hibernateProperties.setProperty("hibernate.dialect", env.getProperty("mysql-hibernate.dialect"));hibernateProperties.setProperty("hibernate.show_sql",env.getProperty("mysql-hibernate.show_sql") != null ? env.getProperty("mysql-hibernate.show_sql"): "false");return hibernateProperties;}

我们可以将Hibernate特定的属性添加到mysql.properties文件中:

mysql-hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
mysql-hibernate.show_sql=true
mysql-hibernate.hbm2ddl.auto=create-drop

3.5 自定义条件

如果我们不想使用Spring Boot中的任何可用条件,我们还可以通过扩展SpringBootCondition类并重写getMatchOutcome()方法来定义自定义条件。

让我们为additionalProperties()方法创建一个名为HibernateCondition的条件,该方法将验证类路径上是否存在HibernateEntityManager类:

static class HibernateCondition extends SpringBootCondition {private static final String[] CLASS_NAMES = { "org.hibernate.ejb.HibernateEntityManager","org.hibernate.jpa.HibernateEntityManager" };@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {ConditionMessage.Builder message = ConditionMessage.forCondition("Hibernate");return Arrays.stream(CLASS_NAMES).filter(className -> ClassUtils.isPresent(className, context.getClassLoader())).map(className -> ConditionOutcome.match(message.found("class").items(Style.NORMAL, className))).findAny().orElseGet(() -> ConditionOutcome.noMatch(message.didNotFind("class", "classes").items(Style.NORMAL, Arrays.asList(CLASS_NAMES))));}}

然后我们可以将条件添加到additionalProperties()方法:

@Conditional(HibernateCondition.class)
Properties additionalProperties() {//...
}

3.6 申请条件

我们还可以通过添加@ConditionalOnWebApplication或@ConditionalOnNotWebApplication注释来指定只能在Web上下文内部/外部加载配置。

4. 测试自动配置

让我们创建一个非常简单的例子来测试我们的自动配置。我们将使用Spring Data 创建一个名为MyUser的实体类和一个MyUserRepository接口:

@Entity
public class MyUser {@Idprivate String email;public MyUser() {}public MyUser(String email) {super();this.email = email;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}}
public interface MyUserRepository extends JpaRepository<MyUser, String> {}

要启用自动配置,我们可以使用@SpringBootApplication或@EnableAutoConfiguration注解:

@SpringBootApplication
public class AutoconfigurationApplication {public static void main(String[] args) {SpringApplication.run(AutoconfigurationApplication.class, args);}
}

接下来,让我们编写一个保存MyUser实体的JUnit测试:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AutoconfigurationApplication.class)
@EnableJpaRepositories(basePackages = { "com.peterwanghao.samples.springboot.autoconfiguration.example" })
public class AutoconfigurationLiveTest {@Autowiredprivate MyUserRepository userRepository;@Testpublic void whenSaveUser_thenOk() {MyUser user = new MyUser("user@email.com");userRepository.save(user);}}

由于我们尚未定义DataSource配置,因此应用程序将使用我们创建的自动配置连接到名为myDb的MySQL数据库。

连接字符串包含createDatabaseIfNotExist = true属性,因此数据库不需要存在。但是,需要创建用户mysqluser或通过mysql.user属性指定的用户mysqluser。

我们可以检查应用程序日志,看看是否正在使用MySQL数据源:

10:31:47.092 [main] INFO  org.hibernate.Version - HHH000412: Hibernate Core {5.3.7.Final}
10:31:47.094 [main] INFO  org.hibernate.cfg.Environment - HHH000206: hibernate.properties not found
10:31:47.227 [main] INFO  o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
10:31:48.039 [main] INFO  org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL5InnoDBDialect
Hibernate: drop table if exists MyUser
Hibernate: create table MyUser (email varchar(255) not null, primary key (email)) engine=InnoDB
10:31:48.655 [main] INFO  o.h.t.s.internal.SchemaCreatorImpl - HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@3a0b6a'
10:31:48.666 [main] INFO  o.s.o.j.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default'
10:31:49.496 [main] INFO  o.s.s.c.ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'
10:31:49.569 [main] WARN  o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration$JpaWebMvcConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
10:31:49.701 [main] WARN  o.s.b.a.t.ThymeleafAutoConfiguration$DefaultTemplateResolverConfiguration - Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)
10:31:50.091 [main] INFO  c.p.s.s.a.AutoconfigurationLiveTest - Started AutoconfigurationLiveTest in 4.803 seconds (JVM running for 5.519)
Hibernate: select myuser0_.email as email1_0_0_ from MyUser myuser0_ where myuser0_.email=?
Hibernate: insert into MyUser (email) values (?)
10:31:50.279 [Thread-2] INFO  o.s.s.c.ThreadPoolTaskExecutor - Shutting down ExecutorService 'applicationTaskExecutor'
10:31:50.281 [Thread-2] INFO  o.s.o.j.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
10:31:50.282 [Thread-2] INFO  o.h.t.s.i.SchemaDropperImpl$DelayedDropActionImpl - HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
Hibernate: drop table if exists MyUser

5. 禁用自动配置类

如果我们想要从加载中排除自动配置,我们可以将带有exclude或excludeName属性的@EnableAutoConfiguration注解添加到配置类:

@Configuration
@EnableAutoConfiguration(exclude={MySQLAutoconfiguration.class})
public class AutoconfigurationApplication {//...
}

禁用特定自动配置的另一个方法是设置spring.autoconfigure.exclude属性:

spring.autoconfigure.exclude=com.peterwanghao.samples.springboot.autoconfiguration.MySQLAutoconfiguration

6. 结论

在本教程中,我们介绍了Spring Boot是如何自动加载配置类,以及背后所隐藏的具体实现。展示了如何创建自定义Spring Boot自动配置。

Spring Boot四大神器之Auto Configuration相关推荐

  1. Spring Boot:四大神器之CLI

    文章目录 简介 优势 一.安装 Spring Boot CLI 二.测试 2.1 Spring Boot CLI 创建 Hello World 示例 2.2 @Grab 2.3 @ Controlle ...

  2. Spring Boot四大神器之CLI

    1.简介 Spring Boot CLI 为Spring Cloud 提供了Spring Boot 命令行功能.您可以编写groovy脚本来运行Spring Cloud 组件应用程序(例如@enabl ...

  3. Spring Boot四大神器之Starter

    1.概述 依赖管理是任何复杂项目的关键方面.手动完成这些操作并不理想; 你花在它上面的时间越多,你在项目的其他重要方面所花费的时间就越少. 构建Spring Boot启动器是为了解决这个问题.Star ...

  4. Spring Boot开发利器之STS(Spring Tool Suite 4)下载与安装

    Spring Boot开发利器之STS(Spring Tool Suite 4)下载与安装 背景 正文 前置条件 下载 安装 背景 在使用eclipse进行Spring Boot或Spring Clo ...

  5. 第二章 Spring Boot四大核心组件

    文章目录 前言 一.Spring Boot Starter 1.1 Starter的应用示例 1.2 Spring Boot之前的Thymeleaf和Mybatis应用 1.2.1 Thymeleaf ...

  6. Springboot监控之一:SpringBoot四大神器之Actuator之3-springBoot的监控和管理--指标说明...

    Spring Boot包含很多其他的特性,它们可以帮你监控和管理发布到生产环境的应用.你可以选择使用HTTP端点,JMX或远程shell(SSH或Telnet)来管理和监控应用.审计(Auditing ...

  7. java.vm.info_SpringBoot四大神器之Actuator的使用小结

    序 Spring Boot有四大神器,分别是auto-configuration.starters.cli.actuator,本文主要讲actuator.actuator是spring boot提供的 ...

  8. Spring Boot 四大核心机制

    起步依赖机制:通过起步依赖机制(Starter),简化jar包的引用,解决jar版本冲突问题. 自动配置:可以实现简单配置,甚至是零配置,就能搭建整套框架. StringBoot CLI:一种命令工具 ...

  9. 十三、机器学习四大神器之XGBoost、LightGBM、Catboost和NGBoost

    前言: XGBoost.LightGBM 和 Catboost 是三个基于 GBDT(Gradient Boosting Decision Tree)代表性的算法实现 GBDT 是机器学习中的一个非常 ...

最新文章

  1. redistemplate 设置永不过期_“密码已过期,拒绝访问!”
  2. 【转】强大的B树B+树
  3. TCP/IP 协议簇的逐层封装
  4. 不想当全栈的设计师不是_但我不想成为产品设计师
  5. dataframe修改数据_数据处理进阶pandas入门(一)
  6. 读《NoSQL精粹》前三章有感
  7. 酷酷跑真有java游戏吗_JAVA版光影分享【仅此一次】下
  8. 实验四 图像复原及几何校正
  9. Win10如何下载安装Java,配置环境变量,并顺利的使用系统发育树编辑软件figtree,需要java环境的软件都可依此教程下载安装配置
  10. Build/Launch EDKII emulator in Windows and Linux:编译/运行Windows和Linux环境下EDKII模拟器[4]
  11. c语言大作业点歌系统,基于C语言的KTV点歌系统
  12. 微擎跳过云平台_2018年10月微擎安装之阿里云懒人教程篇
  13. 计算机应用基础客观答案,20春国家开放大学计算机应用基础客观题资料参考答案...
  14. 信安软考 第十二章 网络安全审计技术
  15. 准备工作(正则表达式学习)
  16. JZOJ2020年8月11日提高组T3 页
  17. 阿里云服务器糟挖矿程序攻击
  18. VL6810X驱动 距离和光线传感器 stm32 HAL库
  19. 如何恢复丢失的桌面文件
  20. Java:Excel模板下载

热门文章

  1. 2022年南京大学计算机拔尖班初试考后感想
  2. 电子邮箱是什么,电子邮箱注册值得入手的邮箱都在这!
  3. 转:比谎言更可怕的,是对真相视而不见
  4. Proteus8.15(集电路仿真、PCB设计件和虚拟模型仿真于一体)工具的安装使用
  5. 短视频剪辑软件分享,短视频剪辑软件这几个很不错。​
  6. 宾馆如何锁定计算机,酒店密码锁怎么反锁 公寓酒店密码门锁使用操作说明
  7. 线性代数-MIT 18.06-7(a)
  8. DIY一个测量心率装置
  9. 爬取钉钉在B站卑微道歉视频弹幕,做成词云
  10. 苹果2022春季新品发布会 苹果春季新品发布会直播地址