`
tangay
  • 浏览: 94678 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

利用Spring实现配置文件动态替换

阅读更多

在很多项目里面都有后缀名为properties的配置文件,我们一般会把这些文件放到名为conf之类的目录下面,随同jar一起发布。运行时会把conf目录加到jvm的classpath下面去。麻烦的是,程序运行时,我们改动了配置文件,如何让我们的配置文件无需重启程序起作用。我这里有个比较简陋的解决方案,有兴趣的可以看看,应该还可以做些优化。

 

解决方案的技术思路:

 

起一个定时器,定时的监控配置的文件的修改时间,如果一旦发现修改,重新装载文件。由于Spring的配置值表达式不支持OGNL类的表达式,于是使用Spring自带的method replace(方法替换)来模拟OGNL类的表达。

 

代码并不复杂,用到包有asm,cglib,spring2.x,commons-logging4个而已。demo结构如下:

 


 

文件简介:

 

FileListener:监测配置文件修改的接口

FileMonitor:一个TimeTask的子类,检查文件有无改动

ConfigManager: 核心类,里面有个Properties成员装载配置文件信息

ConcreteConfig:配置的“反射”类

Main:测试类

 

conf/monitor.properties 配置文件

conf/monitorContext.xml Spring配置文件

 

具体实现代码为:

 

FileListener.java

 

import java.io.File;

/**
 * an interface to listen the notifications when the file has been changed
 * 
 */
public interface FileListener {

	/**
	 * a notification when the file changed
	 * 
	 * @param file
	 *            the file which has been changed
	 */
	public void onFileChanged(File file);
}

 

FileMonitor.java

import java.io.File;
import java.util.TimerTask;


/**
 * a class to monitor if the file has been changed
 * 
 */
public class FileMonitor extends TimerTask {
	private FileListener listener;
	private File file;
	private long lastModified;

	/**
	 * constructor
	 * 
	 * @param file
	 *            a file which will be monitor
	 * @param listener
	 *            a listener which will be notified when the file has been
	 *            changed
	 */
	public FileMonitor(File file, FileListener listener) {
		if (file == null || listener == null) {
			throw new NullPointerException();
		}

		this.file = file;
		this.lastModified = this.file.lastModified();
		this.listener = listener;
	}

	@Override
	public void run() {		
		long lastModified = this.file.lastModified();
		if (this.lastModified != lastModified) {
			this.lastModified = lastModified;
			this.listener.onFileChanged(this.file);
		}
	}
}
 

ConfigManager.java

 

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Properties;
import java.util.Set;
import java.util.Timer;

import org.springframework.beans.factory.support.MethodReplacer;

public class ConfigManager implements FileListener, MethodReplacer{
	private static final long FILE_MONITOR_INTERVAL = 5000;
	private static final String MONITOR_CONF_FILE_PATH = "monitor.properties";
	private FileMonitor monitor;
	private Timer timer = new Timer("Timer", true);
	
	private Properties properties = new Properties();
	
	
	private ConfigManager() throws IOException {
		properties.load(getClass().getResourceAsStream("/monitor.properties"));
		
		// monitor the configuration file change
		monitor = new FileMonitor(getFileByClassPath(MONITOR_CONF_FILE_PATH), this);
		timer.schedule(monitor, FILE_MONITOR_INTERVAL, FILE_MONITOR_INTERVAL);
		
	}
	private File getFileByClassPath(String filepath) {
		URL url = getClass().getResource(
				filepath);
		if (url == null) {
			System.err.println("failed to find the file " + filepath);
			return null;
		}

		try {
			File file = new File(url.toURI());
			return file;
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}

		return null;
	}

	@Override
	public synchronized void onFileChanged(File file) {
		Properties newProperties = new Properties();
		try {
			newProperties.load(new FileInputStream(file));
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}
		Set<String> keys = properties.stringPropertyNames();
		for(String key : keys) {
			String newValue = (String)newProperties.get(key);
			String oldValue = properties.getProperty(key);
			System.out.println("newValue:"+newValue+" oldValue:"+oldValue);
			if(newValue != null) {
				properties.setProperty(key, newValue);
			}
			else {
				properties.remove(key);
			}
		}
	}
	
	public synchronized  String getProperty(String key) {
		return properties.getProperty(key);
	}
	
	@Override
	public Object reimplement(Object arg0, Method arg1, Object[] arg2)
			throws Throwable {
		String methodName = arg1.getName();
		String tmp = methodName.substring("get".length());
		char ch = tmp.charAt(0);
		ch = Character.toLowerCase(ch);
		tmp = ch+tmp.substring(1);
		return getProperty(tmp);
	}
	
}

 

ConcreteConfig.java

public class ConcreteConfig {
	private String zookeeperQuorum;
	private String zookeeperPort;
	/**
	 * @param zookeeperQuorum the zookeeperQuorum to set
	 */
	public void setZookeeperQuorum(String zookeeperQuorum) {
		this.zookeeperQuorum = zookeeperQuorum;
	}
	/**
	 * @return the zookeeperQuorum
	 */
	public String getZookeeperQuorum() {
		return zookeeperQuorum;
	}
	/**
	 * @param zookeeperPort the zookeeperPort to set
	 */
	public void setZookeeperPort(String zookeeperPort) {
		this.zookeeperPort = zookeeperPort;
	}
	/**
	 * @return the zookeeperPort
	 */
	public String getZookeeperPort() {
		return zookeeperPort;
	}
	
}

 

Main.java

import org.springframework.context.support.FileSystemXmlApplicationContext;


public class Main {
	public static void main(String[] args) throws InterruptedException {
		FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
                new String[]{
                        "classpath:monitorContext.xml"
                });
		ConcreteConfig config = (ConcreteConfig)context.getBean("concreteConfig");
		while(true) {
			System.out.println(config.getZookeeperQuorum());
			System.out.println(config.getZookeeperPort());
			Thread.sleep(5000);
		}
	}
}

 

monitor.properties

 

zookeeperQuorum = host1:2181,host2:2181,host3:2181
zookeeperPort = 2181
 

 

monitorContext.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-lazy-init="false">

	<bean id="configManager" class="ConfigManager"/>
	
	<bean id="concreteConfig" class="ConcreteConfig">
  		<replaced-method name="getZookeeperQuorum" replacer="configManager"/>
  		<replaced-method name="getZookeeperPort" replacer="configManager"/>
  	</bean>
	
</beans>

 

 

测试过程:

在Eclipse里面以Main做主类运行,观察控制台输出。

然后改动monitor.properties,再看控制台输出,可以发现改动很快生效。

 

 

  • 大小: 22 KB
分享到:
评论
3 楼 tangay 2010-12-31  
如果是在分布式环境中,当改动配置时需要影响到所有的节点的情况,建议把配置信息放到zookeeper中,并在需要侦听配置改动的地方配置一个Zookeeper的watcher,这样配置改动就能被watcher侦听到,采取相应的动作。
2 楼 tangay 2010-12-29  
不是这样啦,现在一般软件home目录下面都有个conf,配置文件一般都放在这下面,启动时候把conf目录加入classpath里面的。war包部署的我倒是没考虑过,如果是war的话,我建议用jmx来管理吧,把配置项做成个MBean,修改应用配置通过读写MBean来完成。
1 楼 myyate 2010-12-29  
war包部署直接打开war包替换properties文件?

相关推荐

    Spring动态加载配置文件

    要实现动态加载配置文件,我们可以利用Spring的`PropertyPlaceholderConfigurer`或`@PropertySource`注解。`PropertyPlaceholderConfigurer`是Spring早期版本中处理属性文件的工具,而`@PropertySource`则是从Spring...

    Spring PropertyPlaceholderConfigurer配置文件加载器集成ZooKeeper来实现远程配置读取

    5. 在Spring配置文件中声明`ZooKeeperPropertyPlaceholderConfigurer`,并配置ZooKeeper连接信息。 6. 使用`@Value`注解或其他方式引用配置,Spring会在启动时从ZooKeeper读取并注入值。 这个过程涉及到的知识点...

    spring springmvc jpa配置文件

    在IT行业中,Spring框架是Java开发中的核心组件,它提供了丰富的功能来简化应用程序的构建。Spring MVC是Spring框架的一部分,...在实际项目中,这些配置文件通常会被更复杂和详细的设置所替换,以满足特定的业务需求。

    Spring—下载与配置

    Spring框架是Java开发中不可或缺的一部分,它以其强大的依赖注入(DI)和面向切面编程(AOP)功能,以及丰富的生态系统,...通过不断地学习和实践,你将能够充分利用Spring的强大功能,构建高效、可维护的企业级应用。

    struts1整合spring 通过代理RequestProcessor实现

    2. **Struts1配置**:接着,需要在Struts的配置文件(struts-config.xml)中,将默认的RequestProcessor替换为我们定义的Spring代理RequestProcessor。这样,当Struts接收到HTTP请求时,就会调用我们自定义的代理...

    详解利用Spring加载Properties配置文件

    在本文中,我们将深入探讨如何利用Spring加载Properties配置文件,并了解两种主要的方法:`PropertiesFactoryBean` 和 `PreferencesPlaceholderConfigurer`。 首先,我们需要创建一个Properties配置文件,例如`jdbc...

    springboot spring aop 拦截器注解方式实现脱敏

    总结一下,通过上述步骤,我们已经在Spring Boot应用中利用Spring AOP和注解方式实现了数据脱敏。这个拦截器可以在不修改原有业务代码的情况下,确保敏感信息在响应给客户端之前得到处理,提高了应用的安全性。同时...

    spring3升级到4.3.15所需要的jar包及升级步骤

    Spring 4.0引入了一些新的配置元素和属性,因此在升级过程中,需要检查并更新所有的Spring配置文件(如`applicationContext.xml`)。例如,XML配置中的命名空间可能需要从`...

    Spring Flex 配置文档

    - **使用@RemotingDestination注解**:讨论了如何利用Spring的注解驱动编程模型来简化远程服务的配置,并特别提到了如何指定外部化通道ID。 #### 四、使用AMF与Spring MVC RESTful端点通信 ##### 4.1 概述 这部分...

    采用Java SPI和Spring factories来实现插件化的demo

    项目"cab5-plugins-demo"可能包含了上述所有步骤的示例代码,包括服务接口、实现类、SPI配置文件以及Spring配置文件。通过这个项目,开发者可以学习到如何将Java SPI和Spring Factories结合使用,以实现灵活、可扩展...

    Spring基于注解的缓存配置--web应用实例

    在Spring配置文件中,我们需要启用缓存管理和指定使用的缓存管理器。例如,如果我们使用Ehcache,我们需要配置EhcacheManager,并声明缓存的bean。 7. **Web应用集成** 在Web应用程序中,缓存通常需要考虑到并发...

    Spring Cloud 配置教程.docx

    Spring Cloud Bus 就是这样一种机制,它利用 AMQP(Advanced Message Queuing Protocol)协议来实现服务间的事件传播。 - **Spring Cloud Bus**: - Spring Cloud Bus 提供了对 Spring Cloud 应用程序之间消息传递...

    spring2.0升级到spring3.0.5的开发包

    2. **注解驱动的配置**:Spring 3.0大力推广了基于注解的配置,例如`@Component`、`@Service`、`@Repository`和`@Controller`等,使得XML配置文件大大减少,提高了代码的可读性和可维护性。 3. **JSR-303/JSR-349 ...

    springloaded-1.2.6实现spring热部署,最全的教程

    本教程将详细介绍如何利用SpringLoaded实现Spring应用的热部署。 1. **SpringLoaded简介** SpringLoaded是一个JVM插件,它在运行时动态地重新加载类,使得在修改了源代码后,无需重启服务器就能观察到改动。这对于...

    SpringBatch+Spring+Mybatis+MySql (spring batch 使用jar)

    1、利用Spring编程模型:使程序员专注于业务处理,让Spring框架管理流程。 2、明确分离批处理的执行环境和应用。 3、提供核心的,共通的接口。 4、提供开箱即用(out of the box)的简单的默认的核心执行接口。 5、...

    eclipse配置文件没有提示问题解决方案.docx

    本解决方案主要针对Spring框架的XML配置文件,通过引入schema约束来实现代码提示功能。具体步骤如下: 1. **复制约束路径** 在Spring框架的安装目录中,可以找到一系列的schema约束文件,这些文件通常位于`spring...

    Spring IoC简单示例-注解配置-Maven构建

    在本文中,我们将深入探讨Spring框架的核心特性——控制反转(Inversion of Control,简称IoC)和依赖注入...通过学习和实践这些知识,开发者可以更好地利用Spring框架的优势,创建出易于维护和扩展的高质量软件。

    配置文件内容加密jasypt demo

    在Spring Boot中,我们可以利用jasypt来加密配置文件(如application.properties或application.yml)中的敏感数据,确保即使配置文件被泄露,也无法直接获取到原始的明文信息。 首先,我们需要在项目中引入jasypt的...

    Spring 和 struts 整合的三种方式

    Struts的配置文件将不再包含Action和业务逻辑的映射,而是完全由Spring的配置文件来控制。 配置Spring MVC时,需要在`web.xml`中配置DispatcherServlet,并在Spring的配置文件中定义Controller。例如: ```xml ...

Global site tag (gtag.js) - Google Analytics