Springboot学习笔记(一)快速上手-自动装配原理

前言:
学习B站UP主狂神说视频笔记整理视频链接
笔记链接

概述

什么是springboot

Spring Boot是由Pivotal团队提供的脚手架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置

Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。

springboot的优点

  • 为所有Spring开发者更快的入门

  • 开箱即用,提供各种默认配置来简化项目配置

  • 内嵌式容器简化Web项目

  • 没有冗余代码生成和XML配置的要求

什么是微服务

微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成―系列小服务的组合;可以通过http的方式进行互通。要说微服务架构,先得说说过去我们的单体应用架构。

单体架构

所谓单体应用架构(allin one)足指,我们将一个应用中的所有服务都制装在一个应用中。
无论是ERP,CRM或是其他什么系统,你都把数据库访问,web访问,等等各个功能放到一个war包内。

  • 这样做的好处是,易干开发和测试: 他十分方便部署:当需要扩底时,只需要将war复制多份,然后放到多个服务器上,再做个负载均衡就可以了.
  • 单体应用架构的缺点是,哪怕我要修改一个非常小的地方,我都需要停掉整个服务,重新打包、部署这个应用war包,特别是对于一个大型应用,我们不可能吧所有内容都放在一个应用里面,我们如何维护、如何分工合作都是问题。

微服务架构

all in one的架构方式,我们把所有的功能单元放在一个应用里面。然后我们把整个应用部署到服务器上。如果负载能力不行,我们将整个应用进行水平复制,进行扩展,然后在负载均衡。

所谓微服务架构,就是打破之前all in one的架构方式,把每个功能元素独立出来。把独立出来的功能元素的动态组合,需要的功能元素才去拿来组合,需要多一些时可以整合多个功熊元素。所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制。
在这里插入图片描述

Martin Fowler微服务架构论文-译文

SpringBoot快速上手

初识SpringBoot

Spring官方提供了非常方便的工具让我们快速构建应用

官网地址:

Spring Initializr:https://start.spring.io/

如果访问缓慢可以使用阿里云镜像地址:

https://start.aliyun.com/

创建项目

项目创建方式一 : 使用Spring Initializr 的 Web页面创建项目

1、打开 https://start.spring.io/

2、填写项目信息

3、点击”Generate Project“按钮生成项目;下载此项目

4、解压项目包,并用IDEA以Maven项目导入,一路下一步即可,直到项目导入完毕。

5、如果是第一次使用,可能速度会比较慢,包比较多、需要耐心等待一切就绪。

项目创建方式二:使用 IDEA 直接创建项目

1、创建一个新项目

2、选择spring initalizr , 可以看到默认就是去官网的快速构建工具那里实现
在这里插入图片描述

3、填写项目信息
在这里插入图片描述

4、选择初始化的组件(初学勾选 Web 即可)
在这里插入图片描述

5、填写项目路径
在这里插入图片描述

6、等待项目构建成功
在这里插入图片描述

编写一个http接口

1、在主程序的同级目录下,新建一个controller包,一定要在同级目录下,否则识别不到

2、在包中新建一个HelloController

@RestController
public class HelloController {
    
    

    @RequestMapping("/hello")
    public String hello() {
    
    
        return "Hello World";
    }
    
}

3、编写完毕后,从主程序启动项目,浏览器发起请求,看页面返回;控制台输出了 Tomcat 访问的端口号!
在这里插入图片描述
简单几步,就完成了一个web接口的开发,SpringBoot就是这么简单。所以我们常用它来建立我们的微服务项目!

将项目打成jar包

点击 maven的 package
在这里插入图片描述
如果遇到以上错误,可以配置打包时 跳过项目运行测试用例

<!--
    在工作中,很多情况下我们打包是不想执行测试用例的
    可能是测试用例不完事,或是测试用例会影响数据库数据
    跳过测试用例执
    -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <!--跳过项目运行测试用例-->
        <skipTests>true</skipTests>
    </configuration>
</plugin>

如果打包成功,则会在target目录下生成一个 jar 包

彩蛋

如何更改启动时显示的字符拼成的字母,SpringBoot呢?也就是 banner 图案;

只需一步:到项目下的 resources 目录下新建一个banner.txt 即可。

图案可以到:https://www.bootschool.net/ascii 这个网站生成,然后拷贝到文件中即可!
在这里插入图片描述

自动装配原理

我们之前写的HelloSpringBoot,到底是怎么运行的呢,Maven项目,我们一般从pom.xml文件探究起;

pom.xml

项目父级依赖

其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件!

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

点进去,发现还有一个父依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;

以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了

启动器

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

springboot-boot-starter-xxx:就是spring-boot的场景启动器

spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;

SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义 starter;

主启动类

//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringBootStudyApplication {
    
    

    public static void main(String[] args) {
    
    
        //将springboot 应用启动
        SpringApplication.run(SpringBootStudyApplication.class, args);
    }

}

但是一个简单的启动类并不简单!我们来分析一下这些注解都干了什么

@SpringBootApplication

进入这个注解:可以看到上面还有很多其他注解!
在这里插入图片描述

@ComponentScan

扫描主启动类同级的包

@SpringBootConfiguration

在这里插入图片描述

@EnableAutoConfiguration

开启自动配置功能

以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效

点进注解接续查看:
在这里插入图片描述
@AutoConfigurationPackage :自动配置包

  • 在这里插入图片描述
  • Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;

AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码

1.这个类中有一个这样的方法
在这里插入图片描述

// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    
    
         
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    
    
     // 启动类下的所有资源被导入
        return EnableAutoConfiguration.class;
    }

1.1 进入loadFactoryNames()方法

 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    
    
 //这里它又调用了 loadSpringFactories 方法
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

1.2 进入loadSpringFactories ()方法


private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    
    
    //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
    
    
        return result;
    } else {
    
    
        try {
    
    
            //去获取一个资源 "META-INF/spring.factories"
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            //将读取到的资源遍历,封装成为一个Properties
            while(urls.hasMoreElements()) {
    
    
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
    
    
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
    
    
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
    
    
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}

2.发现一个多次出现的文件:spring.factories,全局搜索它
在这里插入图片描述
在这里插入图片描述
WebMvcAutoConfiguration
我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration
在这里插入图片描述
这里就是关于SpringMVC的相关配置

所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。

结论
  • SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值

  • 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;

  • 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;

  • 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;

  • 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

思考

spring.factories的配置类那么多,为什么只生效了部分?

需要导入start才能生效

核心注解:@ConditionalOnClass
在这里插入图片描述
括号内的条件满足才会生效

@ConditionalOnClass相关注解:
在这里插入图片描述

SpringApplication

我最初以为就是运行了一个main方法,没想到却开启了一个服务;


@SpringBootApplication
public class SpringbootApplication {
    
    
    public static void main(String[] args) {
    
    
    //启动了一个服务
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

分析该方法主要分两部分,
一部分是SpringApplication的实例化,二是run方法的执行;

这个类主要做了以下四件事情:

1、推断应用的类型是普通的项目还是Web项目

2、查找并加载所有可用初始化器 , 设置到initializers属性中

3、找出所有的应用程序监听器,设置到listeners属性中

4、推断并设置main方法的定义类,找到运行的主类

流程分析

在这里插入图片描述

总结

springboot是通过main方法下的SpringApplication.run方法启动的,启动的时候他会调用refshContext方法,先刷新容器,然后根据解析注解或者解析配置文件的形式注册bean,而它是通过启动类的SpringBootApplication注解进行开始解析的,他会根据EnableAutoConfiguration开启自动化配置,里面有个核心方法ImportSelect选择性的导入,根据loadFanctoryNames根据classpash路径以MATA-INF/spring.factorces下面以什么什么EnableAutoConfiguration开头的key去加载里面所有对应的自动化配置,他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解,先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器

yml配置文件详解

SpringBoot使用一个全局的配置文件,配置文件名称是固定的- application.properties

  • 语法结构: key=value application.yml
  • 语法结构: key:空格value

配置文件的作用︰修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了;

application.properties写法

# 应用名称
spring.application.name=springBootStudy
# 应用服务 WEB 访问端口
server.port=8080

application.yml写法

# 对空格要求十分高 格式 key:空格 value
# 应用名称
spring:
  application:
    name: springBootStudy
# 应用服务 WEB 访问端口
server:
  port: 8080

# 行内写法
student: {
    
    name: 秦疆,age: 3}

# 数组
pets: [cat,dog,pig]

属性赋值

yml的强大之处是可以给属性赋值

以下是原来给属性赋值的方式:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Component //被spring管理
public class Dog {
    
    
   //通过@Value 为属性赋值
    @Value("周去")
    private String name;
    @Value("3")
    private int age;
}

当我们使用yml之后可以给属性这样赋值:

方式一

使用@ConfigurationProperties

  • 将配置文件中配置的每一个属性的值,映射到这个组件中;
  • 告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定
  • 参数 prefix = "person”:将配置文件中的person下面的所有属性——对应
  • 只有这个组件是容器中的组件,才能使用容器提供的@configurationProperties功能
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component//spring管理
@ConfigurationProperties(prefix = "person") //此注解将yml配置文件的值赋值给属性
public class Person {
    
    
    private String name;
    private int age;
    private Boolean happy;
    private Date date;
    private Map<String,Object> map;
    private List<Object> list;
    private Dog dog;
}

yml配置文件如下:

# 属性赋值
Person:
  name: 陈平安
  age: 3
  happy: true
  date: 2011/11/02
  map: {
    
    k1: v1,K2: v2}
  list:
    - java
    - python
    - js
  dog:
    name: 剑来
    age: 1000

在这里插入图片描述
使用@ConfigurationProperties注解会爆红,可根据官方提示在pom.xml配置文件中添加如下依赖:

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

重启IDEA即可,当然不添加此依赖不会影响程序正常运行

方式二

可以自定义properties配置文件为属性赋值

# zhouqu.properties
Dog.name=周去
Dog.age=3
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component //被spring管理
//绑定配置文件
@PropertySource(value = "classpath:zhouqu.properties")
public class Dog {
    
    
    @Value("${Dog.name}")
    private String name;
    @Value("${Dog.age}")
    private int age;
}

需要在IDEA settings中设置propertiesUTF-8编码以防出现中文乱码
在这里插入图片描述

两种方式对比

@ConfigurationProperties @properties
功能 批量注入配置文件中的属性 使用Value一个个指定
松散绑定(松散语法) 支持 不支持
SpEL 部支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持

这里我们看到一个松散绑定,这个是什么意思呢

比如我的yml中写的last-name,这个和lastName是一样的, 后面跟着的字母默认是大写的。这就是松散绑定
跟与mybatis中的驼峰命名规则映射有点相似

配置文件占位符

配置文件还可以编写占位符生成随机数

person:
    name: qinjiang${random.uuid} # 随机uuid
    age: ${random.int}  # 随机int
    happy: false
    birth: 2000/01/01
    maps: {
    
    k1: v1,k2: v2}
    lists:
      - code
      - girl
      - music
    dog:
      name: ${person.hello:other}_旺财
      age: 1

配置文件可以配什么?

前情回顾:
SpringBoot自动装配会在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值

我们用HttpEncodingAutoConfiguration来举例说明
在这里插入图片描述

// 标注自动装配类
@Configuration(
        proxyBeanMethods = false
)
//自动装配的属性 ServerProperties.class
@EnableConfigurationProperties({
    
    ServerProperties.class})
//spring底层注解 根据不同的条件 来判断当前配置类是否生效
@ConditionalOnWebApplication(
        type = ConditionalOnWebApplication.Type.SERVLET
)
@ConditionalOnClass({
    
    CharacterEncodingFilter.class})
@ConditionalOnProperty(
        prefix = "server.servlet.encoding",
        value = {
    
    "enabled"},
        matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
    
    
    private final Encoding properties;

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

自动配置类可以配置哪些内容,我们完全可以找到@EnableConfigurationProperties({ServerProperties.class})注解后标注的class

点击ServerProperties.class
在这里插入图片描述
application.yml配置文件中,使用server能点出来的内容都可以在ServerProperties.class中找到!
在这里插入图片描述

结论

在其中我们发现了一些规律,所有能点出来的配置都会有一个XXXXProperties.class 这个类又会和XXXXAutoConfiguration自动配置类绑定(默认值) 我们可以通过改变XXXXProperties.class来实现自定义配置

由此我们可以得到自动配置原理:

  1. SpringBoot启动会加载大量的自动配置类
  2. 我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
  3. 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
  4. 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
    xxxAutoConfigurartion:自动配置类;给容器中添加组件
    xxxxProperties:封装配置文件中相关属性;

如何知道哪些自动配置类是否生效?

# 在application.yml中配置 
# 可以在日志中查看到哪些自动配置类是否生效
debug: true

JSR303数据校验

spring-boot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统—处理。我们这里来写个注解让我们的name只能支持Email格式

@Data
@NoArgsConstructor
@AllArgsConstructor
@Component //被spring管理
@PropertySource(value = "classpath:zhouqu.properties")//绑定配置文件
@Validated//数据校验
public class Dog {
    
    
    @Email(message="不是一个邮箱!")
    private String email;
}

如果@Email注解报红 是因为新版本需要validation启动器

解决方法:在pom.xml 加入下面依赖:

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

使用数据校验,可以保证数据的正确性;

常见参数:

otNull(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 对象是否符合正则表达式的规则

多环境配置

配置文件加载位置

从官网了解配置文件可以放在四个地方
在这里插入图片描述

在这里插入图片描述
他们的优先级是:

  1. file: ./ config/
  2. file: ./
  3. classpath : /config/
  4. classpath:/

多环境切换

准备三个配置文件
在这里插入图片描述

方式一:
application.yml配置文件中选择激活哪个配置文件

# SpringBoot多环境配置可以选择激活哪一个配置文件
spring:
  profiles:
    active: dev #只需要写 - 后面的名字 application-dev.yml

方式二:
如果觉得分成三个配置文件较为麻烦,可以都写在application.yml这一个配置文件中

# 多个环境通过 ---分割
spring:
  profiles:
    active: dev  
---
server:
  port: 8002
spring:
  profiles: dev    
---
server:
  port: 8003  
spring:
  profiles: test

猜你喜欢

转载自blog.csdn.net/weixin_46684099/article/details/116044531
今日推荐