Spring boot

Spring Boot 是由 Pivotal 团队提供的全新 框架 ,其设计目的是用来 简化 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员 不再需要定义样板化的配置 。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

SpringBoot 框架中还有两个非常重要的策略: 开箱即用 约定大于配置

开箱即用 :是指在开发过程中,通过在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期。这个特点使得开发人员摆脱了复杂的配置工作以及依赖的管理工作,更加专注于业务逻辑。

约定大于配置 :是一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。这一特点虽降低了部分灵活性,增加了BUG定位的复杂性,但减少了开发人员需要做出决定的数量,同时减少了大量的XML配置,并且可以将代码编译、测试和打包等工作自动化。

快速创建 Spring boot

1、官网创建

进入官网的后点击 Spring Initializr 也可以直接点 快速构建 Spring Boot

进入页面填写相关信息

<!--版本会从父依赖里面找,不需要设置,也可以设置,设置什么版本优先使用自己设置的-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 再次启动程序,(记得刷新 Maven依赖库)
  • 访问 http://localhost:8080 (出现以下信息配置成功,只是没有对应页面)
  • 3、修改启动端口号

    resources目录下的 application.properties 中添加如下配置

    # 更改项目启动端口
    server.port=9999
    

    修改后测试访问 http://localhost:9999 也能正常启动

    resources目录下添加 banner.txt 里面填写的内容可以替换掉启动 LOG

    Spring boot自动装配原理

    1、依赖管理

    项目依赖管理

    创建 SpringBoot 项目,都会导入一个父依赖,如下

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

    点开上面的依赖 里面还有一个父依赖,如下

    <!--这个依赖包含了基本上会用到依赖版本-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.4.5</version>
    </parent>
    

    点开 spring-boot-dependencies 可以看到许多的依赖版本

    <!--这个依赖很长,列出一小部分-->
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <modelVersion>4.0.0</modelVersion>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>2.4.5</version>
      <packaging>pom</packaging>
      <name>spring-boot-dependencies</name>
      <description>Spring Boot Dependencies</description>
      <url>https://spring.io/projects/spring-boot</url>
      <licenses>
        <license>
          <name>Apache License, Version 2.0</name>
          <url>https://www.apache.org/licenses/LICENSE-2.0</url>
        </license>
      </licenses>
      <developers>
        <developer>
          <name>Pivotal</name>
          <email>info@pivotal.io</email>
          <organization>Pivotal Software, Inc.</organization>
          <organizationUrl>https://www.spring.io</organizationUrl>
        </developer>
      </developers>
        <url>https://github.com/spring-projects/spring-boot</url>
      <properties>
        <activemq.version>5.16.1</activemq.version>
        <antlr2.version>2.7.7</antlr2.version>
        <appengine-sdk.version>1.9.88</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>
    

    几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制,所有我们基本上不需要导入依赖

    1、见到很多 spring-boot-starter-* : *就某种场景
    2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
    3、SpringBoot所有支持的场景,可以参考官网 Spring-Boot-starter 启动器

    4、见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
    5、所有场景启动器最底层的依赖

    <!--所有场景启动器最底层的依赖,所有场景都需要这个启动器-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.4.5</version>
        <scope>compile</scope>
    </dependency>
    

    1、引入依赖默认都可以不写版本
    2、引入非版本仲裁的jar,要写版本号。

    3、不想使用仲裁版本时,添加依赖,自己给出版本号就可以了

    2、自动配置

    自动配好Tomcat

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <version>2.4.5</version>
        <scope>compile</scope>
    </dependency>
    

    自动配好SpringMVC

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.6</version>
        <scope>compile</scope>
    </dependency>
    

    自动配好Web常见功能,如:字符编码问题,拦截器配置。

    默认的包结构

    主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来

    无需以前的包扫描配置

    想要改变扫描路径,@SpringBootApplication(scanBasePackages="com")

    也可以将主程序上的 @SpringBootApplication 注解修改为下面三条记录

    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan("com")
    这三个注解和 @SpringBootApplication(scanBasePackages="com") 等价
     * 2、配置类本身也是组件
     * 3、proxyBeanMethods:代理bean的方法
     *      Full(proxyBeanMethods = true)【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
     *      Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
     *      组件依赖必须使用Full模式默认。其他默认是否Lite模式
    @Configuration(proxyBeanMethods = false) 
    

    @Bean :向IOC容器注入一个类。

    @Component:将类注入 IOC 容器。

    //@Component 等价于 <bean id="user" class="com.wyx.pojo.User"/>
    @Component
    public class User {
        // @Value 等价于 <property name="name" value="王玉星"/> 
        @Value("但是公司")
        public String name;
    

    下面三个注解和 @Component一样,只是使用场景不同

    @Controller:标志这是一个Controller组件(控制器)

    @Service:标志这是一个Service组件(Service层)

    @Repository:标志这是一个Dao组件 (Dao层)

    @Bean 与 @Component 的区别

    仔细观察,发现上面的两个注解都是,向IOC 容器注入一个类

    但是一般他们的使用情况不太一样。一般@Confiuration配合@Bean使用,写在配置文件中,返回的是某一个对象的实例;而@Component是对应某一个类,配合@ComponentScan使用,然后让spring ioc容器实例化。而且使用@Confiugraion和@Bean, 会进行cglib增强,拿到的是一个代理对象;@Component拿到的是一个普通的java对象。

    @ComponentScan:扫描包

    //扫描 com.wyx.dao 包  将下面的User类注入IOC容器
    @componentScan("com.wyx.dao")
    public class config(){
        @Bean //方法名作为组件的id 返回类型就是组件类型,返回值就是容器中的实例
        public User user(){
            return new User();
    

    @Import: 合并配置类,类似于两个 beans.xml 合并

    @Import(User.class) //参数类型是一个数组,可以用{}来添加多个
    

    @Conditional:条件装配,满足条件才开始装配。

    @ImportResource:导入资源

    作用:以前的配置是使用配置文件 beans.xml配置,不想在使用配置类来重新写配置文件,只需要在类上面写如下配置,就可以将配置文件的东西解析出来。

    @ImportResource("classpath:beans.xml")
    

    @Component + @ConfigurationProperties

    1、在 application.properties 中添加配置属性

    user.name=王玉星
    user.sex=男
    user.age=20
    

    2、编写实体类

    package com.wyx.dao;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    @Component
    @ConfigurationProperties(prefix = "user")
    public class User {
        private String name;
        private String sex;
        private int age;
        public String getName() {
            return name;
        public void setName(String name) {
            this.name = name;
        public String getSex() {
            return sex;
        public void setSex(String sex) {
            this.sex = sex;
        public int getAge() {
            return age;
        public void setAge(int age) {
            this.age = age;
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", sex='" + sex + '\'' +
                    ", age=" + age +
    

    3、编写控制器

    package com.wyx.controller;
    import com.wyx.dao.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    @Controller
    public class UserController {
        @Autowired
        User user;
        @ResponseBody
        @RequestMapping("/user")
        public User user(){
            return user;
    

    4、测试方法

    从结果可以看出,application.properties 中配置的属性被加入了类

    第二种方法:将实体类中的 @Component 删除掉 ,在配置类上加入@EnableConfigurationProperties(User.class) 即可

    4、自动装配原理

    了解了Spring Boot的依赖,自动配置及其很多底层注解,下面我们开始深入了解自动装配原理。

    自动配置原理图

    1、自动包规则

    从原理图中可以看出 @EnableAutoConfiguration 自动导入配置下面的注解有 @AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class) 分别是 自动配置包和自动选择导入配置。

    自动配置包@AutoConfigurationPackage,下面有@Import(AutoConfigurationPackages.Registrar.class),点击 Registrar可以看到以下信息(部分)。

    //继承ImportBeanDefinitionRegistrar和DeterminableImports
    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        //重写它们的方法,利用new PackageImports(metadata).getPackageNames().toArray(new String[0]),将主程序启动类的目录及其下面的目录导入配置类的信息,从而实现自动配置包
        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    	//重写该方法将,将上一步获得的属性利用集合方式去重,并确定导入。
        @Override
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImports(metadata));
    

    2、自动配置选择器

    自动配置选择器,@Import(AutoConfigurationImportSelector.class)点开类,可以看到 selectImports 方法

    selectImports 方法接着调用了 getAutoConfigurationEntry方法,查看 getAutoConfigurationEntry方法

    //此方法是获取候选的配置进行一系列处理后返回给getAutoConfigurationEntry
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        //getCandidateConfigurations() 获取候选配置。
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        //通过调试发现经过这一步后发现 configurations 由130减少到27 
        configurations = getConfigurationClassFilter().filter(configurations);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    

    我们接着查看 getCandidateConfigurations 方法

    // 获取spring-boot-autoconfigure-2.4.5.jar 下的META-INF/spring.factories 将它加载进来
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        //利用Spring的工厂加载器加载 spring.factories 到类中
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                                                                             getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                        + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    

    查看 spring-boot-autoconfigure-2.4.5.jar 下的 META-INF/spring.factories 如下

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
    org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
    org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
    org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
    org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
    org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
    org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
    org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
    org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
    org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
    org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
    org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
    org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
    org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
    org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
    org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
    org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
    org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
    org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
    org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
    org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
    org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
    org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration,\
    org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
    org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
    org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
    org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
    org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
    org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
    org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration,\
    org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
    org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
    org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\
    org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
    org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
    org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
    org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
    org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
    org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
    org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
    org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
    org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
    org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
    org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
    org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
    org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
    org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
    org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
    org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
    

    观察spring.factories下面两张图片,发现开始是 21 结束时 151 刚好满足上面图片加载的 130 配置类加载的数量

    然后我们接着调试

    当走到以下位置时

    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        //getCandidateConfigurations() 获取候选配置。
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        //通过调试发现经过这一步后发现 configurations 由130减少到23 
        configurations = getConfigurationClassFilter().filter(configurations);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    

    由上面两张图,明显看出 configurations 大量减少,于是我们查看 getConfigurationClassFilter().filter();

    getConfigurationClassFilter 调试发现,这个方法会将需要过滤的配置信息封装到 configurationClassFilter 变量中

    private ConfigurationClassFilter getConfigurationClassFilter() {
        // configurationClassFilter 在类初始化时就是 null 所以一定会走
        if (this.configurationClassFilter == null) {
            // 获取过滤规则这里过滤规则 会调用Spring工厂加载器的loadFactories方法,规则具体参见源码,不做过多讲解
            /* 具体调用如下
    protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
    		return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, 		this.beanClassLoader);
            */ 下面的invokeAwareMethods查看源码说判断这个过滤规则需要用什么资源加载器加载,具体查看源码
            List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
            for (AutoConfigurationImportFilter filter : filters) {
                invokeAwareMethods(filter);
            // 将过滤规则和beanClassLoader加载器通过ConfigurationClassFilter
            this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
        return this.configurationClassFilter;
    

    上面的 configurationClassFilter 过滤规则就是将配置文件读取的(spring.factories)与 Spring 的 ioc容器通过过滤规则比较,如果有则不添加到过滤范围,否则添加。具体对比参见ConfigurationClassFilter类的构造器调用的 AutoConfigurationMetadataLoader.loadMetadata(classLoader),这个类(AutoConfigurationMetadataLoader)的主要功能就是将 spring-autoconfigure-metadata.properties(和spring.factories在同级目录)的信息用过滤规则过滤后注入 autoConfigurationMetadata 常量

    //这里是 ConfigurationClassFilter 的构造器,它下面封装了filter方法
    ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
        this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
        this.filters = filters;
    

    解释一下,getAutoConfigurationImportFilters(过滤规则)主要就是将容器中没有注入的容器的组件添加成规则,用于后面过滤。

    容器是否添加 这里使用的是底层注解 @Conditional 来判断是否向ioc容器注入组件

    查看filter 方法是将配置文件spring.factories 中读取的信息与上一步经过处理的configurationsautoConfigurationMetadata进行比较,如果相同则不保留,取它们不相同的值,并返回结果。如果全相同也返回结果,如果logger.isTraceEnabled() 日志判断为 true 就打印日志信息

    List<String> filter(List<String> configurations) {
        long startTime = System.nanoTime();
        String[] candidates = StringUtils.toStringArray(configurations);	//获取上一步经过处理的configurations
        boolean skipped = false;
        //将configurations与autoConfigurationMetadata(filter的类加载器加载出来的,具体看前面ConfigurationClassFilter的构造器)进行比较如果相同令它为空,相当于取不同的值(全部的configurations和过滤规则下autoConfigurationMetadata,相当于取SpringBoot已经需要加载的)
        for (AutoConfigurationImportFilter filter : this.filters) {
            boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
            for (int i = 0; i < match.length; i++) {
                if (!match[i]) {
                    candidates[i] = null;
                    skipped = true;
        if (!skipped) {
            return configurations;
        List<String> result = new ArrayList<>(candidates.length);
        for (String candidate : candidates) {
            if (candidate != null) {
                result.add(candidate);
        if (logger.isTraceEnabled()) {
            int numberFiltered = configurations.size() - result.size();
            logger.trace("Filtered " + numberFiltered + " auto configuration class in "
                         + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
        return result;
    

    使用filter 过滤后的配置文件就是需要加载的配置类。

    到了这里我们了解了 Spring-Boot 是如何选择自动装配需要装配的组件了。

    底层是通过反射(类加载器)判断容器中是否有该组件,在来修改装配的文件那么我们底层到底是如何装配的呢? 我们接着看下面

    IOC 容器的组件自动装配

    首先我们点开 spring-boot-autoconfigure-2.4.5.jar 查看

    由上图可以看出很多的 XXXXAutoConfiguration 意思是XXX自动装配

    随便点开一个类,这里点开的的 AopAutoConfiguration

    通过上图,我们可以看出很多底层注解都是 @ConditionalXXX 意思是按照某种条件装配,这也是 SpringBoot底层注解的核心,按照某种条件装配,如果条件成立才会装配配置类。我们随便举一些例子

    //如果application.properties 配置文件中有spring.aop.auto等于true则装配,默认值就是true 
    @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
    //如果有Advice类则配置生效
    @ConditionalOnClass(Advice.class) 
    //如果容器中没有这个Bean则装配
    @ConditionalOnMissingBean
    //容器中没有这个名字 multipartResolver 的组件则装配
     @ConditionalOnMissingBean(name = multipartResolver)
    //判断是上面类型的应用程序,是则配置
    @ConditionalOnWebApplication(type = Type.SERVLET)
    //如果容器中存在这个Bean才装配
    @ConditionalOnBean(MultipartResolver.class)
    	以下是SpringBoot的一个注解,作用就是保证名字规范
            @Bean
            @ConditionalOnBean(MultipartResolver.class)  //容器中有这个类型组件
            @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
            public MultipartResolver multipartResolver(MultipartResolver resolver) {
                //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
                //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
                // Detect if the user has created a MultipartResolver but named it incorrectly
                return resolver;
    

    以上这些是常见的更多的条件装配查看下图

    SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration

    每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。如果我们需要修改值可以通过application.properties直接修改,具体如何修改,看对应类的 @ConditionalOnProperty@ConfigurationProperties()@EnableConfigurationProperties(User.class)携带的值

    生效的配置类就会给容器中装配很多组件

    只要容器中有这些组件,对应组件的功能就能有

    自定义配置

  • 第一种方法修改application.properties对应组件的值
  • 第二种方法利用注解原理使用 @Bean @Component 等注解,来重写底层原理
  • 以下是使用 SpringBoot 使用的最佳方法

    引入场景依赖

  • https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
  • 查看自动配置了哪些(选做)

  • 自己分析,引入场景对应的自动配置一般都生效了
  • 配置文件中debug=true开启自动配置报告。Negative(不生效)\Positive(生效)
  • 是否需要修改

  • 参照文档修改配置项
  • https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
  • 自己分析。xxxxProperties绑定了配置文件的哪些。
  • YAML是"YAML Ain't a Markup Language"(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。

    非常适合用来做以数据为中心的配置文件

  • key: value;kv之间有空格
  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格(idea开发直接使用tab不会错
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • '#'表示注释
  • 字符串无需加引号,如果要加,单引号不转义,双引号转义
  • 字面量:单个的、不可再分的值。date、boolean、string、number、null
  • 对象:键值对的集合。map、hash、set、object
  • 行内写法:  k: {k1:v1,k2:v2,k3:v3}
        k1: v1
      k2: v2
      k3: v3
    
  • 数组:一组按次序排列的值。array、list、queue
  • 行内写法:  k: [v1,v2,v3]
    

    自定义的类和配置文件绑定一般没有提示。 需要加入下面依赖,并且打包时不加入其中打包文件中

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
     <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-configuration-processor</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    

    SpringMVC自动配置

    Spring Boot为Spring MVC提供了自动配置,适用于大多数应用程序

    自动配置在Spring的默认值之上添加了以下功能:

  • 包含 ContentNegotiatingViewResolver (内容协商视图解析器) 和 BeanNameViewResolver (BeanName视图解析器)的 beans。
  • 支持提供静态资源,包括对WebJars的支持。
  • 自动注册 Converter (转换器), GenericConverter(通用转换器) 和 Formatter(格式化器) 的 beans。
  • 支持 HttpMessageConverters(HTTP消息转换器) 。
  • 自动注册 MessageCodesResolver (国际化)。
  • 静态 index.html 支持。
  • 自定义 Favicon 支持。
  • 自动使用 ConfigurableWebBindingInitializer (可配置绑定初始化器)bean。
  • 默认情况下,Spring Boot从类路径中的/static(或 /public /resources/META-INF/resources )目录或 ServletContext 的根目录中提供静态内容。

    默认情况下在上面列出的目录我们通过 项目发布路径/+资源名 都能访问到。

    原理: 静态资源都是/** 请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面

    它使用来自Spring MVC的 ResourceHttpRequestHandler(Http资源请求处理程序) ,以便您可以通过添加自己的 WebMvcConfigurer 并覆 盖 addResourceHandlers 方法来修改该行为。

    默认情况下,资源映射到 /** ,但您可以使用 spring.mvc.static-path-pattern 属性对其进行调整。例如,将所有资源重新定位到 /res/** 可以实现如下:

    #在res文件夹下放入资源
    spring:
        static-path-pattern: /res/**
    

    上面是修改静态资源在哪个包下面,当然我们也可以用 spring.resources.static-locations 来改变Spring Boot默认的静态资源路径

    # 修改默认的资源访问路径,参数是一个数组,可以传递多个值,请求的时候Servlet的根目录也会自动添加
    spring:
        resources:
          static-locations: [classpath:/res/,classpath:/static/]
    

    Webjars

    如果我们想要访问的资源是以Webjars格式打成的jar包,那么具有 /webjars/** 中路径 的任何资源都将从jar文件中提供。

    https://www.webjars.org/ 这个路径是Webjars打包的官网,可以去里面找相关jar包的依赖

    例如:我们引入在 pom.xml 中引入以下依赖

    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>jquery</artifactId>
        <version>3.5.1</version>
    </dependency>
    

    那么我们通过访问 项目发布路径/+/webjars/jquery/3.5.1/jquery.min.js 就能获得 jquery的相关信息(源码)

    Spring Boot支持静态和模板化的欢迎页面。它首先在配置的静态内容位置中查找 index.html 文件。如果找不到,则会查找 index 模板。如果找到任何一个,它将自动用作应用程序的欢迎页面。

    上面这段话的意思是:默认会找 index.html 如果没有找到,就会找 controller 处理 /index 请求的路径来作为欢迎页

    可以配置静态资源路径spring.resources.static-locations

    但是不可以配置静态资源的访问前缀spring.mvc.static-path-pattern。否则导致 index.htmlFavicon不能被默认访问

    Favicon

    favicon.ico 放在静态资源目录下即可。

    静态资源底层源码分析

    首先我们找到 spring-boot-autoconfigure-2.4.5.jar Spring自动配置的源码,接下来找 web 目录下的 WebMvcAutoConfiguration配置类

    这个类的注解生效规则如下

    @Configuration(proxyBeanMethods = false)   //声明是一个配置类
    @ConditionalOnWebApplication(type = Type.SERVLET)	//是Servlet类才生效
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })		//存在这3个类才生效,一般导入mvc都会生效
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)		//没有这个类才生效
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)			//控制配置类的加载顺序
    @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
    		ValidationAutoConfiguration.class })
    // 从上面的配置看,基本上配置都会生效
    

    接着看配置类分别配置了什么

    	@Configuration(proxyBeanMethods = false)
    	@Import(EnableWebMvcConfiguration.class)
    	@EnableConfigurationProperties({ WebMvcProperties.class,
    			org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
    	@Order(0)
    	public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {}
    

    WebMvcProperties 它绑定了 spring.mvc

    org.springframework.boot.autoconfigure.web.ResourceProperties 它绑定了 spring.resources

    WebProperties 它绑定了 spring.web

    查看配置类分别配置了什么

    private final WebMvcProperties mvcProperties;	//获取和spring.mvc绑定的所有的值的对象
    private final ListableBeanFactory beanFactory;	//获取 beanFactory IOC容器工厂 返回值是List
    private final ObjectProvider<HttpMessageConverters> messageConvertersProvider;	//HTTP消息转换器
    private final ObjectProvider<DispatcherServletPath> dispatcherServletPath;		//视图解析器路径
    private final ObjectProvider<ServletRegistrationBean<?>> servletRegistrations;	//Servlet注册
    final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;	//资源处理程序注册
    public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties,
                                          ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
                                          ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
                                          ObjectProvider<DispatcherServletPath> dispatcherServletPath,
                                          ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
        this.mvcProperties = mvcProperties;
        this.beanFactory = beanFactory;
        this.messageConvertersProvider = messageConvertersProvider;
        this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
        this.dispatcherServletPath = dispatcherServletPath;
        this.servletRegistrations = servletRegistrations;
        this.mvcProperties.checkConfiguration();
    

    配置静态资源,我们在类中找到一个方法addResourceHandlers

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        super.addResourceHandlers(registry);
        /*resourceProperties,一个变量在上面类中获取了spring.web.resources的值 isAddMappings()方法是获取一个变量为 			addMappings 的值 默认值是true,由这个判断,我们可以将 spring.web.resources.addMappings的值设置为false,
        就相当于禁用默认的静态资源规则。
        if (!this.resourceProperties.isAddMappings()) {
            logger.debug("Default resource handling disabled");
            return;
        //获取Servlet的Context 即项目发布路径
        ServletContext servletContext = getServletContext();
        //添加Webjars的访问路径规则
        addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
        //添加静态资源访问路径规则
        addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
            registration.addResourceLocations(this.resourceProperties.getStaticLocations());
            if (servletContext != null) {
                registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));
    

    从上面的源码分析,我们可以的到一个结论,我们要禁用静态资源规则只需要在配置文件中添加

    # 添加这个可以禁用SpringBoot的默认静态资源规则
    spring:
        resources:
          add-mappings: false
    

    配置欢迎页,我们在类中找一个方法 WelcomePageHandlerMapping

    @Bean
    public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
                                                               FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
        // 这里 new 了一个WelcomePageHandlerMapping
        WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
            new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
            this.mvcProperties.getStaticPathPattern());
        welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
        welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
        return welcomePageHandlerMapping;
    
    // WelcomePageHandlerMapping类 的构造器
    WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
                              ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {
        //判断 welcomePage 默认值就是index.html,并且 /** 的请求路径等不等于静态资源路径。
        if (welcomePage != null && "/**".equals(staticPathPattern)) {
            logger.info("Adding welcome page: " + welcomePage);
            // 如果是就设置视图解析器为转发到 index.html
            setRootViewName("forward:index.html");
        //如果上面条件不满足就 找controller请求有没有/index有则设置视图的解析为index
        else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
            logger.info("Adding welcome page template: index");
            setRootViewName("index");
    

    RestFul风格使用

    RestFul 风格的讲解参考 SpringMVC

    在 SpringBoot中使用 RestFul风格 ,因为表单中只有 getpost 两种提交方式,所以在SpringBoot中使用 RestFul风格必须走以下流程

    1、开启 RestFul 风格支持

    # 默认值是 false 需要开启才能使用
    spring:
        hiddenmethod:
          filter:
            enabled: true
    

    2、在提交的表单中添加以下隐藏域属性 <input type="text" name="_method" value="提交方法" hidden>

    <!--原来的网页是这样写的-->
    <!DOCTYPE html>
    <html lang="en">
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <form action="/user" method="get">
        <input type="submit" value="get提交">
    </form>
    <form action="/user" method="post">
        <input type="submit" value="post提交">
    </form>
    <form action="/user" method="put">
        <input type="submit" value="put提交">
    </form>
    <form action="/user" method="delete">
        <input type="submit" value="delete提交">
    </form>
    </body>
    </html>
    <!--使用RestFul风格后,提交的表单需要添加一个隐藏域,隐藏一个叫 _method-->
    <!DOCTYPE html>
    <html lang="en">
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <form action="/user" method="get">
        <input type="submit" value="get提交">
    </form>
    <form action="/user" method="post">
        <input type="submit" value="post提交">
    </form>
    <form action="/user" method="post">
        <input type="text" name="_method" value="put" hidden>
        <input type="submit" value="put提交">
    </form>
    <form action="/user" method="post">
        <input type="text" name="_method" value="delete" hidden>
        <input type="submit" value="delete提交">
    </form>
    </body>
    </html>
    

    3、Controller编写

    package com.wyx.controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    @Controller
    public class RestFulController {
        @ResponseBody
        @GetMapping("/user")
        public String get(){
            return "get方式提交";
        @ResponseBody
        @PostMapping("/user")
        public String post(){
            return "post方式提交";
        @ResponseBody
        @PutMapping("/user")
        public String put(){
            return "put方式提交";
        @ResponseBody
        @DeleteMapping("/user")
        public String delete(){
            return "delete方式提交";
    

    RestFul 风格底层源码分析

    首先我们找到 spring-boot-autoconfigure-2.4.5.jar Spring自动配置的源码,接下来找 web 目录下的 WebMvcAutoConfiguration配置类,找到 hiddenHttpMethodFilter方法,翻译为Http方法过滤,查看源码如下

    @Bean
    @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) //如果没有这个Bean才注入
    //spring.mvc.hiddenmethod.filter为true才注入,默认值是false,这里说明了第一步,我们为什么要修改配置文件为 true 
    @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
    public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
        return new OrderedHiddenHttpMethodFilter();
    

    接着查看它 new 的类 OrderedHiddenHttpMethodFilter 这个方法继承了HiddenHttpMethodFilter类 `查看源码其中有一段

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //将原生的请求赋值给 requestToUse 后面在放行它 filterChain.doFilter((ServletRequest)requestToUse, response)
        HttpServletRequest requestToUse = request;	
        //判断请求方式是不是 POST请求并查看请求有没有出错的地方,这里也说明了表单提交为什么要使用 post提交
        if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
            /*获取请求参数中名为methodParam的参数,
            methodParam在上面有定义:private String methodParam = "_method";,就是_method,这里也说明了为什么我们隐藏域
            中要这样写 <input type="text" name="_method" value="提交方法" hidden>
            String paramValue = request.getParameter(this.methodParam);
            //判断请求参数有没有长度
            if (StringUtils.hasLength(paramValue)) {
                //将请求参数转换为全大写,证明我们在写请求参数时不区分大小写
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                /*ALLOWED_METHODS 静态常量
                    static {	ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(
                    HttpMethod.PUT.name(),
                    HttpMethod.DELETE.name(), 
                    HttpMethod.PATCH.name()));
        			支持 PUT DELETE PATCH 请求
        			//判断是不是支持的请求,如果是,就用HttpMethodRequestWrapper来包装它并返回给 requestToUse
                if (ALLOWED_METHODS.contains(method)) {
                    requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
        filterChain.doFilter((ServletRequest)requestToUse, response);
    

    拓展 修改请求参数 _method

    ​ 查看源码,我们知道,容器中没有HiddenHttpMethodFilter我们才注入,我们不妨来自己注入 HiddenHttpMethodFilter

    package com.wyx.config;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.filter.HiddenHttpMethodFilter;
    //声明是一个配置类
    @Configuration(proxyBeanMethods = false)
    public class WebConfig {
        @Bean//注入IOC容器
        public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
            HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
            // 查看源码,它的 methodParam参数可以修改值
            methodFilter.setMethodParam("requestName");
            return methodFilter;
    //	编写完成后我们隐藏域中的
    //	<input type="text" name="_method" value="提交方法" hidden> 需要改写为
    //	<input type="text" name="requestName" value="提交方法" hidden>
    

    SpringMVC配置扩展

    上面是官方文档的一段翻译,意思是我们可以扩展 SpringMVC的功能,实现功能的扩展,那么我们需要以下几步

    1、创建一个类并实现 WebMvcConfigurer 接口

    2、在配置定义的类上添加 @Configuration 注解

    package com.wyx.config;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.View;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    import java.util.Locale;
    @Configuration
    public class MyWebConfig implements WebMvcConfigurer {
        //添加Controller请求跳转视图,WebMvcConfigurer 里面有非常多的方法可以重写
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/hello").setViewName("index");
        //自定义视图解析器
        public static class MyViewResolver implements ViewResolver{
            @Override
            public View resolveViewName(String viewName, Locale locale) throws Exception {
                return null;
        //将自定义的视图解析器装载到 IOC 容器
        @Bean
        public MyViewResolver myViewResolver(){
            MyViewResolver resolver = new MyViewResolver();
            return resolver;
    

    官网特别说明:不能加 @EnableWebMvc 我们查看这个注解 它给我们导入了一个类 DelegatingWebMvcConfiguration

    @Import(DelegatingWebMvcConfiguration.class)
    public @interface EnableWebMvc {
    

    在查看 DelegatingWebMvcConfiguration 发现它继承 WebMvcConfigurationSupport

    @Configuration(proxyBeanMethods = false)
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    

    当我们添加@EnableWebMvc 时就会将 WebMvcConfigurationSupport 注入IOC 容器,然而在 WebMvcAutoConfiguration的配置类中有这么一条条件装配注解 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) 如果存在这个类就不会生效。

    总结:如果我们添加@EnableWebMvc 注解,那么Spring自动装配的功能都会失效。

    Spring Boot拦截器

    1、编写拦截器类

    SpringBoot 中有一个接口 HandlerInterceptor 专门用来做拦截功能,我们想要实现拦截器,可以编写一个类实现HandlerInterceptor 接口在重写接口中的三个方法即可。下面是三个方法的解释

    preHandle():目标方法执行之前执行

    postHandle():目标方法执行完成以后执行

    afterCompletion():页面渲染以后执行

    package com.wyx.config;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    public class LoginHandlerInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            Object loginUser = request.getSession().getAttribute("loginUser");
            if(loginUser == null){
                request.setAttribute("msg","请登录后访问");
                request.getRequestDispatcher("/").forward(request,response);
                return false;
            return true;
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    

    2、将编写的拦截器类注入IOC容器(实现WebMvcConfigurer的addInterceptors

    package com.wyx.config;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
        //拦截过滤器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoginHandlerInterceptor())
                    .addPathPatterns("/**")  //拦截所有请求
                    .excludePathPatterns("/","/index.html","/css/*","/js/*","/img/*","/login");  //放行哪些请求
    

    3、当有多个拦截器时同时作用一个方法时

    preHandle():按照实现拦截器的顺序优先进行拦截,postHandle()afterCompletion()按照拦截器的顺序,反向执行。(类似于IO流的先用后关操作)

    SpringBoot 已经为我们向容器中添加了文件上传功能 MultipartFile,使用文件上传功能我们只需要

    1、在表单中使用enctype="multipart/form-data"代表是文件上传

    2、在控制层中接收表单的参数解析成为@RequestPart("headerImg") MultipartFile headerImg即可

    <form method="post" action="/upload" enctype="multipart/form-data">
        <input type="file" name="file"><br>
        <input type="submit" value="提交">
    </form>
    
    @PostMapping("/upload")
    public String upload(@RequestPart("file") MultipartFile headerImg
                        ) throws IOException {
        if(!headerImg.isEmpty()){
            //保存到文件服务器,OSS服务器
            String originalFilename = headerImg.getOriginalFilename();
            headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
        return "main";
    

    Web原生组件注入(Servlet、Filter、Listener)

    1、使用Servlet API

    @ServletComponentScan(basePackages = "com.wyx.servlet") :指定原生Servlet组件都放在那里

    @WebServlet(urlPatterns = "/my"):效果:直接响应,没有经过Spring的拦截器

    @WebFilter(urlPatterns={"/css/*","/images/*"})

    @WebListener

    2、使用RegistrationBean

    ServletRegistrationBean`, `FilterRegistrationBean`, and `ServletListenerRegistrationBean
    @Configuration
    public class MyRegistConfig {
        @Bean
        public ServletRegistrationBean myServlet(){
            MyServlet myServlet = new MyServlet();
            return new ServletRegistrationBean(myServlet,"/my","/my02");
        @Bean
        public FilterRegistrationBean myFilter(){
            MyFilter myFilter = new MyFilter();
    //        return new FilterRegistrationBean(myFilter,myServlet());
            FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
            filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
            return filterRegistrationBean;
        @Bean
        public ServletListenerRegistrationBean myListener(){
            MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
            return new ServletListenerRegistrationBean(mySwervletContextListener);
    

    连接数据源

    1、整合JDBC

    1、导入依赖

    <!--JDBC启动器-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!--MySQL连接驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    

    2、配置数据库连接源

    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&charterEncoding=utf8&serverTimezone=GMT%2B8
        password: 970699
        username: root
        driver-class-name: com.mysql.cj.jdbc.Driver
    

    3、测试连接数据源

    @SpringBootTest
    class SpringbootDataJdbcApplicationTests {
        //DI注入数据源
        @Autowired
        DataSource dataSource;
        @Test
        public void contextLoads() throws SQLException {
            //看一下默认数据源
            System.out.println(dataSource.getClass());
            //获得连接
            Connection connection =   dataSource.getConnection();
            System.out.println(connection);
            //关闭连接
            connection.close();
            // 测试得到的数据源是 HikariDataSource
    

    4、使用JDBC模板

    SpringBoot中即使不使用第三方第数据库操作框架,如 MyBatis等,Spring 本身也对原生的JDBC 做了轻量级的封装,即JdbcTemplate。

    1、数据库操作的所有 CRUD 方法都在 JdbcTemplate 中。

    2、Spring Boot 不仅提供了默认的数据源,同时默认已经配置好了 JdbcTemplate 放在了容器中,程序员只需自己注入即可使用

    3、JdbcTemplate 的自动配置是依赖 org.springframework.boot.autoconfigure.jdbc 包下的 JdbcTemplateConfiguration 类

    JdbcTemplate主要提供以下几类方法:

  • execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
  • update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句;
  • query方法及queryForXXX方法:用于执行查询相关语句;
  • call方法:用于执行存储过程、函数相关语句。
  • package com.kuang.controller;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    @RestController
    @RequestMapping("/jdbc")
    public class JdbcController {
         * Spring Boot 默认提供了数据源,默认提供了 org.springframework.jdbc.core.JdbcTemplate
         * JdbcTemplate 中会自己注入数据源,用于简化 JDBC操作
         * 还能避免一些常见的错误,使用起来也不用再自己来关闭数据库连接
        @Autowired
        JdbcTemplate jdbcTemplate;
        //查询employee表中所有数据
        //List 中的1个 Map 对应数据库的 1行数据
        //Map 中的 key 对应数据库的字段名,value 对应数据库的字段值
        @GetMapping("/list")
        public List<Map<String, Object>> userList(){
            String sql = "select * from employee";
            List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
            return maps;
        //新增一个用户
        @GetMapping("/add")
        public String addUser(){
            //插入语句,注意时间问题
            String sql = "insert into employee(last_name, email,gender,department,birth)" +
                    " values ('狂神说','24736743@qq.com',1,101,'"+ new Date().toLocaleString() +"')";
            jdbcTemplate.update(sql);
            return "addOk";
        //修改用户信息
        @GetMapping("/update/{id}")
        public String updateUser(@PathVariable("id") int id){
            //插入语句
            String sql = "update employee set last_name=?,email=? where id="+id;
            Object[] objects = new Object[2];
            objects[0] = "秦疆";
            objects[1] = "24736743@sina.com";
            jdbcTemplate.update(sql,objects);
            return "updateOk";
        //删除用户
        @GetMapping("/delete/{id}")
        public String delUser(@PathVariable("id") int id){
            //插入语句
            String sql = "delete from employee where id=?";
            jdbcTemplate.update(sql,id);
            return "deleteOk";
    

    2、整合Druid

    Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控。

    Spring Boot 2.0 以上默认使用 Hikari 数据源,可以说 Hikari 与 Driud 都是当前 Java Web 上最优秀的数据源,我们来重点介绍 Spring Boot 如何集成 Druid 数据源,如何实现数据库监控。

    1、导入Druid依赖

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.21</version>
    </dependency>
    

    2、切换数据源;之前已经说过 Spring Boot 2.0 以上默认使用 Hikari 数据源,但可以 通过 spring.datasource.type 指定数据源。

    spring:
      datasource:
        username: root
        password: 970699
        url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源
    

    下面是对 Druid数据源的一些参数配置讲解

    spring:
      datasource:
    	#Spring Boot 默认是不注入这些属性值的,需要自己绑定
        #druid 数据源专有配置
        initialSize: 5
        minIdle: 5
        maxActive: 20
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
        #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
        #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
        filters: stat,wall,log4j
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    

    我们以上配置的属性该如何让它生效呢?

    利用 SpringBoot 的自动配置原理,我们只需要编写一个类来获取数据即可

    @Configuration
    public class DruidConfig {
           将自定义的 Druid数据源添加到容器中,不再让 Spring Boot 自动创建
           绑定全局配置文件中的 druid 数据源属性到 com.alibaba.druid.pool.DruidDataSource从而让它们生效
           @ConfigurationProperties(prefix = "spring.datasource"):作用就是将 全局配置文件中
           前缀为 spring.datasource的属性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名参数中
        @ConfigurationProperties(prefix = "spring.datasource")
        @Bean
        public DataSource druidDataSource() {
            return new DruidDataSource();
    

    测试自己配置的东西是否生效

    @SpringBootTest
    class SpringbootDataJdbcApplicationTests {
        //DI注入数据源
        @Autowired
        DataSource dataSource;
        @Test
        public void contextLoads() throws SQLException {
            //看一下默认数据源
            System.out.println(dataSource.getClass());
            //获得连接
            Connection connection =   dataSource.getConnection();
            System.out.println(connection);
            DruidDataSource druidDataSource = (DruidDataSource) dataSource;
            System.out.println("druidDataSource 数据源最大连接数:" + druidDataSource.getMaxActive());
            System.out.println("druidDataSource 数据源初始化连接数:" + druidDataSource.getInitialSize());
            //关闭连接
            connection.close();
    

    Druid的后台监控功能实现

    //由于没有Web.xml配置文件,所以使用 Spring Boot 的注册 Servlet 方式
    @Configuration
    public class MyRegistConfig {
        @Bean
        public ServletRegistrationBean statViewServlet() {
            ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
            // 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet 
            // 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
            Map<String, String> initParams = new HashMap<>();
            initParams.put("loginUsername", "admin"); //后台管理界面的登录账号
            initParams.put("loginPassword", "123456"); //后台管理界面的登录密码
            //后台允许谁可以访问
            //initParams.put("allow", "localhost"):表示只有本机可以访问
            //initParams.put("allow", ""):为空或者为null时,表示允许所有访问
            initParams.put("allow", "");
            //deny:Druid 后台拒绝谁访问
            //initParams.put("kuangshen", "192.168.1.20");表示禁止此ip访问
            //设置初始化参数
            bean.setInitParameters(initParams);
            return bean;
        //WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
        //配置过滤器,因为SpringBoot中配置的过滤器并不会拦截原生组件配置的Servlet请求
        @Bean
        public FilterRegistrationBean webStatFilter() {
            FilterRegistrationBean bean = new FilterRegistrationBean();
            bean.setFilter(new WebStatFilter());
            //exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
            Map<String, String> initParams = new HashMap<>();
            initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
            bean.setInitParameters(initParams);
            //"/*" 表示过滤所有请求
            bean.setUrlPatterns(Arrays.asList("/*"));
            return bean;
    

    访问:http://localhost:8080/druid/login.html 可以查看监控页面

    3、整合Mybatis(重点)

    1、导入相关依赖

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.1</version>
    </dependency>
    

    2、配置数据源

    spring:
      datasource:
        username: root
        password: 970699
        url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.cj.jdbc.Driver
    

    3、创建实体类

    package com.wyx.pojo;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
        private String name;
        private Integer age;
        private String sex;
    

    4、创建Mapper接口

    //@Mapper : 表示本类是一个 MyBatis 的 Mapper
    @Mapper
    @Repository
    public interface DepartmentMapper {
        // 获取所有用户
        List<User> getUsers();
        // 通过姓名获得用户
        User getUser(String name);
    

    5、编写对应的Mapper文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.wyx.mapper.User">
        <select id="getUsers" resultType="User">
           select * from user;
        </select>
        <select id="getUser" resultType="User" parameterType="string">
           select * from user where name = #{name};
        </select>
    </mapper>
    

    6、处理Maven资源过滤问题

    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
    

    7、编写测试方法

    @RestController
    public class UserController {
        @Autowired
        UserMapper userMapper;
        @GetMapping("/getUser")
        public List<Department> getUsers(){
            return userMapper.userMapper();
        @GetMapping("/getDepartment/{name}")
        public Department getDepartment(@PathVariable("name") String name){
            return userMapper.getUser(name);