原创文章,转载请注明
不定期挑选一些spring-boot的文档进行翻译,学习之余,也为他人提供一点帮助。
其实现在已经深深的成为spring的粉丝了,告诉我,还有什么理由不用spring全家桶。还有什么理由不用spring的第二春 spring-boot+spring-cloud
我会对一些地方做出自己的解释,通过(译者注)这样的形式。
这里放上我所翻译版本的原文,以便我翻译有误的话方便大家查看原文http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#using-boot-devtools-restart-triggerfile
spring-boot并没有特定什么代码结构,但是,有些经验之谈。
一个类不声明package,那么会被认为在default package之中。这种行为是不提倡的,应该杜绝。这样做的话,在class或jar包中的class,使用@ComponentScan
,@EntityScan或者
注释时会引发特殊的错误。@SpringBootApplication
我们建议使用java倡导的包的命名规范,比如com.example.project
我们建议main application类位于所有类之上的根package之中。因为@EnableAutoConfiguration
注释往往在main application类之上,而且暗中之名了它注释类的包为“基础包”(译者注:这是什么意思呢,就是说该包及以下的包中的类才会纳入spring进行管理)。比如说,如果你的应用为jpa应用,那么,@EnableAutoConfiguration
注释类所在的包之下的包,将被spring寻找,看是否有@Entity
注释的类。
因此,这样做也使得@ComponentScan
注释不必设置basePackage
属性(译者注:该属性就是指定spring扫描的包)。如果你的main application类在根package下,那么你也可以用@SpringBootApplication
注释(译者注:由于java7引入了组合注释,@SpringBootApplication
就是组合注释,它包含了@ComponentScan
注释)。
下面是典型的结构
com +- example +- myproject +- Application.java | +- domain | +- Customer.java | +- CustomerRepository.java | +- service | +- CustomerService.java | +- web +- CustomerController.java
Application.java
需要声明 main
方法,使用 @Configuration
注释 (译者注:@Configuration是spring4所提倡的java配置方式,spring3时代提倡的是注解配置,再之前只有xml配置)
package com.example.myproject;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
尽管通过SpringApplication.run()
的方式也可以用xml作为配置,但是我们还是建议基于java的方式配置。通常建议main
方法的类作为主要配置类标注上@Configuration
注释。
网上的大部分例子都是基于xml配置的,如果可能,请尝试基于java的配置方式来进行替换。你可以在本手册中搜索enable*
注释来作为你的着手点。
你不需要把所有的配置放在一个@Configuration
类中,@Import
注释用来引进额外的配置类。当然,你也可以使用@ComponentScan
注释来引入其他的spring管理的类,自然也包括了其他@Configuration类。
15.2 导入xml配置
如果你不得不引入xml配置,那么我们建议你还是以@Configuration
类开始,在其上使用@ImportResource
注释来引入xml配置文件。
spring-boot试图通过你引入的jar依赖来自动为你配置。比如,如果在你的classpath下引入了HSQLDB
的jar包,那么你不需要手工配置它的数据库连接,我们会自动把它配置成内存数据库(译者注:其实实现原理为spring4引入的@conditional注释)。
自动配置需要在你的任一
注释。@Configuration
类上使用@EnableAutoConfiguration或@SpringBootApplication
你只需要添加@EnableAutoConfiguration
到一个
上,我们建议添加到主要的@Configuration
类
。@Configuration
类上
自动配置是非侵入式的,你可以在任何时候替换成你自己的配置。比如你增加了你自己的dataresource bean,那么之前自动配置的内存数据库将作废。
如果你想要知道自动配置了哪些东西,为什么被配置。你可以通过--debug
模式运行你的应用(译者注:其实就是在你的application.yml中增加debug:true这个配置)。这样将在控制台打印出debug信息以及自动配置的信息。
如果你发现你并不需要某些自动配置,那么你可以使用@EnableAutoConfiguration
注释的exclude属性去掉它们。
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.jdbc.*;
import org.springframework.context.annotation.*;
@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}
如果你要取消的自动配置类不在classpath下,你需要使用该注释的 excludeName
属性,指定全路径名。最后,你也可以使用 spring.autoconfigure.exclude
属性来取消一系列的自动配置类。 (译者注:同样是在application.yml中配置)。
你可以即在注释里面配置,也可以在配置文件中配置(译者注:application.yml或者application.propertites)。
你可以自由选择用spring框架的任意方式来实现spring管理和依赖注入。简单的,使用@ComponentScan注释来扫描bean,用
@Autowired
来注入。
如果你像15节中建议的那样构建你的的代码(根package中放入main application类),那么你可以使用
@ComponentScan注释
而不指定任何属性。你所有被@Component
,@Service
,@Repository
, @Controller
等注释的类将被自动注册为spring管理的bean。
下面的例子为@Service注释的类通过构造器注入所需要的
RiskAssessor
实例。
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
@Autowired
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
注意如何使用构造器注入,被注入的实例需要被标记为final,意为后续不能被修改。
18.使用@SpringBootApplication注释
许多spring-boot开发者总是在main class上加上@Configuration
,@EnableAutoConfiguration
和@ComponentScan
注释。因为这些注释使用概率很高(特别是遵循了以上的最佳实践经验)。spring-boot提供了便捷的@SpringBootApplication来替代。
@SpringBootApplication等同于
@Configuration
,@EnableAutoConfiguration
和@ComponentScan
注释,属性则为他们的默认值。
package com.example.myproject;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication
也提供了别名来定制化 ,@EnableAutoConfiguration
和@ComponentScan
注释
的属性
19.运行你的应用
将你的应用打包成jar并且内嵌http服务的最大优势之一是你可以直接运行你的应用。debug同样非常容易。不需要额外的IDE插件。(译者注:想必大家都深有体会,代码开发的时候不再需要在myeclipse中配置tomcat,各种发布,启动服务。现在直接run或者debug main class就可以了,加大了开发效率。另外部署的时候直接双击运行jar就可了,不再需要安装tomcat等web容器,不再需要配置环境变量)。
这里只涉及到打包为jar包,如果你想打包成war包,需要参考你使用的web容器和IDE的文档。
这个就不翻译了,没什么参考价值,基本都是用myeclise吧。
如果你使用spring-boot Maven或者Gradle插件打包可执行jar,你可以通过java -jar
命令运行你的应用,比如:
$ java -jar target/myproject-0.0.1-SNAPSHOT.jar
$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n \ -jar target/myproject-0.0.1-SNAPSHOT.jar
略
略
因为spring-boot应用是纯粹的java应用,因此它也很好的支持JVM热插拔。但是JVM热插拔在字节码的改动上多少有些限制(译者注:比如你改变了方法名,改变了方法返回值,改变了方法参数等)。为了解决这些问题,有一些额外的解决方案JRebel和Spring Loaded项目。spring-boot-devtools
可以快速重启spring-boot达到解决目的。(译者注:该方法在你对字节码进行任何修改,或者配置文件修改后,会立即重启spring-boot,达到改变马上生效的效果。目前发现端口号不能马上生效,可能也应为笔者应用了spring-cloud的服务注册和配置中心的缘故,尚未做考究)。
接下来的第20章将详细介绍热插拔以及如何使用Developer tools。
spring-boot包含一些列的额外工具使你的开发体验得到一点加强。添加spring-boot-devtools
的支持,只需要加入如下依赖:
Maven
org.springframework.boot
spring-boot-devtools
true
dependencies {
compile("org.springframework.boot:spring-boot-devtools")
}
developer tool在完全打包的应用中默认不起作用。如果你用 java -jar启动应用,或者使用一个指定的类加载器,那么该应用将被视为生产环境应用,从而developer tool不起作用。maven中在依赖中标注optional是一个不错的开发经验,防止将developer tools关联到你其他的应用模块(译者注:意思是其他应用依赖你现有的应用,当然你不希望developer tools起效果,这就将防止这个问题)。gradle没有optional的支持,所以你需要同时关注propdeps-plugin
20.1默认属性
spring-boot提供若干lib来通过缓存提高性能。比如,模板引擎提供模板缓存防止重复解析编译好的模板。同样的,当通过http请求静态文件时,同样设置了reponse的缓存相关头信息。(译者注:这样就能使用304状态了)
和缓存对生产环境有益相对,在开发环境就极为不友好。它会使你看不到你刚做的修改。因此,spring-boot-devtools默认会禁止那些缓存。
缓存设置一般在application.properties
文件中。比如,提供了spring.thymeleaf.cache
属性。相比手动进行配置,spring-boot-devtools自动地为开发环境提供了缓存配置。
完整的属性清单,请看DevToolsPropertyDefaultsPostProcessor类
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.devtools.env;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.devtools.restart.Restarter;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
/**
* {@link EnvironmentPostProcessor} to add properties that make sense when working at
* development time.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.3.0
*/
@Order(Ordered.LOWEST_PRECEDENCE)
public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor {
private static final Map PROPERTIES;
static {
Map properties = new HashMap();
properties.put("spring.thymeleaf.cache", "false");
properties.put("spring.freemarker.cache", "false");
properties.put("spring.groovy.template.cache", "false");
properties.put("spring.mustache.cache", "false");
properties.put("server.session.persistent", "true");
properties.put("spring.h2.console.enabled", "true");
properties.put("spring.resources.cache-period", "0");
properties.put("spring.resources.chain.cache", "false");
properties.put("spring.template.provider.cache", "false");
properties.put("spring.mvc.log-resolved-exception", "true");
properties.put("server.jsp-servlet.init-parameters.development", "true");
PROPERTIES = Collections.unmodifiableMap(properties);
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
if (isLocalApplication(environment) && canAddProperties(environment)) {
PropertySource> propertySource = new MapPropertySource("refresh",
PROPERTIES);
environment.getPropertySources().addLast(propertySource);
}
}
private boolean isLocalApplication(ConfigurableEnvironment environment) {
return environment.getPropertySources().get("remoteUrl") == null;
}
private boolean canAddProperties(Environment environment) {
return isRestarterInitialized() || isRemoteRestartEnabled(environment);
}
private boolean isRestarterInitialized() {
try {
Restarter restarter = Restarter.getInstance();
return (restarter != null && restarter.getInitialUrls() != null);
}
catch (Exception ex) {
return false;
}
}
private boolean isRemoteRestartEnabled(Environment environment) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment,
"spring.devtools.remote.");
return resolver.containsProperty("secret");
}
}
使用了spring-boot-devtools
的应用在文件或classpath改变的时候会自动重启。这对于使用IDE来说非常有用,可以很快对修改进行反馈。默认的,任何classpath下的文件夹都被监控。注意,某些特定的资源,比如静态资源,模板的改变将不会重启。
DevTools监控classpath下的资源,触发重启的唯一方式是classpath更新。何时更新classpath跟你的IDE有关。比如,eclipse更新classpath的时机是保存修改了的文件。IntelliJ IDEA则是在编译项目的时候Build → MakeProject
。
Maven,Gradle也会引起重启
LiveReload也会引起重启
SpringApplication.setRegisterShutdownHook(false)
后将不会重启(译者注:shutdownHook是销毁spring管理bean的方式之一)
以下类的改变不会引起重启spring-boot
, spring-boot-devtools
,spring-boot-autoconfigure
,spring-boot-actuator
, andspring-boot-starter
重启VS重载
spring-boot的重启技术是通过两个类加载器实现的。不会改变的类(比如第三方引入的类)被base classloader加载。你正在开发的类被restart classloader加载。应用重启的时候,restart classloader将卸载并新建。这意味着比冷启动快很多,因为base classloader没有改变。(译者注:由于base classloader没有变,因此不用重新加载类。只有restart classloader将修改的类重新加载,因此损耗也在这里。)
如果你觉得这种方法不够快,或则遇到了问题,那么可以考虑使用JRebel。它是通过重新编译classes再load,这样跟可靠。Spring Loaded是另外一种选择。
特定的资源的改变不会触发重启。比如Thymeleaf模板。默认的以下路径下的资源的改变不会触发重启/META-INF/maven
,/META-INF/resources
,/resources
,/static
,/public
或者/templates,它们只会触发重载。如果你想定制化哪些资源不会触发重启,可以使用
spring.devtools.restart.exclude
配置,比如排除/static
和/public
spring.devtools.restart.exclude=static/**,public/**
spring.devtools.restart.additional-exclude
你可能想在classpath之外的路径监控文件的变化,使得触发重启或重载。要这样做的话,你需要使用spring.devtools.restart.additional-paths
来新增监控变化的路径。你可以使用上面提到的spring.devtools.restart.exclude
来排除额外的路径触发重启,而只会触发重载。
你可能不想要重启,你可以通过spring.devtools.restart.enabled
来取消。大多数情况下在
application.properties文件中配置。(这仍然会初始化restart classloader,只是他不再监听文件的变化)
如果你需要完全禁止重启(译者注:包括restart classloader),比如它和其它lib冲突。那么你可以在调用SpringApplication.run(…)
之前设置一个System的属性,比如
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(MyApp.class, args);
}
如果你的开发工具会不停地编译,你可能更希望在特定的时间来重启。这个时候你就可以使用触发文件,当你想要重启检查的时候就需要修改该文件。改变这个文件只会重启检查,只有当你做了实质的事情后才会触发重启(译者注:改变这个文件只会触发检查,如果检查出classpath下没有改变,那么是不会重启的)。触发文件可以手动修改,也可以通过开发工具的插件。
触发文件属性为为spring.devtools.restart.trigger-file(译者注:这里需要配置文件路径,也可以使用*号等正则匹配,默认根目录为项目claspath,任何文件均可)
你可能想把spring.devtools.restart.trigger-file
作为全局变量配置,这样所有项目都可以通过该文件触发重启。(译者注:全局变量的配置方法见下文)
(译者注:建议最好设置一个触发文件,因为你只要在方法中改代码,不去修改大的东西,新增或删除类,方法等。你是不需要重启的。等你实际改了需要重启的东西,再统一触发一次就好了)
在重启VS重载部分,我们提到重启功能是通过两个类加载器完成的,对于大部分应用,这样是没有问题的,但是有些会引起问题。
默认的,你开发工具中的所有类将被restart classloader加载,所有jar包中的类被base classloader加载。如果你的应用是分模块的,并且你没有把所有的模块引入开发工具,那么你需要一些设置。你需要创建一个META-INF/spring-devtools.properties
文件。
和spring-devtools.properties
文件可以包含restart.exclude.
restart.include.
前缀的属性。include中的元素是指restart classloader加载的类,而exclude中的元素则是指base classloader加载的类。属性值默认为classpath根目录,可以使用正则表达式。
比如
restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar
restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar
calsspath下的所有
META-INF/spring-devtools.properties
将被识别,不论在jar包中,或在lib中。
20.2.6局限
使用标准ObjectInputStream
进行反序列化的对象,重启机制支持的不是很好。如果你需要反序列化,你可以使用ConfigurableObjectInputStream
结合Thread.currentThread().getContextClassLoader()
来实现。
不幸的是,某些第三方lib没有这样反序列化,如果你遇到这个问题,你只能找第三方lib的作者进行处理。
spring-boot-devtools
包含了一个内嵌实时加载的服务,这样可以使得你的文件修改后浏览器不用刷新就可以实时变更。在Chrome,火狐和Safari中,实时加载是一个额外的免费组件,livereload.com
如果在应用运行时,你不想要开启实时加载,你可以设置spring.devtools.livereload.enabled
属性为false
你只能同时运行一个实时加载服务,启动应用时请确保没有开启其他的实时加载服务。如果有多个实时加载服务,只有第一个开启的应用会生效。
(译者注:实时加载对于前端来说还是很好的工具,可以增加开发效率)
你可以通过在$Home环境下新增.spring-boot-devtools.properties
文件来全局设置devtools(注意这里的文件名以"."开始)。该文件中的任何属性都会对机器上的任何sring-boot,并且使用了devtools的应用生效。比如设置一个重启触发文件。
~/.spring-boot-devtools.properties.
spring.devtools.reload.trigger-file=.reloadtrigger
Spring Boot developer tools并不局限于本地开发,你也可以在远程应用上使用一些功能。原创支持是可选的,请确认如下配置
org.springframework.boot
spring-boot-maven-plugin
false
spring.devtools.remote.secret
属性,比如
spring.devtools.remote.secret=mysecret
远程应用 开启
spring-boot-devtools
是有风险的,绝不要用在生产环境。
devtools支持分为两部分:接受客户连接的服务节点和你开发工具中的客户端应用。服务端只要设置了spring.devtools.remote.secret
属性,会默认开启devtools
。客户端需要手动配置。
20.5.1运行远程客户端(译者注:这里的远程是指你相对于云端服务)
远程客户端可以被设计为在你的开发工具中运行远程应用。你需要在远程应用的相同classpath下运行org.springframework.boot.devtools.RemoteSpringApplication类。需要设置一个不可选(手输入)的选项——你需要连接的URL(译者注:吐槽一下,官方文档还真是啰嗦,不过逻辑严密)。
比如,你使用Eclipse或者STS,你有一个名为my-app
的应用,发布在Cloud Foundry,你可能需要这样做
从run菜单中选择Run Configurations…
创建一个新的Java Application
“launch configuration”
选择my-app
工程目录
使用org.springframework.boot.devtools.RemoteSpringApplication
作为main class
添加https://myapp.cfapps.io
到Program arguments(或者其他的你需要远程的URL)
成功运行的远程客户端应用看起来如下
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \
\\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) )
' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / /
=========|_|==============|___/===================================/_/_/_/
:: Spring Boot Remote :: 1.5.2.RELEASE
2015-06-10 18:25:06.632 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication : Starting RemoteSpringApplication on pwmbp with PID 14938 (/Users/pwebb/projects/spring-boot/code/spring-boot-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code/spring-boot-samples/spring-boot-sample-devtools)
2015-06-10 18:25:06.671 INFO 14938 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2a17b7b6: startup date [Wed Jun 10 18:25:06 PDT 2015]; root of context hierarchy
2015-06-10 18:25:07.043 WARN 14938 --- [ main] o.s.b.d.r.c.RemoteClientConfiguration : The connection to http://localhost:8080 is insecure. You should use a URL starting with 'https://'.
2015-06-10 18:25:07.074 INFO 14938 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2015-06-10 18:25:07.130 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication : Started RemoteSpringApplication in 0.74 seconds (JVM running for 1.105)
由于远程客户端和实际的服务端classpath一致,所以服务端可以直接读取客户端的属性文件。所以 spring.devtools.remote.secret可以作为安全认证
通常建议使用
https://协议来保证加密,密码信息不会被截获
如果你需要代理访问远程应用,你可以设置
spring.devtools.remote.proxy.host和
spring.devtools.remote.proxy.port属性
20.5.2远程修改
远程应用将如同本地应用那样监视classpath并提供重启服务。任何资源的修改将通知远程,根据情况自动重启。这在你使用云服务的时候特别有帮助(译者注:比如你使用阿里云,就可以不用远程登陆,直接在本地更新)。通常来说,远程修改和重启比起你编译,打包,在服务器上发布这个过程来说有效率多了。
文件监视服务只在你的远程客户端运行时才有用(译者注:也就是你开发工具中的应用开启远程客户端功能,并且运行该应用)。如果你没有运行远程客户端,那么更改将不会传到远程服务端
java远程调试对于诊断远程应用的问题是很有帮助的。不幸的是,在你的数据中心之外部署的应用不总是可以用java远程调试的。如果你使用了基于容器化的技术,比如说Docker,也是不可以的(译者注:spring还是很靠谱,很追随潮流,支持Docker)
为了解决这些限制,devtools提供了一个HTTP之上的通信通道。原创客户端提供一个8080为端口的本地服务,以便于远程调试。一旦连接建立,调试信息在HTTP之上传输到远程应用。如果你想使用其他端口,你可以使用spring.devtools.remote.debug.local-port
属性配置。
你要确认你的远程应用开启了远程调试属性。通常通过JAVA_OPTS
配置。比如,在Cloud Foundry中,可以添加以下配置到manifest.yml
文件中
---
env:
JAVA_OPTS: "-Xdebug -Xrunjdwp:server=y,transport=dt_socket,suspend=n"
address=NNNN
到 -Xrunjdwp
。如果省略,java将随机选择一个空闲端口
通过Internet 远程调试可能很慢,所以你需要增加你开发工具中timeouts的时间。比如,在eclipse中,你需要从Preferences…
中选择Java
→ Debug
,设置Debugger timeout (ms)
到一个更合理的数值(大部分场景,设置60000
就可以了)
(译者注:IntelliJ的设置注意实现就略过了,因为笔者用的是myeclipse,就懒得看了)
可执行的jar包可用来生产环境部署。应为自带容器,因此也适用于基于云端的部署。
额外的生产环境功能,比如健康检测,审计,度量(metric) REST或者JMX节点。这些功能都在spring-boot-actuator
之中,将在第5部分spring-boot-actuator
生产环境准备中介绍。