SpringBoot2笔记

文章目录

1.SpringBoot的基本内容

1.1 Spring的生态圈

在这里插入图片描述
以前的spring相当于现在的springframework
SpringBoot的底层是SpringFramework
SpringBoot有接口的默认实现
SpringBoot的优点:

  • Create stand-alone Spring applications
    创建独立Spring应用
  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
    内嵌web服务器
  • Provide opinionated ‘starter’ dependencies to simplify your build configuration
    自动starter依赖,简化构建配置
  • Automatically configure Spring and 3rd party libraries whenever possible
    自动配置Spring以及第三方功能
  • Provide production-ready features such as metrics, health checks, and externalized configuration
    提供生产级别的监控、健康检查及外部化配置
  • Absolutely no code generation and no requirement for XML configuration
    无代码生成、无需编写XML

SpringBoot是整合Spring技术栈的一站式框架
SpringBoot是简化Spring技术栈的快速开发脚手架

缺点:
封装太深,内部原理复杂,不容易精通

1.2 时代背景

微服务的具体概念

  • 微服务是一种架构风格

  • 一个应用拆分为一组小型服务

  • 每个服务运行在自己的进程内,也就是可独立部署和升级

  • 服务之间使用轻量级HTTP交互

  • 服务围绕业务功能拆分

  • 可以由全自动部署机制独立部署

  • 去中心化,服务自治。服务可以使用不同的语言、不同的存储技术

  • 分布式:
    在这里插入图片描述

  • 远程调用

  • 服务发现

  • 负载均衡

  • 服务容错

  • 配置管理

  • 服务监控

  • 链路追踪

  • 日志管理

  • 任务调度

    分布式的解决
    • SpringBoot + SpringCloud
    在这里插入图片描述

  • 云原生
    原生应用如何上云。 Cloud Native

  • 上云的困难
    • 服务自愈
    • 弹性伸缩
    • 服务隔离
    • 自动化部署
    • 灰度发布
    • 流量治理
    在这里插入图片描述

2. SpringBoot基础

2.1 环境要求:

  • IDEA 2019.1+
  • Maven 3.3+
  • jdk1.8+

配置Maven的setting.xml
更换maven源和jdk版本:

 <profiles>
         <profile>
              <id>jdk-1.8</id>
              <activation>
                <activeByDefault>true</activeByDefault>
                <jdk>1.8</jdk>
              </activation>
              <properties> 
                        <maven.compiler.source>1.8</maven.compiler.source>               
                        <maven.compiler.target>1.8</maven.compiler.target>                
                        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
              </properties>
         </profile>
  </profiles>
<mirrors>
      <mirror>
        <id>nexus-aliyun</id>
        <mirrorOf>central</mirrorOf>
       <name>Nexus aliyun</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>
      </mirror>
  </mirrors>

2.2 编写一个helloworld

浏览器发送一个/hello请求,响应hello,springBoot2

  • 首先创建一个maven项目
    在这里插入图片描述
  • 修改pom.xml文件
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tjk</groupId>
    <artifactId>HelloWorld</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!---添加父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>

    <!--添加依赖-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>
  • 启动主类
/**
 * 表明这是一个springBoot的应用
 * 表明这是主程序类
 */
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class,args);
    }
}

@SpringBootApplication:表明这是一个springBoot的应用 * 表明这是主程序类,可以用做启动类

  • 编写逻辑类
@RestController //(@ResponseBody=@ResponseBody  +  @Controller)
public class HelloController {
    @RequestMapping("/hello")
    public  String handle(){
        return "helloSpringBoot 2";
    }
}
  • 启动程序,并访问
    在这里插入图片描述
    在这里插入图片描述
  • 简化配置:
    统一的配置文件
    在这里插入图片描述- 简化部署(可以将写好的项目打包成jar包然后进行运行):
 <build>
        <plugins>
            <plugin>                
            <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

注意:
取消cmd的快速编辑模式(防止在运行的时候,卡住)

  • 访问的时候:
    在这里插入图片描述
    然后在浏览器上进行访问即可

2.3 依赖管理

SpringBoot引入一个已经创建好的一个官方父项目,这个项目中引入的所有jar包全部都会导入到你所构造的项目当中,里面所有的jar包版本是已经规定好的,如果你需要修改,那么必须按照如下操作:

  <!---添加父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>
//他的父项目

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.4.RELEASE</version>
  </parent>
几乎声明了所有开发中常用的以来版本号

如果想要使用自己想要的jar包版本号
可以在pom.xml中进行声明:

<properties>
<mysql.version>版本号</mysql.version>
</properties>

1、见到很多 spring-boot-starter-* : *就某种场景
2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot所有支持的场景
starter文档
4、见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
5、所有场景启动器最底层的依赖

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

2.4 自动配置

  • 自动配置好Tomcat
    a. 引入Tomcat依赖
    b. 配置tomcat
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-tomcat</artifactId>
       <version>2.3.4.RELEASE</version>
       <scope>compile</scope>
     </dependency>
    
  • 自动配置好SpringMVC
    a. 引入SpringMVC全套组件
    b.配置好SpringMVC常用组件
  • 自动配置好web常见功能 字符编码问题
    SpringBoot帮我们配置好了所有web开发的常见场景
  • 默认的包结构
    a. 主程序所在包及其下面所有子包里面的组件都会被默认扫描进来
    b. 无需以前的包扫描配置
    c.想要改变扫描配置 @SpringBootApplication(scanBasePackages="com.tjk") 或者@ComponentScan 指定扫描路径
@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.tjk.boot")
  • 各种配置都拥有默认值
  1. 默认配置最终都是映射到某个类上如:MultipartProperties
  2. 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
  • 按需加载所有的配置项
  1. 非常多的starter
  2. 引入了哪些场景这个场景的自动配置才会开启
  3. SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面

2.5 容器功能

2.5.1 组件添加

  1. @Configuration
  • 基本使用
  • Full模式与Lite模式
    a. 示例
    b. 最佳实战
    1. 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
    2. 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
    • config类:

/**
* 配置类里面使用@Bean标注在方法上给容器注册组件 默认也是单实例的
* 配置类也是组件
* proxyBeanMethods:代理bean的方法
* proxyBeanMethods = true:Full
* proxyBeanMethods = false:
*     Lite  组建依赖  如果是false 每次调用容器中的组件就会创建一个新的实例
*/
@Configuration(proxyBeanMethods = true) //告诉springboot这是一个配置类  === 配置文件
public class MyConfig {


   /**
    * 外部无论对配置类的这个组件注册方法调用多少次,获取的都是之前注册容器中的单实例对象
    * @return
    */
   @Bean //给容器中添加组件 以方法名作为组件的id 返回类型作为组件的类型  返回数据就是组件在容器中的实例
   public Stu stu01(){
       return new Stu("tjk","男",18);
   }

   @Bean
   public Animal animal01(){
       return new Animal("xiaobai ");
   }
}

  • 测试类
/**
 * 表明这是一个springBoot的应用
 * 表明这是主程序类
 */
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        /**
        1. 返回ioc容器
         2. 查看容器里面的所有组件
         */
    ConfigurableApplicationContext run =SpringApplication.run(MainApplication.class,args);
    String[] names= run.getBeanDefinitionNames();
     /*   for (String name : names) {
            System.out.println(name);

        }*/

     //从容器中获取组件
        Stu stu01=run.getBean("stu01", Stu.class);
        Stu stu02=run.getBean("stu01", Stu.class);
        System.out.println(stu01==stu02);

        /**
         * 如果@Configuration(proxyBeanMethods = true)代理对象调用方法,SpringBoot总会检查这个组件 是否存在容器中
         * 保持组件单实例
         */
    }
  1. @import
    是为了将组件导入到配置类中,可以调用组件的无参构造,然后创建出组件的对象,默认组件的名字就是 全类名
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. @Conditional
    条件装配:满足Conditional指定的条件,则进行组件注入
    在这里插入图片描述
    在这里插入图片描述
    @ConditionalOnBean(name = "tom2"):配置类中有名字为tom2的装配类时,才会执行下面的方法
    @ConditionalOnMissingBean(name=“tom2”):配置类中没有名字为tom2的装配类时才会进行下面操作

2.5.2 原生配置文件引入

  1. @ImportResource
    原生配置文件引入(必须在配置类中写)
    在这里插入图片描述- 配置文件:
    <bean id="haha" class="com.tjk.mainApplication.bean.Stu">
        <property name="name" value="tjk"></property>
        <property name="age" value="18"></property>
        <property name="sex" value=""></property>
    </bean>


    <bean id="hehe" class="com.tjk.mainApplication.bean.Animal">
        <property name="name" value="小白"></property>
    </bean>
  • 测试类:
 boolean haha = run.containsBean("haha");
 System.out.println("容器中haha:"+haha);
 boolean hehe = run.containsBean("hehe");
 System.out.println("容器中hehe:"+hehe);
  • 结果
    在这里插入图片描述

2.5.3 配置绑定

如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;

  • 首先将配置文件中的数据提取出来,然后封装成javaBean,然后赋值给相应的对象。
public class getProperties {
     public static void main(String[] args) throws FileNotFoundException, IOException {
         Properties pps = new Properties()
         pps.load(new FileInputStream("a.properties"));
         Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
         while(enum1.hasMoreElements()) {
             String strKey = (String) enum1.nextElement();
             String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue); //封装到JavaBean
       }         
     }
 }

SpringBoot的方法:

  1. @ConfigurationProperties(prefix = "mycar")+@component

@component (把普通pojo实例化到spring容器中,相当于配置文件中的

  • 配置文件:
mycar.brand=BYD
mycar.price=100000
  • 实体类

在这里插入图片描述

  • 测试类:
  @Autowired
    Car car;

    @RequestMapping("/car")
    public Car car(){
        return car;
    }
  • 效果


2. @EnableConfigurationProperties + @ConfigurationProperties
这个需要在需要注入的类和配置类上进行操作

@EnableConfigurationProperties(Car.class)
/*
1. 开启car的属性配置功能
2. 把这个car自动注册到容器中
*/
  • 配置文件和上面的一样

在这里插入图片描述

在这里插入图片描述

2.6 自动配置的原理分析(入门)

  • SpringApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication
  • SpringBootConfiguration
    里面是一个@Configuration
    是一个配置类
  • @ComponentScan
    其实就是一个包扫描,指定扫描那些
  • @EnableAutoConfiguration
    是下面两个注解的合成
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
  1. AutoConfigurationPackage 自动配置包
    指定了默认包规则
@Import({Registrar.class})//给容器导入一个组件
public @interface AutoConfigurationPackage {
利用Registrar给容器中导入一系列组件
将指定的一个包下面所有组件导入进来即默认是MianApplication所在包下
  1. @Import({AutoConfigurationImportSelector.class})
1.利用getAutoConfigurationEntry(annotationMetadata)给容器中批量导入一些组件
2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。
    默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
    spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories

在这里插入图片描述

文件中写死了Spring-Boot启动就给容器中加载的所有配置类
 spring-boot-autoconfigure-2.3.4.RELEASE.jar META-INF/spring.factories
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.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.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
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.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\
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

2.6.1 按需开启自动配置项

SpringBoot先默认加载所有的配置项,在进行筛选,将需求的加载项进行加载

  1. 虽然我们127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
  2. 按照条件装配规则(@Conditional),最终会按需配置。

2.6.2 修改默认配置

  • 给容器中加入了文件上传解析器;
  @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默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先

    @Bean
	@ConditionalOnMissingBean
	public CharacterEncodingFilter characterEncodingFilter() {
    }

总结:
• SpringBoot先加载所有的自动配置 xxxxxAutoConfiguration
• 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
• 生效的配置类就会给容器中装配很多组件
• 只要容器中有这些组件,相当于这些功能就有了
• 定制化配置

  1. 用户直接自己@Bean替换底层的组件
  2. 用户去看这个组件是获取的配置文件什么值就去修改。
    xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties

2.7 最佳实践

  • 引入场景依赖
    点击查看
  • 查看自动配置了哪些(选做)
    自己分析,引入场景对应的自动配置一般都生效了
    配置文件中debug=true开启自动配置报告。Negative(不生效)\Positive(生效)
  • 是否需要修改
    1. 参照文档修改配置项
      点击查看
    2. 自己分析。xxxxProperties绑定了配置文件的哪些。
      自定义加入或者替换组件
      @Bean、@Component
      自定义器 XXXXXCustomizer;

2.8 开发技巧

2.8.1 Lombok

简化javaBean的开发

 <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
 </dependency>

在Idea的setting里面进行搜索插件进行安装
在这里插入图片描述

@NoArgsConstructor//无参构造器
@AllArgsConstructor//有参构造器
@ToString//toString方法
@Data//get   set方法  equals hashcode  toString方法
public class Stu {
   private String name;
   private  String sex;
   private Integer age;
   private  Animal animal;
}

@NoArgsConstructor//无参构造器
@AllArgsConstructor//有参构造器
@ToString//toString方法
@Data//get set方法
@EqualsAndHashCode equals hashcode

  • 最大的作用是
    进行日志记录,可以输出数据
@Slf4j
public  ceshi(){
public int shishi(){
log.info(“启动了呢”);
}
}

结果:
在这里插入图片描述

2.8.2 dev-tools自动重启

 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
 </dependency>

更改代码的任意部分,只需要按ctrl+f9就可进行热部署
但是上面的是进行重启
官方有需要付费的JRebel插件,是动态更新更新了的部分

2.8.3 Spring-Initailizr(项目的初始化向导)

创建好项目的初始结构
引入了项目的必须包
创建好了主程序类
在这里插入图片描述

3. SpringBoot的核心

3.1 配置文件

  1. properties
    和以前的properties用法是相同的
  2. yaml的用法
    点击查看

3.2 Web开发

在这里插入图片描述

3.2.1 SpringMVC自动配置概览

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)
The auto-configuration adds the following features on top of Spring’s defaults:
• Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
• 内容协商视图解析器和BeanName视图解析器
• Support for serving static resources, including support for WebJars (covered later in this document)).
• 静态资源(包括webjars)
• Automatic registration of Converter, GenericConverter, and Formatter beans.
• 自动注册 Converter,GenericConverter,Formatter
• Support for HttpMessageConverters (covered later in this document).
• 支持 HttpMessageConverters (后来我们配合内容协商理解原理)
• Automatic registration of MessageCodesResolver (covered later in this document).
• 自动注册 MessageCodesResolver (国际化用)
• Static index.html support.
• 静态index.html 页支持
• Custom Favicon support (covered later in this document).
• 自定义 Favicon
• Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
• 自动使用 ConfigurableWebBindingInitializer ,(DataBinder负责将请求数据绑定到JavaBean上)
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.
不用@EnableWebMvc注解。使用 @Configuration + WebMvcConfigurer 自定义规则
If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.
声明 WebMvcRegistrations 改变默认底层组件
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.
使用 @EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC

3.2.2 Web开发场景

3.2.2.1 简单功能分析
  1. 静态资源访问
    只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources
    访问 : 当前项目根路径/ + 静态资源名
    原理: 静态映射/**。
    请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面
  • 改变默认的静态资源路径(只是访问的时候需要加,目录中并没有)
#浏览器进行访问的时候必须要加的前缀 默认是没有的
spring:
  mvc:
    static-path-pattern: /res/**
#标注 哪个文件夹是生效的静态资源访问路径      
#默认是上面的所有都生效 
resources:
    static-locations: [classpath:/haha/]
  • webjars
    点击查看
    导入会自动进行映射 不用管上面的配置属性
    在这里插入图片描述
jquery的jar包
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.5.1</version>
</dependency>
3.2.2.2 欢迎页
  • 静态资源路径下 index.html
  1. 可以配置静态资源路径
  2. 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
  • controller能处理 /index
spring:
#  mvc:
#    static-path-pattern: /res/**   这个会导致welcome page功能失效
  resources: 
    static-locations: [classpath:/haha/]
3.2.2.3 自定义 favicon.ico

将favicon.ico资源放在静态资源根目录下即可 可以起到每次访问的时候网页上的小图标
但是配置默认访问路劲会引起失效

spring:
#  mvc:
#    static-path-pattern: /res/**   这个会导致 Favicon 功能失效
3.2.2.4 静态资源配置原理
  • SpringBoot启动默认加载xxx.AutoConfiguration类(自动配置类)
  • SpringMvc功能的自动配置类WebMvcAutoConfiguration,生效
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {}
1. 给容器中配置了什么
@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 {}
  • 配置文件的相关属性和XX进行了绑定 WebMvcProperties=spring.mvc ResourceProperties=spring.resources
  • 配置类只有一个有参构造器
//有参构造器所有参数的值,都会从该容器中确定
//ResourceProperties resourceProperties;获取和spring.resources绑定的所有的值的对象
//WebMvcProperties mvcProperties 获取和spring.mvc绑定的所有的值的对象
//ListableBeanFactory beanFactory Spring的beanFactory
//HttpMessageConverters 找到所有的HttpMessageConverters
//ResourceHandlerRegistrationCustomizer 找到 资源处理器的自定义器。=========
//DispatcherServletPath  
//ServletRegistrationBean   给应用注册Servlet、Filter....
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();
		}
2. 资源处理的默认规则
@Override
   	public void addResourceHandlers(ResourceHandlerRegistry registry) {
   		if (!this.resourceProperties.isAddMappings()) {
   			logger.debug("Default resource handling disabled");
   			return;
   		}
   		Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
   		CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
   		//webjars的规则
           if (!registry.hasMappingForPattern("/webjars/**")) {
   			customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
   					.addResourceLocations("classpath:/META-INF/resources/webjars/")
   					.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
   		}
           
           //
   		String staticPathPattern = this.mvcProperties.getStaticPathPattern();
   		if (!registry.hasMappingForPattern(staticPathPattern)) {
   			customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
   					.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
   					.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
   		}
   	}
spring:
#  mvc:
#    static-path-pattern: /res/**

 resources:
   add-mappings: false   禁用所有静态资源规则
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {

   private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
   		"classpath:/resources/", "classpath:/static/", "classpath:/public/" };

   /**
    * Locations of static resources. Defaults to classpath:[/META-INF/resources/,
    * /resources/, /static/, /public/].
    */
   private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
3.欢迎页的处理规则
	HandlerMapping:处理器映射。保存了每一个Handler能处理哪些请求。	

   @Bean
   	public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
   			FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
   		WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
   				new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
   				this.mvcProperties.getStaticPathPattern());
   		welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
   		welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
   		return welcomePageHandlerMapping;
   	}

   WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
   		ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {
   	if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
           //要用欢迎页功能,必须是/**
   		logger.info("Adding welcome page: " + welcomePage.get());
   		setRootViewName("forward:index.html");
   	}
   	else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
           // 调用Controller  /index
   		logger.info("Adding welcome page template: index");
   		setRootViewName("index");
   	}
   }

4. favicon

浏览器会发送 /favicon.ico请求获取到图标,整个session期间不再获取

5. 请求参数处理
5.1. 请求映射

@xxxMapping;

5.1.1 Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
现在:/user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
核心Filter;HiddenHttpMethodFilter
用法:表单method=post,隐藏域 _method=put

SpringBoot中手动开启
• 扩展:如何把_method 这个名字换成我们自己喜欢的。

    @RequestMapping(value = "/user",method = RequestMethod.GET)
    public String getUser(){
        return "GET-张三";
    }
    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String saveUser(){
        return "POST-张三";
    }
    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public String putUser(){
        return "PUT-张三";
    }
    @RequestMapping(value = "/user",method = RequestMethod.DELETE)
    public String deleteUser(){
        return "DELETE-张三";
    }    
@Bean	@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
	@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
	public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
		return new OrderedHiddenHttpMethodFilter();
	}
//自定义filter
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        methodFilter.setMethodParam("_m");
        return methodFilter;
    }
#是配置rest风格
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true

Rest原理(表单提交要使用REST的时候)
• 表单提交会带上_method=PUT
• 请求过来被HiddenHttpMethodFilter拦截
• 请求是否正常,并且是POST
• 获取到_method的值。
• 兼容以下请求;PUT.DELETE.PATCH
• 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
• 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
测试rest风格
需要首先开启rest风格的开关
然后界面上需要一个_method的参数 method可以写post

测试REST风格;
<form action="/user" method="get">
    <input value="REST-GET 提交" type="submit"/>
</form>
<form action="/user" method="post">
    <input value="REST-POST 提交" type="submit"/>
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="delete"/>
    <input name="_m" type="hidden" value="delete"/>
    <input value="REST-DELETE 提交" type="submit"/>
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="PUT"/>
    <input value="REST-PUT 提交" type="submit"/>
</form>

Rest使用客户端工具,
• 如PostMan直接发送Put、delete等方式请求,无需Filter。

  1. 修改默认的"_method"参数
@Configuration(proxyBeanMethods = false)
public class MyConfig {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
       HiddenHttpMethodFilter hiddenHttpMethodFilter= new  HiddenHttpMethodFilter();
       hiddenHttpMethodFilter.setMethodParam("_m");
       return hiddenHttpMethodFilter;
    }
}
5.1.2 请求映射原理

首先传入原生的request 然后进行包装
在这里插入图片描述
SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet-》doDispatch()

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		try {
			ModelAndView mv = null;
			Exception dispatchException = null;
			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);
				// 找到当前请求使用哪个Handler(Controller的方法)处理
				mappedHandler = getHandler(processedRequest);   
                //HandlerMapping:处理器映射。/xxx->>xxxx

在这里插入图片描述
在这里插入图片描述
所有的请求映射都在HandlerMapping中。
• SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
• SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
• 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
• 如果有就找到这个请求对应的handler
• 如果没有就是下一个 HandlerMapping
• 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}
5.2 普通参数与基本注解
1. 注解:
@PathVariable(路径变量)
@RequestHeader(获取请求头)
@RequestParam(获取请求参数)
@CookieValue(获取cookie值)
@RequestBody(获取请求体[POST])
@RequestAttribute(获取request域属性)
@MatrixVariable(矩阵变量)
@RestController
public class ParameterTestController {


    //  car/2/owner/zhangsan
    @GetMapping("/car/{id}/owner/{username}")
    public Map<String,Object> getCar(@PathVariable("id") Integer id,
                                     @PathVariable("username") String name,
                                     @PathVariable Map<String,String> pv,
                                     @RequestHeader("User-Agent") String userAgent,
                                     @RequestHeader Map<String,String> header,
                                     @RequestParam("age") Integer age,
                                     @RequestParam("inters") List<String> inters,
                                     @RequestParam Map<String,String> params,
                                     @CookieValue("_ga") String _ga,
                                     @CookieValue("_ga") Cookie cookie){


        Map<String,Object> map = new HashMap<>();

//        map.put("id",id);
//        map.put("name",name);
//        map.put("pv",pv);
//        map.put("userAgent",userAgent);
//        map.put("headers",header);
        map.put("age",age);
        map.put("inters",inters);
        map.put("params",params);
        map.put("_ga",_ga);
        System.out.println(cookie.getName()+"===>"+cookie.getValue());
        return map;
    }


    @PostMapping("/save")
    public Map postMethod(@RequestBody String content){
        Map<String,Object> map = new HashMap<>();
        map.put("content",content);
        return map;
    }


    //1、语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd
    //2、SpringBoot默认是禁用了矩阵变量的功能
    //      手动开启:原理。对于路径的处理。UrlPathHelper进行解析。
    //              removeSemicolonContent(移除分号内容)支持矩阵变量的
    //3、矩阵变量必须有url路径变量才能被解析
    @GetMapping("/cars/{path}")
    public Map carsSell(@MatrixVariable("low") Integer low,
                        @MatrixVariable("brand") List<String> brand,
                        @PathVariable("path") String path){
        Map<String,Object> map = new HashMap<>();

        map.put("low",low);
        map.put("brand",brand);
        map.put("path",path);
        return map;
    }

    // /boss/1;age=20/2;age=10

    @GetMapping("/boss/{bossId}/{empId}")
    public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
                    @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
        Map<String,Object> map = new HashMap<>();

        map.put("bossAge",bossAge);
        map.put("empAge",empAge);
        return map;
    }
}

/car/{path}?xxx=xxx&aaa=ccc 这种传递参数的方式叫做queryString 查询字符串 用@RequestParam注解
/car/{path;low=34;brand=byd,audi,yd} ;这种传递参数的方式叫做矩阵变量

  • 问题:
    页面开发,cookie禁用了,session里面的内容怎么使用
    session.set(a,b)—>jsessionid---->cookie---->每次发请求都会自动携带
    url重写:/abc;jsessionid=xxxx把cookie的值使用矩阵变量的方式进行传递
    @MatrixVariable|(矩阵变量)
  • 首先需要开启SpringBoot的矩阵变量的配置
    SpringBoot默认是禁用了矩阵变量的功能
    • 手动开启:原理对于路径的处理 UrlPathHelper进行解析
    • removeSemicolonContext(移除分号后的内容)支持矩阵变量的
      在配置类中进行组件的注入
第一种:
    //开启SpringBoot的矩阵变量,因为SpringBoot的矩阵变量默认是禁止的,开启需要重写WebMvcConfigurer类中的下面的方法
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper=new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);//设置为不移除,这样矩阵变量就会开启
        configurer.setUrlPathHelper(urlPathHelper);
    }
第二种
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper=new UrlPathHelper();
                //不移除,后面的内容 矩阵变量矩阵变量功能就可以生效
                urlPathHelper.setRemoveSemicolonContent(false);
             configurer.setUrlPathHelper(urlPathHelper);
            }
        };
    }

页面上测试

<a href="/cars/sell;low=34;brand=byd,audi,yd">@MatrixVariable(矩阵变量)</a>
<a href="/cars/sell;low=34;brand=byd;brand=audi;brand=yd">@MatrixVariable(矩阵变量)</a>
<a href="/boss/1;age=20/2;age=10">@MatrixVariable(矩阵变量)/boss/{bossId}/{empId}</a>

后台

/**
     * 2. SpringBoot默认是禁用了矩阵变量的功能
     *      手动开启:原理对于路径的处理 UrlPathHelper进行解析
     *               removeSemicolonContext(移除分号后的内容)支持矩阵变量的
     * @param low
     * @param brand
     * @return
     */
    @GetMapping("cars/{path}")
    public  Map carSell(@MatrixVariable("low") Integer low,
                        @MatrixVariable("brand" )List<String> brand,
                        @PathVariable("path") String path){
        Map<String,Object> map=new HashMap<>();
        map.put("brand",brand);
        map.put("low",low);
        map.put("path",path);
        return  map;
    }

    /**
     *   /boss/1;age=20/2;age=10 同样的参数进行参数的绑定
     *
     */
    @GetMapping("/boss/{bossId}/{empId}")
    public Map boss(@MatrixVariable(value="age",pathVar = "bossId") Integer bossAge,
                    @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
        Map<String,Object> map=new HashMap<>();
        map.put("bossAge",bossAge);
        map.put("empAge",empAge);
        return map;
    }
2 Servlet API

WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
ServletRequestMethodArgumentResolver 可以解析以上的部分参数

@Override
	public boolean supportsParameter(MethodParameter parameter) {
		Class<?> paramType = parameter.getParameterType();
		return (WebRequest.class.isAssignableFrom(paramType) ||
				ServletRequest.class.isAssignableFrom(paramType) ||
				MultipartRequest.class.isAssignableFrom(paramType) ||
				HttpSession.class.isAssignableFrom(paramType) ||
				(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
				Principal.class.isAssignableFrom(paramType) ||
				InputStream.class.isAssignableFrom(paramType) ||
				Reader.class.isAssignableFrom(paramType) ||
				HttpMethod.class == paramType ||
				Locale.class == paramType ||
				TimeZone.class == paramType ||
				ZoneId.class == paramType);
	}
3. 复杂参数

Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

Map<String,Object> map,  Model model, HttpServletRequest request 都是可以给request域中放数据,
request.getAttribute();

Map、Model类型的参数,会返回 mavContainer.getModel();—> BindingAwareModelMap 是Model 也是Map
mavContainer.getModel(); 获取到值的
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. 自定义对象参数

可以自动类型转换与格式化,可以级联封装。

/**
 *     姓名: <input name="userName"/> <br/>
 *     年龄: <input name="age"/> <br/>
 *     生日: <input name="birth"/> <br/>
 *     宠物姓名:<input name="pet.name"/><br/>
 *     宠物年龄:<input name="pet.age"/>
 */
@Data
public class Person {
    
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
    
}

@Data
public class Pet {

    private String name;
    private String age;

}

result
5.3 POJO封装过程

ServletModelAttributeMethodProcessor

5.4 参数处理原理
  • HandlerMapping中找到能处理请求的Handler(Controller.method())
  • 为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter
  • 适配器执行目标方法并确定方法参数的每一个值
  1. HandlerAdapter
    在这里插入图片描述0 - 支持方法上标注@RequestMapping
    1 - 支持函数式编程的
    xxxxxx
  2. 执行目标方法
// Actually invoke the handler.
//DispatcherServlet -- doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

mav = invokeHandlerMethod(request, response, handlerMethod); //执行目标方法


//ServletInvocableHandlerMethod
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

  1. 参数解析器-HandlerMethodArgumentResolver
    确定将要执行的目标方法的每一个参数的值是什么;
    SpringMVC目标方法能写多少种参数类型。取决于参数解析器。
    在这里插入图片描述
    在这里插入图片描述
  2. 返回值处理器
    在这里插入图片描述
  3. 如何确定目标方法每一个参数的值
============InvocableHandlerMethod==========================
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}

		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				// Leave stack trace for later, exception may actually be resolved and handled...
				if (logger.isDebugEnabled()) {
					String exMsg = ex.getMessage();
					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, exMsg));
					}
				}
				throw ex;
			}
		}
		return args;
	}
  • 5.1 挨个判断所有参数解析器那个支持解析这个参数
	@Nullable
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}
  • 5.2 解析这个参数的值
调用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可
  • 5.3 自定义类型参数 封装POJO
    ServletModelAttributeMethodProcessor 这个参数处理器支持
    是否为简单类型
public static boolean isSimpleValueType(Class<?> type) {
   	return (Void.class != type && void.class != type &&
   			(ClassUtils.isPrimitiveOrWrapper(type) ||
   			Enum.class.isAssignableFrom(type) ||
   			CharSequence.class.isAssignableFrom(type) ||
   			Number.class.isAssignableFrom(type) ||
   			Date.class.isAssignableFrom(type) ||
   			Temporal.class.isAssignableFrom(type) ||
   			URI.class == type ||
   			URL.class == type ||
   			Locale.class == type ||
   			Class.class == type));
   }
@Override
	@Nullable
	public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
		Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

		String name = ModelFactory.getNameForParameter(parameter);
		ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
		if (ann != null) {
			mavContainer.setBinding(name, ann.binding());
		}

		Object attribute = null;
		BindingResult bindingResult = null;

		if (mavContainer.containsAttribute(name)) {
			attribute = mavContainer.getModel().get(name);
		}
		else {
			// Create attribute instance
			try {
				attribute = createAttribute(name, parameter, binderFactory, webRequest);
			}
			catch (BindException ex) {
				if (isBindExceptionRequired(parameter)) {
					// No BindingResult parameter -> fail with BindException
					throw ex;
				}
				// Otherwise, expose null/empty value and associated BindingResult
				if (parameter.getParameterType() == Optional.class) {
					attribute = Optional.empty();
				}
				bindingResult = ex.getBindingResult();
			}
		}

		if (bindingResult == null) {
			// Bean property binding and validation;
			// skipped in case of binding failure on construction.
			WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
			if (binder.getTarget() != null) {
				if (!mavContainer.isBindingDisabled(name)) {
					bindRequestParameters(binder, webRequest);
				}
				validateIfApplicable(binder, parameter);
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new BindException(binder.getBindingResult());
				}
			}
			// Value type adaptation, also covering java.util.Optional
			if (!parameter.getParameterType().isInstance(attribute)) {
				attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
			}
			bindingResult = binder.getBindingResult();
		}

		// Add resolved attribute and BindingResult at the end of the model
		Map<String, Object> bindingResultModel = bindingResult.getModel();
		mavContainer.removeAttributes(bindingResultModel);
		mavContainer.addAllAttributes(bindingResultModel);

		return attribute;
	}

WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder :web数据绑定器,将请求参数的值绑定到指定的JavaBean里面
WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean – Integer)
byte – > file

@FunctionalInterfacepublic interface Converter<S, T>
在这里插入图片描述在这里插入图片描述未来我们可以给WebDataBinder里面放自己的Converter;
private static final class StringToNumber implements Converter<String, T>

    //1、WebMvcConfigurer定制化SpringMVC的功能
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                // 不移除;后面的内容。矩阵变量功能就可以生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }

            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(new Converter<String, Pet>() {

                    @Override
                    public Pet convert(String source) {
                        // 啊猫,3
                        if(!StringUtils.isEmpty(source)){
                            Pet pet = new Pet();
                            String[] split = source.split(",");
                            pet.setName(split[0]);
                            pet.setAge(Integer.parseInt(split[1]));
                            return pet;
                        }
                        return null;
                    }
                });
            }
        };
    }
    1. 目标方法执行完成
      将所有的数据都放在 ModelAndViewContainer;包含要去的页面地址View。还包含Model数据。
      在这里插入图片描述- 7. 处理派发结果
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
      renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
InternalResourceView:
@Override
	protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		// Expose the model object as request attributes.
		exposeModelAsRequestAttributes(model, request);

		// Expose helpers as request attributes, if any.
		exposeHelpers(request);

		// Determine the path for the request dispatcher.
		String dispatcherPath = prepareForRendering(request, response);

		// Obtain a RequestDispatcher for the target resource (typically a JSP).
		RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
		if (rd == null) {
			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
					"]: Check that the corresponding file exists within your web application archive!");
		}

		// If already included or response already committed, perform include, else forward.
		if (useInclude(request, response)) {
			response.setContentType(getContentType());
			if (logger.isDebugEnabled()) {
				logger.debug("Including [" + getUrl() + "]");
			}
			rd.include(request, response);
		}

		else {
			// Note: The forwarded resource is supposed to determine the content type itself.
			if (logger.isDebugEnabled()) {
				logger.debug("Forwarding to [" + getUrl() + "]");
			}
			rd.forward(request, response);
		}
	}
暴露模型作为请求域属性
// Expose the model object as request attributes.
		exposeModelAsRequestAttributes(model, request);
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
			HttpServletRequest request) throws Exception {

    //model中的所有数据遍历挨个放在请求域中
		model.forEach((name, value) -> {
			if (value != null) {
				request.setAttribute(name, value);
			}
			else {
				request.removeAttribute(name);
			}
		});
	}
3.2.2.5 数据响应与内容协商

在这里插入图片描述

1. 响应JSON
1.1 jackson.jar+@ResponseBody
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
web场景自动引入了json场景
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>

在这里插入图片描述给前端自动返回json数据;

  • 1.返回值解析器
    在这里插入图片描述- 利用返回值处理器处理返回值
try {
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
  • 处理返回值 查找哪个返回值处理器能够处理当前的返回值
	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}
RequestResponseBodyMethodProcessor  	
@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
        // 使用消息转换器进行写出操作
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}
  • 2返回值解析器原理
    在这里插入图片描述• 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
    • 2、返回值处理器调用 handleReturnValue 进行处理
    • 3、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。
    • a. 利用 MessageConverters 进行处理 将数据写为json
      • a.1 . 内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
        在这里插入图片描述
      • a.2. 服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
      • a. 3. SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?
        • a. 3. 1 得到MappingJackson2HttpMessageConverter可以将对象写为json
        • a. 3. 2 利用MappingJackson2HttpMessageConverter将对象转为json再写出去。
1.2 SpringMVC到底支持哪些返回值
ModelAndView
Model
View
ResponseEntity 
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且为对象类型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor进行处理;
1.3 HTTPMessageConverter原理
  1. MessageConverter规范
    在这里插入图片描述
    HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。
    例子:Person对象转为JSON。或者 JSON转为Person
  2. 默认的MessageConverter
    在这里插入图片描述0 - 只支持Byte类型的
    1 - String
    2 - String
    3 - Resource
    4 - ResourceRegion
    5 - DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class
    6 - MultiValueMap
    7 - true
    8 - true
    9 - 支持注解方式xml处理的。

最终最终messageConverter 中MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)
在这里插入图片描述

@ResponseBody,利用返回值处理器里面的消息转换器进行处理
2. 内容协商

根据客户端接收能力不同,返回不同媒体类型的数据
比如返回Json或者xml的数据要求

2.1 引入xml依赖
 <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

在这里插入图片描述数据变成xml类型的数据

2.2 postman分别测试返回json和xml

只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型
在这里插入图片描述

2.3 开启浏览器参数方式内容协商功能

为了方便内容协商,开启基于请求参数的内容协商功能。

  • 默认
    在这里插入图片描述
  • 进行配置
#开启请求参数内容协商模式
spring:
   contentnegotiation:
     favor-parameter: true 

发请求:需要携带format参数
http://localhost:8080/test/person?format=json http://localhost:8080/test/person?format=xml
在这里插入图片描述确定客户端接收什么样的内容类型;
1、Parameter策略优先确定是要返回json数据(获取请求头中的format的值)
在这里插入图片描述2、最终进行内容协商返回给客户端json即可。

2.4 内容协商原理

• 1、判断当前响应头中是否已经有确定的媒体类型。MediaType
• 2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】
contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略
在这里插入图片描述
HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型
在这里插入图片描述3、遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)
4、找到支持操作Person的converter,把converter支持的媒体类型统计出来。
5、客户端需要【application/xml】。服务端能力【10种、json、xml】
在这里插入图片描述
6、进行内容协商的最佳匹配媒体类型
浏览器和服务端进行支持的数据类型匹配
在这里插入图片描述

7、用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。
在这里插入图片描述
导入了jackson处理xml的包,xml的converter就会自动进来

WebMvcConfigurationSupport
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);

if (jackson2XmlPresent) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
		}
2.5 自定义 MessageConverter
/**
 * 1. 浏览器发送请求直接返回xml数据  【application/xml】 jacksonXmlConverter
 * 2. 如果事ajax请求,返回json    [application/json]   jacksonJsonConverter
 * 3. 如果是硅谷app发送的请求,返回自定义协议的数据  [application/x-guigu]  xxxConverter
 *  属性值1;属性值2
 *
 *
 * 步骤:
 *    1. 添加自定义的MessageCoverter进系统的底层
 *    2. 系统底层就会统计出所有的MessageCoverter能进行操作那些类型
 *    3. 客户端内容协商(guigu-->guigu)
 * @return
 */

实现多协议数据兼容。json、xml、x-guigu

  1. @ResponseBody 响应数据出去 调用 RequestResponseBodyMethodProcessor 处理
  2. Processor 处理方法返回值。通过 MessageConverter 处理
  3. 所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)
  4. 内容协商找到最终的 messageConverter;
    SpringMVC的什么功能。一个入口给容器中添加一个 WebMvcConfigurer
    在这里插入图片描述
  • 第一步:自定义的Converter:
/**
 * 自定义的Converter
 * 需要继承HttpMessageConverter 然后进行重写所有的方法
 * 这里主要展示的是写
 */
public class GuiguCoverter implements HttpMessageConverter<Person>  {
    @Override
    public boolean canRead(Class<?> aClass, MediaType mediaType) {
        return false;
    }
第一步:
    /*写*/
    @Override
    public boolean canWrite(Class<?> aClass, MediaType mediaType) {
        return aClass.isAssignableFrom(Person.class);
    }
第二步:
    /**
     * 服务器要统计所有MessageCoverter都能写出那些内容类型
     * application/x-guigu
     * @return
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-guigu"); //规定可以写入那些类型的内容
    }

    @Override
    public Person read(Class<? extends Person> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }
第三步:
//规定格式是啥:
    @Override
    public void write(Person person, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
        String  data=person.getName()+";"+person.getSex()+";"+person.getAge();

第四步:
//写出去
        OutputStream body=httpOutputMessage.getBody();
        body.write(data.getBytes());

    }
}

第二步:WebMvcConfigurer
在这里插入图片描述

 @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {

            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

            }
        }
    }
  • 内容协商管理器的内容协商策略

  • 下面进行修改自定义请求参数 内容协商修改的具体方法:
    请求方式有两种,一种是请求头 Accept=XXX 一种是请求参数 format=json
    在配置类中加入下面代码:

 /**
     * 请求方式有两种,一种是请求头 Accept=XXX   一种是请求参数  format=json
     */
      //自定义内容协商策略
    //用请求参数的方法进行请求
    //配置协商功能   需要先将内容协商开启
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        //Map<String, MediaType> mediaTypes
        Map<String, MediaType> mediaType=new HashMap<>();
        mediaType.put("json",MediaType.APPLICATION_JSON);
        mediaType.put("xml",MediaType.APPLICATION_XML);
        mediaType.put("gg",MediaType.parseMediaType("application/x-guigu"));
        //指定支持解析那些参数对应的那些媒体类型
        ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(mediaType);//参数的内容协商策略
        configurer.strategies(Arrays.asList(parameterStrategy));//设置内容协商策略
    }

协商策略修改之前的:
在这里插入图片描述进行协商策略修改以后的:
在这里插入图片描述
有可能我们添加的自定义的功能会覆盖默认很多功能,导致一些默认的功能失效。
大家考虑,上述功能除了我们完全自定义外?SpringBoot有没有为我们提供基于配置文件的快速修改媒体类型功能?怎么配置呢?【提示:参照SpringBoot官方文档web开发内容协商章节】

3.2.2.6 视图解析与模板引擎
1. 视图解析

视图解析:SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染。
在这里插入图片描述

1.1 视图解析原理流程
  1. 目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer 里面。包括数据和视图地址
  2. 方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在 ModelAndViewContainer
  3. 任何目标方法执行完成以后都会返回 ModelAndView(数据和视图地址)。
  4. processDispatchResult 处理派发结果(页面该如何响应)

4.1. render(mv, request, response); 进行页面渲染逻辑

4.1.1. 根据方法的String返回值得到 View 对象【定义了页面的渲染逻辑】

4.1.1.1. 所有的视图解析器尝试是否能根据当前返回值得到View对象
4.1.1.2. 得到了 redirect:/main.html --> Thymeleaf new RedirectView()
4.1.1.3. ContentNegotiationViewResolver 里面包含了下面所有的视图解析器,内部还是利用下面所有视图解析器得到视图对象。
4.1.1.4. view.render(mv.getModelInternal(), request, response); 视图对象调用自定义的render进行页面渲染工作
• RedirectView 如何渲染【重定向到一个页面】
• 1、获取目标url地址
• 2、response.sendRedirect(encodedURL);

视图解析:

  • 返回值以 forward: 开始: new InternalResourceView(forwardUrl); --> 转发request.getRequestDispatcher(path).forward(request, response);

  • 返回值以 redirect: 开始: new RedirectView() --》 render就是重定向

  • 返回值是普通字符串: new ThymeleafView()—>
    自定义视图解析器+自定义视图; 大厂学院。
    在这里插入图片描述

  • 默认的视图解析器的种类:
    在这里插入图片描述返回视图解析器的视图对象
    在这里插入图片描述
    视图对象调用render方法进行视图渲染
    在这里插入图片描述

2. 模板引擎-Thymeleaf
2.1 thymeleaf简介

点击查看

3. 快速构建一个后台管理系统
3.1 项目创建

thymeleaf、web-starter、devtools、lombok
在这里插入图片描述

3.2 静态资源处理

自动配置好,我们只需要把所有静态资源放到 static 文件夹下
在这里插入图片描述

3.3 路径构建

th:action="@{/login}"
在这里插入图片描述

3.4 模板抽取

th:insert/replace/include
将公共部分的界面代码抽取出来,在别的界面引用即可

  • 公共部分
  <link href="css/style.css" th:href="@{/css/style.css}" rel="stylesheet">
    <link href="css/style-responsive.css" th:href="@{/css/style-responsive.css}" rel="stylesheet">

这样的路径写法就可以动态加入项目名字

  1. 公共部分的名字
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:fragment="commonheader">
  1. 每一部分可以引用利用id进行标记
<div id="leftmenu" class="left-side sticky-left-side">
  
</div>
  • 引用部分
  1. 第一种:
 <div th:include="common :: commonheader"> </div>

效果:

  <div>
    &copy; 2011 The Good Thymes Virtual Grocery
  </div>
  1. 第二种
<div th:replace="common :: #leftmenu"></div>

效果


  <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
  </footer>
  1. 第三种
 <div th:insert="footer :: copy"></div>

效果

  <div>
    <footer>
      &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
  </div>
3.5 页面跳转
    @PostMapping("/login")
    public String main(User user, HttpSession session, Model model){

        if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){
            //把登陆成功的用户保存起来
            session.setAttribute("loginUser",user);
            //登录成功重定向到main.html;  重定向防止表单重复提交
            return "redirect:/main.html";
        }else {
            model.addAttribute("msg","账号密码错误");
            //回到登录页面
            return "login";
        }

    }
3.6 数据渲染
  @GetMapping("dynamic_table")
    public String dynamic_table(Model model){
        List<User> users = Arrays.asList(new User("tjk", "123"),
                new User("zjj", "123"),
                new User("yzy", "123"),
                new User("mlh", "123"),
                new User("ww", "123"));
        model.addAttribute("users",users);
        return "table/dynamic_table";
    }
    }
          <table class="display table table-bordered table-striped" id="dynamic-table">
                                    <thead>
                                    <tr>
                                        <th>序号</th>
                                        <th>name</th>
                                        <th>age</th>
                                    </tr>
                                    </thead>
                                    <tbody>
                                    <tr class="gradeX" th:each="user,stats: ${users}">
                                        <td th:text="${stats.count}"></td>
                                        <td>[[${user.username}]]</td>
                                        <td th:text="${user.password}"></td>
                                    </tr>
                                    </tfoot>
                                </table>

效果:
在这里插入图片描述

3.2.3 SpringBoot在web开发常用的功能

3.2.3.1 拦截器

拦截器在底层用到的接口是HandlerInterceptor
在这里插入图片描述preHandle:是执行请求之前进行拦截
postHandle:执行请求之后进行执行的拦截器 可以带参数
afterCompletion:执行请求之后进行执行的拦截器,是最后的拦截器

在这里插入图片描述
拦截器的步骤:

  1. 编写拦截器 必须继承HandlerInterceptor实现类
  2. 将拦截器注册到容器中

第一步:

/**
 * 登陆检查
 * 1. 配置好拦截器需要拦截那些请求
 * 2. 把这些配置放到容器中
 */
//应用拦截器必须实现HandlerInterceptor类
    @Slf4j
public class LoginIntercepted implements HandlerInterceptor {

    /**
     * 目标方法执行之前
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        StringBuffer requestURL = request.getRequestURL();
        log.info("拦截器的请求路径是{}",requestURL);
        Object user = session.getAttribute("user");
        log.info("preHandle执行了");
        //检查登录的逻辑
        if(!user.equals("")&&user!=null){
            log.info("放行了");
            return true;//进行放行操作
        }else {
            session.setAttribute("msg","请先登录");
//            response.sendRedirect("/");//重定向进入首页
            request.getRequestDispatcher("/").forward(request,response);
            return false;
        }

    }


    /**
     * 目标方法拦截之后
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle执行了");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion执行了");
    }
}

第二步:

@Configuration
public class AdminMvcConfig implements WebMvcConfigurer {

    /**
     * 将拦截器加入到容器中
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginIntercepted()).
                addPathPatterns("/**").//拦截了静态的资源
                excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**");
    }
}

需要注意的是拦截器进行拦截的时候‘/**’也拦截了静态资源,需要后面使用excludePathPatterns进行释放,释放的时候别忘了/js/**星星

3.2.3.2 拦截器的原理

在这里插入图片描述

1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】
2、先来顺序执行 所有拦截器的 preHandle方法
• 1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
• 2、如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;
3、如果任何一个拦截器返回false。直接跳出不执行目标方法
4、所有拦截器都返回True。执行目标方法
5、倒序执行所有拦截器的postHandle方法。
6、前面的步骤有任何异常都会直接倒序触发 afterCompletion
7、页面成功渲染完成以后,也会倒序触发 afterCompletion

在这里插入图片描述

3.2.3.3 文件上传
  • 单文件上传和多文件上传
  • 界面代码
<div class="panel-body">
                        <form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
                            <div class="form-group">
                                <label for="exampleInputEmail1">邮箱</label>
                                <input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
                            </div>
                            <div class="form-group">
                                <label for="exampleInputPassword1">名字</label>
                                <input type="text" name="username" class="form-control" id="exampleInputPassword1" placeholder="Password">
                            </div>
                            <div class="form-group">
                                <label for="exampleInputFile">头像</label>
                                <input type="file" name="headerImg" id="exampleInputFile">
                            </div>
                            <div class="form-group">
                                <label for="exampleInputFile">生活照</label>
                                <input type="file" name="photos" multiple>
                            </div>
                            <div class="checkbox">
                                <label>
                                    <input type="checkbox"> Check me out
                                </label>
                            </div>
                            <button type="submit" class="btn btn-primary">提交</button>
                        </form>

在这里插入图片描述

  • 后端代码
 /**
     * MultipartFile  自动封装上传来的文件
     * @param email
     * @param username
     * @param headerImg
     * @param photos
     * @return
     */
      @PostMapping("/upload")
    public String upload(@RequestParam("email") String email,
                         @RequestParam("username") String username,
                         @RequestPart("headerImg")MultipartFile headerImg,
                         @RequestPart("photos") MultipartFile[] photos) throws IOException {
          log.info("上传的信息:email={},username={},headerImg={},photos={}",email,username,headerImg.getSize(),photos.length);
           //单文件上传
          if(headerImg.getSize()>0) {
              String originalFilename = headerImg.getOriginalFilename();
              headerImg.transferTo(new File("F://cache//" + originalFilename));
          }
          //多文件上传
          if(photos.length>0){
              for (MultipartFile photo:photos){
                  if(!photo.isEmpty()){
                      photo.transferTo(new File("F://photo//"+photo.getOriginalFilename()));
                  }
              }
          }
          return "main";
      }

注意配置单文件最大可上传量和总文件最大上传量

#单文件
spring.servlet.multipart.max-file-size=100MB
#最大总容量
spring.servlet.multipart.max-request-size=100MB
  • 自动配置原理
    文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties
    • 自动配置好了 StandardServletMultipartResolver 【文件上传解析器】
  • 原理步骤
    • 1、请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求
    • 2、参数解析器来解析请求中的文件内容封装成MultipartFile
    • 3、将request中文件信息封装为一个Map;MultiValueMap<String, MultipartFile>
    FileCopyUtils。实现文件流的拷
    @PostMapping("/upload")

    public String upload(@RequestParam("email") String email,

                         @RequestParam("username") String username,

                         @RequestPart("headerImg") MultipartFile headerImg,

                         @RequestPart("photos") MultipartFile[] photos)

参数处理:
在这里插入图片描述

3.2.3.4 异常处理
3.2.3.4.1 错误处理
默认规则

• 默认情况下,Spring Boot提供/error处理所有错误的映射
• 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
在这里插入图片描述默认情况是这样的:
在这里插入图片描述

  • 修改为自定义的异常界面
    error/下的4xx,5xx页面会被自动解析;
    在这里插入图片描述
  • 想让界面显示出错误信息和状态码等信息
  <section class="error-wrapper text-center">
            <h1><img alt="" src="images/404-error.png"></h1>            
            <h2 th:text="${status}">page not found</h2>
            <h3 th:text="${message}">We Couldn’t Find This Page</h3>
             <p class="nrml-txt" th:text="${trace}">Why not try refreshing you page? Or you can <a href="#">contact our support</a> if the problem persists.</p>
            <a class="back-btn" th:href="@{/main.html}"> Back To Home</a>
        </section>

status、message、trace都是json中的数据,可以自己提取

定制错误处理逻辑
  1. 自定义错误页
    error/404.html error/5xx.html;有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页

  2. @ControllerAdvice+@ExceptionHandler处理全局异常;底层是 ExceptionHandlerExceptionResolver 支持的

  3. @ResponseStatus+自定义异常 ;底层是 ResponseStatusExceptionResolver ,把responsestatus注解的信息底层调用 response.sendError(statusCode, resolvedReason);tomcat发送的/error

  4. Spring底层的异常,如 参数类型转换异常;DefaultHandlerExceptionResolver 处理框架底层的异常。
    response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
    在这里插入图片描述
    在这里插入图片描述

  5. ErrorViewResolver 实现自定义处理异常;
    • response.sendError 。error请求就会转给controller
    • 你的异常没有任何人能处理。tomcat底层 response.sendError。error请求就会转给controller
    basicErrorController 要去的页面地址是 ErrorViewResolver

  6. 自定义错误页面
    a. error/404.html error/5xx.html

  7. @ControllerAdvice+@ExceptionHandle 处理异常

  8. ErrorViewResolver实现自定义处理异常

  9. 实现HandlerExceptionResolver处理异常

异常处理自动配置原理

ErrorMvcAutoConfiguration自动配置异常处理规则

  1. 容器中的类型:DefaultErrorAttributes -> id: errorAttributes
    a. public class DefaultErrorAttributes implements ErorAttributes, HandlerExceptionResolver
    b. DefaultErrorAttributes:定义错误页面中可以包含哪些数据。
    在这里插入图片描述
    在这里插入图片描述

  2. 容器中的组件:类型:BasicErrorController --> id:basicErrorController(json+白页 适配响应)

    • 处理默认 /error 路径的请求;页面响应 new ModelAndView(“error”, model);
    • 容器中有组件 View->id是error;(响应默认错误页)
    • 容器中放组件 BeanNameViewResolver(视图解析器);按照返回的视图名作为组件的id去容器中找View对象。
  3. 容器中的组件:类型:DefaultErrorViewResolver -> id:conventionErrorViewResolver

    • 如果发生错误,会以HTTP的状态码 作为视图页地址(viewName),找到真正的页面
    • error/404、5xx.html

如果想要返回页面;就会找error视图【StaticView】。(默认是一个白页)

BasicErrorController中有两个处理的方法:

  • 响应的是json
    在这里插入图片描述

  • 响应的是错误页面
    在这里插入图片描述

异常处理步骤流程

1、执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException
2、进入视图解析流程(页面渲染?)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
3、mv = processHandlerException;处理handler发生的异常,处理完成返回ModelAndView;
a. 遍历所有的 handlerExceptionResolvers看谁能处理当前异常【HandlerExceptionResolver处理器异常解析器】
在这里插入图片描述
b. 系统默认的 异常解析器
在这里插入图片描述

  • 1、DefaultErrorAttributes先来处理异常。把异常信息保存到rrequest域,并且返回null;

  • 2、默认没有任何人能处理异常,所以异常会被抛出
    • 1、如果没有任何人能处理最终底层就会发送 /error 请求。会被底层的BasicErrorController处理
    • 2、解析错误视图;遍历所有的 ErrorViewResolver 看谁能解析。

    在这里插入图片描述

  • 3、 默认的 DefaultErrorViewResolver ,作用是把响应状态码作为错误页的地址,error/500.html

  • 4、模板引擎最终响应这个页面 error/500.html

3.2.3.5 Web原生组件注入(Servlet Filter Listener)
1. Servlet的ApI

拦截器:
@ServletComponentScan(basePackages = "com.tjk.admin.servlet") :指定原生Servlet组件都放在那里
@WebServlet(urlPatterns = "/my"):效果:直接响应,没有经过Spring的拦截器?
@WebFilter(urlPatterns={"/css/*","/images/*"})
@WebListener
推荐使用这种方式
扩展DispatchServlet 如何注册进来

  1. 容器中自动配置了 DispatcherServlet 属性绑定到 WebMvcProperties;对应的配置文件配置项是 spring.mvc。
  2. 通过 ServletRegistrationBean 把 DispatcherServlet 配置进来。
  3. 默认映射的是 / 路径。
    在这里插入图片描述Tomcat-Servlet;
    多个Servlet都能处理到同一层路径,精确优选原则
    A: /my/
    B: /my/1
  • servlet
@ServletComponentScan(basePackages="com.tjk.practice5.servlet")
@SpringBootApplication
public class Practice5Application {

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

}
  • servlet文件
@WebServlet(urlPatterns = "/my")
public class Myservlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             resp.getWriter().write("6666");
    }
}
  • 界面响应
    在这里插入图片描述
  • 过滤器
@Slf4j
@WebFilter(urlPatterns = {"/css/*","/images/*"})
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("Filter初始化完成");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("真正的过滤器处理逻辑");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
    log.info("Filter已经销毁");
    }

在这里插入图片描述

  • 监听器
@Slf4j
@WebListener()
public class MyListeren implements ServletContextListener {

    public void contextInitialized(ServletContextEvent sce) {
        log.info("MyListeren已经初始化完成");
    }

    public void contextDestroyed(ServletContextEvent sce) {
        log.info("MyListeren已经销毁");
    }
}

在这里插入图片描述

2.使用RegistrationBean

ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean

  • 第一种
    在这里插入图片描述
  • 第二种
    在这里插入图片描述- 代码:(servlet 过滤器 监听器)
@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);
    }
}
3.2.3.6 嵌入式Servlet容器
1. 切换嵌入式Servlet容器
  • 默认支持的webServer
  1. Tomcat, Jetty, or Undertow
  2. ServletWebServerApplicationContext 容器启动寻找ServletWebServerFactory 并引导创建服务器
  3. 切换服务器(默认有三个)
    在这里插入图片描述
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions><!--排除默认的tomcat组件-->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  • 原理:
  1. SpringBoot应用启动发现当前是Web应用。web场景包-导入tomcat
  2. web应用会创建一个web版的ioc容器 ServletWebServerApplicationContext
  3. ServletWebServerApplicationContext 启动的时候寻找 ServletWebServerFactory(Servlet 的web服务器工厂—> Servlet 的web服务器)
  4. SpringBoot底层默认有很多的WebServer工厂;TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory
  5. 底层直接会有一个自动配置类。ServletWebServerFactoryAutoConfiguration
  6. ServletWebServerFactoryAutoConfiguration导入了ServletWebServerFactoryConfiguration(配置类)
  7. ServletWebServerFactoryConfiguration 配置类 根据动态判断系统中到底导入了那个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有 TomcatServletWebServerFactory
  8. TomcatServletWebServerFactory 创建出Tomcat服务器并启动;TomcatWebServer 的构造器拥有初始化方法initialize—this.tomcat.start();
  9. 内嵌服务器,就是手动把启动服务器的代码调用(tomcat核心jar包存在)
2. 定制Servlet容器
  • 实现 WebServerFactoryCustomizer
  1. 把配置文件的值和ServletWebServerFactory 进行绑定
  • 修改配置文件 server.xxx
  • 直接自定义 ConfigurableServletWebServerFactory
    xxxxxCustomizer:定制化器,可以改变xxxx的默认规则
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}
3.2.3.7 定制化原理
1. 定制化的常见方式
  • 修改配置文件;
  • xxxxxCustomizer;
  • 编写自定义的配置类 xxxConfiguration;+ @Bean替换、增加容器中默认组件;视图解析器
  • Web应用 编写一个配置类实现 WebMvcConfigurer 即可定制化web功能;+ @Bean给容器中再扩展一些组件
@Configuration
public class AdminWebConfig implements WebMvcConfigurer
  • @EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所有规则全部自己重新配置; 实现定制和扩展功能

  • 原理:

    1. WebMvcAutoConfiguration 默认的SpringMVC的自动配置功能类。静态资源、欢迎页…
    1. 一旦使用 @EnableWebMvc 、。会 @Import(DelegatingWebMvcConfiguration.class)
    1. DelegatingWebMvcConfiguration 的 作用,只保证SpringMVC最基本的使用
      a. 把所有系统中的 WebMvcConfigurer 拿过来。所有功能的定制都是这些 WebMvcConfigurer 合起来一起生效
      自动配置了一些非常底层的组件。b.RequestMappingHandlerMapping、这些组件依赖的组件都是从容器中获取
      c. public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
    1. WebMvcAutoConfiguration 里面的配置要能生效 必须 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    1. @EnableWebMvc 全面接管了SpringMvc的所有自动配置类。导致了 WebMvcAutoConfiguration 没有生效。
2. 原理分析套路

场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties – 绑定配置文件项

在这里插入图片描述
在这里插入图片描述

3.3 数据访问

3.3.1 SQL

1. 数据源的自动配置
 <!--导入jdbc配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>

在这里插入图片描述数据库驱动?
为什么导入JDBC场景,官方不导入驱动?官方不知道我们接下要操作什么数据库。
数据库版本和驱动版本对应

默认版本:<mysql.version>8.0.22</mysql.version>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
<!--            <version>5.1.49</version>-->
        </dependency>
想要修改版本
1、直接依赖引入具体版本(maven的就近依赖原则)
2、重新声明版本(maven的属性的就近优先原则)
    <properties>
        <java.version>1.8</java.version>
        <mysql.version>5.1.49</mysql.version>
    </properties>
2. 分析自动配置
1. 自动配置的类
  • DataSourceAutoConfiguration : 数据源的自动配置
    1. 修改数据源相关的配置:spring.datasource
    2. 数据库连接池的配置,是自己容器中没有DataSource才自动配置的
    3. 底层配置好的连接池是:HikariDataSource
    4. DataSourceTransactionManagerAutoConfiguration: 事务管理器的自动配置
    5. JdbcTemplateAutoConfiguration: JdbcTemplate的自动配置,可以来对数据库进行crud
 	@Configuration(proxyBeanMethods = false)
   @Conditional(PooledDataSourceCondition.class)
   @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
   @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
   		DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
   		DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
   protected static class PooledDataSourceConfiguration
3. 修改配置类
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/student
    username: root
    password: tiger
  #设置多少秒请求即超时
  jdbc:
    template:
      query-timeout: 3s
4. 测试类
package com.tjk.practice5;

import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;

@Slf4j
@SpringBootTest
class Practice5ApplicationTests {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Test
    void contextLoads() {

        Long aLong = jdbcTemplate.queryForObject("select count(*) from student", Long.class);
        log.info("当前查到的总数为{}",aLong);
    }

}

在这里插入图片描述
注意:
没有描述测试类和启动项类必须在同样的包名下面

3.3.2 使用Druid数据源

1. druid官方github地址

点击查看
整合第三方技术的两种方式
• 自定义
• 找starter

2. 自定义方式
1. 创建数据源
<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.17</version>
</dependency>
  • 第一种:用xml进行注入
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="maxActive" value="20" />
		<property name="initialSize" value="1" />
		<property name="maxWait" value="60000" />
		<property name="minIdle" value="1" />
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		<property name="minEvictableIdleTimeMillis" value="300000" />
		<property name="testWhileIdle" value="true" />
		<property name="testOnBorrow" value="false" />
		<property name="testOnReturn" value="false" />
		<property name="poolPreparedStatements" value="true" />
		<property name="maxOpenPreparedStatements" value="20" />
  • 第二种:用yml文件和配置类进行组合
    在这里插入图片描述
@Configuration
public class MyDataSourceConfigurtion {
    @ConfigurationProperties("spring.datasource")//该配置类是,将配置文件和配置类中的属性相互绑定
    @Bean
    public DataSource dataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        return druidDataSource;
    }
}

@ConfigurationProperties("spring.datasource")//该配置类是,将配置文件和配置类中的属性相互绑定
在这里插入图片描述

2.进行sql监控
  1. 首先需要将创建一个监控的servlet
  • 第一种:利用xml进行servlet的注册
  <servlet>
      <servlet-name>DruidStatView</servlet-name>
      <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>DruidStatView</servlet-name>
      <url-pattern>/druid/*</url-pattern>
  </servlet-mapping>
  • 第二种: 利用配置文件将servlet进行注册进容器
/**
* 配置druid的监控页功能
*/
    @Bean
    public ServletRegistrationBean servletRegistrationBean(){
        StatViewServlet statViewServlet = new StatViewServlet();
        ServletRegistrationBean<StatViewServlet> statViewServletServletRegistrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");
        return statViewServletServletRegistrationBean;
    }
  1. 将数据源的Filter功能进行开启
  • 第一种方法:利用xml进行bena的注册
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  	... ...
  	<property name="filters" value="stat" />
  </bean>
  • 第二种:利用配置类进行Filter的开启
    druidDataSource.setFilters("stat");//打开了druid监控功能
@Configuration
public class MyDataSourceConfigurtion {

    @ConfigurationProperties("spring.datasource")//该配置类是,将配置文件和配置类中的属性相互绑定
    @Bean
    public DataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setFilters("stat");//打开了druid监控功能
        return druidDataSource;
    }

效果:
直接访问注册的servlet路径就可以进行sql的监控
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

3. 监控web应用
  • 第一种 :利用web.xml进行配置Filter
  <filter>
  	<filter-name>DruidWebStatFilter</filter-name>
  	<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
  	<init-param>
  		<param-name>exclusions</param-name>
  		<param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
  	</init-param>
  </filter>
  <filter-mapping>
  	<filter-name>DruidWebStatFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  • 第二种 通过配置类来进行filter的注册
 @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        WebStatFilter webStatFilter=new WebStatFilter();
        FilterRegistrationBean<WebStatFilter> filterFilterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
        filterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));//拦截所有的路径请求
        filterFilterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,/sql");//排除拦截的请求
        return filterFilterRegistrationBean;
    }

web应用的监控已经开启
在这里插入图片描述Url监控
在这里插入图片描述防火墙:需要在数据源的配置上加上wall参数
在这里插入图片描述

在这里插入图片描述

4. 登录功能

需要给servlet设置初始化参数loginUsername、loginPassword

 @Bean
    public ServletRegistrationBean servletRegistrationBean(){
        StatViewServlet statViewServlet = new StatViewServlet();
        ServletRegistrationBean<StatViewServlet> servletRegistrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");
        servletRegistrationBean.addInitParameter("loginUsername","tjk");
        servletRegistrationBean.addInitParameter("loginPassword","zjj");
        return servletRegistrationBean;
    }

在这里插入图片描述

  • 设置最大活跃数
 @ConfigurationProperties("spring.datasource")//该配置类是,将配置文件和配置类中的属性相互绑定
    @Bean
    public DataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setFilters("stat,wall");//打开了druid监控功能
        druidDataSource.setMaxActive(10);//设置最大活跃数
        return druidDataSource;
    }

druidDataSource.setMaxActive(10);//设置最大活跃数

3. 官方的start
1. 引入druid-starter
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.17</version>
        </dependency>
2. 分析自动配置
  • 扩展配置项 spring.datasource.druid
  • DruidSpringAopConfiguration.class, 监控SpringBean的;配置项:spring.datasource.druid.aop-patterns
  • DruidStatViewServletConfiguration.class, 监控页的配置:spring.datasource.druid.stat-view-servlet默认开启
  • DruidWebStatFilterConfiguration.class, web监控配置;spring.datasource.druid.web-stat-filter;默认开启
  • DruidFilterConfiguration.class}) 所有Druid自己filter的配置
    private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat";
    private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config";
    private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding";
    private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j";
    private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j";
    private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2";
    private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log";
    private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filter.wall";
3. 配置实例
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db_account
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

    druid:
      aop-patterns: com.atguigu.admin.*  #监控SpringBean
      filters: stat,wall     # 底层开启功能,stat(sql监控),wall(防火墙)

      stat-view-servlet:   # 配置监控页功能
        enabled: true
        login-username: admin
        login-password: admin
        resetEnable: false

      web-stat-filter:  # 监控web
        enabled: true
        urlPattern: /*
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'


      filter:
        stat:    # 对上面filters里面的stat的详细配置
          slow-sql-millis: 1000
          logSlowSql: true
          enabled: true
        wall:
          enabled: true
          config:
            drop-table-allow: false

3.3.3 SpringBoot整合Mybatis

点击查看

3.3.4 SpringBoot整合Mybatis-plus

点击查看

3.3.5 SpringBoot整合redis

点击查看

3.3.6 单元测试

点击查看

3.4 指标监控

1. SpringBoot Actuator

1. 简介

未来每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。

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

在这里插入图片描述

2. 1.x与2.x的不同

在这里插入图片描述

3. 如何使用
  • 引入场景
  • 访问 http://localhost:8080/actuator/**
  • 暴露所有监控信息为HTTP
management:
  endpoints:
    enabled-by-default: true #暴露所有端点信息
    web:
      exposure:
        include: '*'  #以web方式暴露

测试

http://localhost:8080/actuator/beans
http://localhost:8080/actuator/configprops
http://localhost:8080/actuator/metrics
http://localhost:8080/actuator/metrics/jvm.gc.pause
http://localhost:8080/actuator/endpointName/detailPath

4. 可视化

https://github.com/codecentric/spring-boot-admin

2. Actuator Endpoint

1. 最常使用的端点
ID描述
auditevents暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件。
beans显示应用程序中所有Spring Bean的完整列表。
caches暴露可用的缓存。
conditions显示自动配置的所有条件信息,包括匹配或不匹配的原因。configprops
env暴露Spring的属性ConfigurableEnvironment
flyway显示已应用的所有Flyway数据库迁移。需要一个或多个Flyway组件。
health显示应用程序运行状况信息。
httptrace显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个HttpTraceRepository组件。
info显示应用程序信息。
integrationgraph显示Spring integrationgraph 。需要依赖spring-integration-core。
loggers显示和修改应用程序中日志的配置。
liquibase显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase组件。
metrics显示当前应用程序的“指标”信息。
mappings显示所有@RequestMapping路径列表。
scheduledtasks显示应用程序中的计划任务。
sessions允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。
shutdown使应用程序正常关闭。默认禁用。
startup显示由ApplicationStartup收集的启动步骤数据。需要使用SpringApplication进行配置BufferingApplicationStartup。
threaddump执行线程转储。

如果您的应用程序是Web应用程序(Spring MVC,Spring WebFlux或Jersey),则可以使用以下附加端点:

ID描述
heapdump返回hprof堆转储文件。
jolokia通过HTTP暴露JMX bean(需要引入Jolokia,不适用于WebFlux)。需要引入依赖jolokia-core。
logfile返回日志文件的内容(如果已设置logging.file.name或logging.file.path属性)。支持使用HTTPRange标头来检索部分日志文件的内容。
prometheus以Prometheus服务器可以抓取的格式公开指标。需要依赖micrometer-registry-prometheus。

最常用的Endpoint
Health:监控状况
Metrics:运行时指标
Loggers:日志记录

2. Health Endpoint

健康检查端点,我们一般用于在云平台,平台会定时的检查应用的健康状况,我们就需要Health Endpoint可以为平台返回当前应用的一系列组件健康状况的集合。

  • 重要的几点:
  1. health endpoint返回的结果,应该是一系列健康检查后的一个汇总报告
  2. 很多的健康检查默认已经自动配置好了,比如:数据库、redis等
  3. 可以很容易的添加自定义的健康检查机制
    在这里插入图片描述
3. Metrics Endpoint

提供详细的、层级的、空间指标信息,这些信息可以被pull(主动推送)或者push(被动获取)方式得到;

  1. 通过Metrics对接多种监控系统
  2. 简化核心Metrics开发
  3. 添加自定义Metrics或者扩展已有Metrics
    在这里插入图片描述
4. 管理Endpoints
1. 开启与禁用Endpoints
  • 默认所有的Endpoint除过shutdown都是开启的。
  • 需要开启或者禁用某个Endpoint。配置模式为 management.endpoint..enabled = true
management:
  endpoint:
    beans:
      enabled: true
  • 或者禁用所有的Endpoint然后手动开启指定的Endpoint
management:
  endpoints:
    enabled-by-default: false
  endpoint:
    beans:
      enabled: true
    health:
      enabled: true
2. 暴露Endpoints
  • 支持的暴露方式
  1. HTTP:默认只暴露health和info Endpoint
  2. JMX:默认暴露所有Endpoint
  3. 除过health和info,剩下的Endpoint都应该进行保护访问。如果引入SpringSecurity,则会默认配置安全访问规则
IDJMXWeb
auditeventsYesNo
beansYesNo
cachesYesNo
conditionsYesNo
configpropsYesNo
envYesNo
flywayYesNo
healthYesYes
heapdumpN/ANo
httptraceYesNo
infoYesYes
integrationgraphYesNo
jolokiaN/ANo
logfileN/ANo
loggersYesNo
liquibaseYesNo
metricsYesNo
mappingsYesNo
prometheusN/ANo
scheduledtasksYesNo
sessionsYesNo
shutdownYesNo
startupYesNo
threaddumpYesNo

3. 定制 Endpoint

1. 定制 Health 信息
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
public class MyHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        int errorCode = check(); // perform some specific health check
        if (errorCode != 0) {
            return Health.down().withDetail("Error Code", errorCode).build();
        }
        return Health.up().build();
    }

}

构建Health
Health build = Health.down()
                .withDetail("msg", "error service")
                .withDetail("code", "500")
                .withException(new RuntimeException())
                .build();
management:
    health:
      enabled: true
      show-details: always #总是显示详细信息。可显示每个模块的状态信息
@Component
public class MyComHealthIndicator extends AbstractHealthIndicator {

    /**
     * 真实的检查方法
     * @param builder
     * @throws Exception
     */
    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        //mongodb。  获取连接进行测试
        Map<String,Object> map = new HashMap<>();
        // 检查完成
        if(1 == 2){
//            builder.up(); //健康
            builder.status(Status.UP);
            map.put("count",1);
            map.put("ms",100);
        }else {
//            builder.down();
            builder.status(Status.OUT_OF_SERVICE);
            map.put("err","连接超时");
            map.put("ms",3000);
        }


        builder.withDetail("code",100)
                .withDetails(map);

    }
}
2. 定制info信息

常用两种方式

1. 编写配置文件
info:
  appName: boot-admin
  version: 2.0.1
  mavenProjectName: @project.artifactId@  #使用@@可以获取maven的pom文件值
  mavenProjectVersion: @project.version@
2. 编写InfoContributor
import java.util.Collections;

import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;

@Component
public class ExampleInfoContributor implements InfoContributor {

    @Override
    public void contribute(Info.Builder builder) {
        builder.withDetail("example",
                Collections.singletonMap("key", "value"));
    }

}

http://localhost:8080/actuator/info 会输出以上方式返回的所有info信息

3. 定制Metrics信息
1.SpringBoot支持自动适配的Metrics
  1. JVM metrics, report utilization of:
    • Various memory and buffer pools
    • Statistics related to garbage collection
    • Threads utilization
    • Number of classes loaded/unloaded
  2. CPU metrics
  3. File descriptor metrics
  4. Kafka consumer and producer metrics
  5. Log4j2 metrics: record the number of events logged to Log4j2 at each level
  6. Logback metrics: record the number of events logged to Logback at each level
  7. Uptime metrics: report a gauge for uptime and a fixed gauge representing the application’s absolute start time
  8. Tomcat metrics (server.tomcat.mbeanregistry.enabled must be set to true for all Tomcat metrics to be registered)
  9. Spring Integration metrics
2. 增加定制Metrics
class MyService{
    Counter counter;
    public MyService(MeterRegistry meterRegistry){
         counter = meterRegistry.counter("myservice.method.running.counter");
    }

    public void hello() {
        counter.increment();
    }
}


//也可以使用下面的方式
@Bean
MeterBinder queueSize(Queue queue) {
    return (registry) -> Gauge.builder("queueSize", queue::size).register(registry);
}
4. 定制Endpoint
@Component
@Endpoint(id = "container")
public class DockerEndpoint {


    @ReadOperation
    public Map getDockerInfo(){
        return Collections.singletonMap("info","docker started...");
    }

    @WriteOperation
    private void restartDocker(){
        System.out.println("docker restarted....");
    }

}

场景:开发ReadinessEndpoint来管理程序是否就绪,或者LivenessEndpoint来管理程序是否存活;
当然,这个也可以直接使用
此链接

3.5 原理解析

1. Profile功能

为了方便多环境适配,springboot简化了profile功能。

1. application-profile功能
  1. 默认配置文件 application.yaml;任何时候都会加载
  2. 指定环境配置文件 application-{env}.yaml
  3. 激活指定环境
    1. 配置文件激活
    2. 命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
      修改配置文件的任意值,命令行优先
  4. 默认配置与环境配置同时生效
  5. 同名配置项,profile配置优先
2. @Profile条件装配功能
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {

    // ...

}
3. profile分组
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq

使用:--spring.profiles.active=production  激活

2. 外部化配置

点击查看

  1. Default properties (specified by setting SpringApplication.setDefaultProperties).
  2. @PropertySource annotations on your @Configuration classes. Please note that such property sources are not added to the Environment until the application context is being refreshed. This is too late to configure certain properties such as logging.* and spring.main.* which are read before refresh begins.
  3. Config data (such as application.properties files)
  4. A RandomValuePropertySource that has properties only in random.*.
  5. OS environment variables.
  6. Java System properties (System.getProperties()).
  7. JNDI attributes from java:comp/env.
  8. ServletContext init parameters.
  9. ServletConfig init parameters.
  10. Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
  11. Command line arguments.
  12. properties attribute on your tests. Available on @SpringBootTest and the test annotations for testing a particular slice of your application.
  13. @TestPropertySource annotations on your tests.
  14. Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active.
1.外部配置源

常用:Java属性文件、YAML文件、环境变量、命令行参数

2. 配置文件查找位置

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

3. 配置文件加载顺序:
  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
4. 指定环境优先,外部优先,后面的可以覆盖前面的同名配置项

3. 自定义starter

1. starter启动原理

starter-pom引入 autoconfigurer 包
在这里插入图片描述1. autoconfigure包中配置使用 META-INF/spring.factoriesEnableAutoConfiguration 的值,使得项目启动加载指定的自动配置类
2. 编写自动配置类 xxxAutoConfiguration -> xxxxProperties
2.1. @Configuration
2.2. @Conditional
2.3. @EnableConfigurationProperties
2.4@Bean
3. 引入starter — xxxAutoConfiguration — 容器中放入组件 ---- 绑定xxxProperties ---- 配置项

2. 自定义starter

atguigu-hello-spring-boot-starter(启动器)
atguigu-hello-spring-boot-starter-autoconfigure(自动配置包)

4. SpringBoot原理

Spring原理Spring注解、SpringMVC原理、自动配置原理、SpringBoot原理

1. SpringBoot启动过程

在这里插入图片描述

public interface Bootstrapper {

	/**
	 * Initialize the given {@link BootstrapRegistry} with any required registrations.
	 * @param registry the registry to initialize
	 */
	void intitialize(BootstrapRegistry registry);

}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

@FunctionalInterface
public interface ApplicationRunner {

	/**
	 * Callback used to run the bean.
	 * @param args incoming application arguments
	 * @throws Exception on error
	 */
	void run(ApplicationArguments args) throws Exception;

}
@FunctionalInterface
public interface CommandLineRunner {

	/**
	 * Callback used to run the bean.
	 * @param args incoming main method arguments
	 * @throws Exception on error
	 */
	void run(String... args) throws Exception;

}

2. Application Events and Listeners

点击查看
ApplicationContextInitializer
ApplicationListener
SpringApplicationRunListener

3. ApplicationRunner 与 CommandLineRunner

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值