SpringBoot 的关键词是“约定俗成”,它根据长久以来的 Spring 开发配置经验,整理出一套适用、广泛、你们都承认的配置方案。因此 SpringBoot 的学习过程当中心态必定要放宽,不必太去钻牛角尖,重点是配置和自定义...java
Spring Boot 简化了基于Spring的应用开发,为Spring平台及第三方库提供了开箱即用的设置,你只须要“run”就能建立一个独立的,产品级的 Spring 应用。git
SpringBoot 的 Startsers 是以一个依赖描述符的集合,包含了不少搭建,快速运行项目所须要的依赖,并提供一致的,可管理传递性的依赖集。你能够获取全部 Spring 及相关技术的一站式服务,而不须要翻阅示例代码,拷贝粘贴大量的依赖描述符。全部官方的 starters 遵循类似的命名模式:spring-boot-starter-* 。github
SpringBoot 的 Auto-configuration 设计成能够跟 Starters 一块儿很好的使用,AutoConfiguration 会根据你所依赖的 jar 包,会尽最大努力去自动配置你的应用。web
Spring Boot 每次发布都关联一个 Spring 框架的基础版本,因此强烈建议你不要本身指定Spring版本。spring
spring-boot-devtools 是 SpringBoot 中内置的一个开发者工具 ,用于自重启,功能固然没有 Jrebel 那么强大,但正常的开发也差很少够用了。spring-boot-devtools 默认检测 classpath 路径下的文件,只要目录下的文件有变更,它就会自动重启。chrome
自动重启跟 LiveReload 能够一块儿很好的工做。浏览器
若是你使用 JRebel,自动重启将禁用以支持动态类加载。缓存
<!-- 一、在运行一个完整的,打包过的应用时(Java -jar),会禁用开发者工具。 二、防止 devtools 传递到项目中的其余模块,须要设置依赖级别 optional 三、只要 classpath 下的文件有变更(Eclipse 中文件保存就能重启,IDEA 中须要 Build ——> Build Project),系统就会自动重启。 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
spring: devtools: restart: # devtools 排除不须要检测的资源 和 增长额外须要监测的资源 exclude: application.yml additional-paths: src/main/webapp # 是否重启,若是设置为false禁用,依旧会初始化重启类加载器,但它不会监控文件变化 enabled: true # 触发器文件,只有在修改该文件的时候,才能触发工程重启 #trigger-file: trigger-file livereload: # 内嵌的 LiveReload 服务器,能够在资源改变时,触发浏览器更新,禁用设置为false enabled: true
LiveReload 是一个 spring-boot-devtools 模块中的内嵌服务器,它能够在资源改变时触发浏览器更新,LiveReload 浏览器扩展的谷歌插件下载地址:https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei ,若是不想使用该功能的话,能够在 application.yml 中禁用,如上。这工具仍是很值得推荐的,能够大大的节约开发时间。服务器
SpringApplication 姑且称它为 SpringBoot 的引导类吧!它将为咱们建立正确类型的 Spring 上下文 ApplicationContext 。默认状况下,SpringApplication 根据咱们开发的是否为web应用(能够手动指定是否为 Web 应用)决定使用 AnnotationConfigApplicationContext 或 AnnotationConfigEmbeddedWebApplicationContext。除此以外,SpringApplication 还有一些无关痛痒的设置,好比:是否打印 Banner 这些的。app
经常须要在 SpringApplication 加载前或者退出后作一些相关的操做,好比初始化一些信息,关闭一些流、文件 什么的。怎么实现呢?
实现 CommandLineRunner 或者 ApplicationRunner 接口能够在 SpringApplication 启动后,run() 方法运行前执行一些特殊的代码。
实现 ExitCodeGenerator 接口能够在 Application 退出后返回特殊的特征码,用于 SpringApplication.exit() 时使用。
实现 DisposableBean 接口,用于在 SpringApplication 退出后(SpringApplication.exit())实现本身的一些逻辑,或者使用 @PreDestroy 注解。
@Component public class ApplicationArgumentsBean implements CommandLineRunner, ApplicationRunner, ExitCodeGenerator, DisposableBean { private static final Logger logger = LoggerFactory.getLogger(ApplicationArgumentsBean.class); /** * 若是运行的时候使用 java -jar *.jar --debug logfile.txt * 则:debug = true,files=["logfile.txt"] * * @param args */ @Autowired public ApplicationArgumentsBean(ApplicationArguments args) { boolean debug = args.containsOption("debug"); List<String> files = args.getNonOptionArgs(); } @Override public void run(ApplicationArguments args) throws Exception { logger.info("重写 ApplicationRunner 的 run 方法:{}", args.containsOption("debug")); } @Override public void run(String... args) throws Exception { logger.info("重写 CommandLineRunner 的 run 方法:{}", args); } @Override public int getExitCode() { return 0; } @Override public void destroy() throws Exception { logger.info("重写 DisposableBean 的 destroy 方法,用于在 SpringApplication 退出后执行一些操做"); } @PreDestroy public void predestroy() { logger.info("使用 @PreDestroy 用于在 SpringApplication 退出后执行一些操做"); } }
@EnableAutoConfiguration @Configuration @ComponentScan public class Example { public static void main(String[] args) { // 咱们须要将Example.class做为参数传递给run方法,以此告诉SpringApplication谁是主要的Spring组件 SpringApplication app = new SpringApplication(Example.class); // 手动调用setWebApplicationType() 指定为 web 应用 app.setWebApplicationType(WebApplicationType.SERVLET); // 设置打印 Banner 的方式 app.setBannerMode(Banner.Mode.LOG); // 是否将启动时的命令行属性添加到 Environment app.setAddCommandLineProperties(true); ConfigurableApplicationContext run = app.run(args); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { SpringApplication.exit(run, (ExitCodeGenerator) run.getBean("applicationArgumentsBean")); } }); } }
tips:建议将应用的main类放在其余类所在包的顶层(root package)。
SpringBoot 的默认配置文件是 application.yml 或者 application.properties,若是想要引入其余的 properties 文件 或者 yml 文件,该怎么操做呢?
properties 文件引入较为简单,跟 Spring 同样。在配置类上使用 @PropertySource 注解引入,在其余地方使用 @Value 注解读取。
咱们先从 SpringBoot 的默认配置文件 application.yml 文件聊起,application.yml 的文件内容,是能够经过 @Value 的方式读取到的,好比 @Value("${server.port}") 这样。究其缘由的话,应该是 SpringBoot 底层把 ApplicationContext 注册进 PropertySourcesPlaceholderConfigurer 致使的吧!
那么咱们自定义的 yml 文件要怎么引入呢?看了网上的一些教程,不少人推荐用以下这种方式:
@Bean public PropertySourcesPlaceholderConfigurer properties() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); yaml.setResources(new ClassPathResource("my.yml")); configurer.setProperties(yaml.getObject()); return configurer; }
这种方式,确实能够用,经过这种方式把 yml 文件加载到 PropertySourcesPlaceholderConfigurer 后,经过 @Value 方式读取到属性值。可是!可是!原来的 application.yml 中的 @Value 属性全获取不到了,我猜测是否是 Bean 覆盖致使的,我试着手动把 application.yml 和 my.yml 都加载进 PropertySourcesPlaceholderConfigurer ,以及使用 @Primary 注解,结果都无论用!因此就放弃了这种作法。
那要怎么加载咱们自定义的 yml 文件呢 ?经过 YamlPropertiesFactoryBean 或者 YamlMapFactoryBean 类:
@Test public void test3() { YamlPropertiesFactoryBean yml = new YamlPropertiesFactoryBean(); yml.setResources(new ClassPathResource("my.yml")); Properties properties = yml.getObject(); Iterator<Map.Entry<Object, Object>> iterator = properties.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Object, Object> entry = iterator.next(); logger.info("YamlPropertiesFactoryBean 读取的配置文件内容是:{}-{}", entry.getKey(), entry.getValue()); } logger.info("--------华丽的分割线------------"); YamlMapFactoryBean yamlMapFactoryBean = new YamlMapFactoryBean(); yamlMapFactoryBean.setResources(new ClassPathResource("my.yml")); Map<String, Object> map = yamlMapFactoryBean.getObject(); Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, Object> entry = it.next(); logger.info("YamlPropertiesFactoryBean 读取的配置文件内容是:{}-{}", entry.getKey(), entry.getValue()); } }
另外须要说起的是 SpringBoot 还提供了 @ConfigurationProperties(prefix = "spring.datasource") 注解,将 yml 文件中的属性直接转换成 Bean 中的属性(前提是有 set 方法),并且属性的匹配很宽松,采用 Relaxed 绑定,以 firstName 举例(可匹配firstName、first-name、first_name、FIRST_NAME)。以后再在启动类中使用 @EnableConfigurationProperties(JavaConfig.class) 使之生效。
不一样于 Spring 中的单元测试 —— 采用 @RunWith(SpringJUnit4ClassRunner.class) 和 @ContextConfiguration(locations = "classpath:applicationContext.xml"),SpringBoot 中使用 @RunWith(SpringRunner.class) 和 @SpringBootTest(classes = Example.class) 。
Spring Boot的@*Test注解会自动搜索主配置类,即便你没有显式定义它。(不过仍是建议显示配置下)
你可使用@SpringBootTest的webEnvironment属性定义怎么运行测试:
MOKE -- 默认,加载WebApplicationContext,并提供一个mock servlet环境,使用该注解时内嵌servlet容器将不会启动。若是classpath下不存在servlet APIs,该模式将建立一个常规的non-web ApplicationContext。
RANDOM_PORT -- 加载EmbeddedWebApplicationContext,并提供一个真实的servlet环境。使用该模式内嵌容器将启动,并监听在一个随机端口。
DEFINED_PORT -- 加载EmbeddedWebApplicationContext,并提供一个真实的servlet环境。使用该模式内嵌容器将启动,并监听一个定义好的端口(好比application.properties中定义的或默认的8080端口)。
NONE -- 使用SpringApplication加载一个ApplicationContext,但不提供任何servlet环境(不论是mock仍是其余)。
@RunWith(SpringRunner.class) @SpringBootTest(classes = Example.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class Test1 { @Autowired private TestRestTemplate testRestTemplate; @LocalServerPort // 用于注入测试用例实际使用的端口 private Integer localServerPort; static { //for localhost testing only javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() { public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) { if (hostname.equals("localhost")) { return true; } return false; } }); } @Test public void Test1() { System.out.println("测试启动的随机端口是" + localServerPort); Area area = testRestTemplate.getForObject("/area/list", Area.class); System.out.println(area.getInfo()); } }
Spring Boot 提供一个 @MockBean 注解,可用于为 ApplicationContext 中的bean定义一个Mockito mock,你可使用该注解添加新beans,或替换已存在的bean定义。该注解可直接用于测试类,也可用于测试类的字段,或用于@Configuration注解的类和字段。当用于字段时,建立mock的实例也会被注入。Mock beans每次调用完测试方法后会自动重置。
import static org.mockito.BDDMockito.*; @RunWith(SpringRunner.class) @SpringBootTest(classes = Example.class) public class Test2 { @MockBean private UserSercice userSercice; @Test public void test2(){ User user = new User("111111", "张三", "15980292662", 23, 1); given(this.userSercice.getUserByUuid("111111")).willReturn(user); User userByUuid = userSercice.getUserByUuid("111111"); System.out.println(userByUuid); } }
tips:Spring测试框架在测试过程当中会缓存应用上下文,所以,只要你的测试共享相同的配置(不论是怎么发现的),加载上下文的潜在时间消耗都只会发生一次。
一个完整的Spring Boot Starter 可能包含如下组件:
-- Autoconfigure模块,包含自动配置类的代码。好比一些能够直接使用的 Bean 定义。
-- Starter模块,提供 Autoconfigure模块 及其余有用的 jar 依赖,简而言之,添加本 Starter 就能开始使用该library。
自动配置能够捆绑在外部的jar包中,由Spring Boot提取 ———— Spring Boot会检查你发布的jar中是否存在META-INF/spring.factories文件,该文件中以EnableAutoConfiguration为key的属性列出你的配置类。
自动配置能够关联一个 "Starter",用于提供 auto-configuration 的代码及须要引用的libraries。
Starter 实际上是一个空的模块,它的惟一目的其实就是提供一个必要的依赖关系。
官方 Auto-configuration 示例:https://github.com/snicoll-demos/spring-boot-master-auto-configuration
演示源代码地址:https://github.com/JMCuixy/Boot