一些原理

基础入门

spring与springboot

  1. 一个项目或者说大型应用,功能模块众多,我们应该把一个大的项目拆分成一个一个微小的功能模块,每一个微小的功能模块我们称之为一个微服务,spring就可以快速地帮我们创建和开发一个微服务

  2. 为什么用springboot

    springboot能快速创建出生产级别的spring应用,让开发变得简单

  3. springboot的优点

    • 创建独立spring应用

    • 内嵌web服务器(嵌入式的内部容器tomcat、jetty等)

      以前要开发web应用,这个web应用最终会被打包成一个war包,这个war包最终会被部署到tomcat上,所以我们的环境还得装tomcat,还要部署tomcat,自己下载,或者通过IDEA下载配置。

      而通过springboot进行web开发,目标环境即使没有tomcat也没有关系,我们创建的应用里面已经带了tomcat服务器,已经集成了。

    • 自动starter依赖,简化构建配置

      以前依赖的jar包的版本必须控制好,都是自己去控制、添加依赖,现在springboot给我们提供了starter启动器,假如我们想要开发web应用,我们只需要添加一个web的启动器依赖,不需要管理其他web开发需要的jar包,springboot自动管理jar包,并且保证jar包的版本

    • 自动配置spring以及第三方功能

      不用像以前整合mybatis、spring、springmvc要写一大堆配置

      这些配置现在springboot自动配置

      专注我们的业务逻辑

    • 提供生产级别的监控、健康检查及外部化配置

    • 无代码生成,无需编写xml(自动化配置,我们不需要写一大堆配置。并且不需要写配置不是说springboot给我们自动生成那些配置文件,没有任何代码生成,就是基于spring底层的装配和依赖注入功能)

    总结:自动配置、简化开发、内嵌web服务器,整合整个spring生态圈、技术栈的一站式框架,spring boot是简化spring技术栈的快速开发脚手架

  4. springboot的缺点

    • 版本迭代块,需要时刻关注变化,也不能算作是缺点
    • 封装太深,内部原理复杂,不容易精通

微服务

  1. 什么是分布式:

    分布式是个工作方式,由多台服务器共同完成一件比较复杂的事,这多台服务器共同提供服务,呈现出一台单机服务器提供服务的效果。强调多台服务器共同提供服务

    把原来一个大块系统,根据功能,分成多个子系统或者说子模块,这些子系统或者说子模块分别部署在不同的服务器(集群上),共同提供服务,子服务部署在不同的服务器或集群上,共同完成一件事情,可以理解为分布式。

    将一个大的业务或者说系统拆分成多个子业务或者说子系统、子模块,这些子业务、子系统、子服务分别部署在不同的服务器(或者集群)上,共同对外呈现单机的服务效果。是一种工作方式

    好处:

    • 降低模块之间的耦合性
    • 提高各个子模块、子系统的复用性
  2. 什么是集群:

    分布式强调工作方式,也可以认为是一种思想,就像AOP、IOC都可以认为是思想,是一种方式。

    而集群,是物理层面的多台服务器,多台服务器合起来作为整体完成业务

    分布式强调拆,大的业务拆成小的。

    集群强调合,多个服务器合起来完成业务,说的是物理层面,而分布式可以看作是工作方式或者思想。

    他们不能分开来看,不是并行的概念,集群和分布式是共同存在的。

  3. 什么是微服务

    是一种架构风格。

    把一个应用拆分为一组小型服务,这每个小服务或者说小模块可以独立部署,也就是部署在不同的服务器上!!其实就是说的分布式的概念

    各个小服务之间使用轻量级HTTP交互

    服务围绕业务功能拆分

    各个小服务可以由全自动部署机制独立部署

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

    强调独立、分布式、大模块分为小模块,小模块部署到独立的服务器!!!

  4. 分布式的困难---涉及到服务器之间的通信,配置的同步等问题,根本原因就是因为各个微服务部署在不同的服务器!!

    • 远程调用

    • 负载均衡

    • 服务容错

    • 服务发现

    • 配置管理

    • 服务监控

    • 链路追踪

      a服务器的服务调b,b调c,c调d,d出了问题,怎么追踪

    • 日志管理

    分布式的解决:

    springboot + springcloud。通过springcloud将这些服务网状的构建起来

    springboot帮我们快速地构建出一个应用。

    这些应用通过springcloud构建起来

了解自动配置原理

依赖管理

  • 父项目做依赖管理

    1. 我们引入了父项目,导入了一个spring-boot-starter-web依赖

      父项目一般是做依赖管理,子项目只要继承了父项目,子项目就不需要写版本号了

      image-20211007005513089

      父项目的主要功能就是做依赖管理,对依赖的版本控制,点进dependencies里去,发现所需要依赖的jar包都规定了版本号,这些需要依赖的jar包会自动被springboot引入。

      几乎声明了所有开发中常用的jar的版本号

      这个也可以称为自动版本仲裁机制

    2. 引入依赖默认都可以不屑版本号

      引入非版本仲裁的依赖,要写版本号

    3. 如果说我们需要依赖的jar包版本和springboot里父项目规定的版本号不一致,那么在pom文件里写properties标签,重新规定版本

      比如:

      image-20211007005926535

      • 查看spring-boot-dependencies里面规定的当前依赖的版本用的key
      • 在当前项目的pom文件进行重写
  • 开发导入starter场景启动器

    1. starter是一组依赖的集合描述,一般引入一个starter,开发所需要的完整依赖就被引入了

      见到很多spring-boot-starter-*: *代表某种场景

      只要引入starter,这个场景的所有常规需要的依赖都自动引入

      <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      
    2. 见到的 *-spring-boot-starter:第三方为我们提供的简化开发的场景启动器

    3. 所有场景启动器(starter),最底层的依赖都会依赖于

      image-20211007010839779

      这是springboot自动配置的核心依赖

    4. 我们要开发哪个场景,就添加哪个场景的starter,那么所需要的常规依赖都会被自动引入,这也是maven的特性,所以说springboot简化了依赖管理,就是通过spring-boot-starter-*: *代表某种场景

自动配置

  1. 自动配好tomcat

    • 引入Tomcat依赖---通过依赖管理,spring-boot-starter-web,引入了完整的依赖,包括tomcat、mvc等
    • 自动配置好tomcat
  2. 自动配好springMVC

    • 引入springMVC全套组件
    • 自动配好springMVC常用组件
  3. 自动配好web常见功能

    • springboot帮我们配置好了所有web开发的常见场景
  4. 自动配置好了包的扫描

    • 主程序所在的包及其下面的所有子包里面的组件都会默认被扫描进来(一定要注意主程序和其他类的目录结构,不然扫描不到,这也是一种规范)

    • 如果想要改变扫描路径

      • 通过注解的属性

        image-20211007012036725

      • 或者在主程序上添加注解@ComponentScan("xxx")

  5. 各种配置有默认值

    • 默认配置最终都是映射到某一个类上
    • 配置文件的值最终会绑定到某个类上,这个类会在容器中创建对象(对象交给容器创建,这是spring的IOC思想)
  6. 按需加载自动配置项

    • 非常多的starter,这些starter不是全部启动,我们引入了哪个启动场景,这个场景下的自动配置才会开启

    image-20211007012636111

    springboot所有的自动配置都依赖于这个包,在这个包里

    虽然看External Libraries下有很多类,这些类并不是全部生效,里面有一些是报红的,因为我们并没有引入对应场景的starter,那么对应的自动配置并没有开启

容器功能和注解

  1. @Configuration

    我们编写一个类,用这个注解加在类上面,那么这个类就类似于一个配置文件

    @Configuration告诉springboot这个类是配置类,这个配置类就相当于一个配置文件,以前配置文件能做什么,现在配置类就做什么。以前通过配置文件能创建容器,通过容器能创建单例的实例化对象,那么现在配置类也能完成这些功能!!

    @Configuration
    public class MyConfig {
            /**
         * 外部无论对配置类中的这个组件注册方法调用多少次,都是之前容器中注册的单例对象
         * @Bean:
         * 给容器中添加组件,以方法名作为组件的ID,也就是对象的名字
         * 返回类型:就是组件类型
         * 返回的值就是组件在容器中的实例(是单例的)。之前有容器,现在用springboot同样有容器,容器帮我们创建对象
         * 组件的名字默认是方法名,组件名就是对象实例的名字,也就是对象的自定义名称。方法名就是对象的自定义名称
         * @return
         */
        @Bean
        public User user01() {
            return new User("zhangsan", 18);
        }
    
        @Bean
        public Pet tomcatPet() {
            return new Pet("tomcat");
        }
    }
    

    以上代码写了之后,容器中就有这两个组件,也就是对象实例。

    不要认为写了new,那么就是每次创建新的, 仍然是将对象交给容器创建,仍然是单例。写了上面代码之后,容器中就有这两个对象实例!!之前是在xml文件里写bean标签,这里是加上bean注解!

    /**
     * 这个类也称为主程序类,是所有启动的入口
     * 告诉springboot这是一个springboot应用
     */
    @SpringBootApplication
    public class MainApplication {
        public static void main(String[] args) {
            // 1. 返回我们的IOC容器,(IOC是一种思想,就是将对象交给容器创建,是一种控制反转的思想,本来由我们创建的对象,交给外部容器来管理并创建,这就是控制反转)
            ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
            // 2. 查看容器里的组件
            String[] beanDefinitionNames = run.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                System.out.println(beanDefinitionName);
            }
            // 3. 从容器中获取组件
        }
    }
    

    我们通过这种方式在容器中注册的两个对象,或者说对象实例或组件,默认是单例的!!无论获取多少次,都是同一个实例对象,不要看到new就认为会是不同的对象。单例模式也会有new,new就是造一个单例对象,我们以后都用这个单例对象,不会再去调这个方法重复造对象。我们获取对象,不会通过调配置类里的注册组件的方法来获取对象,而是通过springboot容器拿到对象

            // 3. 从容器中获取组件
            Pet tom01 = run.getBean("tom", Pet.class);
            Pet tom02 = run.getBean("tom", Pet.class);
            System.out.println(tom01 == tom02); // true
    

    这个和之前用spring或springmvc在容器中注册的对象是一样的。本质都是IOC . IOC也有两种方式,一种是基于注解,一种是基于xml配置文件,基于xml配置文件里依赖注入又有两种方式,一种是set注入,一种是构造注入,大多数都是set注入。IOC的底层采用的反射,AOP底层采用的动态代理。

  2. 即使我们通过容器获得myconfig对象,通过myconfig对象来重复调用myconfig类里的创建单例对象的方法,最后获得的对象仍然是同一个对象,是单例!!

            MyConfig bean = run.getBean(MyConfig.class);
            User user01 = bean.user01();
            User user02 = bean.user01();
            System.out.println(user01 == user02); // true
    

    一旦配置类里面,在方法上加了bean,也就是在容器中注册了对象实例或者说注册了组件,那么外部无论调多少次方法,拿到的都是我们在容器中注册的单例对象!!!!取决于configuration注解的属性proxyBeanMethods = true

    // com.atguigu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$2d5007d6@52d97ab6
    MyConfig myconfig = run.getBean(MyConfig.class);
    System.out.println(myconfig);
    // 上面返回的myconfig实例是spring增强后的代理对象,因为springboot2,默认configuration注解的属性proxyBeanMethods = true
    // 那么myconfig是代理对象,也就是通过代理对象调用注册实例的方法,springboot会检查是否已经存在实例,如果有,就返回之前创建好的实例
    // 而不会去重新创建,目的:保持组件单例
    User user01 = myconfig.user01();
    User user02 = myconfig.user01();
    System.out.println(user01 == user02);
    
    // 如果我们将configuration注解的属性proxyBeanMethods改为false
    // 那么拿到的myconfig对象,就是普通的对象而不是代理对象
    // 那么通过myconfig去调用注册实例的方法,就不会检查是否之前有创建好的实例,而会直接创建新的对象
    // com.atguigu.boot.config.MyConfig@654c7d2d
    MyConfig myconfig = run.getBean(MyConfig.class);
    System.out.println(myconfig);
    User user01 = myconfig.user01();
    User user02 = myconfig.user01();
    System.out.println(user01 == user02); // false
    

    configuration注解的属性proxyBeanMethods = true,那么外部随便调用多少次注册组件的方法,都会先去容器里检查是否有创建好的对象实例

  3. proxyBeanMethods = true叫full模式

    proxyBeanMethods = false叫轻量级模式,lite模式

    意思就是说,去调用注册实例的方法的时候,或者说去调用创建对象的new方法的时候,会绕过容器,不会去容器里检查是否有事先创建好的对象,就是轻量级模式,启动就快,就轻量

    full模式,就是说去调用创建对象的new方法的时候,即注册实例的方法,都会去容器里先检查是否有创建好的对象,有的话就直接拿来用,目的是保持单例,启动就稍慢。

  4. 向容器里注册组件,用以下注解也一样是可以的

    • @Bean

      给容器注册组件

    • @Component

    • @Controller

    • @Service

    • @Repository

    • @Import

      image-20211007170017159

      给容器中自动创建出这两个类的对象实例

      @import导入的组件,默认的名字也就是对象名字是全类名

  5. @Conditional

    满足conditional指定的条件,则进行条件注入

  6. @ImportResources

    可以导入配置文件,让配置文件里声明的bean实例对象生效

配置绑定JavaBean

  1. 把properties里所有的配置绑定到javaBean里

    • 采用原生的Java代码的方式来做

      image-20211007173613033

    • 在springboot里,这个过程会变得很简单

      使用@Component + @ConfigurationProperties

      /**
       * 为什么一定要写@Component:只有在容器中的组件才会拥有springboot提供的强大功能
       */
      @Component
      @ConfigurationProperties(prefix = "mycar")
      
    • 使用@EnableConfigurationProperties + @ConfigurationProperties

      EnableConfigurationProperties这个注解写在配置类上,因为配置类是容器中的组件。那么这样,想要读入配置的那个JavaBean上就不用写@Component

      image-20211007175026160

自动配置原理

  1. @SpringBootApplication

    image-20211007191020426

    @SpringBootApplication是上图这些注解的合成注解

    • @SpringBootConfiguration

      image-20211007191113896

      这个注解首先就是一个Configuration

      @Configuration的作用就是告诉springboot这个类是配置类

      也就说明Main程序这个类,也是springboot里的配置类,是核心配置类

    • @ComponentScan

      指定扫描哪些

    • @EnableAutoConfiguration

      点进@EnableAutoConfiguration,发现也是一个合成注解,是以下两个注解的合成注解

      image-20211007191512056

      • @AutoConfigurationPackage

        自动配置包,指定了默认包规则

        image-20211007191601616

        @Import就是给容器中导入组件

        利用Registrar批量给容器注册组件

        Registrar把指定的包下的所有组件注册,哪个包呢?就是Main程序所在的包

        所以为什么我们默认的包路径,是Main程序所在的包,原因就是@SpringBootApplication注解下的@EnableAutoConfiguration下的@AutoConfigurationPackage的@Import,导入了Registrar,Registrar把指定的包下的所有组件注册,根据@SpringBootApplication注解写在Main程序上,那么Main程序所在的包就是Registrar索要注册的默认包路径。

      • @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.factories写死了springboot一启动就要给容器中加载的所有配置类
        

        虽然127个所有场景的自动配置,启动的时候默认全部加载,但是最终会按需(条件)配置

        按需配置,就是条件装配@Condition,让配置不是完全开启

        启动的时候是全部配置都加载,最终按照条件装配,来按需配置,就是把我们需要的组件在容器中注册!!!

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

    • springboot会先加载所有自动配置类

    • 每个自动配置类按照条件(各种@Condition注解)进行生效

    • 每个配置类生效后默认都会绑定配置文件指定的值(从xxxProperties拿值),xxxProperties类和application.Properties配置文件进行了绑定(通过注解@ConfigurationProperties),我们想要修改也很简单,只需要在application.Properties配置文件里进行修改

      xxxProperties只是绑定了配置文件application.properties的类而已,就可以理解为配置文件。

    • 生效的配置类就会给容器中装配(注册)很多组件(配置类里的方法上面有注解@Bean,就是在给容器中注册组件,或者说装配)

    • 只要容器中有这些组件,相当于这些功能就有了。

      只要有用户自己配置的,就以用户的优先。

    • 定制化配置:

      • 用户直接自己@Bean替换底层的组件
      • 用户去看这个组件是获取的配置文件什么值,就去修改
  3. 自动配置从配置文件取值的步骤

    • xxxAutoConfiguration(自动配置类)给我们装配(注册)了很多组件。怎么装配的?按条件装配,@Condition

      装配的意思就是向容器中注册组件(通过注解@Bean),或者说实例对象,供用户使用

    • 这些组件从xxxProperties(是和application.properties绑定的类)里面拿值

    • xxxProperties和application.properties绑定,也就是从application.properties里获取值

      通过注解@ConfigurationProperties

      就像

      image-20211007231310887

      不要把xxxProperties和自动配置类搞混,自动配置类里是有很多装配组件的代码的,也就是加了@Bean的方法。自动配置类装配的这些组件(实例化对象)从xxxProperties取值。xxxProperties只是绑定了配置文件application.properties的JavaBean而已,就可以理解为配置文件。也就是一个application.properties文件对应于多个xxxProperties类。各个xxxProperties类用到application.properties文件里的自己那一部分配置(通过@ConfigurationProperties注解的属性prefix来拿配置使用)

      image-20211008004343664

      xxxAutoConfiguration才是自动配置类

      上面加了注解@Configuration

      并且加了@EnableConfigurationProperties,来让自动配置类里面需要装配的bean,绑定配置文件指定的值(从xxxProperties拿值)(这个类并不是自动配置类,不要弄混,这个类只是和applicaiton.properties绑定的类而已)

开发小技巧

  1. lombok

    简化JavaBean开发

核心功能-web开发

概述

  1. springboot是框架的框架,整合了开发所需要的框架,比如springmvc
  2. 用springboot做web开发,底层仍然使用的是springmvc
  3. springboot对springmvc做了很多自动配置

静态资源请求

  1. 静态资源目录

    image-20211008181155963类路径下

    访问当前项目的根路径 + / + 静态资源名就能访问到了。

    映射的是/**,所以写资源名,就能自动找到静态资源

    如果动态请求名和静态资源名相同,访问的是动态请求,找@RequestMapping

  2. 静态资源访问的原理:

    静态映射 /**

    请求进来,先去找Controller能不能处理(实际是请求先要经过DispatcherServlet获取请求,然后通过server()、doServer()、doDispatch()等一系列方法将请求转发给Controller处理,涉及到处理器映射器,处理器执行链,处理器适配器,最后将请求交给处理器的方法执行),如果没有Controller能处理,那么不能处理的所有请求又都交给静态资源处理器。

    接着看静态资源能不能找到,如果静态资源能找到,就能访问,如果静态资源找不到,就会返回404

  3. 静态资源访问前缀

    image-20211008183710429

    当前项目 + static-path-pattern + 静态资源名 = 能访问到静态资源

    现在开发和之前用springmvc不同的是,根路径直接就是端口号后 + /,就是根路径,之前用springmvc,或者javaweb开发,端口号和斜杠后还有一个工程名,这叫工程路径,工程路径后才是资源路径或者动态请求!!

  4. 我们也可以改变默认的静态资源路径

    image-20211008184239530

    那么resource下的haha目录下的资源才是静态资源。这是规定了静态资源默认路径位置

静态资源配置原理

  1. springboot启动默认加载很多xxxAutoConfiguration类,这些类是自动配置类

    当然也会加载和web开发有关的自动配置类

  2. springmvc功能的自动配置类WebMvcAutoConfiguration

    自动配置类首先全部加载,按条件@Condition相关注解生效

    image-20211008225543585

  3. image-20211008225917153

    这个自动配置类,通过注解@EnableConfigurationProperties,发现用到了WebMvcProperties和ResourceProperties这两个绑定了配置文件的JavaBean

    • WebMvcProperties和配置文件中前缀为spring.mvc的配置进行了绑定
    • ResourceProperties和配置文件中前缀为spring.resources的配置进行了绑定
  4. xxxAutoConfiguration才是配置类,xxxProperties是与配置文件绑定的JavaBean。配置类要用到JavaBean,通过注解@EnableConfigurationProperties

  5. 配置类如果只有一个有参构造器

    有参构造器所有参数的值都会从容器中确定。

  6. 为什么静态资源能从前面提到的那四个默认位置里面拿

    image-20211008232101965

  7. 静态资源都有缓存策略。

欢迎页支持

  1. 给静态资源路径下,放index.html

  2. 编写controller,能处理/index请求

    这两种都会被当成欢迎页

Last Updated:
Contributors: 陈杨