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
注入容器,主要用于在初始化类结束后调用InitializingBean
的afterPropertiesSet
方法进行文件监听 - fileSystemWatcherFactory() 注入一个
fileSystemWatcherFactory
,从中获取到FileSystemWatcher,该类为文件监听的实现类static class RestartConfiguration {
private final DevToolsProperties properties;
RestartConfiguration(DevToolsProperties properties) {
this.properties = properties;
}
//ApplicationListener<ClassPathChangedEvent> 事件广播,当文件发生变化时,会执行Restarter.getInstance().restart(new FileWatchingFailureHandler(fileSystemWatcherFactory));
ApplicationListener<ClassPathChangedEvent> restartingClassPathChangedEventListener(FileSystemWatcherFactory fileSystemWatcherFactory) {
return (event) -> {
if (event.isRestartRequired()) {
Restarter.getInstance().restart(new FileWatchingFailureHandler(fileSystemWatcherFactory));
}
};
}
//ClassPathFileSystemWatcher 该类实现InitializingBean接口,当类初始化完成以后会调用afterPropertiesSet方法,去执行this.fileSystemWatcher.start();开启文件监听
ClassPathFileSystemWatcher classPathFileSystemWatcher(FileSystemWatcherFactory fileSystemWatcherFactory, ClassPathRestartStrategy classPathRestartStrategy) {
URL[] urls = Restarter.getInstance().getInitialUrls();
ClassPathFileSystemWatcher watcher = new ClassPathFileSystemWatcher(fileSystemWatcherFactory, classPathRestartStrategy, urls);
watcher.setStopWatcherOnRestart(true);
return watcher;
}
ClassPathRestartStrategy classPathRestartStrategy() {
return new PatternClassPathRestartStrategy(this.properties.getRestart().getAllExclude());
}
//用于生产FileSystemWatcher
FileSystemWatcherFactory fileSystemWatcherFactory() {
return this::newFileSystemWatcher;
}
(
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;
}
}
- restartingClassPathChangedEventListener() 方法将
当开启
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>
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);
}
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> |
3、编写SpringBoot入口程序
SpringBoot需要一个入口,也就是main方法,来启动SpringBoot的整个应用
package com.zenshin; |
4、编写一个自己的Controller
编写Controller,实现SpringMVC的功能
package com.zenshin.controller; |
5、启动
1、我们可以直接点击IDEA中的main方法执行,打开浏览器就能运行了
2、在pom.xml里面加入maven构建插件,利用maven打成一个jar包,直接java命令运行
<build> |
注意一点:SpringBoot已经集成了嵌入的Tomcat无需配置服务器环境。可以直接运行
SpringBoot-HelloWorld项目分析
1、pom文件
1、父项目
<parent> |
以上也可以称之为SpringBoot的版本仲裁中心;
以后我们导入依赖默认是不需要写版本的,(对于Dependencies里面没有的依赖需要进行版本的声明)
2、启动器
<dependency> |
spring-boot-starter:springBoot场景启动器;帮我们导入web模块正常运行所依赖的组件;
springBoot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用到什么功能就导入什么场景的启动器
2、HelloWord主程序类
@SpringBootApplication :SpringBoot应用标注在某个类上面说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用:
看一下SpringBootApplication里面有很多的注解
({ElementType.TYPE}) |
@SpringBootConfiguration: SpringBoot的配置类:标注在某个类上,表示这是一个SpringBoot的配置类,
- SpringBootConfiguration又被@Configuration注解,只有配置类标注这个注解
- 配置类的作用就是配置文件,配置类也是容器中的一个组件:@Component
@EnableAutoConfiguration:开启自动配置功能;
- 以前我们需要配置的东西,SpringBoot帮助我们自动配置,@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效
自动注解是怎么实现的呢
|
- @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的默认配置);