在 Spring Boot 应用程序中重新加载 bean 配置 xml 文件而无需重新启动
Posted
技术标签:
【中文标题】在 Spring Boot 应用程序中重新加载 bean 配置 xml 文件而无需重新启动【英文标题】:Reload a bean configuration xml file in a spring boot application without restarting 【发布时间】:2018-07-23 22:14:19 【问题描述】:应用程序类。
@SpringBootApplication
public class ServerBApplication
public static void main(String[] args)
SpringApplication.run(ServerBApplication.class, args);
private Student stu;
public ServerBApplication()
FileSystemXmlApplicationContext cont = new FileSystemXmlApplicationContext("./config/student.xml");
stu = cont.getBean(Student.class);
cont.close();
cont.destroy();
@Bean
Student stu()
return stu;
还有xml文件
<bean id="stu" name="stu" class="com.example.demo.Student">
<property name="id" value="100"></property>
<property name="name" value="summer"></property>
</bean>
重新加载控制器。
@RestController
public class ReloadController
@Autowired
Student stu;
@RequestMapping(value = "/reload", method = RequestMethod.GET)
public String reload()
FileSystemXmlApplicationContext cont = new FileSystemXmlApplicationContext("./config/student.xml");
stu = cont.getBean(Student.class);
cont.close();
cont.destroy();
return "Reload success." + stu.toSting();
我想更改 ./config/student.xml 的一些值/属性,然后运行方法 /reload,但我仍然可以通过
获得学生价值id:100,名字:夏天
谁能告诉我当我运行 /reload 方法而不重新启动应用程序时,可以更改 stu 的值/属性。非常感谢。
【问题讨论】:
你想要的叫 Spring Cloud@RequestScope
。第一个任务,您必须将 Spring Cloud 依赖项添加到您的类路径中。你可以看到authoritative example。什么时候使用这种技术?当您想要为您的云应用程序进行动态配置时。出于琐碎的目的,您使用 Spring Boot devtools 进行开发过程。
Request Scope 不是 Spring Cloud 的一部分,它绑定到 Web 请求。 docs.spring.io/spring-framework/docs/current/javadoc-api/org/… 此示例不需要 Spring Cloud Config,因为您可以将该 bean 绑定到 RequestScoping。但是,重新加载配置很困难,因为创建的依赖项(可以)注入到云配置帮助的单例对象中,但您需要以不同的方式设计启动应用程序。
【参考方案1】:
package com.test;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean
private static final Logger logger = LoggerFactory.getLogger(RefreshableSqlSessionFactoryBean.class);
private SqlSessionFactory proxy;
private int interval = 500;
private Timer timer;
private TimerTask task;
private Resource[] refreshableMapperLocations;
private boolean running = false;
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public RefreshableSqlSessionFactoryBean()
@Override
public void afterPropertiesSet() throws Exception
super.afterPropertiesSet();
setRefreshable();
public void setRefreshableMapperLocations(Resource[] refreshableMapperLocations)
this.refreshableMapperLocations = refreshableMapperLocations;
public void setInterval(int ms)
this.interval = ms;
public void setCheckInterval(int ms)
interval = ms;
if (timer != null)
resetInterval();
private void refresh() throws Exception
if (logger.isInfoEnabled())
logger.info("> Refresh SqlMapper...");
w.lock();
try
super.afterPropertiesSet();
logger.info("> Done.");
logger.info("======================================================================================");
finally
w.unlock();
@SuppressWarnings("java:S3776")
private void setRefreshable()
proxy = (SqlSessionFactory) Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[]SqlSessionFactory.class,
(aProxy, method, args) -> method.invoke(getParentObject(),
args));
task = new TimerTask()
private final Map<Resource, Long> map = new HashMap<>();
@Override
public void run()
if (isModified())
try
refresh();
catch (Exception e)
logger.error("caught exception", e);
private boolean isModified()
if (refreshableMapperLocations != null)
List<String> modifiedResources = new ArrayList<>();
for (Resource mappingLocation : refreshableMapperLocations)
modifiedResources.addAll(findModifiedResource(mappingLocation));
if (!modifiedResources.isEmpty())
if(logger.isInfoEnabled())
logger.info("======================================================================================");
logger.info("> Update File name : ", modifiedResources);
return true;
return false;
private List<String> findModifiedResource(Resource resource)
List<String> modifiedResources = new ArrayList<>();
try
long modified = resource.lastModified();
if (map.containsKey(resource))
long lastModified = map.get(resource);
if (lastModified != modified)
map.put(resource, modified);
//modifiedResources.add(resource.getDescription()); // 전체경로
modifiedResources.add(resource.getFilename()); // 파일명
else
map.put(resource, modified);
catch (IOException e)
logger.error("caught exception", e);
return modifiedResources;
;
timer = new Timer(true);
resetInterval();
private SqlSessionFactory getParentObject() throws Exception
r.lock();
try
return super.getObject();
finally
r.unlock();
private void resetInterval()
if (running)
timer.cancel();
running = false;
if (interval > 0)
timer.schedule(task, 0, interval);
running = true;
@Override
public SqlSessionFactory getObject()
try
getParentObject(); // build factory if necessary
catch (Exception e)
e.printStackTrace();
return this.proxy;
@Override
public Class<? extends SqlSessionFactory> getObjectType()
return (this.proxy != null ? this.proxy.getClass() : SqlSessionFactory.class);
@Override
public boolean isSingleton()
return true;
@Override
public void destroy()
timer.cancel();
package com.test;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import javax.sql.DataSource;
import java.io.IOException;
public class NodeConfigurationHelper
private NodeConfigurationHelper()
static SqlSessionFactory createRefreshableSqlSessionFactory(ApplicationContext applicationContext,
DataSource dataSource) throws IOException
RefreshableSqlSessionFactoryBean sqlSessionFactoryBean = new RefreshableSqlSessionFactoryBean();
setSqlSessionFactoryBeanCommonSettings(sqlSessionFactoryBean, applicationContext, dataSource);
// for refresh query
sqlSessionFactoryBean.setInterval(1000);
Resource[] mapper1 = applicationContext.getResources("classpath:/mapper/tibero/**/*.xml");
Resource[] mapper2 = applicationContext.getResources("classpath:/com/daou/sabangnet/dao/**/*.xml");
Resource[] mappers = ArrayUtils.addAll(mapper1, mapper2);
sqlSessionFactoryBean.setRefreshableMapperLocations(mappers);
return sqlSessionFactoryBean.getObject();
static void setSqlSessionFactoryBeanCommonSettings(SqlSessionFactoryBean sqlSessionFactoryBean,
ApplicationContext applicationContext,
DataSource dataSource) throws IOException
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:/mapper/mybatis-config.xml"));
sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath:/mapper/tibero/*.xml"));
package com.test;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.io.IOException;
@Slf4j
@Configuration
@PropertySource("classpath:/application.properties")
public class Node01DatabaseConfiguration
@Autowired
private ApplicationContext applicationContext;
@Value("$spring.datasource.node01.connection-pool-size")
private Integer maxConnectionPool;
@Bean(name="node01Configuration")
@ConfigurationProperties(prefix="spring.datasource.node01")
public HikariConfig node01Config()
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setMaximumPoolSize(maxConnectionPool);
return hikariConfig;
@Bean(name="node01DataSource")
public DataSource node01DataSource()
HikariConfig firstConfig = node01Config();
log.info("Max. Hikari Connections for Node01 DB: ", firstConfig.getMaximumPoolSize());
DataSource dataSource = new HikariDataSource(node01Config());
log.info("datasource : ", dataSource);
return dataSource;
@Bean(name="node01SessionFactory")
@Profile("local")
public SqlSessionFactory refreshableSqlSessionFactory(@Qualifier("node01DataSource") DataSource dataSource)
throws IOException
return NodeConfigurationHelper.createRefreshableSqlSessionFactory(applicationContext, dataSource);
@Bean(name="node01SessionFactory")
@Profile("!local")
public SqlSessionFactory sqlSessionFactory(@Qualifier("node01DataSource") DataSource dataSource) throws Exception
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
NodeConfigurationHelper.setSqlSessionFactoryBeanCommonSettings(sqlSessionFactoryBean, applicationContext, dataSource);
return sqlSessionFactoryBean.getObject();
@Bean(name="01TrManager")
public DataSourceTransactionManager returnTransactionManager(@Qualifier("node01DataSource") DataSource dataSource)
return new DataSourceTransactionManager(dataSource);
@Bean(name="node01SessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("node01SessionFactory") SqlSessionFactory sqlSessionFactory)
return new SqlSessionTemplate(sqlSessionFactory);
【讨论】:
正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。以上是关于在 Spring Boot 应用程序中重新加载 bean 配置 xml 文件而无需重新启动的主要内容,如果未能解决你的问题,请参考以下文章
JUnit 4 & Spring Boot - 在测试前有选择地重新加载上下文/重新加载 Spring Security 配置
如何在运行时更新 Spring Boot 应用程序的配置而不重新加载整个 ApplicationContext
Spring Boot devtools - 静态内容重新加载在 IntelliJ 中不起作用
如何在 Angular、spring-boot、maven 项目中配置项目以自动重新加载浏览器