微服务核心重新认识SpringBoot,掌握核心特性及设计思想

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.

We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need minimal Spring configuration.

这是Spring官网对SpringBoot的定义和评价。

一、SpringBoot的前世今生

对于Spring框架而言,我们接触的比较多的是Spring framework中的SpringMVC、IOC、AOP、DI等。而这些框架在使用过程中需要进行大量的配置文件的编写,或者需要进行很多繁琐的配置才能完成项目的初始化搭建工作。Spring可以说它是万能胶,这样一点没错。下面我们来使用SpringMVC去构建一个Web项目,看看其步骤有多么的繁琐吧。

  • 1、创建一个项目结构(maven/gradle)
  • 2、spring的依赖,spring mvc 、servlet api的依赖
  • 3、web.xml, DispatcherServlet
  • 4、启动一个Spring mVC的配置,Dispatcher-servlet.xml
  • 5、创建一个Controller 发布一个http请求
  • 6、发布到jsp/servlet容器

1.1 SpringBoot的产生过程

2012年10月份,一个叫Mike Youngstrom(扬斯特罗姆)在Spring Jira中创建了一个功能请求,要求在Spring Framework中支持无容器Web应用程序体系结构,他谈到了在主容器引导 spring 容器
内配置 Web 容器服务。

SpringBoot刚出生的时候,引起了很多开源社区的关注,并且也有个人和企业开始尝试使用SpringBoot。 其实直到2016年,SpringBoot才真正在国内被使用起来。

1.2 到底什么是SpringBoot

SpringBoot 框架是为了能够帮助使用Spring框架的开发者快速高效的构建一个基于Spirng框架以及Spring生态体系的应用解决方案。它是对“约定优于配置”这个理念下的一个最佳实践。因此它是一个服务于框架的框架,服务的范围是简化配置文件。

什么才是约定优于配置呢?

  • 只要依赖的spring-boot-starter-web的jar,就会自动内置一个tomcat容器(替换)
    项目结构

  • 默认提供了配置文件application.properties/yml

  • starter启动依赖 - 如果是一个webstarter ,默认认为你是去构建一个spring mvc的应用.

  • EnableAutoConfiguration 默认对于依赖的 starter 进行自动装载

二、SpringBoot与微服务

那为什么Spring Cloud会采用Spring Boot来作为基础框架呢?原因很简单

  1. Spring Cloud它是关注服务治理领域的解决方案,而服务治理是依托于服务架构之上,所以它仍然需要一个承载框架;
  2. Spring Boot 可以简单认为它是一套快速配置Spring应用的脚手架,它可以快速开发单个微服务,所以Spring Cloud的版本和Spring Boot版本的兼容性有很大关联。

三、Spring注解驱动的发展过程

3.1 Spring 1.x

在SpringFramework1.x时代,其中在1.2.0是这个时代的分水岭,当时Java5刚刚发布,业界正兴起了使用Annotation的技术风,SpringFramework自然也提供了支持,比如当时已经支持了@Transactional等注解,但是这个时候,XML配置方式还是唯一选择。

3.2 Spring 2.x

Spring Framework2.x时代,2.0版本在Annotation中添加了@Required、@Repository以及AOP相关的@Aspect等注解,同时也提升了XML配置能力,也就是可扩展的XML,比如Dubbo这样的开源框架就是基于SpringXML的扩展来完美的集成Spring,从而降低了Dubbo使用的门槛。

在2.x时代,2.5版本也是这个时代的分水岭, 它引入了一些很核心的Annotation

  • @Autowired 依赖注入
  • @Qualifier 依赖查找
  • @Component、@Service 组件声明
  • @Controller、@RequestMappring等spring mvc的注解

尽管Spring 2.x时代提供了不少的注解,但是仍然没有脱离XML配置驱动,比如context:annotation-config context:componet-scan,前者的职责是注册Annotation处理器,后者是负责扫描classpath下指定包路径下被Spring模式注解标注的类,将他们注册成为Spring Bean

  • @Required
  • @Repository(Dao)
  • @Aspect

spring 2.5

  • @Component (组件)
  • @Service service(服务接口)
  • @Controller(控制器)
  • @RequetsMapping(请求映射器)

3.3 Spring 3.x

Spring Framework3.0是一个里程碑式的时代,他的功能特性开始出现了非常大的扩展,比如全面拥抱Java5、以及Spring Annotation。更重要的是,它提供了配置类注解@Configuration,它出现的首要任务就是取代XML配置方式。

实现无配置化的方式实现Bean的装配。

  • @Configuraion (去xml化)

把Bean的对象如何以便捷的方式加载到Spring IOC容器中

  • ComponentScan(扫描@Service、@Controller、@Repository)
  • Import(把多个容器配置合并在一个配置中)

Enable模块驱动
在Spring 3.1中,提供了很多以@Enable开头的注解,比如:

  • @EnableWebMvc(引入MVC框架在Spring应用中需要用到的所有的Bean)
  • @EnableScheduling(开启任务计划)
  • @EnableAutoConfiguration
  • @Bean(来声明一个bean)

3.4 Spring 4.x

@Conditional(选择性的对加载的bean进行条件过滤)

3.5 Spring 5.x

四、SpringBoot的特性

首先分析特性的时候,我们不妨从SrpingBootApplication的注解入手,看看它做了什么,首先打开注解的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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 本质上是由 3 个注解组成,分别是:

  • @Configuration;
  • @EnableAutoConfiguration;
  • @ComponentScan。
  • 可以直接用这三个注解也可以启动SpringBoot应用,只是每次配置三个注解比较繁琐,所以直接用一个复合注解更方便些。后面会逐一详尽分析这些注解的,这里先简单介绍一下。

4.1 EnableAutoConfiguration自动装配

打开EnableAutoConfigration注解的源码,不难发现会带有一个@Import的注解。其实所有以Enable开头的注解都会有一个@Import注解。下面来看下源码吧。

4.1.1 @Import注解

  • @EnableAutoConfiguration
1
2
3
4
5
6
7
8
9
10
11
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
Class<?>[] exclude() default {};

String[] excludeName() default {};
}
  • @EnableScheduling
1
2
3
4
5
6
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class})
@Documented
public @interface EnableScheduling {
}
  • @EnableWebMvc
1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}

类似于<import resource/> 形式的注解,就是把多个容器配置合并在一个配置中。可以配置三种不同的class:

  • 普通的bean或者带有@Configuration注解的bean;
  • 实现ImportSelector接口进行动态注入:
  • 实现ImportBeanDefinitionRegistror接口进行动态注入。

4.1.2 EnableAutoConfiguration分析

EnableAutoConfiguration的主要作用就是把SpringBoot中所有符合条件的@Configuration配置都加载到创建并使用的IoC容器中。

在注解源码中我们看到@Import注解中配置了EnableAutoConfigurationImportSelector这个类。

EnableAutoConfigurationImportSelector又是什么呢?

从名字上看一定是实现了ImportSelector接口,所以是基于动态bean的加载功能。来看下selectImports方法的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public String[] selectImports(AnnotationMetadata metadata) {
try {
AnnotationAttributes attributes = this.getAttributes(metadata);
List<String> configurations = this.getCandidateConfigurations(metadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(metadata, attributes);
configurations.removeAll(exclusions);
configurations = this.sort(configurations);
this.recordWithConditionEvaluationReport(configurations, exclusions);
return (String[])configurations.toArray(new String[configurations.size()]);
} catch (IOException var5) {
throw new IllegalStateException(var5);
}
}

此处返回的String数组,是所有类的全类名,它们都会被纳入到Spring的IoC容器中。

其实 EnableAutoConfiguration会帮助SpringBoot应用把所有符合@Configuration 配置都加载到当前SpringBoot创建的IoC容器,而这里面借助了Spring框架提供的一个工具类 SpringFactoriesLoader的支持。以及用到了Spring提供的条件注解 @Conditional,选择性的针对需要加载的 bean 进行条件过滤。

4.1.3 SpringFactoriesLoader

SpringFactoriesLoader其实和java中的SPI机制是一样的。但是不会像SPI一样一次性加载所有的类,而是根据key进行加载。其key是配置在META-INF/spring.factories配置文件中,根据key来加载对于的bean到Ioc容器中。

1
2
3
4
5
6
7
public abstract class SpringFactoriesLoader {
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

public SpringFactoriesLoader() {
}
}

SPI机制

Service provider interface
满足以下条件

  • 需要在classpath目录下创建一个 META-INF/services;

  • 在该目录下创建一个扩展点的全路径名:

    1、文件中填写这个扩展点的实现

    2、文件编码格式UTF-8

    3、ServiceLoader去进行加载

4.1.4 条件过滤Conditional的分析

通过条件过滤减少带有@Configuration注解类的数量,从而减少SpringBoot的启动时间。

@Conditional中的其它注解

  • @ConditionalOnBean(在存在某个bean的时候);
  • @ConditionalOnMissingBean(不存在某个bean的时候);
  • @ConditionalOnClass(当classpath可以找到某个类型的类时);
  • @ConditionalOnMissingClass(当classpath不能找到某个类型的类时);
  • @ConditionalOnResource(当前classpath是否存在某个资源文件);
  • @ConditionalOnProperty(当前jvm是否包含某个系统属性的某个值);
  • @ConditionalOnWebApplication(当前spring context是否是web应用程序)。

4.2 Starter

Starter相当于模块,能将模块所需要的依赖整合起来并对模块内的bean根据环境来进行自动配置。使用者只需要依赖相应功能的Starter,无需做过多的配置和依赖,SpringBoot 就能自动扫描并加载相应的模块。

  • 官方包 spring-boot-starter-xxx;
  • 第三方包 xxx-spring-boot-starter

4.3 Actuator

SpringBoot提供了spring-boot-start-actuator支持对SpringBoot应用的监控。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

4.3.1 endpoint

通过访问地址:http://localhost:8080/actuator

可以看到非常多的 Endpoint。 有一些 Endpoint 是不能访问的,涉及到安全问题。

开启所有的endpoint:(management.endpoints.web.exposure.include=* *)

  • health(健康检查):management.endpoint.health.show-details= always
  • Loggers(日志配置信息,针对每个package 对应的日志级别)
  • beans(IoC 容器中所有的 bean)
  • Dump(获取活动线程的快照)
  • Mappings(全部的 uri 路径,以及和控制器的映射关系)
  • conditions(当前所有的条件注解)
  • shutdown(关闭应用):management.endpoint .shutdown.enabled= true,注意不要开启,比较危险
  • Env(获取全部的环境信息)

4.3.2 Health的原理分析

应用健康状态的检查应该是监控系统中最基本的需求,所以我们基于 health 来分析一下它是如何实现的。
org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration类自动装配载入的,打开对应包下的spring.foctories文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.actuate.autoconfigure.AuditAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.CacheStatisticsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricsDropwizardAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricsChannelAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration

org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration

Actuator 中提供了非常多的扩展点,默认情况下提供了一些常见的服务的监控检查的支持。

  • DataSourceHealthIndicator
  • DiskSpaceHealthIndicator
  • RedisHealthIndicator