0%

JavaWeb之SpringBoot基础

JavaWeb学习中关于SpringBoot的基础知识 💯💤

SpringBoot基础

一、SprinBoot简介

1.1 原有Spring优缺点分析

1.1.1 Spring的优点分析

Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的Java对象(Plain Old Java Object,POJO)实现了EJB的功能。

1.1.2 Spring的缺点分析

虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。

所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少。

除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。

1.2 SpringBoot的概述

1.2.1 SpringBoot解决上述Spring的缺点

SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。

1.2.2 SpringBoot的特点

  • 为基于Spring的开发提供更快的入门体验。
  • 开箱即用,没有代码生成,也无需XML配置。同时也可以修改默认值来满足特定的需求。
  • 提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等。
  • 🎯SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。

1.2.3 SpringBoot的核心功能

  • 起步依赖

    起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。

    简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

  • 自动配置

    Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。

二、SpringBoot快速入门

2.1 通过IDEA创建

  1. 使用idea工具创建一个新的工程并选择Spring Initial。

  2. 根据流程选择下一步,在Dependence中选择需要的模块,如web-web。

  3. 最后自动生成项目。

  4. 通过idea快速创建的SpringBoot项目的pom.xml中已经导入了我们选择的web的起步依赖的坐标

    <?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.4.1</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>cn.sucrelt</groupId>
        <artifactId>sprintboot-all</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>sprintboot-all</name>
        <description>projects 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.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </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>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

2.2 Spring Boot启动类

项目自动生成一个SpringBoot启动类

@SpringBootApplication
public class HelloWorldApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloWorldApplication.class, args);
    }
}

2.2.1 启动类代码分析

  • @SpringBootApplication:标注SpringBoot的启动类。
  • SpringApplication.run(HelloWorldApplication.class, args); 代表运行SpringBoot的启动类,参数为SpringBoot启动类的字节码对象。

2.2.2 ⭐️@SpringBootApplication 注解分析

@SpringBootApplication 可以看作是 @Configuration@EnableAutoConfiguration@ComponentScan 注解的集合。这三个注解的作用分别是:

  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制。
  • @ComponentScan: 扫描被@Component (@Service,@Controller)注解的bean,注解默认会扫描该类所在的包下所有的类。
  • @Configuration:允许在上下文中注册额外的bean或导入其他配置类。

@SpringBootApplication 是几个重要的注解的组合,为了省事,避免了每次开发 Spring Boot 项目都要写一些必备的注解。

2.3 编写Controller

新建一个 controller 文件夹,并在这个文件夹下新建一个名字叫做 HelloWorldController 的类。

@RestController
@RequestMapping("test")
public class HelloWorldController {
    @GetMapping("hello")
    public String sayHello() {
        return "Hello World";
    }
}

2.4测试

执行SpringBoot启动类的主方法,控制台打印日志如下:

D:\Java\jdk-8\bin\java.exe...
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.1)
 ... ...
2020-12-13 15:16:31.361  INFO 15084 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-12-13 15:16:31.374  INFO 15084 --- [           main] c.s.s.SprintbootAllApplication           : Started SprintbootAllApplication in 2.2 seconds (JVM running for 4.157)
2020-12-13 15:16:47.585  INFO 15084 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring 
... ...
  • 打印日志说明启动了SpringBoot内置的Tomcat服务器,端口为8080,默认的访问根路径为’ '。

  • 打开浏览器访问url地址为:http://localhost:8080/test/hello,页面打印Hello World字符串。

2.5 SpringBoot工程热部署

我们在开发中反复修改类、页面等资源,每次修改后都是需要重新启动才生效,这样每次启动都很麻烦,浪费了大量的时间,我们可以在修改代码后不重启就能生效,在 pom.xml 中添加如下配置就可以实现这样的功能,称之为热部署。

<!--热部署配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

**注意:**IDEA进行SpringBoot热部署失败原因:Intellij IEDA默认情况下不会自动编译,需要对IDEA进行自动编译的设置。

三、SpringBoot原理分析

3.1 依赖原理分析

起步依赖的作用就是进行依赖的传递。

  1. parent标签

    pom.xml中引入了spring-boot-starter-parent,其中,一部分坐标的版本、依赖管理、插件管理已经定义好,所以SpringBoot工程继承spring-boot-starter-parent后就已经具备版本锁定等配置了。

  2. dependency标签

    引入发spring-boot-starter-web是将web开发要使用的spring-web、spring-webmvc等坐标进行了“打包”,这样工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。SpringBoot简化Spring配置的原理就是将一系列相关的依赖进行打包,在工程中只要导入一个打包好的坐标即可。

3.2 ⭐️自动配置原理解析

@SpringBootApplication注解相当于三个注解:

  • @SpringBootConfiguration:根据源码发现底层实际上就是Configuration注解,也就是用于标注该类是一个Spring配置类,等同于XML文件。
  • @EnableAutoConfiguration:开启自动配置功能。
  • @ComponentScan:这个注解也就是Spring中的扫描注解,默认是扫描当前类所在包及其子包下的所有类,将其中的@Controller/@Service/@Component/@Repository等注解加载到IOC容器中。

核心注解EnableAutoConfiguration

SpringBoot简化配置的思想是“约定大于配置”,其核心就是@EnableAutoConfiguration注解。

查看注解@EnableAutoConfiguration

...
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	... ... ...
}
  • @AutoConfigurationPackage

    查看该注解:

    ...
    @Import(AutoConfigurationPackages.Registrar.class)
    public @interface AutoConfigurationPackage {
        ... ...
    }
    

    最终定位到Registrar类中:

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
        }
        ...
    }
    

    其功能:就是将主配置类(@SpringBootApplication)的所在包及其子包里边的组件扫描到Spring容器中。

    这与@ComponentScan注解功能很类似,但是比如使用Spring Data JPA,会在实体类上写@Entity注解。这个@Entity注解由@AutoConfigurationPackage扫描并加载,而平时开发用的@Controller/@Service/@Component/@Repository这些注解是由ComponentScan来扫描并加载的。

回到EnableAutoConfiguration类中并进入AutoConfigurationImportSelector

查看源码,主要方法如下:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    @Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
        ...
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
            
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		...
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		...
		return new AutoConfigurationEntry(configurations, exclusions);
	}
            
    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;
	}
    ... ...
}
  • selectImports方法调用getAutoConfigurationEntry方法,getAutoConfigurationEntry调用getCandidateConfigurations方法,最终进入SpringFactoriesLoader类中并调用其中的loadFactoryNames方法。
  • 在打印的提示语中显示了META-INF/spring.factory这个文件。

继续查看SpringFactoriesLoader类

public final class SpringFactoriesLoader {
    ... ...
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		... ...
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}
    ...
    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		...
		try {
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) { 
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());
                ...
            	}
			...
			}
        ...
		}
		...
		return result;
	}
}
  • 通过SpringFactoriesLoader.loadFactoryNames调用了loadSpringFactories方法,在该方法中使用类加载器加载了FACTORIES_RESOURCE_LOCATION中的类。

  • 查看该常量的值:

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    

    与之前控制台打印输出的文件目录相同,即加载的配置文件位置为META-INF/spring.factories

配置文件

在项目中的External Libraries中,找到spring-boot-autoconfigure-2.4.1包,在其下目录中可以发现该配置文件META-INF/spring.factories。

文件中存有以Configuration为结尾的自动配置信息的类名,SpringApplication在获取这些类名后再加载。

... ... ...

org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\

... ... ...

⭐️总结

  • @EnableAutoConfiguration注解最终调用SpringFactoriesLoader中的loadSpringFactories方法加载配置文件。
  • 配置文件的加载位置为:FACTORIES_RESOURCE_LOCATION,其值是META-INF/spring.factories
  • Spring启动自动配置时会扫描jar包路径下的META-INF/spring.factories,使用类加载器进行加载并将其文件包装成Properties对象最终添加到容器中。

四、SpringBoot的配置文件

4.1 yml配置文件

SpringBoot是基于约定的,很多配置都有默认值,如果想使用自定义配置替换默认配置,就需要使用application.properties或者application.yml(application.yaml)配置文件进行配置。

SpringBoot默认会从Resources目录下加载application.properties或application.yml(application.yaml)文件,加载顺序为yml、yaml、properties,如果同时有多个配置文件,后面的配置文件会覆盖前面的。

4.2 配置文件语法

4.2.1 配置普通数据

  • 语法:key: value

  • 示例代码:

  • name: sucre
    
  • 注意:value之前有一个空格

4.2. 配置对象数据

  • 语法:

    ​ key:

    ​ key1: value1

    ​ key2: value2

    ​ 或者:

    ​ key: {key1: value1,key2: value2}

  • 示例代码:

  • person:
      name: sucre
      age: 20
      addr: nanjing
    #或者
    person: {name: sucre,age: 20,addr: nanjing}
    
  
- **注意:key1前面的空格个数不限定,在yml语法中,相同缩进代表同一个级别。**

### 4.2.3 配置数组(List、Set)数据

- 语法: 

  ​	key: 

  ​		- value1

  ​		- value2

  或者:

  ​	key: [value1,value2]

- 示例代码:

- ```yaml
  city:
    - beijing
    - tianjin
    - shanghai
    - chongqing
    
  #或者

  city: [beijing,tianjin,shanghai,chongqing]

  #集合中的元素是对象形式
  student:
    - name: zhangsan
      age: 18
      score: 100
    - name: lisi
      age: 28
      score: 88
    - name: wangwu
      age: 38
      score: 90
  • 注意:value1与之间的 - 之间存在一个空格

4.2.4 配置Map集合与配置对象方式相同

4.3 配置文件与配置类的属性映射方式

4.3.1 使用注解@Value映射

通过@Value注解将配置文件中的值映射到一个Spring管理的Bean的字段上

例如:

application.yml配置如下:

person:
  name: zhangsan
  age: 18

实体Bean代码如下:

@Controller
public class QuickStartController {

    @Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private Integer age;


    @RequestMapping("/quick")
    @ResponseBody
    public String quick(){
        return "springboot 访问成功! name="+name+",age="+age;
    }

}

4.3.2 使用注解@ConfigurationProperties映射

通过注解@ConfigurationProperties(prefix="配置文件中的key的前缀")可以将配置文件中的配置自动与实体进行映射

application.yml配置如下:

person:
  name: zhangsan
  age: 18

实体Bean代码如下:

@Controller
@ConfigurationProperties(prefix = "person")
public class QuickStartController {

    private String name;
    private Integer age;

    @RequestMapping("/quick")
    @ResponseBody
    public String quick(){
        return "springboot 访问成功! name="+name+",age="+age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

⭐️注意:使用@ConfigurationProperties方式可以进行配置文件与实体字段的自动映射,但需要字段必须提供set方法才可以,而使用@Value注解修饰的字段不需要提供set方法。

-------------------本文结束 感谢您的阅读-------------------

本文标题:JavaWeb之SpringBoot基础

文章作者:Sucre

发布时间:2020年12月14日 - 16:35:39

最后更新:2020年12月15日 - 18:00:12

原始链接:https://tangtangsama.github.io/article/754526f9.html/

非商业性使用-转载请保留原文链接及作者。

感谢您的支持和鼓励!