Spring boot 来简化Spring应用开发,约定大于配置,去繁从简,just run就能创建一个独立的,产品级别的应用。
背景
J2EE笨重的开发、繁多的配置、低下的开发效率、复杂的部署流程、第三方技术集成难度大。
解决
“Spring全家桶”时代.
SpringBoot -> J2EE一站式解决方案
SpingCloud -> 分布式整体解决方案
优点
- 快速创建独立运行的Spring项目以及与主流框架集成
- 使用嵌入式的Servlet容器,应用无需打成WAR包
- starters自动依赖与版本控制
- 大量的自动配置,简化开发,也可修改默认值
- 无需配置XML,无代码生成,开箱即用
- 准生产环境的运行时应用监控
- 与云计算的天然集成
微服务
2014,martin fowler
微服务:架构风格(服务微化)
一个应用应该是一组小型服务;可以通过HTTP的方式进行互通;
单体应用:ALL IN ONE
微服务:每一个功能元素最终都是一个可独立替换和独立升级的软件单元;
环境约束:
- jdk1.8:Spring Boot 推荐jdk1.7及以上;java version “1.8.0_112”
- maven3.x:maven 3.3以上版本;Apache Maven 3.3.9
- IntelliJIDEA2017:IntelliJ IDEA 2017.2.2 x64 、STS
- SpringBoot 1.5.9.RELEASE:1.5.9;
密码:jpro
统一环境;
1.Maven设置
给maven 的 settings.xml配置文件的profiles标签添加
<profile>
<id>jdk‐1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
2.IDEA设置
整合maven进来
将maven配置成我们的maven,不使用idea自带的maven。
Maven home directory: maven的安装路径
User setting file:maven的setting配置文件的路径
Local repository:maven的本地仓库的路径。
Spring Boot HelloWorld
一个功能:
浏览器发送hello请求,服务器接受请求并处理,响应Hello World字符串;
创建一个maven项目(jar)
导入spring boot相关的依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐parent</artifactId>
<version>1.5.17.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐web</artifactId>
</dependency>
</dependencies>
现在的spring boot的1.x的版本没有1.5.9,所以我们使用1.5.17.RELEASE。
我们可以参考Spring的官网的文档。Spring官网
添加依赖后,idea就会帮我们开始下载依赖,如果没有自动下载,可以点击idea的右边的Maven Projects,然后点击上面的循环标志。
依赖下载完毕后,可以看到我们的依赖文件:
编写一个主程序
在src/main/java下创建com.liuzhuo的包,并创建HelloWorldApplication类:
添加@SpringBootApplication注解,并编写一个主函数。
@SpringBootApplication //表明这是一个Springboot的启动类
public class HelloWorldApplication {
public static void main(String[] args) {
//启动Springboot应用。
SpringApplication.run(HelloWorldApplication.class);
}
}
编写相关的Controller
在com.liuzhuo.controller下,创建HelloController
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello(){
return "Hello World!";
}
}
运行主程序测试
回到Springboot的启动类中,直接运行main函数:
出现上图所示的效果就说明Springboot应用启动成功。
在浏览器中输入:http://localhost:8080/hello
简化部署
以前,部署Web应用,需要将我们的Web项目生成War,然后在服务器中安装服务器,比如Tomcat等,然后将War放在Tomcat容器中,启动Tomcat容器。
现在,我们不需要这么麻烦了,我们直接生成jar就行。生成jar之前,我们需要在pom文件中添加Springboot的构建插件。
<!-- 这个插件,可以将应用打包成一个可执行的jar包-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
然后生成jar包:
双击:package。
控制台输出:BUILD SUCCESS
我们,可以在target包下,看到我们生成的可运行的jar。
拷贝这个jar到我们的桌面上面:
打开命令行界面:
我们的Springboot应用就启动了,这样是不是很简单,都不需要打成war包和安装Tomcat。
在浏览器中输入:http://localhost:8080/hello
Hello World 的探究
POM文件
1)父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐parent</artifactId>
<version>1.5.17.RELEASE</version>
</parent>
他的父项目是
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐dependencies</artifactId>
<version>1.5.17.RELEASE</version>
<relativePath>../../spring‐boot‐dependencies</relativePath>
</parent>
他来真正管理Spring Boot应用里面的所有依赖版本;
Spring Boot的版本仲裁中心;
以后我们导入依赖,默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号)
2) 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐web</artifactId>
</dependency>
spring-boot-starter-web:
spring-boot-starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件;
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter.
相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器.
主程序类,主入口类
/**
* 描述:
*
* @author liuzhuo
* @create 2018-11-02 16:31
*/
@SpringBootApplication //表明这是一个Springboot的启动类
public class HelloWorldApplication {
public static void main(String[] args) {
//启动Springboot应用。
SpringApplication.run(HelloWorldApplication.class);
}
}
@SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是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) })
public @interface SpringBootApplication
发现@SpringBootApplication注解是一个组合注解,下面我们一一来看。
@SpringBootConfiguration: Spring Boot的配置类;
标注在某个类上,表示这是一个Spring Boot的配置类;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
其实就是一个@Configuration注解,只是@Configuration注解,我们是在Spring中使用,@SpringBootConfiguration是在Springboot中使用,本质都一个配置类。
配置类 —– 配置文件;配置类也是容器中的一个组件;@Component
@EnableAutoConfiguration:开启自动配置功能;
以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自
动配置功能;这样自动配置才能生效;
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
@AutoConfigurationPackage:自动配置包
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
@Import(AutoConfigurationPackages.Registrar.class):
Spring的底层注解@Import,给容器中导入一个组件;导入的组件由AutoConfigurationPackages.Registrar.class注入;
@Order(Ordered.HIGHEST_PRECEDENCE)
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.<Object>singleton(new PackageImport(metadata));
}
}
给这个方法打上断点。debug下
会发现,Spring会把将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器;
@Import(EnableAutoConfigurationImportSelector.class);
给容器中导入组件?
EnableAutoConfigurationImportSelector:导入哪些组件的选择器;
将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中;
会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件;
有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将
这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;以前我们需要自己配置的东
西,自动配置类都帮我们;
J2EE的整体整合解决方案和自动配置都在:spring-boot-autoconfigure-X.X.X.RELEASE.jar
;
所以,@EnableAutoConfiguration自动配置的魔法骑士就变成了:
从classpath中搜寻所有的 META-INF/spring.factories 配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项
通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。
深入探索SpringApplication执行流程
SpringApplication的run方法的实现是我们本次旅程的主要线路,该方法的主要流程大体可以归纳如下:
1) 如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:
- 根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
- 推断并设置main方法的定义类。
2)SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行开始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。
3) 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。
4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
5) 如果SpringApplication的showBanner属性被设置为true,则打印banner。
6) 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。
7) ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。
8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。
9) 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。
10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。
11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。
12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
13) 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)
总结
到此,SpringBoot的核心组件完成了基本的解析,综合来看,大部分都是Spring框架背后的一些概念和实践方式,SpringBoot只是在这些概念和实践上对特定的场景事先进行了固化和升华,而也恰恰是这些固化让我们开发基于Sping框架的应用更加方便高效。
使用Spring Initializer快速创建Spring Boot项目
IDE都支持使用Spring的项目创建向导快速创建一个Spring Boot项目;
选择我们需要的模块;向导会联网创建Spring Boot项目;
默认生成的Spring Boot项目;
主程序已经生成好了,我们只需要我们自己的逻辑
resources文件夹中目录结构:
- static:保存所有的静态资源; js css images;
- templates:保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的- - Tomcat,默认不支持JSP页面);可以使用模板引擎(freemarker、thymeleaf);
- application.properties:Spring Boot应用的配置文件;可以修改一些默认设置;
1)打开idea,创建一个工程Project
2)选择Spring Initializr
3) 填写Group和Artifact
4)选择Spring boot的版本 和 我们需要的模块(Web)
5)填写项目名和项目的路径
6)删除不需要的文件
7)编写逻辑控制层
在com.liuzhuo.springboot的包下,创建controller包,并创建HelloController:
8)启动Springboot应用
9)在浏览器中输入:http://localhost:8080/hello