Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。有了它,你可以更加敏捷地开发Spring应用程序,专注于应用程序的功能,而不用在Spring的配置上多花功夫,甚至完全不用配置。
Spring Boot提供了四个核心功能:
每一个特性都能以自己的方式简化Spring应用的开发。
首先,我们看一下如何开发一个Spring Boot的Hello World程序。
Spring Boot工程与普通的maven/gradle工程无异,只是在依赖中需要添加对Spring Boot的依赖。不过,有一种更简单的方式是直接用Spring官网的Spring Initializer来生成项目骨架。
打开:https://start.spring.io/,填写工程类型、语言、Spring Boot版本号及构件信息,如下图:
点击Generate Project后,会自动下载完整的项目骨架下来,并且在pom.xml中已经帮你依赖好了所需依赖的构件。
pom.xml如下:
4.0.0
com.example
demo
0.0.1-SNAPSHOT
jar
demo
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
2.1.0.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
同时可以看到主类DemoApplication.java:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
运行该类,看到如下输出即为启动成功:
至此,我们便完成了一个最简项目的搭建和运行。
DemoApplication类在我们的Spring Boot应用中有两个作用:配置和启动引导。
我们可以看到在主类上,有一个@SpringBootApplication注解,这个注解开启了Spring的组件扫描和Spring Boot的自动配置功能,查看其源码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
@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 {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class>[] scanBasePackageClasses() default {};
}
可以看到它是把Spring的@Configuration、Spring的@ComponentScan和Spring Boot的@EnableAutoConfiguration组合到了一起。与@Configuration类似,我们可以通过scanBasePackages或者scanBasePackageClasses属性指定组件扫描的包。
SpringApplication.run(DemoApplication.class, args)开启了项目的运行,详细的源码分析可以参考这篇文章:https://blog.csdn.net/doegoo/article/details/52471310。
在传统的Spring应用中,使用某个特定的功能时,需要自己手动添加对应的依赖,不仅麻烦,而且容易因为不同构件版本之间的兼容问题而出错。
Spring Boot提供了众多的起步依赖,降低项目依赖的复杂度。起步依赖本质上是一个Maven项目对象模型,定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
在上面的例子中,期望开发一个Web应用,我们依赖了spring-boot-starter-web,由它去依赖其他的构件。
Spring Boot自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。
例如:Spring的JdbcTemplate是不是在Classpath下?如果是,并且有DataSource Bean,那么就自动配置一个JdbcTemplate的Bean等等。
自动配置的原理是利用了Spring4.0引入的条件化配置的特性,条件化配置允许配置存在于应用程序中,但在满足特定的条件之前都忽略这些配置。
以Spring Boot下的国际化为例,其自动配置类在org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration类中。大致源码如下:
package org.springframework.boot.autoconfigure.context;
@Configuration
@ConditionalOnMissingBean(
value = {MessageSource.class},
search = SearchStrategy.CURRENT
)
@AutoConfigureOrder(-2147483648)
@Conditional({MessageSourceAutoConfiguration.ResourceBundleCondition.class})
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
private static final Resource[] NO_RESOURCES = new Resource[0];
public MessageSourceAutoConfiguration() {
}
@Bean
@ConfigurationProperties(
prefix = "spring.messages"
)
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
...
可以看到这个配置类上使用了条件化注解@ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT),表示在当前上下文中不存在MessageSource Bean时,这个配置类才生效。然后根据其messageSource(MessageSourceProperties)方法,自动创建一个MessageSource Bean。
Spring Boot内置的配置和bean都会有一些自己的配置项,有些配置项Spring Boot已经提供了默认值。比如Spring Boot内置的tomcat启动时所监听的HTTP端口号,它是读取的server.port配置项;以及上面我们所提到的国际化配置,它是读取的spring.messages.basename所指定的路径下的国际化文件,默认值是messages。很多时候我们期望自定义这些属性值,或者增加我们自己的一些配置项,该怎么做呢?
实际上,Spring Boot里我们有多种方式可以设置属性值。包括以下几种:
这个列表按照优先级递减排序,也就是说,命令行参数会覆盖在其他地方配置的属性。
application.properties和application.yml是Spring Boot默认就会加载的,不需要手动指定为属性源。它们可以放在以下位置:
这个列表同样按照优先级递减排序。
举个例子,我们通过@PropertySource来配置自定义属性,修改Spring Boot的内置tomcat启动时所监听的HTTP端口号:
package com.example.demo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
/**
* @author nlz
*/
@SpringBootApplication(scanBasePackages = {"com.example.demo.controller", "com.example.demo.dao"})
@Slf4j
@PropertySource(value = "file:./demo.properties")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
如图,我们在DemoApplication上配置了@PropertySource注解,从同目录下的demo.properties里读入配置,根据优先级规则,这个配置应该覆盖Spring Boot的内置默认配置。
demo.properties配置如下:
server.port=9090
然后我们运行Spring Boot应用,看到如下输出:
2019-09-28 09:59:20.126 INFO 9416 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2019-09-28 09:59:20.126 INFO 9416 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2019-09-28 09:59:20.146 INFO 9416 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-09-28 09:59:20.146 INFO 9416 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-09-28 09:59:20.286 INFO 9416 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2019-09-28 09:59:20.326 INFO 9416 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9090 (http) with context path ''
2019-09-28 09:59:20.336 INFO 9416 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 4.817 seconds (JVM running for 6.322)
可以看到,现在Tomcat的监听端口号变成了我们所配置的9090。
熟悉Spring的JUnit单元测试的同学应该都很清楚,要运行Spring JUnit单元测试,我们需要在测试类上加@RunWith(SpringJUnit4ClassRunner.class),SpringJUnit4ClassRunner会为JUnit测试加载Spring应用程序上下文。同时我们需要通过@ContextConfiguration指定配置class。一个典型的样例如下:
package com.huawei.nlz.snippets.cbb;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CbbConfig.class, loader = AnnotationConfigContextLoader.class)
public class SpringJUnitContext {
}
@ContextConfiguration在加载Spring应用上下文的过程中做了很多事情,但是它不能完整的加载Spring Boot。Spring Boot下,我们用@SpringBootTest替代之。代码如下:
import com.example.demo.DemoApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class DemoTest {
@Test
public void test(){
}
}
运行test方法,会提示JUnit测试执行成功。
Spring Boot应用可以被打包成jar文件直接运行,如果是Web应用,也可以打包成war包丢进传统的应用服务器(如Tomcat等)中运行。由于Spring Boot在应用程序里嵌入了一个Servlet容器,所以Spring Boot开发的Web应用也可以打包成jar包直接运行,而不用部署到传统的Java应用服务器里。
举一个Spring Boot开发RESTful接口的例子:
在前面我们创建好的工程中,加入一个RestController如下:
package com.example.demo;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class DemoService {
@RequestMapping(method = RequestMethod.POST, path = "/rest/evaluate")
public Map query(@RequestBody Map params) {
Map obj = new HashMap(5) {
{
put("price", 19.25);
put("amount", 17891);
put("commodity", "iPhone X");
}
};
return obj;
}
}
然后我们对工程执行mvn clean package,打出jar包,然后通过java -jar命令运行起该jar包,通过postman访问本地8080端口:
可以访问成功。
在我们的pom.xml中,可以看到使用了插件spring-boot-maven-plugin,这个插件的作用是在打jar包时打出一个超级jar,把应用程序所有依赖都打进这个jar中,同时为jar添加一个描述文件,其中的内容能让你用java -jar运行应用程序。我们打开jar包的BOOT-INF/lib目录,可以看到依赖的jar包都被打进来了:
至此,我们完成了对Spring Boot的一个概要的了解,掌握了如何创建Spring Boot应用,Spring Boot的核心特性以及如何打包部署运行Spring Boot程序。快去开发你的Spring Boot应用吧~~