avatar

SpringBoot简单介绍

SpringBoot入门

SpringBoot简介

SpringBoot是伴随spring4.0版本开发出的一个框架,约定大于配置,去繁从简,just run就能创建一个独立的产品集的应用
整个Spring技术栈的一个大整合,J2EE的一站式解决方案

springboot优点

  • 快速创建独立运行的spring项目以及主流框架集成
  • 使用嵌入式的Servlet容器,应用无需打包成war
  • starters自动依赖和版本控制
  • 大量的自动配置,简化开发,也可以修改默认值
  • 无需配置XML,无代码生成,开箱即用
  • 准生产环境的运行时应用监控
  • 与云端的天然集成

SpringBoot缺点

  • SpringBoot集成的是Spring全家桶以及一些外部组件,所以入门简单,深入需要了解Spring的知识。

SpringBoot主要特性

  • SpringBoot Starter: 他将常用的依赖分组进行整合,将其合并到一个依赖中,这样就可以一次性添加到项目的maven或Gradle构建中;

  • 使编码变得简单,SpringBoot采用javaConfig的方式对Spring进行配置,并且提供了大量的注解,极大的提高了工作效率

  • 自动配置: SpringBoot的自动装配特性利用Spring对条件化配置的支持,合理的推测应用所需的bean并且自动化配置它们

  • 使部署变得简单,SpringBoot内置了三种Servlet容器:Tomcat、jetty、undertow我们只需要一个java的运行环境就可以跑SpringBoot的项目了。

    微服务

    2014年 martin fowler阐述了微服务的一些概念
    一个应用应该是一组小型服务;可以通过HTTP的方式进行互通
    每一个功能元素最终都是一个可以独立替换和独立升级的软件单元
    详细参照微服务文档:微服务指南

    学习SpringBoot需要的环境

    • Java1.8
    • IDEA 2018
    • Maven 3.3.9(关于Maven请看我另外一篇文章)
    • SpringBoot 1.5.9(高版本源码与其有出入但是总体实现可以参考)

    SpringBoot—helloworld

    SpringBoot DevTools

    SpringBoot为我们提供了一个热部署工具,在我们修改了某一个类的时候了,我们可以不必重启服务就能看到修改后的效果

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

    实现原理:

  • DevTool有一个ClassLoader,叫做RestartClassLoader,他用于加载classPath下得类

  • 第三方jar中的类交由base-classloader进行加载

  • DevTool中有一个线程会监控classPath下的文件是否发生变化,如果发生变化,会重新

  • 这样就比整个工程启动要快的多
    实现原理初探springboot官方解释

    DevTool源码剖析
  • 首先我们找到spring-boot-devtools包,在包的META_INF下有一个spring.factories文件

  • 打开LocalDevToolsAutoConfiguration文件,其中有一个RestartConfiguration类,该类为SpringBoot的配置类,他将一系列的Bean都加载到Spring容器中

    • restartingClassPathChangedEventListener() 方法将ApplicationListener<ClassPathChangedEvent>监听方法注册到Spring容器中
    • classPathFileSystemWatcher() 将ClassPathFileSystemWatcher注入容器,主要用于在初始化类结束后调用InitializingBeanafterPropertiesSet方法进行文件监听
    • fileSystemWatcherFactory() 注入一个fileSystemWatcherFactory,从中获取到FileSystemWatcher,该类为文件监听的实现类
      static class RestartConfiguration {
      private final DevToolsProperties properties;

      RestartConfiguration(DevToolsProperties properties) {
      this.properties = properties;
      }
      //ApplicationListener<ClassPathChangedEvent> 事件广播,当文件发生变化时,会执行Restarter.getInstance().restart(new FileWatchingFailureHandler(fileSystemWatcherFactory));
      @Bean
      ApplicationListener<ClassPathChangedEvent> restartingClassPathChangedEventListener(FileSystemWatcherFactory fileSystemWatcherFactory) {
      return (event) -> {
      if (event.isRestartRequired()) {
      Restarter.getInstance().restart(new FileWatchingFailureHandler(fileSystemWatcherFactory));
      }

      };
      }

      //ClassPathFileSystemWatcher 该类实现InitializingBean接口,当类初始化完成以后会调用afterPropertiesSet方法,去执行this.fileSystemWatcher.start();开启文件监听
      @Bean
      @ConditionalOnMissingBean
      ClassPathFileSystemWatcher classPathFileSystemWatcher(FileSystemWatcherFactory fileSystemWatcherFactory, ClassPathRestartStrategy classPathRestartStrategy) {
      URL[] urls = Restarter.getInstance().getInitialUrls();
      ClassPathFileSystemWatcher watcher = new ClassPathFileSystemWatcher(fileSystemWatcherFactory, classPathRestartStrategy, urls);
      watcher.setStopWatcherOnRestart(true);
      return watcher;
      }

      @Bean
      @ConditionalOnMissingBean
      ClassPathRestartStrategy classPathRestartStrategy() {
      return new PatternClassPathRestartStrategy(this.properties.getRestart().getAllExclude());
      }

      //用于生产FileSystemWatcher
      @Bean
      FileSystemWatcherFactory fileSystemWatcherFactory() {
      return this::newFileSystemWatcher;
      }

      @Bean
      @ConditionalOnProperty(
      prefix = "spring.devtools.restart",
      name = {"log-condition-evaluation-delta"},
      matchIfMissing = true
      )
      ConditionEvaluationDeltaLoggingListener conditionEvaluationDeltaLoggingListener() {
      return new ConditionEvaluationDeltaLoggingListener();
      }

      private FileSystemWatcher newFileSystemWatcher() {
      Restart restartProperties = this.properties.getRestart();
      FileSystemWatcher watcher = new FileSystemWatcher(true, restartProperties.getPollInterval(), restartProperties.getQuietPeriod(), SnapshotStateRepository.STATIC);
      String triggerFile = restartProperties.getTriggerFile();
      if (StringUtils.hasLength(triggerFile)) {
      watcher.setTriggerFilter(new TriggerFileFilter(triggerFile));
      }

      List<File> additionalPaths = restartProperties.getAdditionalPaths();
      Iterator var5 = additionalPaths.iterator();

      while(var5.hasNext()) {
      File path = (File)var5.next();
      watcher.addSourceDirectory(path.getAbsoluteFile());
      }

      return watcher;
      }
      }
  • 当开启fileSystemWatcher的start方法会执行,其中FileSystemWatcher.Watcher就是真正监听文件变化的类

     public void start() {
    synchronized(this.monitor) {
    this.createOrRestoreInitialSnapshots();
    if (this.watchThread == null) {
    Map<File, DirectorySnapshot> localDirectories = new HashMap(this.directories);
    //初始化一个监听线程
    FileSystemWatcher.Watcher watcher = new FileSystemWatcher.Watcher(this.remainingScans, new ArrayList(this.listeners), this.triggerFilter, this.pollInterval, this.quietPeriod, localDirectories, this.snapshotStateRepository);
    this.watchThread = new Thread(watcher);
    this.watchThread.setName("File Watcher");
    this.watchThread.setDaemon(this.daemon);
    this.watchThread.start();
    }

    }
    }
    ////////////////////////////FileSystemWatcher.Watcher
    private void scan() throws InterruptedException {
    Thread.sleep(this.pollInterval - this.quietPeriod);
    Map current = this.directories
    Map previous;
    do {
    previous = current;
    current = this.getCurrentSnapshots();
    Thread.sleep(this.quietPeriod);
    } while(this.isDifferent(previous, current));
    //当文件发生变化时,更新快照
    if (this.isDifferent(this.directories, current)) {
    this.updateSnapshots(current.values());
    }
    }
    ///////////////////////////////////////updateSnapshots最终将变化的文件存储在一个changeSet中,如果不为空,那么就执行文件监听器的onChange方法
    private void fireListeners(Set<ChangedFiles> changeSet) {
    Iterator var2 = this.listeners.iterator();

    while(var2.hasNext()) {
    FileChangeListener listener = (FileChangeListener)var2.next();
    listener.onChange(changeSet);
    }
    }
    /////////////////////////////////////ClassPathFileChangeListener监听器中执行onChange方法
    class ClassPathFileChangeListener implements FileChangeListener {
    ...
    public void onChange(Set<ChangedFiles> changeSet) {
    boolean restart = this.isRestartRequired(changeSet);
    this.publishEvent(new ClassPathChangedEvent(this, changeSet, restart));
    }

    private void publishEvent(ClassPathChangedEvent event) {
    //广播事件
    this.eventPublisher.publishEvent(event);
    if (event.isRestartRequired() && this.fileSystemWatcherToStop != null) {
    this.fileSystemWatcherToStop.stop();
    }

    }
    ...
    }
  • 发出广播后会执行当初RestartConfiguration中注入Spring容器中的ApplicationListener<ClassPathChangedEvent>

    @Bean
    ApplicationListener<ClassPathChangedEvent> restartingClassPathChangedEventListener(
    FileSystemWatcherFactory fileSystemWatcherFactory) {
    return (event) -> {
    if (event.isRestartRequired()) {
    // restart 重启~
    Restarter.getInstance().restart(new FileWatchingFailureHandler(fileSystemWatcherFactory));
    }
    };
    }
  • Restarter.getInstance().restart进行重启,会开启RestartLauncher线程,并且根据RestartClassLoader类加载器重新加载容器

    public void restart(FailureHandler failureHandler) {
    if (!this.enabled) {
    this.logger.debug("Application restart is disabled");
    return;
    }
    this.logger.debug("Restarting application");
    getLeakSafeThread().call(() -> {
    //首先将所有的额容器关闭,进行垃圾回收,清空缓存,将所有无用的信息都清空掉
    Restarter.this.stop();
    //重新启动
    Restarter.this.start(failureHandler);
    return null;
    });
    }
    /////////////////////////////////启动的核心代码
    private Throwable doStart() throws Exception {
    Assert.notNull(this.mainClassName, "Unable to find the main class to restart");
    URL[] urls = this.urls.toArray(new URL[0]);
    ClassLoaderFiles updatedFiles = new ClassLoaderFiles(this.classLoaderFiles);
    //重新定义一个RestartClassLoader
    ClassLoader classLoader = new RestartClassLoader(this.applicationClassLoader, urls, updatedFiles, this.logger);
    if (this.logger.isDebugEnabled()) {
    this.logger.debug("Starting application " + this.mainClassName + " with URLs " + Arrays.asList(urls));
    }
    return relaunch(classLoader);
    }
    ////////////////////////////RestartLauncher 中执行方法
    class RestartLauncher extends Thread {

    private final String mainClassName;

    private final String[] args;

    private Throwable error;

    RestartLauncher(ClassLoader classLoader, String mainClassName, String[] args,
    UncaughtExceptionHandler exceptionHandler) {
    this.mainClassName = mainClassName;
    this.args = args;
    setName("restartedMain");
    setUncaughtExceptionHandler(exceptionHandler);
    setDaemon(false);
    //将RestartClassLoader放到当前线程中
    setContextClassLoader(classLoader);
    }

    @Override
    public void run() {
    try {
    //找到初始化容器的类
    Class<?> mainClass = Class.forName(this.mainClassName, false, getContextClassLoader());
    //执行main方法
    Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
    mainMethod.invoke(null, new Object[] { this.args });
    }
    catch (Throwable ex) {
    this.error = ex;
    getUncaughtExceptionHandler().uncaughtException(this, ex);
    }
    }

    Throwable getError() {
    return this.error;
    }
    }
  • 到现在,我们的容器通过main方法重新运行,我们的容器又重新被加载了,同时JVM中已经加载到的类(第三方的jar包)并没有重新进行加载,所以重启的速度变快了,只需要加载我们自己编写的部分文件即可。

1、创建一个Maven项目

使用Maven来搭建SpringBoot环境,你也可以使用Spring初始化工具来实现SpringBoot的项目初始化
1、点击File->New->Project
2、新建Maven项目,点击next,填写相应的项目信息,项目目录以及项目的名称,最后点击Finish创建
3、创建后,IDEA会显示一个Maven是否自动安装包,点确定即可

2、编写pom.xml,导入SpringBoot

1、打开项目中的pom.xml文件
2、导入SpringBoot必要的包

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

3、编写SpringBoot入口程序

SpringBoot需要一个入口,也就是main方法,来启动SpringBoot的整个应用

package com.zenshin;

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

/**
* @SpringBootApplication 来标注这是一个主程序,说明这是一个springboot应用
*/
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
//Spring应用启动起来
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}

4、编写一个自己的Controller

编写Controller,实现SpringMVC的功能

package com.zenshin.controller;

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

@Controller
public class HelloController {
@ResponseBody //设置相应body
//设置一个路由接收前端返回的hello请求
@RequestMapping("/hello")
public String hello()
{
return "Hello,World";
}
}

5、启动

1、我们可以直接点击IDEA中的main方法执行,打开浏览器就能运行了
2、在pom.xml里面加入maven构建插件,利用maven打成一个jar包,直接java命令运行

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

注意一点:SpringBoot已经集成了嵌入的Tomcat无需配置服务器环境。可以直接运行

SpringBoot-HelloWorld项目分析

1、pom文件

1、父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>

这个还依赖父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
把父项目点进去,在properties里面就能看到所有的依赖的jar和版本,他来真正的管理SpringBoot应用里面的所有依赖版本

以上也可以称之为SpringBoot的版本仲裁中心;
以后我们导入依赖默认是不需要写版本的,(对于Dependencies里面没有的依赖需要进行版本的声明)

2、启动器
 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

spring-boot-starter:springBoot场景启动器;帮我们导入web模块正常运行所依赖的组件;
springBoot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用到什么功能就导入什么场景的启动器

2、HelloWord主程序类

@SpringBootApplication :SpringBoot应用标注在某个类上面说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用:
看一下SpringBootApplication里面有很多的注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)

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

  • SpringBootConfiguration又被@Configuration注解,只有配置类标注这个注解
  • 配置类的作用就是配置文件,配置类也是容器中的一个组件:@Component

@EnableAutoConfiguration:开启自动配置功能;

  • 以前我们需要配置的东西,SpringBoot帮助我们自动配置,@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效

自动注解是怎么实现的呢

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  • @AutoConfigurationPackage:自动配置包
    使用 @Import(AutoConfigurationPackages.Registrar.class): Import是Spring的底层注解,给容器导入一个组件;导入的组件由Registrar控制
    ==将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器中;==
  • @Import(EnableAutoConfigurationImportSelector.class) :给容器中导入AutoConfigurationImportSelector组件
    导入哪些组件的选择器,将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中
    这个注解会给容器导入非常多的自动配置类(XXXAutoConfigurartion),给容器中导入这个场景需要的所有组件,并配置好这些组件;
    有了自动配置类,免去了我们手动编写配置注入功能组件的工作了。 有了自动配置类,免去了我们手动编写配置注入功能组件等的工作; SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classloader);//实现自动配置的主要代码
  • 功能: 从类路径下的META-INF/spring.facyories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮助我们进行自动配置工作
    以前我们需要自己配置的东西,自动配置类都帮我们实现了
    J2EE的整合解决方案和自动配置都在org.springframework.boot:spring-boot-autoconfigure里面

使用Spring Initializer快速创建一个SpringBoot应用

1、 File -> new -> project

2、设置好包名已经工程名称点击next
3、设置需要导入的模块

4、选择完以后点击finish,等待创建完成。
其中的mvn文件都可以删除掉。

  • 然后我们就可以写我们的SpringBoot的项目了。

默认生成的项目特点

  • 主程序生成好,只需要编写业务逻辑
  • resources文件中目录结构是这个样子
    • static : 保存所有静态资源;js css img;
    • templates: 保存所有的模板页面;(Spring Boot默认是jar包使用嵌入式的TomCat,默认不支持JSP页面);可以使用模板引擎(freemarker、thymeleaf)
    • application.propertes:Spring Boot应用的配置文件(用来修改Springboot的默认配置);
文章作者: zenshin
文章链接: https://zlh.giserhub.com/2020/03/25/springboot/introduction/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 zenshin's blog
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论