SpringBoot入门

一、SpringBoot简介

回顾 spring

Spring是一个开源框架,为了解决企业级应用开发的复杂性而创建的,能简化开发。为了降低Java开发的复杂性,Spring采用了以下4种关键策略:

  • 基于POJO的轻量级和小侵入性编程,所有东西都是bean
  • 通过IOC,依赖注入(DI)和面向接口实现松耦合
  • 基于切面(AOP)和惯例进行声明式编程
  • 通过切面和模版减少样式代码,RedisTemplate,xxxTemplate

但随着 Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那 么易用简单,违背了初的理念,甚至被称为配置地狱!

Spring Boot 正是在这样的一个背景下被抽象出来的开发框架,目的为了更容易的使用 Spring 、更容易的集成各种常用的中间件、开源软件

什么是SpringBoot

Spring Boot 基于 Spring 开发,其本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具

Spring Boot 以约定大于配 置的核心思想,默认帮助我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时Spring Boot集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),在Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用

SpringBoot并不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包一样,spring boot整合了所有的框架

SpringBoot的优势

  • 快速构建项目,敏捷式开发
  • 开箱即用,提供各种默认配置用于简化项目配置
  • 内嵌式容器简化Web项目
  • 与spring cloud天然集成
  • 没有冗余代码生成和XML配置的要求

限制:将现有或传统的Spring Framework项目转换为Spring Boot应用程序是一个非常困难和耗时的过程。它仅适用于全新Spring项目

使用 Spring Boot 最显而易见的好处是简单、快速、方便!需要非常少的几个配置就能迅速方便的搭建起来一套 Web 项目或者是构建一个微服务!

什么是微服务

微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成一系列小型服务的组合。可以这么理解:

  • 它是一种“软件的架构风格”,一个应用应该是一组小型服务
  • 各个小型服务运行在各自的环境中,通过http的方式进行互通
  • 微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合
  • 本质是用一些功能比较明确、业务比较精练的服务去解决更大、更实际的问题
  • 每一个微服务提供单个业务功能的服务,一个服务做一件事
  • 从技术角度看就是一种小而独立的处理过程,类似进程概念,能够自行单独启动或销毁,拥有自己独立的数据库

二、第一个 SpringBoot 项目

环境准备

  • java version "9.0.4"
  • Maven-3.6.1
  • SpringBoot 2.x 最新版
  • 开发IDEA工具:IDEA

创建基础项目

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

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

项目创建方式一:使用Spring Initializr 的 Web页面创建项目
  1. 打开 https://start.spring.io/
  2. 填写项目信息,注意导入Spring Web依赖
  3. 点击”Generate Project“按钮生成项目;下载此项目
  4. 解压项目包,并用IDEA以Maven项目导入,一路下一步即可,直到项目导入完毕。
  5. 如果是第一次使用,可能速度会比较慢,包比较多、需要耐心等待一切就绪
项目创建方式二:使用 IDEA 直接创建项目

1、创建一个新项目

2、选择spring initalizr , 可以看到默认就是去官网的快速构建工具那里实现

3、填写项目信息

4、选择初始化的组件(初学勾选 Web 即可)

5、填写项目路径

6、等待项目构建成功

项目结构分析

通过上面步骤完成基础项目的创建后,就会自动生成以下文件

  1. 程序的主启动类
  2. 一个 application.properties 配置文件
  3. 一个 测试类
  4. 一个 pom.xml
pom.xml 分析

打开 pom.xml ,看看Spring Boot项目中的依赖

<!-- 有一个父项目 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- web场景启动器,包含tomcat,dispatcherServlet,xml...等等 -->
    <!-- 所有的springboot依赖都是使用 spring-boot-starter开头 -->
    <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>
        <!-- 剔除依赖 -->
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>
<build>
    <plugins>
        <!-- 打包插件 -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

这里主要有四个部分

  • 项目元数据信息:创建时输入的 Project Matadata 部分,也就是 Maven 项目的基本元素,包括groupId、artifactld、version、name、description等
  • parent:继承spring-boot-starter-parent的依赖管理,包括控制版本与打包等内容
  • dependencies:项目具体依赖,这里包含了spring-boot-starter-web用于实现HTTP接口(该依赖包含了SpringMVC),官网对它的描述是:使用Spring MVC 构建Web(包括RESTful)应用程序的入门者,使用 Tomcat 作为默认嵌入式容器;spring-boot-starter-test用于编写单元测试的依赖包。更多的功能模块在后面会继续学习
  • build:构建配置部分。默认使用spring-boot-maven-plugin,配合spring-boot-starter-parent就可以把 Spring Boot 应用打包成 JAR 直接运行
编写HTTP接口
  1. 在主程序(这里主程序是 HelloWorldApplication)的同级目录(新建的目录与主程序同级)中,新建一个controller包,一定要在同级目录下,否则识别不到
  2. 在包中新建一个HelloController类
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        return "Hello World";
    }
}
  1. 编写完毕后从主程序启动项目,在浏览器发起请求,看页面返回
  2. 补充:在 resource目录里的配置文件 application.properties 中,可以设置很多内容

① 如可以设置不同的端口号(默认8080)

server.port=8081

此时改为8081

② 可以设置 banner

如此简单几步,就完成了一个web接口的开发,SpringBoot非常简便。所以常用它建立微服务项目

三、运行原理探究

接下来讨论一下上面的的 HelloController 是如何运行的。对于Maven项目一般是从 pom.xml 文件看起

Pom.xml

父依赖

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

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

点进spring-boot-starter-parent,发现还有一个父依赖 :

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

这里真正管理着 SpringBoot 的依赖版本,是SpringBoot的版本控制中心 。以后导入依赖不需要指定版本,但如果导入的包没有在依赖中管理则需要手动配置版本

启动器 spring-boot-starter
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
  • 启动器:springboot的启动场景
  • 如 spring-boot-starter-web:帮我们自动导入web模块正常运行所依赖的组件
  • SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 。以后也可以自己自定义 starter

主启动类

默认的主启动类
//@SpringBootApplication 来标注一个主程序类,标注这个类是Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
    public static void main(String[] args) {
        //看起来是启动了一个方法,其实是启动了一个服务
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

但是一个简单的启动类并不简单!接下来分析一下这些注解的作用

@SpringBootApplication

作用:标注在某个类上说明这个类是SpringBoot的主配置类

SpringBoot 运行这个类的main方法来启动SpringBoot应用。进入这个注解,可以看到还有很多其他注解:

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

作用:自动扫描并加载符合条件的组件或者bean ,将这个bean定义加载到IOC容器中

这个注解在Spring中很重要 ,它对应XML配置中的元素

@SpringBootConfiguration

作用:SpringBoot的配置类

标注在某个类上 , 表示这是一个SpringBoot的配置类;

我们继续进去这个注解查看

// 点进去得到下面的 @Component
@Configuration
public @interface SpringBootConfiguration {}

@Component
public @interface Configuration {}

这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;

里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!

我们回到 SpringBootApplication 注解中继续看。

@EnableAutoConfiguration

@EnableAutoConfiguration :开启自动配置功能

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

点进注解接续查看:

@AutoConfigurationPackage :自动配置包

@Import({Registrar.class})	//自动配置包注册
public @interface AutoConfigurationPackage {
}

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

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

接下来退到上一步,继续看

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

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

  1. 这个类中有一个这样的方法
// 获得候选的配置
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;
}
  1. 这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入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());
}
  1. 我们继续点击查看 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);
        }
    }
}
  1. 发现一个多次出现的文件:spring.factories,全局搜索它
spring.factories

打开spring.factories , 可以就看到有很多自动配置的文件,这就是自动配置根源所在

WebMvcAutoConfiguration

在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

	public static final String DEFAULT_PREFIX = "";

	public static final String DEFAULT_SUFFIX = "";

	private static final String[] SERVLET_LOCATIONS = { "/" };

	@Bean
	@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)

可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!

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

结论
  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器 , 自动配置类就生效,可以进行自动配置工作
  3. 整个JavaEE的整体解决方案和自动配置都在 springboot-autoconfigure 的 jar 包中
  4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入并配置好这个场景需要的所有组件
  5. 有了自动配置类 ,免去手动编写配置注入功能组件等的工作

主启动类如何运行

SpringApplication

最初以为下面是运行了一个main方法,没想到是开启了一个服务

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

接下来对SpringApplication.run进行分析,该方法主要分两部分,一是SpringApplication的实例化,二是run方法的执行

SpringApplication

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

  1. 推断应用的类型是普通的项目还是Web项目
  2. 查找并加载所有可用初始化器 , 设置到initializers属性中
  3. 找出所有的应用程序监听器,设置到listeners属性中
  4. 推断并设置main方法的定义类,找到运行的主类

查看构造器:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    // ......
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.setInitializers(this.getSpringFactoriesInstances();
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}
run方法流程分析

posted @ 2020-08-22 16:51  弥漫s  阅读(144)  评论(0)    收藏  举报