Spring Boot 2 原理篇

配套资料,免费下载
链接:https://pan.baidu.com/s/1jA217UgqXpONi_fV-aOzqw
提取码:bm2g
复制这段内容后打开百度网盘手机App,操作更方便哦

第二章 Spring Boot 2 原理

2.1、自动依赖管理

我们打开 pom.xml 文件,会发现一段 parent 代码,这段代码属于 maven 的知识范畴,他的意思是当前的项目有一个父项目,具体的坐标如下所示,一般在 maven 中存在父项目,那么父项目可以对当前项目进行依赖管理,也就是说,我们平时开发中所需要的所有常见的依赖,在这里已经由 spring boot 管理好了,为了验证是不是这么一回事,我们按住 ctrl 单击 spring-boot-starter-parent 进入该文件。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.1</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

当进来该文件以后,细心的你可能会发现,这个配置文件中并没有具体的说明 spring boot 已经管理了哪些依赖,这个文件主要是对一些常见的属性进行配置,我们会在头部继续找到一段 parent 代码,看他的名字不难知道,他是专门负责依赖管理的,那我们继续进入该文件一探究竟。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.4.1</version>
</parent>

当进来该文件以后,我们可能会被长长的一串依赖配置所吸引,没错,这里就是用来管理依赖版本的,我在这里给大家列出来了。

<properties>
    <activemq.version>5.16.0</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.83</appengine-sdk.version>
    <artemis.version>2.15.0</artemis.version>
    <aspectj.version>1.9.6</aspectj.version>
    <assertj.version>3.18.1</assertj.version>
    <atomikos.version>4.0.6</atomikos.version>
    <awaitility.version>4.0.3</awaitility.version>
    <bitronix.version>2.1.4</bitronix.version>
    <build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
    <byte-buddy.version>1.10.18</byte-buddy.version>
    <caffeine.version>2.8.8</caffeine.version>
    <cassandra-driver.version>4.9.0</cassandra-driver.version>
    <classmate.version>1.5.1</classmate.version>
    <commons-codec.version>1.15</commons-codec.version>
    <commons-dbcp2.version>2.8.0</commons-dbcp2.version>
    <commons-lang3.version>3.11</commons-lang3.version>
    <commons-pool.version>1.6</commons-pool.version>
    <commons-pool2.version>2.9.0</commons-pool2.version>
    <couchbase-client.version>3.0.10</couchbase-client.version>
    <db2-jdbc.version>11.5.5.0</db2-jdbc.version>
    <dependency-management-plugin.version>1.0.10.RELEASE</dependency-management-plugin.version>
    <derby.version>10.14.2.0</derby.version>
    <dropwizard-metrics.version>4.1.16</dropwizard-metrics.version>
    <ehcache.version>2.10.6</ehcache.version>
    <ehcache3.version>3.9.0</ehcache3.version>
    <elasticsearch.version>7.9.3</elasticsearch.version>
    <embedded-mongo.version>2.2.0</embedded-mongo.version>
    <flyway.version>7.1.1</flyway.version>
    <freemarker.version>2.3.30</freemarker.version>
    <git-commit-id-plugin.version>3.0.1</git-commit-id-plugin.version>
    <glassfish-el.version>3.0.3</glassfish-el.version>
    <glassfish-jaxb.version>2.3.3</glassfish-jaxb.version>
    <groovy.version>2.5.14</groovy.version>
    <gson.version>2.8.6</gson.version>
    <h2.version>1.4.200</h2.version>
    <hamcrest.version>2.2</hamcrest.version>
    <hazelcast.version>4.0.3</hazelcast.version>
    <hazelcast-hibernate5.version>2.1.1</hazelcast-hibernate5.version>
    <hibernate.version>5.4.25.Final</hibernate.version>
    <hibernate-validator.version>6.1.6.Final</hibernate-validator.version>
    <hikaricp.version>3.4.5</hikaricp.version>
    <hsqldb.version>2.5.1</hsqldb.version>
    <htmlunit.version>2.44.0</htmlunit.version>
    <httpasyncclient.version>4.1.4</httpasyncclient.version>
    <httpclient.version>4.5.13</httpclient.version>
    <httpcore.version>4.4.14</httpcore.version>
    <infinispan.version>11.0.8.Final</infinispan.version>
    <influxdb-java.version>2.20</influxdb-java.version>
    <jackson-bom.version>2.11.3</jackson-bom.version>
    <jakarta-activation.version>1.2.2</jakarta-activation.version>
    <jakarta-annotation.version>1.3.5</jakarta-annotation.version>
    <jakarta-jms.version>2.0.3</jakarta-jms.version>
    <jakarta-json.version>1.1.6</jakarta-json.version>
    <jakarta-json-bind.version>1.0.2</jakarta-json-bind.version>
    <jakarta-mail.version>1.6.5</jakarta-mail.version>
    <jakarta-persistence.version>2.2.3</jakarta-persistence.version>
    <jakarta-servlet.version>4.0.4</jakarta-servlet.version>
    <jakarta-servlet-jsp-jstl.version>1.2.7</jakarta-servlet-jsp-jstl.version>
    <jakarta-transaction.version>1.3.3</jakarta-transaction.version>
    <jakarta-validation.version>2.0.2</jakarta-validation.version>
    <jakarta-websocket.version>1.1.2</jakarta-websocket.version>
    <jakarta-ws-rs.version>2.1.6</jakarta-ws-rs.version>
    <jakarta-xml-bind.version>2.3.3</jakarta-xml-bind.version>
    <jakarta-xml-soap.version>1.4.2</jakarta-xml-soap.version>
    <jakarta-xml-ws.version>2.3.3</jakarta-xml-ws.version>
    <janino.version>3.1.2</janino.version>
    <javax-activation.version>1.2.0</javax-activation.version>
    <javax-annotation.version>1.3.2</javax-annotation.version>
    <javax-cache.version>1.1.1</javax-cache.version>
    <javax-jaxb.version>2.3.1</javax-jaxb.version>
    <javax-jaxws.version>2.3.1</javax-jaxws.version>
    <javax-jms.version>2.0.1</javax-jms.version>
    <javax-json.version>1.1.4</javax-json.version>
    <javax-jsonb.version>1.0</javax-jsonb.version>
    <javax-mail.version>1.6.2</javax-mail.version>
    <javax-money.version>1.1</javax-money.version>
    <javax-persistence.version>2.2</javax-persistence.version>
    <javax-transaction.version>1.3</javax-transaction.version>
    <javax-validation.version>2.0.1.Final</javax-validation.version>
    <javax-websocket.version>1.1</javax-websocket.version>
    <jaxen.version>1.2.0</jaxen.version>
    <jaybird.version>3.0.9</jaybird.version>
    <jboss-logging.version>3.4.1.Final</jboss-logging.version>
    <jboss-transaction-spi.version>7.6.0.Final</jboss-transaction-spi.version>
    <jdom2.version>2.0.6</jdom2.version>
    <jedis.version>3.3.0</jedis.version>
    <jersey.version>2.32</jersey.version>
    <jetty-el.version>8.5.54</jetty-el.version>
    <jetty-jsp.version>2.2.0.v201112011158</jetty-jsp.version>
    <jetty-reactive-httpclient.version>1.1.4</jetty-reactive-httpclient.version>
    <jetty.version>9.4.35.v20201120</jetty.version>
    <jmustache.version>1.15</jmustache.version>
    <johnzon.version>1.2.8</johnzon.version>
    <jolokia.version>1.6.2</jolokia.version>
    <jooq.version>3.14.4</jooq.version>
    <json-path.version>2.4.0</json-path.version>
    <json-smart.version>2.3</json-smart.version>
    <jsonassert.version>1.5.0</jsonassert.version>
    <jstl.version>1.2</jstl.version>
    <jtds.version>1.3.1</jtds.version>
    <junit.version>4.13.1</junit.version>
    <junit-jupiter.version>5.7.0</junit-jupiter.version>
    <kafka.version>2.6.0</kafka.version>
    <kotlin.version>1.4.21</kotlin.version>
    <kotlin-coroutines.version>1.4.2</kotlin-coroutines.version>
    <lettuce.version>6.0.1.RELEASE</lettuce.version>
    <liquibase.version>3.10.3</liquibase.version>
    <log4j2.version>2.13.3</log4j2.version>
    <logback.version>1.2.3</logback.version>
    <lombok.version>1.18.16</lombok.version>
    <mariadb.version>2.7.1</mariadb.version>
    <maven-antrun-plugin.version>1.8</maven-antrun-plugin.version>
    <maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
    <maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
    <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
    <maven-dependency-plugin.version>3.1.2</maven-dependency-plugin.version>
    <maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
    <maven-enforcer-plugin.version>3.0.0-M3</maven-enforcer-plugin.version>
    <maven-failsafe-plugin.version>2.22.2</maven-failsafe-plugin.version>
    <maven-help-plugin.version>3.2.0</maven-help-plugin.version>
    <maven-install-plugin.version>2.5.2</maven-install-plugin.version>
    <maven-invoker-plugin.version>3.2.1</maven-invoker-plugin.version>
    <maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
    <maven-javadoc-plugin.version>3.2.0</maven-javadoc-plugin.version>
    <maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
    <maven-shade-plugin.version>3.2.4</maven-shade-plugin.version>
    <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
    <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
    <maven-war-plugin.version>3.3.1</maven-war-plugin.version>
    <micrometer.version>1.6.2</micrometer.version>
    <mimepull.version>1.9.13</mimepull.version>
    <mockito.version>3.6.28</mockito.version>
    <mongodb.version>4.1.1</mongodb.version>
    <mssql-jdbc.version>8.4.1.jre8</mssql-jdbc.version>
    <mysql.version>8.0.22</mysql.version>
    <nekohtml.version>1.9.22</nekohtml.version>
    <neo4j-java-driver.version>4.1.1</neo4j-java-driver.version>
    <netty.version>4.1.55.Final</netty.version>
    <netty-tcnative.version>2.0.35.Final</netty-tcnative.version>
    <oauth2-oidc-sdk.version>8.23.1</oauth2-oidc-sdk.version>
    <nimbus-jose-jwt.version>9.1.3</nimbus-jose-jwt.version>
    <ojdbc.version>19.3.0.0</ojdbc.version>
    <okhttp3.version>3.14.9</okhttp3.version>
    <oracle-database.version>19.8.0.0</oracle-database.version>
    <pooled-jms.version>1.2.1</pooled-jms.version>
    <postgresql.version>42.2.18</postgresql.version>
    <prometheus-pushgateway.version>0.9.0</prometheus-pushgateway.version>
    <quartz.version>2.3.2</quartz.version>
    <querydsl.version>4.4.0</querydsl.version>
    <r2dbc-bom.version>Arabba-SR8</r2dbc-bom.version>
    <rabbit-amqp-client.version>5.10.0</rabbit-amqp-client.version>
    <reactive-streams.version>1.0.3</reactive-streams.version>
    <reactor-bom.version>2020.0.2</reactor-bom.version>
    <rest-assured.version>3.3.0</rest-assured.version>
    <rsocket.version>1.1.0</rsocket.version>
    <rxjava.version>1.3.8</rxjava.version>
    <rxjava-adapter.version>1.2.1</rxjava-adapter.version>
    <rxjava2.version>2.2.20</rxjava2.version>
    <saaj-impl.version>1.5.2</saaj-impl.version>
    <selenium.version>3.141.59</selenium.version>
    <selenium-htmlunit.version>2.44.0</selenium-htmlunit.version>
    <sendgrid.version>4.6.8</sendgrid.version>
    <servlet-api.version>4.0.1</servlet-api.version>
    <slf4j.version>1.7.30</slf4j.version>
    <snakeyaml.version>1.27</snakeyaml.version>
    <solr.version>8.5.2</solr.version>
    <spring-amqp.version>2.3.2</spring-amqp.version>
    <spring-batch.version>4.3.1</spring-batch.version>
    <spring-data-bom.version>2020.0.2</spring-data-bom.version>
    <spring-framework.version>5.3.2</spring-framework.version>
    <spring-hateoas.version>1.2.2</spring-hateoas.version>
    <spring-integration.version>5.4.2</spring-integration.version>
    <spring-kafka.version>2.6.4</spring-kafka.version>
    <spring-ldap.version>2.3.3.RELEASE</spring-ldap.version>
    <spring-restdocs.version>2.0.5.RELEASE</spring-restdocs.version>
    <spring-retry.version>1.3.0</spring-retry.version>
    <spring-security.version>5.4.2</spring-security.version>
    <spring-session-bom.version>2020.0.1</spring-session-bom.version>
    <spring-ws.version>3.0.10.RELEASE</spring-ws.version>
    <sqlite-jdbc.version>3.32.3.3</sqlite-jdbc.version>
    <sun-mail.version>1.6.5</sun-mail.version>
    <thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
    <thymeleaf-extras-data-attribute.version>2.0.1</thymeleaf-extras-data-attribute.version>
    <thymeleaf-extras-java8time.version>3.0.4.RELEASE</thymeleaf-extras-java8time.version>
    <thymeleaf-extras-springsecurity.version>3.0.4.RELEASE</thymeleaf-extras-springsecurity.version>
    <thymeleaf-layout-dialect.version>2.5.1</thymeleaf-layout-dialect.version>
    <tomcat.version>9.0.41</tomcat.version>
    <unboundid-ldapsdk.version>4.0.14</unboundid-ldapsdk.version>
    <undertow.version>2.2.3.Final</undertow.version>
    <versions-maven-plugin.version>2.8.1</versions-maven-plugin.version>
    <webjars-hal-browser.version>3325375</webjars-hal-browser.version>
    <webjars-locator-core.version>0.46</webjars-locator-core.version>
    <wsdl4j.version>1.6.3</wsdl4j.version>
    <xml-maven-plugin.version>1.0.2</xml-maven-plugin.version>
    <xmlunit2.version>2.7.0</xmlunit2.version>
</properties>

经过上边的分析以后,我们知道了,平时开发中常见的依赖关系和依赖导入已经不需要我们来进行维护了,因为 spring boot 他已经帮你维护了,那么,问题又来了?在这个列表中,请你找到 mysql 数据库的驱动包,你会发现他的版本是 8.0.22 ,而在实际的开发中,我们安装的数据库的版本可能远远低于这个版本,一般是5.x,那连接数据库的驱动也应该是5.x的,所以,这个问题就是,我们要是想要更换指定依赖的版本,该怎么解决呢?这里给大家提供了两种最常用的方法。

第一种: 找到当前你自己工程的 pom.xml 在依赖项中直接配置依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

第二种: 找到当前你自己工程的 pom.xml 在配置中直接覆盖父配置

<properties>
    <java.version>1.8</java.version>
    <mysql.version>5.1.49</mysql.version>
</properties>

既然 spring boot 已经帮我们进行了依赖管理,但是,这么多依赖总不能都生效吧,因此呢,spring boot 就提供了一种场景启动器的东西,每一个场景启动器都对应我们开发中的一个场景,而这个场景所需要的依赖就会的自动开启,举个例子,我们先来到自己的 pom.xml 中,在依赖中会发现这么一项依赖,我们称之为 web 场景启动器。只要你导入了 web 场景启动器,那么开发 web 所需要的依赖就会自动开启了,很方便吧。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

到这里,可能有同学会有疑问,spring boot 他都提供了哪些场景启动器,我怎么知道我以后要配置哪一个?细心的官方已经帮大家都整理好了。

原文地址:https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter

因此,以后你想要使用什么场景,只需要导入对应的场景启动器就可以了,但是,官方的场景启动器不可能把所有的第三方的技术都整合起来,技术发展日新月异,为了第三方发展的需要,spring boot 就是声明了,第三方你自己可以制作你自己的场景启动器,但是名字你不能和我官方整的一样,需要遵守命名规范 *-spring-boot-starter ,以后,大家在寻找场景启动器的时候,首先先去官方列表中查找,官方列表没有,就去第三方平台中查找,一般来说,知名且活跃度高的一些项目都会提供相对应的场景启动器。

2.2、自动配置注入

2.2.1、注入原理

为了了解 spring boot 在启动的过程中是如何帮我们进行配置信息自动注入的,我们首先来到主程序类 SpringBootQuickApplication

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

这个类是我们程序的启动入口,因此,我们需要从这里下手了解自动注入机制。我们按住 ctrl 单击 @SpringBootApplication 注解进入文件。

@SpringBootApplication
等同于
@SpringBootConfiguration/*这个注解只是代表是一个配置类,他跟 @Configuration 是一样的作用*/
@EnableAutoConfiguration/*重点*/
@ComponentScan("com.caochenlei.springbootquick") /*在启动的时候,spring boot 会自动扫描启动类所在包及其子包下所有组件*/

我们按住 ctrl 单击 @EnableAutoConfiguration 注解进入文件。

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

@AutoConfigurationPackage 代表自动配置包,我们进到这个注解文件中去。

@Import(AutoConfigurationPackages.Registrar.class)  //给容器中导入一个组件
public @interface AutoConfigurationPackage {}

利用Registrar给容器中导入一系列组件,将指定的一个包下的所有组件导入进来,哪个包呢?默认就是 SpringBootQuickApplication 所在包。

我们按住 ctrl 单击 AutoConfigurationImportSelector.class 进入文件。在这个文件中,我们需要定位到一段关键的的代码。

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

这段代码的含义大概是这样的,首先将所有场景的自动配置类都导入进来,不是当前场景所需要的自动配置类则排除掉,那么问题来了,所有的自动配置类在哪?这显然是一个问题,在这段代码中有一获取方法,那就是 getCandidateConfigurations(); 获取候选配置,我们点进入。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    ...
    return configurations;
}

我们再进入 loadFactoryNames 方法内部。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

我们再进入 loadSpringFactories 方法内部。

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
...
...    
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    result = new HashMap<>();
    try {
        Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                String[] factoryImplementationNames =
                    StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                    result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());
                }
            }
        }

        // Replace all lists with unmodifiable lists containing unique elements
        result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                          .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
        cache.put(classLoader, result);
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    return result;
}

他是从 META-INF/spring.factories 位置来加载一个文件,默认扫描我们当前系统里面所有 META-INF/spring.factories 位置的文件。

spring-boot-autoconfigure-2.4.1.jar 包内也有的 META-INF/spring.factories 配置:

官方网址:https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-auto-configuration-classes.html#auto-configuration-classes-from-autoconfigure-module

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter= ...

目前一共是130个自动配置类,版本不一样,这个数量多少会有些变化,我们采用DEBUG进行调试,发现最后只有23个自动配置类真正生效。

因为目前我们只开启了 web 这一个场景,所以里边生效的自动配置类大部分都是关于 web 场景的。

image-20210104123626614

我们以处理编码的自动配置类 org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration 进行讲解,打开此类:

@Configuration(proxyBeanMethods = false)/*说明这是一个配置类*/
@EnableConfigurationProperties(ServerProperties.class)/*开启 ServerProperties 配置属性,就相当于 ServerProperties 绑定了一个配置文件*/
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)/*如果是web应用,这个组件才会导入到容器中*/
@ConditionalOnClass(CharacterEncodingFilter.class)/*如果容器中有编码过滤器,这个组件才会导入容器中*/
/*判断属性文件中是否有以 server.servlet.encoding 开头的属性配置,默认是开启状态,即使没有我也认为你有*/
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
    
	private final Encoding properties;

	public HttpEncodingAutoConfiguration(ServerProperties properties) {
		this.properties = properties.getServlet().getEncoding();
	}

	@Bean/*向容器中注册字符编码过滤器*/
	@ConditionalOnMissingBean/*如果容器中没有这个组件,我才注册这个组件*/
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
		return filter;
	}
    
    ...
}

我们看看 ServerProperties.class 这个配置类中都有哪些默认配置

/*跟配置文件中的 server 前缀的属性进行了绑定,并且忽略了不知道的字段*/
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
    public static class Servlet {
        ...
		@NestedConfigurationProperty
		private final Encoding encoding = new Encoding();
        ...
	}
}

按住 CTRL 鼠标单击 Encoding 类,进入后如下:

public class Encoding {
	public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
	private Charset charset = DEFAULT_CHARSET;
    ...
}

我们会发现,spring boot已经帮我配置好了字符集编码过滤器得默认编码是 utf-8,所以,即使我们现在在页面中返回中文也不会乱码,怎么验证呢,找到 HelloController 修改代码如下;

@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        return "Hello World,你好 世界";
    }
}

重启应用,然后访问,你应该看到的效果是以下这样:

image-20210104151834312

那我们想要修改这个默认配置,我们应该怎么做?有两种办法,第一种就是,我们自己写一个编码过滤器然后使用 @Bean 注解注册到容器中,这样 spring boot的编码过滤器就不会生效了,但是这样很麻烦,我们往往会选择第二种,自己直接修改配置文件,因为配置文件和配置类进行了绑定,所以配置文件修改了就会覆盖 spring boot 设置的默认值,配置文件在 spring boot 中支持两个格式,一种是常见的 application.properties ,而另一种则是 application.yaml ,这两种各有千秋,具体选择哪个看个人喜好和项目团队要求了,接下来我就分别演示一下,如何使用配置文件修改默认编码。

第一种: application.properties

server.servlet.encoding.charset=ISO-8859-1

第二种: application.yaml

yaml教程请参考:https://caochenlei.blog.csdn.net/article/details/108544801,第三章关于yaml的教学

server:
  servlet:
    encoding:
      charset: ISO-8859-1

重启应用,然后访问,你应该看到的效果是以下这样:

image-20210104152801935

这说明,经过我们的修改,编码方式已经发生了改变,这时候又会引出另外一个问题,既然后两种配置方式可以选择,那么它们之间有没有先后顺序,在不同路径下谁又会先生效谁又会后生效。

2.2.2、配置顺序

同一级目录中顺序

application.properties先会被加载,application.yaml后会被加载。

因此,application.yaml中的同名配置会覆盖application.properties中的配置。

配置文件查找位置

  1. classpath 根路径
  2. classpath 根路径下config目录
  3. jar包当前目录
  4. jar包当前目录的config目录
  5. /config子目录的直接子目录

配置文件加载顺序

  1. 当前jar包内部的application.properties 和 application.yml
  2. 当前jar包内部的application-{profile}.properties 和 application-{profile}.yml(指定环境,后边介绍)
  3. 引用的外部jar包的application.properties 和 application.yml
  4. 引用的外部jar包的application-{profile}.properties 和 application-{profile}.yml

注意问题:指定环境优先,外部优先,后面的可以覆盖前面的同名配置项。

2.2.3、环境切换

如何切换指定环境

  • 配置文件定义不同环境:指定环境配置文件,application.yaml、application-dev.yaml、application-prod.yaml

  • 注解形式定义不同环境:为了方便多环境适配,springboot简化了profile功能。

@Configuration(proxyBeanMethods = false)
@Profile("prod")
public class ProductionConfiguration {

    // ...

}
  • 配置式激活环境,在application.yaml中激活
spring:
  profiles:
    active: dev
  • 命令行激活环境
java -jar xxx.jar --spring.profiles.active=prod

2.3、自定义启动器

我们现在已经学习了 spring boot 的核心知识,我们要是想要自己开发一个启动器该怎么做呢?

image-20210104162543355

image-20210104162900741

image-20210104163009224

image-20210104163123227

image-20210104163817409

image-20210104163334470

image-20210104163406751

image-20210104164039194

image-20210104164105882

image-20210104164126442

image-20210104164220346

<dependencies>
    <dependency>
        <groupId>com.caochenlei</groupId>
        <artifactId>hello-spring-boot-starter-autoconfigure</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

image-20210104164350225

删除部分无用文件,删除后效果如下:

image-20210104164637326

我们创建一个HelloService组件

image-20210104164812422

我们创建一个HelloProperties组件

image-20210104165321934

image-20210104165350392

@ConfigurationProperties(prefix = "hello")
public class HelloProperties {
    private String prefix;
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}
public class HelloService {
    @Autowired
    private HelloProperties helloProperties;

    public String sayHello(String name) {
        return helloProperties.getPrefix() + ":" + name + ":" + helloProperties.getSuffix();
    }
}

我们创建一个HelloServiceAutoConfiguration组件

20210104165858

//代表这是一个配置类
@Configuration
//开启HelloProperties.class的自动属性绑定
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
    @Bean//向容器中注册组件
    //只有容器中没有HelloService的时候才会注册该组件
    @ConditionalOnMissingBean(HelloService.class)
    public HelloService helloService() {
        return new HelloService();
    }
}

将自动配置类暴露给 spring boot ,好让他进行扫描加载

image-20210104183423036

META-INF/spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.caochenlei.hellospringbootstarterautoconfigure.auto.HelloServiceAutoConfiguration

image-20210104183522159

image-20210104171044619

在启动的时候可能会报错,那是因为单元测试的影响,所以,我们先对工程进行一下简单修改,去掉单元测试依赖和单元测试类

在 pom.xml 中删除插件项,去掉其它依赖,只保留 spring-boot-starter

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.caochenlei</groupId>
	<artifactId>hello-spring-boot-starter-autoconfigure</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>hello-spring-boot-starter-autoconfigure</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
	</dependencies>
</project>

image-20210104171544225

image-20210104171617675

image-20210104171640387

image-20210104171703829

image-20210104171737466

image-20210104171805616

接下来我们创建一个全新的项目来使用我们的启动器

image-20210104171857541

image-20210104172019188

image-20210104172034414

image-20210104172053152

修改 pom.xml 换上咱们自己的启动器

image-20210104172309537

再导入开发 web 场景的启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

image-20210104173301102

image-20210104173356210

在application.properties配置一下我们组件所需要的一些属性信息

hello.prefix=Hello
hello.suffix=World

image-20210104173020113

@RestController
public class HelloController {
    @Autowired
    private HelloService helloService;

    @RequestMapping("/hello")
    public String sayHello() {
        return helloService.sayHello("张三");
    }
}

启动应用进行访问,如果一切正常那么你将会看到:

image-20210104183905014

原文地址:https://caochenlei.blog.csdn.net

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Nacos 中的参数有很多,如:命名空间、分组名、服务名、保护阈值、服务路由类型、临时实例等,那这些参数都是什么意思?又该如何设置?接下来我们一起来盘它。 1.命名空间 在 Nacos 中通过命名空间(Namespace)+ 分组(Group)+服务名(Name)可以定位到一个唯一的服务实例。 命名
Nacos 支持两种 HTTP 服务请求,一个是 REST Template,另一个是 Feign Client。之前的文章咱们介绍过 Rest Template 的调用方式,主要是通过 Ribbon(负载均衡) + RestTemplate 实现 HTTP 服务调用的,请求的核心代码是这样的: @
Nacos 是 Spring Cloud Alibaba 中一个重要的组成部分,它提供了两个重要的功能:服务注册与发现和统一的配置中心功能。 服务注册与发现功能解决了微服务集群中,调用者和服务提供者连接管理和请求转发的功能,让程序的开发者无需过多的关注服务提供者的稳定性和健康程度以及调用地址,因为这
Spring Cloud Alibaba 是阿里巴巴提供的一站式微服务开发解决方案,目前已被 Spring Cloud 官方收录。而 Nacos 作为 Spring Cloud Alibaba 的核心组件之一,提供了两个非常重要的功能:服务注册中心(服务注册和发现)功能,和统一配置中心功能。 Nac
在 Nacos 的路由策略中有 3 个比较重要的内容:权重、保护阈值和就近访问。因为这 3 个内容都是彼此独立的,所以今天我们就单独拎出“保护阈值”来详细聊聊。 保护阈值 保护阈值(ProtectThreshold):为了防止因过多实例故障,导致所有流量全部流入剩余健康实例,继而造成流量压力将剩余健
前两天遇到了一个问题,Nacos 中的永久服务删除不了,折腾了一番,最后还是顺利解决了。以下是原因分析和解决方案,建议先收藏,以备不时之需。 临时实例和持久化实例是 Nacos 1.0.0 中新增了一个特性。临时实例和持久化实例最大的区别是健康检查的方式:临时实例使用客户端主动上报的健康检查模式,而
Spring Cloud Alibaba 技术体系中的 Nacos,提供了两个重要的功能:注册中心(服务注册与发现)功能和配置中心功能。 其中注册中心解决了微服务调用中,服务提供者和服务调用者的解耦,让程序开发者可以无需过多的关注服务提供者和调用者的运行细节,只需要通过 Nacos 的注册中心就可以
负载均衡通器常有两种实现手段,一种是服务端负载均衡器,另一种是客户端负载均衡器,而我们今天的主角 Ribbon 就属于后者——客户端负载均衡器。 服务端负载均衡器的问题是,它提供了更强的流量控制权,但无法满足不同的消费者希望使用不同负载均衡策略的需求,而使用不同负载均衡策略的场景确实是存在的,所以客
本篇文章为大家展示了如何解决Spring Cloud 服务冲突问题,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、背景...
本篇内容主要讲解“spring cloud服务的注册与发现怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“spri...
本篇内容介绍了“Dubbo怎么实现Spring Cloud服务治理 ”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处...
本篇内容主要讲解“SpringCloud相关面试题有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SpringCloud相...
如何分析Spring Cloud Ribbon、Spring Cloud Feign以及断路器,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希
这篇文章主要讲解了“springcloud微服务的组成部分有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“s...
这篇文章主要讲解了“SpringCloud的OpenFeign项目怎么创建”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习...
本篇内容主要讲解“spring cloud oauth3整合JWT后获取用户信息不全怎么办”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带...
怎样解析微服务架构SpringCloud,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。...
这篇文章主要介绍spring cloud中API网关的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、服务网关简介1、外观模式客户端...
本篇内容介绍了“Spring Cloud微服务的相关问题有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处...
本文小编为大家详细介绍“spring cloud config整合gitlab如何搭建分布式的配置中心”,内容详细,步骤清晰,细节处理妥当,希望这篇“spring cloud config整合gi...