前言:

本文内容:第一个SpringBoot程序、IDEA快速创建、SpringBoot自动装配原理

推荐免费SpringBoot基础教程视频:[【狂神说Java】SpringBoot最新教程通俗易懂_哔哩哔哩_bilibili

第一个SpringBoot程序

搭建环境

  • JDK1.8
  • Maven3.6.1
  • springBoot:最新版
  • IDEA

官方提供了一个快速生成的网站,IDEA集成了这个网站。

SpringBoot快速搭建:Spring Initializr

66

搭建好的项目:helloworld.zip

将搭建好的项目导入IDEA即可

IDEA快速创建

  1. New Project->选择Spring Initializr->Next 等待下载

    注意:

    如果这里spring.io加载速度过慢或者加载失败可以使用国内镜像

    springboot中文镜像:Spring Initializr (springboot.io)

    阿里云镜像(版本更新较慢):阿里云知行动手实验室-在浏览器沉浸式学习最新云原生技术 (aliyun.com)

  2. 填写包名、项目名、选择Maven、语言选择Java、Packaging选择jar、Java Version选择8 ->Next

  3. 勾选Web中的Spring Web支持->Next

  4. 选择好存放的地址,点击Finish

  5. 等待依赖下载完成即可

踩了一个小坑:IDEA中勾选了Maven->Work offine导致无法从服务器下载Jar包

如果下载好发现plugin报错,需要在本地maven仓库找到下载的对应版本替换即可(尤其是使用了国内镜像)

附上修改后可以运行的pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jokerdig</groupId>
<artifactId>springboot-01-helloworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-01-helloworld</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

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

</project>

简单测试

  1. 新建com.jokerdig.controller包,然后新建HelloController.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.jokerdig.controller;

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;

    /**
    * @author Joker大雄
    * @data 2022/7/11 - 16:04
    **/
    @Controller
    @RequestMapping("/hello")
    public class HelloController {

    @RequestMapping("/hello")
    @ResponseBody
    public String hello(){
    return "Hello World";
    }
    }
  2. 运行启动文件Springboot01HelloworldApplication

  3. 访问地址

    localhost:8080/hello/hello

    浏览器显示:Hello World

  4. 搭建成功

简单配置

  1. 修改端口号

    修改resources文件夹下的application.properties

    1
    2
    # 更改端口号
    server.port=8081
  2. 修改SpringBoot Banner

    Spring Boot和终端命令行banner会让项目更有意思 bootschool.net

    Spring Boot banner在线生成工具 bootschool.net

    在网站生成后复制内容,并在resources文件夹下新建banner.txt,粘贴复制的内容即可

SpringBoot自动装配原理

pom.xml

  • spring-boot-dependencies:核心依赖在父工程中
  • 我们在写或者引入一些SpringBoot依赖的时候,不需要指定版本,就是因为这些版本仓库

启动器

新版本启动器被放置在每一个单独启动器里了(之前是直接在pom.xml)

1
2
3
4
5
6
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.0.RELEASE</version>
<scope>compile</scope>
</dependency>
  • 启动器就是SpringBoot的启动场景
  • 例如启动spring-boot-starter-web,它会帮我们自动导入web环境所有的依赖
  • SpringBoot会将所有的功能场景都变成一个个启动器
  • 要使用什么功能,就只需要找到对应的启动器就可以了starter

主程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.jokerdig;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// @SpringBootApplication 标注这个类是一个SpringBoot的应用
@SpringBootApplication
public class Springboot01HelloworldApplication {

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

}

注解

  • @SpringBootApplication 标注这个类是一个SpringBoot的应用

  • @ComponentScan扫描主启动类同级包

  • @SpringBootConfigurationSpringBoot的配置

    • @ConfigurationSpring配置类
      • @Component这是Spring的组件
  • @EnableAutoConfiguration自动配置

    • @AutoConfigurationPackage自动配置包

      • @Import(AutoConfigurationPackages.Registrar.class)自动配置包
    • @Import(AutoConfigurationImportSelector.class)自动配置导入选择

      • // 获取所有配置 configurations
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        
        // getCandidateConfigurations 方法
        	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
        				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;
        	}
        
        
        // 1. 加载的工厂类最后返回了EnableAutoConfiguration 自动配置
        // 通过EnableAutoConfiguration的注解标记SpringBoot主启动类的所有组件
        protected Class<?> getSpringFactoriesLoaderFactoryClass() {
            return EnableAutoConfiguration.class;
        }
        
        // 2. loadFactoryNames加载工厂
        public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
            String factoryTypeName = factoryType.getName();
            return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); // 调用了 loadSpringFactories方法
        }
        
        	// loadSpringFactories方法
        	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        		MultiValueMap<String, String> result = cache.get(classLoader);// 缓存 若有缓存则直接返回
        		if (result != null) {
        			return result;
        		}
        
        		try {
                    // 通过枚举遍历URL  并通过类加载器(classLoader)获取所有资源
        			Enumeration<URL> urls = (classLoader != null ?
        					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
        					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); // 获取系统资源
        			result = new LinkedMultiValueMap<>();
        			while (urls.hasMoreElements()) { // 通过while循环遍历,hasMoreElements判断有没有更多元素
        				URL url = urls.nextElement();
        				UrlResource resource = new UrlResource(url);
        				Properties properties = PropertiesLoaderUtils.loadProperties(resource);// 把url获取所有资源加载到Properties中
        				for (Map.Entry<?, ?> entry : properties.entrySet()) {
        					String factoryTypeName = ((String) entry.getKey()).trim();
        					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
        						result.add(factoryTypeName, factoryImplementationName.trim());
        					}
        				}
        			}
        			cache.put(classLoader, result);// 最后put放置到缓存中
        			return result;
        		}
        		catch (IOException ex) {
        			throw new IllegalArgumentException("Unable to load factories from location [" +
        					FACTORIES_RESOURCE_LOCATION + "]", ex);
        		}
        	}
        

        META-INF/spring.factories:自动配置的核心文件

        65

      • org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

自动装配原理分析流程图

64

小结

SpringBoot自动配置都在启动类中扫描并加载,扫描spring.factories配置文件,所有的自动配置类都在该配置文件中,但是并不是都会生效,需要满足核心注解:@ConditionalOnXXX的条件后才能生效(需要导入了对应的starter才能生效)。

  1. SpringBoot在启动的时候,从类路径下/META-INF/spring.factories获取指定的值;
  2. 将这些自动配置的类导入容器,自动配置就能生效;
  3. 以前我们需要自动配置的东西,现在SpringBoot帮我们做了;
  4. 整合JavaEE,解决方案和自动配置的内容都在spring-boot-autoconfigure-2.3.0.RELEASE.jar这个包下
  5. 它会把所有需要导入的组件,以类名的方式返回,这些组件被添加到容器中;
  6. 容器中也会存在非常多XXXAutoConfiguration的文件(@Bean),就是这些类给容器中导入了该场景需要的所有组件,并自动配置@Configuration
  7. 有了自动配置类,免去了我们手动编写配置文件的工作;