1、Spring Boot简介
背景
J2EE笨重的开发、繁多的配置、低下的开发效率、复杂的的部署流程、第三方技术集成难度大。
解决
“Spring全家桶”时代:
Spring Boot -> J2EE一站式解决方案
Spring Cloud -> 分布式整体解决方案
简述——什么是Spring Boot?
Spring Boot来简化Spring应用开发,约定大于配置,去繁从简,just run就能创建一个独立的,产品级别的应用:
- 简化spring应用开发的一个框架
- 整个spring技术栈的一个大整合
- J2EE开发的一站式解决
优点
快速创建独立运行的Spring
项目以及与主流框架集成
使用嵌入式的Servlet
容器,应用无需发成war
包
starters
自动依赖与版本控制
大量的自动配置,简化开发,也可以修改默认值
无需配置XML
,无代码生成,开箱即用
准生产环境的运行时应用监控
与云计算的天然集成
2、微服务
什么是微服务?
简而言之,微服务架构风格是将单个应用程序开发为一套小型服务的方法,每个小型服务都在自己的流程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。 这些服务围绕业务功能搭建,可通过全自动部署机制独立部署。有一个集中管理的最低限度的这些服务,可以用不同的编程语言和使用不同的数据存储技术。
- 架构风格:服务微化
- 一个应用应该是一组小型的服务:可以通过http的方式进行切割。
下面我们来对比一个单体应用与微服务架构
单体应用 all in one
- 开发简单
- 测试简单
- 部署简单:直接打包成
war
包丢到tomcat
中 - 扩展简单:把应用复制多份,部署到集群中,负载均衡时并发分配到各个应用中。
微服务
每一个功能元素都是一个可独立替换和可独立升级的软件单元。
图中每一个黑圆圈都是一个独立的功能单元,每个功能单元之间通过http
进行通信,在对某一个单元进行操作时(升级,替换,移除等)不会影响到其他单元。这么多的功能模块如果真要我们来微化,创建那么多的项目来微化这些服务的时候,每个项目可能都是要整合各种场景,如果按照以前的方式再来构建项目,这么多模块,光创建项目,搭建环境就得花费好多时间。
面对这么大型的分布式应用,springboot
可以帮我们快速的构建出一个应用,整个分布式应用网之间该怎么互调呢,用springclound
进行网状互联互调,包括在分布式中间要进行一些流式计算,批处理怎么办呢,springclound data flow
,也就是官方为我们整个应用想清楚了应用的整个出路。
创建项目
https://start.spring.io/
下载下来后,直接导入idea即可使用。
写一个hello,world!
导入`web`依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "hello world";
}
直接运行启动类,即可
//标注一个主程序类,说明这是一个springboot应用
@SpringBootApplication
public class SpringBoot01HelloworldApplication {
public static void main(String[] args) {
//spring应用启动起来
SpringApplication.run(SpringBoot01HelloworldApplication.class, args);
}
}
服务器上部署:
导入springboot maven插件:
<!--可以将应用打包成一个可执行的jar包-->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
将项目打包,部署
java -jar spring-boot-01-helloworld-0.0.1-SNAPSHOT.jar
可以发现我们不用打成war
,然后扔到tomcat
里去运行了。解压这个可执行的jar,在BOOT-INF\lib
下,我们能看到嵌入式tomcat
的依赖,因此目标环境就不需要tomcat
服务器了。
HelloWord探究
1.pom
文件
父项目
<!--父项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.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.2.4.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
进入spring-boot-dependencies,发现
它里面定义了很多依赖的版本,实际上它来真正管理spring boot应用的所有依赖版本;
它是springboot的版本仲裁中心:以后我们导入依赖默认是不需要写版本
(没有在dependencies里面管理的依赖自然需要声明版本号)
2.启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter:springboot场景启动器
进入spring-boot-starter-web
:发现帮我们导入了web模块正常运行所依赖的组件。包括:
starter,json,tomcat,validation,web,webmvc
springboot
将所有的功能场景都抽取出来,做成一个个的starters
(启动器),只需要在项目里面引入这些starter
相关场景的所有依赖就都会导入进来。要用什么功能就导入什么场景的启动器。
如果我们想要要用JMS或者mq,那么就有mq的启动器spring-boot-starter-activemq
,要做aop,我们就导入spring-boot-starter-aop
。。。。
官网参考starter
:
https://docs.spring.io/spring-boot/docs/2.2.4.RELEASE/reference/html/using-spring-boot.html#using-boot-starter
主程序类
//标注一个主程序类,说明这是一个springboot应用
@SpringBootApplication
public class SpringBoot01HelloworldApplication {
public static void main(String[] args) {
//spring应用启动起来
SpringApplication.run(SpringBoot01HelloworldApplication.class, args);
}
}
@SpringBootApplication
:springboot
应用标注在某个类上说明这个类是springboot
的主配置类;springboot
就应该运行这个类的main
方法来启动springboot
应用。
查看该注解:
@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}
)}
)
1.@SpringBootConfiguration
:SpringBoot
配置类;
标注在类上,表示这是一个springboot
的配置类
查看该注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Configuration
:配置类上标注这个注解,配置类相当于配置文件,配置类也是容器中的一个组件;@Component
老版springboot自动配置原理
2.@EnableAutoConfiguration
:开启自动配置功能;
以前我们需要配置的东西,springboot帮我们自动配置;
查看该注解:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
2.1@AutoConfigurationPackage
:自动配置包
查看该注解:
@Import({Registrar.class})
spring的底层注解@Import,给容器中导入组件;
查看Registrar
发现它实现了接口ImportBeanDefinitionRegistrar
,@Import
的使用方式之一,重点看registerBeanDefinitions
方法:
将主配置类(@SpringBootApplication
标注的类)的所在包及下面所有子包里面的所有组件扫描到spring
容器
2.2 @Import({AutoConfigurationImportSelector.class})
:查看该注解的自动配置选择器
发现AutoConfigurationImportSelector
实现了ImportSelector
,@Import
的另一种使用方式,重点看selectImports
方法:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
在 selectImports
方法中调用了一个 getAutoConfigurationEntry()
方法。
调用链:在 getAutoConfigurationEntry()
-> getCandidateConfigurations()
-> loadFactoryNames()
。
在这里 loadFactoryNames()
方法传入了 EnableAutoConfiguration.class
这个参数。先记住这个参数,等下会用到。
loadFactoryNames()
中关键的三步:
- 从当前项目的类路径中获取所有
jar
类路径下的META-INF/spring.factories
这个文件下的信息。 - 将上面获取到的信息封装成一个
Map
返回。 - 从返回的
Map
中通过刚才传入的EnableAutoConfiguration.class
参数,获取该key
下的所有值
META-INF/spring.factories 探究
我们来看一下 META-INF/spring.factories
这类文件是什么就不懵了。当然在很多第三方依赖中都会有这个文件,一般每导入一个第三方的依赖,除了本身的jar
包以外,还会有一个 xxx-spring-boot-autoConfigure
,这个就是第三方依赖自己编写的自动配置类。我们现在就以 spring-boot-autocongigure
这个依赖来说。
可以看到
EnableAutoConfiguration
下面有很多类,这些就是我们项目进行自动配置的类。
一句话:将类路径下 META-INF/spring.factories
里面配置的所有 EnableAutoConfiguration
的值加入到 Spring
容器中。
这么多的配置类,明显有很多自动配置我们平常是没有使用到的,没理由全部都生效吧。
举个例子,看其中一条配置:
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
进入到AopAutoConfiguration
类中:
于是,我们又看到了熟悉的@ConditionalOnXXX
注解,这个AopAutoConfiguration
类加载不加载完全由@ConditionalOnXXX
注解们来控制!下面详细分析下加载条件:
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
解析:在我们自己工程的application.properties
里,有spring.aop.auto=true
时加载AopAutoConfiguration
。但是缺少spring.aop.auto=true
时,也可继续验证(matchIfMissing = true)
,并不直接放弃加载。
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,AnnotatedElement.class })
解析:在JVM运行时加载了EnableAspectJAutoProxy、Aspect、Advice、AnnotatedElement
这些类后,才加载AopAutoConfiguration
。
@ConditionalOnXXX 这类springboot注解进去会发现是基于spring的@Conditional的
有了自动配置的AopAutoConfiguration
AOP配置类,我们就可以省略下面的配置类了:
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
//业务逻辑类加入容器中
@Bean
public MathCalculator calculator(){
return new MathCalculator();
}
//切面类加入容器中
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
还有一个典型的例子:在学习servlet3.0整合springmvc的定义与接管Springmvc时,配置springmvc
的配置类要么通过实现WebMvcConfigurer
接口,要么继承WebMvcConfigurerAdapter
,但是WebMvcAutoConfiguration
通过实现WebMvcConfigurer
接口帮我们把视图解析器,Filter等等;以前我们需要配置的东西,自动配置类都帮我们配置了。
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration