spring-boot相关具体示例代码,可以查看GitHub中spring-boot-demo
起步依赖 spring-boot-starter-xx
向项目中添加依赖是件富有挑战的事,需要什么库,哪个版本?它的Group和Artifact是什么,具体依赖的版本和项目中的其他依赖是否会发生冲突?
Spring boot 通过起步依赖为项目的依赖管理提供帮助。起步依赖其实就是特殊的Maven依赖(or Gradle),利用传递依赖解析,把常用库聚合在一起,组成了几个为特定功能而定制的依赖。
举个栗子,假设你正在用Spring MVC构造一个REST API,并将JSON (JavaScript Object Notation)作为资源表述。此外,你还想运用遵循JSR-303 规范的声明式校验,并使用嵌入式的Tomcat 服务器来提供服务。要实现以上目标,你在Maven或Gradle里至少需要以下8个依赖:
- org.springframework:spring-core
- org.springframework:spring-web
- org.springframework:spring-webmvc
- com.fasterxml.jackson.core:jackson-databind
- org.hibernate:hibernate-validator
- org.apache.tomcat.embed:tomcat-embed-core
- org.apache.tomcat.embed:tomcat-embed-el
- org.apache.tomcat.embed:tomcat-embed-logging-juli
再说了,你怎么知道这个列表是完整的?在一行代码都没写的情况下,离开始构建还有很长的路要走。
不过,如果打算利用Spring Boot的起步依赖,你只需添加Spring Boot的Web 起步依赖(org.springframework.boot:spring-boot-starter-web ),仅此一个。它会根据依赖传递把其他所需依赖引入项目里,你都不用考虑它们。
比起减少依赖数量,起步依赖还引入了一些微妙的变化。向项目中添加了Web起步依赖,实际上指定了应用程序所需的一类功能,这就是指定基于功能的依赖。因为应用是个Web应用程序,所以加入了Web起步依赖。
与之类似,如果应用程序要用到JPA 持久化,那么就可以加入jpa起步依赖。如果需要安全功能,那就加入security起步依赖。简而言之,不再需要考虑支持某种功能要用什么库了,引入相关起步依赖就行。
此外,Spring Boot 的起步依赖还把你从“需要这些库的哪些版本”这个问题里解放了出来。起步依赖引入的库的版本都是经过测试的,因此你可以完全放心,它们之间不会出现不兼容的情况。
AutoConfig
Spring Boot 的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring 配置应该用哪个,不该用哪个。
举个栗子,Spring Boot 自动配置要考虑的:
- Spring 的JdbcTemplate是不是在Classpath 里?如果是,并且有DataSource的Bean,则自动配置一个JdbcTemplate的Bean。
- Thymeleaf是不是在Classpath 里?如果是,则配置Thymeleaf的模板解析器、视图解析器以及模板引擎。
- Spring Security是不是在Classpath 里?如果是,则进行一个非常基本的Web安全设置。
每当应用程序启动的时候,Spring Boot 的自动配置都要做将近200个这样的决定,涵盖安全、集成、持久化、Web开发等诸多方面。所有这些自动配置就是为了尽量不让使用者自己写配置。
配置是Spring Framework的核心元素,必须要有东西告诉Spring如何运行应用程序。
在向应用程序加入Spring Boot 时,有个名为spring-boot-autoconfigure 的JAR 文件,其中包含了很多配置类。每个配置类都在应用程序的Classpath 里,都有机会为应用程序的配置添砖加瓦。这些配置类里有用于Thymeleaf的配置,有用于Spring Data JPA的配置,有用于Spiring MVC的配置,还有很多其他东西的配置,你可以自己选择是否在Spring 应用程序里使用它们。
所有这些配置如此与众不同,原因在于它们利用了Spring 的条件化配置,这是Spring 4.0引入的新特性。条件化配置允许配置存在于应用程序中,但在满足某些特定条件之前都忽略这个配置。
在Spring 里可以很方便地编写你自己的条件,你所要做的就是实现Condition接口,覆盖它的matches()方法。举例来说,下面这个简单的条件类只有在Classpath 里存在JdbcTemplate时才会生效:
|
|
当你用Java 来声明Bean的时候,可以使用这个自定义条件类:
|
|
在这个例子里,只有当JdbcTemplateCondition 类的条件成立时才会创建MyService这个Bean。也就是MyService Bean 创建的条件是Classpath 里有JdbcTemplate。否则,这个Bean的声明就会被忽略掉。
@EnableAutoConfiguration自动配置原理
通过@EnableAutoConfiguration启用Spring应用程序上下文的自动配置,这个注解会导入一个EnableAutoConfigurationImportSelector的类,而这个类会去读取一个spring.factories下key为EnableAutoConfiguration对应的全限定名的值。
这个spring.factories里面配置的那些类,主要作用是告诉Spring Boot这个stareter所需要加载的那些xxxAutoConfiguration类,也就是你真正的要自动注册的那些bean或功能。然后,我们实现一个spring.factories指定的类,标上@Configuration注解,一个starter就定义完了。
如果想从自己的starter种读取应用的starter工程的配置,只需要在入口类上加上如下注解即可:
|
|
读取spring.factories文件的实现
是通过org.springframework.core.io.support.SpringFactoriesLoader实现。
SpringFactoriesLoader的实现类似于SPI(Service Provider Interface,在java.util.ServiceLoader的文档里有比较详细的介绍。java SPI提供一种服务发现机制,为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要[3])。
SpringFactoriesLoader会加载classpath下所有JAR文件里面的META-INF/spring.factories文件。
其中加载spring.factories文件的代码在loadFactoryNames方法里:
|
|
通过org.springframework.boot.autoconfigure.AutoConfigurationImportSelector里面的getCandidateConfigurations方法,获取到候选类的名字List
|
|
其中,getSpringFactoriesLoaderFactoryClass()方法直接返回的是EnableAutoConfiguration.class, 代码如下:
|
|
所以,getCandidateConfigurations方法里面的这段代码:
|
|
会过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的全限定名对应的值。全限定名都使用如下命名方法:
|
|
SpringBoot中的META-INF/spring.factories(完整路径:spring-boot/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories)中关于EnableAutoConfiguration的这段配置如下:
|
|
当然了,这些AutoConfiguration不是所有都会加载的,会根据AutoConfiguration上的@ConditionalOnClass等条件,再进一步判断是否加载。
条件化注解 | 配置生效条件 |
---|---|
@ConditionalOnBean | 配置了某个特定Bean |
@ConditionalOnMissingBean | 没有配置特定的Bean |
@ConditionalOnClass | Classpath 里有指定的类 |
@ConditionalOnMissingClass | Classpath 里缺少指定的类 |
@ConditionalOnExpression | 给定的SpEL表达式计算结果为true |
@ConditionalOnJava | Java 的版本匹配特定值或者一个范围值 |
@ConditionalOnJndi | 参数中给定的JNDI位置必须存在一个 |
@ConditionalOnProperty | 指定的配置属性要有一个明确的值 |
@ConditionalOnResource | Classpath 里有指定的资源 |
@ConditionalOnWebApplication | 这是一个Web应用程序 |
@ConditionalOnNotWebApplication | 这不是一个Web应用程序 |
spring boot自动配置构建于spring的条件化配置之上,提供了众多带有@Conditional注解的配置类,根据条件决定是否要自动配置这些Bean。
DataSourceAutoConfiguration里嵌入了一个JdbcTemplateConfiguration类,自动配置了一个JdbcTemplate Bean :
|
|
JdbcTemplateConfiguration 使用了@Conditional注解,判断DataSourceAvailable- Condition条件是否成立——基本上就是要有一个DataSource Bean 或者要自动配置创建一个。假设有DataSource Bean ,使用了@Bean注解的jdbcTemplate() 方法会配置一个JdbcTemplate Bean 。这个方法上还加了@ConditionalOnMissingBean注解,因此只有在不存在JdbcOperations(即JdbcTemplate实现的接口)类型的Bean时,才会创建JdbcTemplate Bean。
自动配置会做出以下配置决策,它们和之前的例子息息相关。
- 因为Classpath 里有Hibernate (Spring Data JPA 传递引入的)的实体管理器,所以自动配置会配置与Hibernate 相关的Bean,包括Spring 的LocalContainerEntityManager- FactoryBean 和JpaVendorAdapter。
- 因为Classpath 里有Spring Data JPA ,所以它会自动配置为根据仓库的接口创建仓库实现。
- 因为Classpath 里有Thymeleaf,所以Thymeleaf会配置为Spring MVC的视图,包括一个Thymeleaf的模板解析器、模板引擎及视图解析器。视图解析器会解析相对于Classpath 根目录的/templates目录里的模板。
- 因为Classpath 里有Tomcat (通过Web起步依赖传递引用),所以会启动一个嵌入式的Tomcat容器,监听8080 端口。
由此可见,Spring Boot 自动配置承担起了配置Spring 的重任,因此你能专注于编写自己的应用程序。
命令行界面
这是Spring Boot的可选特性,借此你只需写代码就能完成完整的应用程序,无需传统项目构建。
创建一个文件 app.groovy
:
|
|
然后执行shell命令:
|
|
在浏览器中打开 localhost:8080 预览结果:
|
|
Spring Boot CLI让只写代码即可实现应用程序成为可能。Spring Boot CLI 利用了起步依赖和自动配置,让你专注于代码本身。不仅如此,你是否注意代码里没有import ?CLI如何知道RequestMapping和RestController来自哪个包呢?说到这个问题,那些类最终又是怎么跑到Classpath 里的呢?
说得简单一点,CLI能检测到你使用了哪些类,它知道要向Classpath 中添加哪些起步依赖才能让它运转起来。一旦那些依赖出现在Classpath 中,一系列自动配置就会接踵而来,确保启用DispatcherServlet和Spring MVC,这样控制器就能响应HTTP请求了。
Spring Boot CLI 是Spring Boot 的非必要组成部分。虽然它为Spring 带来了惊人的力量,大大简化了开发,但也引入了一套不太常规的开发模型。要是这种开发模型与你的口味相去甚远,那也没关系,抛开CLI,你还是可以利用Spring Boot 提供的其他东西。
Actuator
Spring Boot的最后一块“拼图”是Actuator,其他几个部分旨在简化Spring 开发,而Actuator则要提供在运行时检视应用程序内部情况的能力。安装了Actuator就能窥探应用程序的内部情况了,包括如下细节:
- Spring 应用程序上下文里配置的Bean
- Spring Boot 的自动配置做的决策
- 应用程序取到的环境变量、系统属性、配置属性和命令行参数
- 应用程序里线程的当前状态
- 应用程序最近处理过的HTTP请求的追踪情况
- 各种和内存用量、垃圾回收、Web请求以及数据源用量相关的指标
Actuator通过Web端点和shell 界面向外界提供信息。如果要借助shell 界面,你可以打开SSH(Secure Shell),登入运行中的应用程序,发送指令查看它的情况。
总结
从本质上来说,Spring Boot就是Spring,它做了那些没有它你自己也会去做的SpringBean配置。谢天谢地,幸好有Spring Boot,你不用再写这些样板配置了,可以专注于应用程序的逻辑,这些才是应用程序独一无二的东西。