写在前面:入坑-1阶段主要学习了黑马57期的springboot,觉得还不够深入,为了更深入学习springboot,入坑-2阶段学习了B站狂神的springboot,特来记录笔记,源码部分还是不能很理解,希望下次再来看的时候可以更深入理解。

1. Springboot运行原理

详见:入坑-1篇3

2. yaml与properties的配置注入

2.1 yaml配置注入

yaml和yml都是springboot推荐的配置,它们的语法一模一样。

详见:https://blog.csdn.net/JAYU_37/article/details/105960838
4.1处

yaml配置注入:

  1. @Value注入方式:

    1. Dog类
    @Component
    public class Dog {
         
        @Value("阿黄")
        private String dname;
        @Value("18")
        private Integer dage;
        .............................
        省略setter和getter方法+toString()
        .............................
    }
    
    1. 测试类:
    @SpringBootTest
    class Spring01KuangFirstApplicationTests {
         
        @Autowired
        private Dog dog;
    
        @Test
        void contextLoads() {
         
            System.out.println(dog);
        }
    }
    
    1. 输出
  2. yml注入

    1. Person类
    @Component
    @ConfigurationProperties(prefix = "person")
    public class Person {
         
        //@Value("${name}")
        private String name;
        private Integer age;
        private Boolean happy;
        private Date birth;
        private Map<String,Object> maps;
        private List<Object> lists;
        private Dog dog;
        .............................
        省略setter和getter方法+toString()
        .............................
        }
    
    
    1. yml配置文件
    #复杂对象 person
    person:
      name: liuzeyu${
         random.uuid}
      age: 23
      happy: true
      birth: 1998/09/19
      maps:
        hobby: ctrl
        singer: jay
        code: java${
         random.int}
      lists:
        - eat
        - drink
        - dream
      dog:
        dname: 旺财
        dage: 2
    
    
    
    1. 测试函数
    @SpringBootTest
    class Spring01KuangFirstApplicationTests {
         
        @Autowired
        private Person person;
        @Test
        void contextLoads() {
         
            System.out.println(person);
        }
    
    1. 输出
    2. 注意:如果在yam或properties中输入了中问的数据,控制台打印出乱码,则在setting中修改配置文件的编码

2.2 properties配置注入

  1. 在resources下新建person.properties
    name=kuangshen
    
  2. 在Person类中注入
  3. 测试函数
    @SpringBootTest
    class Spring01KuangFirstApplicationTests {
         
        @Autowired
        private Person person;
        @Test
        void contextLoads() {
         
            System.out.println(person);
        }
    
  4. 输出

2.3 yaml与properties的比较

@Value这个用起来并不方便,要为每一个属性单独赋值,太麻烦了,使用于注入参数较少的情况下。

由上图可知,yaml和properties的区别已经很明显了

  1. @ConfigurationProperties只需要写一次就好了,而@Value要写很多次
  2. 松散绑定,例如我们在yml中写last-name在如果属性是lastName,也可以注入成功,-后面是大写字母没有影响。
  3. JSR303数据校验,这个就是我们可以在字段上增加一层过滤验证,来包证数据的合法性。
  4. 复杂类型封装对象,yml可以封装对象,使用value就不可以。

2.4 JSR303数据校验

  1. 为Person类添加校验
  2. 运行结果

    并且爆出异常,注入失败!
    使用数据校验,可以保证数据的正确性;
  3. 常见参数
@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;

空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY.
    
Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
    
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) string is between min and max included.

日期检查
@Past       验证 Date 和 Calendar 对象是否在当前时间之前  
@Future     验证 Date 和 Calendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

.......等等
除此以外,我们还可以自定义一些数据校验规则

3. 自动配置原理

spring的自动配置机制使得我们可以使用官方提供的起步依赖和一些默认值开启springboot项目,例如springboot内嵌了tomcat服务器,当springboot启动后,默认使用了8080端口,当然我们也可以修改它,就是新建配置文件去覆盖默认的配置。
以tomcat的默认端口的自动配置为例,跟踪源码来探探自动配置的原理:

1. springboot启动时加载主配置类,开启了@EnableAutoConfiguration


2. @EnableAutoConfiguration的作用是什么?

利用AutoConfigurationImportSelector给容器导入组件

进入AutoConfigurationImportSelector类

3. 发现获取组件实体类方法


List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取候选配置这个配置就是默认的配置,从哪里获取呢继续进入getCandidateConfigurations方法

4. SpringFactoriesLoader做了什么?

SpringFactoriesLoader是spring的工厂加载器,负责从工厂创建bean对象加载容器

调用了一个loadFactoryNames,这个此时我们可以猜测加载的这是一个配置文件,而且是一堆全限定类名,通过反射创建对象,继续查看SpringFactoriesLoader做了什么?

5. 通过类加载器加载配置文件META-INF/spring.factories

6. META-INF/spring.factories


果然发现一些自动配置类的全限定类名,格式XXXXAutoConfiguration
部分自动配置:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
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.ElasticsearchAutoConfiguration,\
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.ReactiveRestClientAutoConfiguration,\
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.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.web.servlet.ServletWebServerFactoryAutoConfiguration,

进入
ServletWebServerFactoryAutoConfiguration类

7. ServletWebServerFactoryAutoConfiguration


spring的底层注解:根据不同的条件,来判断当前配置或类是否生效
@ConditionalOnClass:表示的是这是一个什么类,当前项目由没有这个类
@ConditionalOnWebApplication:当前项目是不是web项目

常见的ConditionalXXXX

8. 进入ServerProperties类


发现我们熟悉的@ConfigurationProperties,它的作用就是从外界配置文件注入数据到类的属性中

9.自动配置的过程

看到这里大致明白了一些自动配置的过程

  1. 从有主类带注解@EnableAutoConfiguration开始,@EnableAutoConfiguration作用 就是开启了自动配置功能
  2. springboot启动类启动就会去加载factories中的类名,并反射创建对象,但不是全部都有,只有添加了相关依赖jar包的才有
  3. ServletWebServerFactoryAutoConfiguration所做的事就是创建一些默认的bean存入容器中
  4. 加载我们自定义配置的信息来覆盖默认的配置,在@EnableConfigurationProperties的属性中进行,通过属性绑定的XXXProperties类来产生关联
  5. XXXAutoConfiguration----> 将默认值封装成XXXXProperties对象<------->与yml/prorperties绑定
  6. 因此我们可以通过自定义配置yml/prorperties来修改默认的自动配置

10.自动配置的原理

  1. springboot启动默认会加载大量的自动配置类
  2. 这些自动配置类是否在我们添加的起步依赖jar包中,如果在的话,我们就不用进行手动配置了
  3. 从容器自动配置添加组件(bean)的时候,会从XXXProperties类中获取某些属性,我们只需要在配置文件中指定即可

XXXAutoConfiguration:自动配置类,给容器中添加组件
XXXProperties:封装配置文件中相关的属性,与spring的配置文件相关联

11.最后看哪些自动配置类生效了

最后如果想看哪些自动配置ide类生效了,我们可以在配置文件中指定debug = true
例如

不满足添加的类将不会生效


4. springboot静态资源的位置

4.1 webjars

进入WebMvcAutoConfiguration
阅读源码

if (!registry.hasMappingForPattern("/webjars/**")) {
   
    this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{
   "/webjars/**"}).addResourceLocations(new String[]{
   "classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}

可以通过外部maven坐标引入webjars资源目录

<dependency><!--Jquery组件(前端)-->
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.3.1</version>
</dependency>


我们可以通过地址
http://127.0.0.1:8080/webjars/jquery/3.3.1/jquery.js来访问
其中/META-INF/maven/resources下的目录就是webjars,也是webjars资源目录的根目录。

4.2 其它资源文件的位置

如果使用默认的路径,即没有去修改spring.mvc.static-path-pattern则会匹配这里的路径

 String staticPathPattern = this.mvcProperties.getStaticPathPattern();
 if (!registry.hasMappingForPattern(staticPathPattern)) {
   
     this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{
   staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
 }

进入ResourceProperties的类
发现存放静态资源对应了下面四个地方

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

路径:

"classpath:/META-INF/resources/"   此路径下就是webjar
"classpath:/resources/", 	//resouces/resouces/资源文件下
"classpath:/static/", 		//resouces/static/资源文件下
"classpath:/public/"		//resouces/public/资源文件下

/** 下的所有资源都会被解析,但是它目录们四个是具有访问优先级,resources > static > public,<mark>但切记不能直接放在/ 下,即resources下</mark>

4.3 小结

springboot中,我们可以使用以下方式处理静态资源

存放位置:							访问方式
webjars								http://localhost:8080/webjar/...
/public,/static,/resouces		http://localhost:8080/...

注意:使用/**方式访问,/public,/static,/**,/resouces具有不一样的优先级 resources > static > public 

5. Thymeleft模板引擎

Thymeleft模板引擎是一个类似于jsp的视图解析器,但是jsp由于过老,已经被springboot逐渐弃用,springboot更是推荐使用thymeleft

引入问题:
未使用thymeleft前,我们如果想要访问资源文件下的templates下文件,会出现404将无法访问,因为这是springboot官方规定的,必须使用模板引擎才能访问。

5.1 Thymeleft介绍

Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎。类似JSP,Velocity,FreeMaker等, 它也可以轻易的与Spring MVC等Web框架进行集成作为Web应用的模板引擎。与其它模板引擎相比, Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用。

5.2 Thymeleft语法

可以查看官方文档或者

https://segmentfault.com/a/1190000020401122

6. MVC自动配置原理

6.1 自定义视图解析器

手段:通过阅读官方文档+源码的方式理解。(其实不是很理解)

官方文档:https://docs.spring.io/spring-boot/docs/2.2.7.RELEASE/reference/html/spring-boot-features.html#boot-features-developing-web-applications

7.1.1. Spring MVC Auto-configuration
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.

Support for serving static resources, including support for WebJars (covered later in this document)).

Automatic registration of Converter, GenericConverter, and Formatter beans.

Support for HttpMessageConverters (covered later in this document).

Automatic registration of MessageCodesResolver (covered later in this document).

Static index.html support.

Custom Favicon support (covered later in this document).

Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

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.

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.

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.

翻译如下

7.1.1。 Spring MVC自动配置
Spring Boot为Spring MVC提供了自动配置,可与大多数应用程序完美配合。

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

包含ContentNegotiatingViewResolver和BeanNameViewResolver Bean。

支持服务静态资源,包括对WebJars的支持(在本文档的后面部分有介绍)。

自动注册Converter,GenericConverter和Formatter Bean。

对HttpMessageConverters的支持(在本文档后面介绍)。

自动注册MessageCodesResolver(在本文档后面介绍)。

静态index.html支持。

自定义Favicon支持(在本文档后面介绍)。

自动使用ConfigurableWebBindingInitializer Bean(在本文档后面介绍)。

如果要保留这些Spring Boot MVC定制并进行更多的MVC定制(拦截器,格式化程序,视图控制器和其他功能),则可以添加自己的类型为WebMvcConfigurer的@Configuration类,但不添加@EnableWebMvc。

如果要提供RequestMappingHandlerMapping,RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义实例,并且仍然保留Spring Boot MVC自定义,则可以声明WebMvcRegistrations类型的Bean,并使用它提供这些组件的自定义实例。

如果要完全控制Spring MVC,则可以添加用@EnableWebMvc注释的自己的@Configuration,或者按照@EnableWebMvc的Javadoc中的说明添加自己的@Configuration注释的DelegatingWebMvcConfiguration。

抓住这两个主要的类:

ContentNegotiatingViewResolver和BeanNameViewResolver Bean。
  1. 先进入WebMvcAutoConfiguration ,之后按住CTRL+shift+alt+N进入全局搜索ContentNegotiatingViewResolver
public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered, InitializingBean {
   
    @Nullable
    private ContentNegotiationManager contentNegotiationManager;
    private final ContentNegotiationManagerFactoryBean cnmFactoryBean = new ContentNegotiationManagerFactoryBean();
    private boolean useNotAcceptableStatusCode = false;
    @Nullable
    private List<View> defaultViews;
    @Nullable
    private List<ViewResolver> viewResolvers;
    private int order = -2147483648;
    private static final View NOT_ACCEPTABLE_VIEW = new View() {
   
        @Nullable
        public String getContentType() {
   
            return null;
        }

        public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
   
            response.setStatus(406);
        }
    };

    public ContentNegotiatingViewResolver() {
   
    }
......................
  1. 找到对应的解析视图的代码

  2. getCandidateViews是如何获取它的候选视图解析器
    Iterator var5 = this.viewResolvers.iterator();

    因此得出结论:ContentNegotiatingViewResolver视图解析器是用来组合所有的视图解析器的。
    此时我们可以查看viewResolvers的值是哪里来的,CTRL+f搜索

  3. 是从IOC容器中获取的,于是我们可以猜测,是否可以自定义视图解析器呢?
    答案是可以的,根据官方文档的说法:

    如果要保留这些Spring Boot MVC定制并进行更多的MVC定制(拦截器,格式化程序,视图控制器和其他功能),则可以添加自己的类型为WebMvcConfigurer的@Configuration类,但不添加@EnableWebMvc

  4. 于是自定义如下:

@Configuration
public class MyMvcConfig  implements WebMvcConfigurer {
   

    //MyVieResolver就是一个视图解析器加入容器中
    @Bean
    public ViewResolver setMyVieResolver(){
   
        return new MyVieResolver();
    }

    //自定义一个自己的视图解析器MyVieResolver
    private static class MyVieResolver implements ViewResolver{
   
        @Override
        public View resolveViewName(String s, Locale locale) throws Exception {
   
            return null;
        }
    }
}

如何验证是否加入容器成功呢?
6. 我们给 DispatcherServlet 中的 doDispatch方法 加个断点进行调试一下,因为所有的请求都会走到这个方法中

发现了我们自己添加进去的ViewResolver

没有添加之前默认只是5个

ContentNegotiatingViewResolver使用所有其他视图解析器来定位视图,因此它应该具有较高的优先级

6.2 扩展springMVC

修改springboot默认配置,通过阅读上面的官方文档

如果要保留这些Spring Boot MVC定制并进行更多的MVC定制(拦截器,格式化程序,视图控制器和其他功能),则可以添加自己的类型为WebMvcConfigurer的@Configuration类,但不添加@EnableWebMvc

我们要做的事创建一个配置类,放在包config下,实现WebMvcConfigurer接口,但是不能添加@EnableWebMvc

@Configuration
public class ExtendsMvc implements WebMvcConfigurer {
   

    //方法的作用是添加视图的控制跳转
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
   
        registry.addViewController("/liu").setViewName("index");
    }
}

访问成功

这就是官方推荐我们使用的扩展方法,即保留课springboot的原有配置,也扩展了我们想要的配置。
我们来分析一些内部的原理

  1. WebMvcAutoConfiguration是springMVC的自动配置类,它有一个内部类WebMvcAutoConfigurationAdapter,作用则是去适配springMVC的各种组件。
  2. 进来查看WebMvcAutoConfigurationAdapter类,发现被导入了一个配置
    @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
  3. 进EnableWebMvcConfiguration类查看,发现这是一个静态内部类
    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration,并且继承了DelegatingWebMvcConfiguration

它的父类DelegatingWebMvcConfiguration有一个这样的方法setConfigurers

这个方法的作用是从容器中获取所有的configurers

进入addViewControllers方法

将所有的WebMvcConfigurer相关配置来一起调用!包括我们自己配置的和Spring给我们配置的
所以我们得出结论:所有的WebMvcConfiguration被作用,不止spring自己的配置类,我们自定义的也会被调用。

6.3 全面接管SpringMVC

所谓的全面接管SpringMVC,则是在配置类上使用@EnableWebMvc
作用则是springboot对springmvc的所有自动配置失效,由我们自己配置springmvc
不加@EnableWebMvc

加@EnableWebMvc

可见所有的springMVC配置全部都失效了,但实际开发中,我们并不推荐全面接管springMVC。
为什么说加上@EnableWebMvc所有配置就失效 了呢
阅读源码:

  1. 进入@EnableWebMvc

    EnableWebMvc导入了一个配置类DelegatingWebMvcConfiguration
  2. 进入DelegatingWebMvcConfiguration

    DelegatingWebMvcConfiguration继承了一个WebMvcConfigurationSupport
  3. 进入springMVC自动配置类WebMvcAutoConfiguration,发现了

    @ConditionalOnMissingBean({WebMvcConfigurationSupport.class})的作用:如果不是WebMvcConfigurationSupport类,则自动配置类生效
  4. 显然我们EnableWebMvc导入了WebMvcConfigurationSupport的子类,于是DelegatingWebMvcConfiguration也是属于WebMvcConfigurationSupport,故自动配置类就失效了。