SpringBoot Web开发与源码分析

2022-09-03 09:48:16

SpringBoot 快速入门

概念性的东西见: https://www.yuque.com/atguigu/springboot/rmxq85

需要环境: jdk8 & 兼容java14, maven 3.3+, idea 219.1.2

maven设置:

去到 maven 的配置文件中修改添加:

<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><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>

Step 1: 创建 maven 工程

Step 2: 在pom文件引入依赖:

<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>

step 3: 在 src/main/java 下创建包和主类(也叫主程序应用)

// 告诉 springboot 这是一个springboot 应用// 主程序应用@SpringBootApplicationpublicclassMainApplication{publicstaticvoidmain(String[] args){SpringApplication.run(MainApplication.class, args);}}

step 4: 在另一个包下创建 Controller 并编写业务代码

/*
可以用 RestController 代替
@ResponseBody // 代表这个类返回的是字符串
@Controller
*/@RestControllerpublicclassHelloController{@RequestMapping("/hello")publicStringhandle01(){return"hello SpringBoot";}}

step 5: 直接运行主应用的 main 方法

step 6: 在 resource 下创建 application.properties 文件, 该文件可以在全局进行配置, 详细见官方文档

step 7: 简化部署

通过加入插件便可以生成jar包, 直接在目标服务器执行即可
(命令行输入: 如java -jar springboot01-1.0-SNAPSHOT.jar)注意取消cmd的快速编辑模式

<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

在 target/下 SpringBoot 帮我们打包成了 jar 包, 里面有我们运行时所需的第三方 jar 包

SpringBoot自动配置原理

1.0 SpringBoot 基本特点

1.1 依赖管理

依赖管理<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>
几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制, 所以我们无需声明版本号

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

自定义修改版本号

1、查看spring-boot-dependencies里面规定当前依赖的版本
2、在当前项目里面重写配置<properties><mysql.version>5.1.43</mysql.version></properties>

Staters

一组依赖的集合描述, 是 maven 的依赖特性

1、见到很多 spring-boot-starter-* : *是某种场景
2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot所有支持的场景
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-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>

1.2 自动配置

  • 自动配置好 Tomcat

    • 引入依赖
    • 配置 Tomcat
  • 自动配置好 SpringMVC

    • 引入了 SpringMVC 的全套组件
      如何查看 IOC 容器有那些组件:

      主程序应用下的主方法:publicstaticvoidmain(String[] args){// 返回 IOC 容器ConfigurableApplicationContext run=SpringApplication.run(MainApplication.class, args);// 查看容器组件名String[] names= run.getBeanDefinitionNames();for(String s: names){System.out.println(s);}
          	run.getBean("yang",User.class);// 容器中只有一个组件 单实例}
    • 自动配置好了 SpringMVC 常用组件 (功能)

  • 自动配置好 Web 常见功能, 如: 字符编码问题

  • 默认的包结构

    • 主程序所在包及其下面的子包所有组件都会被默认扫描进来
    • 无需以前的包扫描配置
    • 想要改变扫描路径,@SpringBootApplication(scanBasePackages=“com.yangyu”)
      或者 @ComponentScan 指定扫描路径
@SpringBootApplication
等同于@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan("com.atguigu.boot")// 可以在这里指定扫描路径
  • 各种配置拥有默认值
    • 默认配置最终都是映射到某个类上,如:MultipartProperties
    • 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
  • 按需加载所有自动配置项
    • 非常多的 starter
    • 只要引入了这个场景, 这个场景的自动配置就会开启
    • SpringBoot 所有的自动配置功能都在 spring-boot-autoconfigure 包里面

2.0 容器功能

2.1 组件添加

1. @Configuration

Spring 添加组件的方法: 创建类, 创建 spring 的配置文件, 在 xml 文件中编写<bean>

SpringBoot 添加组件的方法:

  • 配置类组件之间无依赖关系用 Lite 模式加速容器启动过程,减少判断

  • 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用 Full 模式

/*
1. @Bean 是给容器注册组件, 默认单实例
2. 配置类本身也是一个组件
3. proxyBeanMethods: 代码 bean 的方法  默认为true  
	Full(proxyBeanMethods = true) (保证每个 @Bean 方法被调用多少次返回的组件都是单实例的) 
	Lite(proxyBeanMethods = false) (每个 @Bean 方法被调用多少次返回的组件都是新创建的, 容器不保存代理对象)
    组件依赖必须使用 Full 模式默认。其他默认是 Lite 模式
*/@Configuration(proxyBeanMethods=true)// 告诉 SpringBoot 这是一个配置类 默认单实例publicclassMyConfig{// Full: 外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象@Bean// 给容器添加组件, 以方法名作为组件的 id, 返回类型就是组件类型, 返回的值就是组件在容器中的实例publicUseruser01(){returnnewUser("01",20);}@Bean("yang")publicUseryangyu(){returnnewUser("yangyu",20);}}

2. @Component @Controller @Service @Repository (适用于标注自己写的类)@Bean (适用于标注第三方包里面的组件)

3. @ComponentScan @Import (快速给容器导入组件)

@Import({User.class,DBHelper.class})
给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名@Import({User.class,DBHelper.class})@Configuration(proxyBeanMethods=false)publicclassMyConfig{}

ImportSelector: 是一个接口, 需要自定义类进行实现, 需要重写的方法可以返回需要导入的组件的全类名数组, 需要返回哪些类的全类名就通过自己的逻辑去得到全类名然后返回一个全类名字符串数组注意: 在config类中需要 @import 你实现的这个接口的类, 这样就会自动到容器中生成对应的实例, 你实现的这个接口的类不会在容器中生成实例

ImportBeanDefinitionRegistrar: 也是一个接口, 需要实现. 把所有需要添加到容器的 bean 调用BeanDefinitionRegistry.registerBeanDefinition 手工注册

FactorBean (Spring 提供): 也是一个接口, 需要实现.

4. @Conditional

条件装配: 满足指定条件则进行组件注入

ConditionOnBean: 当容器中有这个组件时才…

@ConditionalOnBean(name={"user01","myConfig"})

2.2 原生配置文件引入

1. @ImportResource

原生xml: resouces/beans.xml

@ImportResource("classpath:beans.xml")publicclassMyConfig{}

2.3 配置绑定

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

原生java做法:

publicclass getProperties{publicstaticvoidmain(String[] args)throwsFileNotFoundException,IOException{Properties pps=newProperties();
         pps.load(newFileInputStream("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。}}}

1. @ConfigurationProperties

xxx.properties:

mycar.aaa=3
mycar.bbb=好

Car类:

@Component// 只有容器中的组件才有SpringBoot提供的强大功能@ConfigurationProperties(prefix="mycar")publicclassCar{privateInteger aaa;privateString bbb;}

2. @EnableConfigurationProperties + @ConfigurationProperties

如果是第三方类, 就在配置类上@EnableConfigurationProperties(Car.class)

  1. 开启 Car 配置绑定
  2. 把 Car 这个组件自动注册到容器里

和上一种区别: 要么用 @Component 加入到容器中, 要么用@EnableConfigurationProperties

3.0 自动配置原理入门

3.1 引导加载自动配置类

@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters={@Filter(type=FilterType.CUSTOM, classes=TypeExcludeFilter.class),@Filter(type=FilterType.CUSTOM, classes=AutoConfigurationExcludeFilter.class)})public@interfaceSpringBootApplication{}

1. @SpringBootConfiguration
其中的 @Configuration 代表当前类是一个配置类

2. @Component
指定要扫描哪些包, 参照 Spring 注解

3. EnableAutoConfiguration

@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})public@interfaceEnableAutoConfiguration{}
  1. @AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class)// 给容器中导入一个组件public@interfaceAutoConfigurationPackage{}// 利用Registrar给容器中导入一系列组件// 将指定的一个包下的所有组件导入进来: MainApplication 所在包下
  1. @Import({AutoConfigurationImportSelector.class})
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件2、调用List<String> configurations=getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类3、利用工厂加载Map<String,List<String>>loadSpringFactories(@NullableClassLoader classLoader) 得到所有的组件4、从 META-INF/spring.factories 位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
文件里面写死了spring-boot一启动就要给容器中加载的所有配置类

3.2 按需开启自动配置项

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

3.3 源码分析与修改默认配置

@Bean@ConditionalOnBean(MultipartResolver.class)// 容器中有这个类型组件@ConditionalOnMissingBean(name=DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)// 容器中没有这个名字 multipartResolver 的组件publicMultipartResolvermultipartResolver(MultipartResolver resolver){// 给 @Bean 标注的方法传入了对象参数,这个参数的值就会从容器中找。// SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;}
给容器中加入了文件上传解析器;

SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先

@Bean@ConditionalOnMissingBeanpublicCharacterEncodingFiltercharacterEncodingFilter(){}

如果想自己配:publicCharacterEncodingFilterfilter(){returnnull;}

总结:

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

  • 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿. xxxProperties和配置文件进行了绑定

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

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

  • 定制化配置

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

xxxxxAutoConfiguration —> 组件 —>xxxxProperties里面拿值 ----> application.properties

3.4 最佳实践

  • 引入场景依赖
    https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
  • 查看自己配置了哪些 (选做)
    • 自己分析源码, 引入场景的自动配置一般都生效了
    • application.properties中输入 debug=true 开启自动配置报告 Negative matches (不生效) \ Positive matches (生效)
  • 是否需要修改
    • 参照文档修改配置项
      https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
      自己分析: xxxxProperties绑定了配置文件的哪些
    • 自定义加入或替代组件
      @Bean @Component
    • 自定义器 XXXCustomizer
开发技巧
LomBok

简化 JavaBean 开发

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
并在 idea 搜索安装 lombok 插件
@NoArgsConstructor@AllArgsConstructor@Data@ToString@EqualsAndHashCode============================================// 简化日志开发@Slf4jpublicclassHelloController{@RequestMapping("/hello")publicStringhandle01(){
        log.info("请求进来了....");return"123"}}
dev-tools

热更新 (实则是自动重启)

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

项目或者页面修改以后:Ctrl+F9

Spring Initailizr

IDEA 快速创建 SpringBoot 应用, 可以勾选你需要的场景, 并自动生成 pom 文件

4.0 配置文件

4.1 properties

4.2 yaml

仍然是一种标记语言

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

基本语法:

  • key: value;k v之间有空格

  • 大小写敏感

  • 使用缩进表示层级关系

  • 缩进不允许使用tab,只允许空格

  • 缩进的空格数不重要,只要相同层级的元素左对齐即可

  • '#'表示注释

  • 字符串无需加引号,如果要加,’'与""表示字符串内容 会被 转义/不转义

数据类型:

  • 字面量:单个的、不可再分的值。date、boolean、string、number、null

    k: v
  • 对象:键值对的集合。map、hash、set、object

    行内写法:  k:{k1:v1,k2:v2,k3:v3}#无空格#或k:k1: v1k2: v2k3: v3
  • 数组: 一组按次序排列的值: array、list、queue

    行内写法:  k:[v1,v2,v3]#或者k:- v1- v2- v3

示例:

# yaml表示以上对象person:userName: zhangsanboss:falsebirth: 2019/12/12 20:12:33# private Date birth;age:18pet:# private Pet petname: tomcatweight:23.4interests:[篮球,游泳]# private String[] interests;animal:# private List<String> animal;- jerry- marioscore:# private Map<String, Object> score;english:first:30second:40third:50math:[131,140,148]chinese:{first:128,second:136}salarys:# private Set<Double> salarys;-9999.98-9999.99allPets:# private Map<String, List<Pet>> allPets;sick:-{name: tom}-{name: jerry,weight:47}-name: 阿虫weight:77.77health:[{name: mario,weight:47}]

4.3 配置提示

自定义的类和配置文件绑定一般没有提示

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- 防止打包的时候打进去 --><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><
  • 作者:洋芋洋芋洋芋
  • 原文链接:https://blog.csdn.net/qq_45560445/article/details/120788533
    更新时间:2022-09-03 09:48:16