[学习笔记] SpringBoot之SpringBoot入门

# 学习 # · 2021-04-17

初识Spring

1、Spring:是为了解决企业级应用开发的复杂性而创建的,简化开发。

2、Spring如何简化Java开发:

(1)基于POJO的轻量级和最小侵入性编程,所有东西都是bean。

(2)通过IOC,依赖注入(DI)和面向接口实现松耦合。

(3)基于切面(AOP)和惯例进行声明式编程。

(4)通过切面和模版减少样式代码。

3、SpringBoot:简化Spring应用开发的一个框架、整个Spring技术栈的一个大整合、J2EE 开发的一站式解决方案。

4、SpringBoot的主要优点:

(1)为所有Spring开发者更快的入门。

(2)开箱即用,提供各种默认配置来简化项目配置。

(3)内嵌式容器简化Web项目。

(4)没有冗余代码生成和XML配置的要求。

5、微服务架构风格(服务微化):一个应用应该是一组小型服务;可以通过 HTTP 的方式进行互通。


SpringBoot入门

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

(1)访问在线工具:https://start.spring.io/

(2)填写项目信息,创建并导出。

(3)使用IDEA导入项目。

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

(1)创建一个新项目,选择spring initalizr,填写项目信息。Custom填写:https://start.aliyun.com/

(2)选择初始化的组件。

(3)填写项目路径,等待项目构建成功。

3、项目结构分析:

4、HelloWorld:

(1)在主程序的同级目录下,新建一个controller包。

(2)在包中新建一个HelloController类。

/**
 * @Package: com.aduo.springboot.controller
 * @Description: Hello控制器
 * @Author 多仔
 */
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        return "Hello SpringBoot";
    }
}

(3)从主程序启动项目,浏览器发起请求,看页面返回。

5、将项目打成jar包:


HelloWorld原理初探

1、pom.xml依赖文件全解:

<?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>
    <groupId>com.aduo</groupId>
    <artifactId>springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot</name>
    <description>Demo project for Spring Boot</description>

    <!-- 相关依赖的版本号信息 -->
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <!-- web依赖、启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- MyBatis依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <!-- MySQL依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- Lombok依赖 -->
        <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>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <!-- 管理依赖版本号 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <!-- Maven -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- 打包插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.aduo.springboot.SpringbootApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2、启动器(spring-boot-starter):

(1)springboot-boot-starter-xxx:spring-boot的场景启动器。

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

(3)SpringBoot将所有的功能场景都抽取出来,做成一个个启动器,只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来。

3、主启动类SpringbootApplication:

@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {
        // Spring应用启动起来
        SpringApplication.run(SpringbootApplication.class, args);
    }

}

4、@SpringBootApplication:标注这个类是SpringBoot的应用。

// @SpringBootApplication源码视图
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
      @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//...

(1)@SpringBootConfiguration:SpringBoot的配置类,配置类也是容器中的一个组件(源码视图中有@Component)。

// @SpringBootConfiguration源码视图
@Configuration
public @interface SpringBootConfiguration {}

// @Configuration源码视图
// 说明启动类本身也是Spring中的一个组件而已,负责启动应用
@Component
public @interface Configuration {}

(2)@ComponentScan:它对应XML配置中的元素,自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中。

(3)@EnableAutoConfiguration:开启自动配置功能。

// @EnableAutoConfiguration源码视图
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}

(4)AutoConfigurationPackage :自动配置包。

// @AutoConfigurationPackage源码视图
@Import({Registrar.class})
public @interface AutoConfigurationPackage {}

(5)@import :Spring底层注解@import,给容器中导入一个组件。

(6)Registrar.class:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器。

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

(8)AutoConfigurationImportSelector:自动配置导入选择器。

5、AutoConfigurationImportSelector剖析:

(1)AutoConfigurationImportSelector类中的getCandidateConfigurations()方法。

// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // 这里的getSpringFactoriesLoaderFactoryClass()方法
    // 返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
    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;
}

(2)追踪:SpringFactoriesLoader类中的loadFactoryNames()。

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

(3)追踪: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;
    }
    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()) {
            //...

(4)追踪:spring.factories文件:自动配置根源所在。

6、原理初探结论:

(1)SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值。

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

(3)整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中。

(4)它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration),并配置好这些组件。

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

7、SpringApplication启动器:

(1)SpringApplication的实例化:

  ①推断应用的类型是普通的项目还是Web项目。

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

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

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

(2)run方法的执行:

如无特殊说明,本博所有文章均为博主原创。

如若转载,请注明出处:一木林多 - https://www.l5v.cn/archives/286/

评论