spring boot构建的项目,开发完成后通常配置文件会自动打包在项目中,尽管我们启动项目时可以通过–spring.config.location参数设置读取application.xml或者application.yml文件的位置,但我们还是希望项目启动时可以自动从某些路径读取配置文件。
如果我们用spring boot构建web项目,尽管程序会自动将http服务自动启动,可默认情况下,项目的启动停止只对http服务有效。当我们的项目需要添加其他服务或监听(如tcp服务、webservice服务)时,项目默认的启动停止无法对其他服务生效。
有什么方法能够让我们的spring boot项目在启动停止时,让我们可以指定一些其他操作呢,下面介绍三种实现方法。
spring boot启动时,我们可以在启动方法SpringApplicationd的run方法执行前,设置启动参数或添加自定义的监听程序。代码案例如下,其中getFilePath()方法是启动类中的私有方法;GramServiceListener类为自己编写的服务监听类(需要实现 org.springframework.context.ApplicationListener接口),可根据实际需要自己编写修改该类。
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
String path = getFilePath();//获取文件路径方法
//InputStream in = Runner.class.getClassLoader().getResourceAsStream("C:/Users/Administrator/cwht_dir/batchproject/batchConf.properties");
InputStream in = new FileInputStream(new File(path));
properties.load(in);
SpringApplication app = new SpringApplication(Runner.class);
app.setDefaultProperties(properties);
app.addListeners(new GramServiceListener());
app.run(args);
in.close();
}
GramServiceListener类内容如下
@Override
public void onApplicationEvent(ApplicationEvent event) {
// TODO Auto-generated method stub
if (event instanceof ApplicationEnvironmentPreparedEvent){ // 初始化环境变量
logger.info("\n\n-======初始化环境变量 ");
}else if (event instanceof ApplicationPreparedEvent){ // 初始化完成
logger.info("\n\n======初始化完成 ");
} else if (event instanceof ContextRefreshedEvent) { // 应用刷新 }
logger.info("\n\n=======应用刷新 ");
}else if (event instanceof ApplicationStartingEvent ) {// 应用已启动完成}
logger.info("\n\n=======应用已启动完成");
}else if (event instanceof ContextStartedEvent) { //应用启动,需要在代码动态添加监听器才可捕获 }
logger.info("\n\n=======应用启动,需要在代码动态添加监听器才可捕获");
}else if (event instanceof ContextStoppedEvent) { // 应用停止 }
logger.info("\n\n=======应用停止");
}else if(event instanceof ContextClosedEvent) { // 应用关闭 }
logger.info("\n\n=======应用关闭");
}
}
该方法与对SpringApplication对象添加自定义监听类的效果是一样的。
使用该方法需要我们在spring boot项目的启动类中实现org.springframework.boot.CommandLineRunner接口;同时需要我们在启动类中编写监听方法,并需要为自定义的监听方法添加注解 @EventListener 且方法必须为无返回的公用方法(public void修饰)
以下为案例
public class Application implements CommandLineRunner{
private static Logger logger = LoggerFactory.getLogger(Application.class);
/**
* 描述 : 程序启动入口
* @param args
* @return void
* @throws IOException
*/
public static void main(String[] args) throws IOException {
SpringApplication.run(Application.class,args);
}
@EventListener
public void eventApplicationListener(ApplicationEvent applicationEvent) throws Exception{
if(applicationEvent instanceof ApplicationReadyEvent){
logger.info("~~~~~~~正在启动服务~~~~~~~~~");
server.start(port);
}else if(applicationEvent instanceof ContextClosedEvent){
logger.info("~~~~~~~正在关闭服务~~~~~~~~~");
}
}
@Override
public void run(String... strings) throws Exception {
}
}
注意:由于CommandLineRunner接口中存在run方法,故启动类中必须重新实现run方法;否则spring boot项目无法正常启动
该方法需要项目导入Actuator插件(spring-boot-starter-actuator),且由于该方法采用自定义执行器的方式,在spring boot运行的生命流程中,执行我们自己定义的执行器中的相关内容,故可能造成项目运行故障。同时,使用该方式修改,需要编写代码更多,且需要注解的配合或其他插件。故不建议采用该方式实现功能
注意:本文中的执行器基于spring boot 1.5;spring boot 2.0以后已经对执行器进行了升级修改,变化较大,修改时请参考官方文档
我们以执行器插件中停止spring boot服务的执行器ShutdownEndpoint相关的代码为例
import java.util.Collections;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
@ConfigurationProperties(prefix="endpoints.shutdown")
public class ShutdownEndpoint
extends AbstractEndpoint
import java.util.Collections;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@ConfigurationProperties(prefix="endpoints.shutdown")
public class ShutdownMvcEndpoint
extends EndpointMvcAdapter
{
public ShutdownMvcEndpoint(ShutdownEndpoint delegate)
{
super(delegate);
}
@PostMapping(produces={"application/vnd.spring-boot.actuator.v1+json", "application/json"})
@ResponseBody
public Object invoke()
{
if (!getDelegate().isEnabled()) {
return new ResponseEntity(
Collections.singletonMap("message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
}
return super.invoke();
}
}
在EndpointWebMvcManagementContextConfiguration类中有各种状态检查的配置,其中与停止服务相关的配置如下
import org.springframework.boot.actuate.autoconfigure.EndpointCorsProperties;
import org.springframework.boot.actuate.autoconfigure.HealthMvcEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@ManagementContextConfiguration
@EnableConfigurationProperties({HealthMvcEndpointProperties.class, EndpointCorsProperties.class})
public class EndpointWebMvcManagementContextConfiguration
{
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean({ShutdownEndpoint.class})
@ConditionalOnEnabledEndpoint(value="shutdown", enabledByDefault=false)
public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate)
{
return new ShutdownMvcEndpoint(delegate);
}
}
自定义的执行器可参考shutdown执行器代码,自己添加相应配置即可