现在很多J2EE程序都多多少少有一些配置文件,比如xxx.properties,xxx.xml,xxx.ini等。将应用程序用到的,但在程序发布后有时会改动的信息都配置在这些不同的配置文件中。比如:JDBC连数据库的URL,JDBC DRIVER,DB USER,DB PASSWORD,以及相关物理盘上的目录路径等。
如果一个应用程序或者说一个项目有很多个J2EE组件组成,每个组件又有各自不同的配制信息,当我们在程序开发完打包后,在SIT阶段由部署人员根据SIT的环境,手工修改各个组件的jar包、war或ear包,然后再发布;到UAT客户测试阶段,又需要相关人员根据UAT的环境再手工修改各个组件的jar包、war或ear包;最后发布到了发布到正式的生产环境时,以得由相关人员根据生产环境再手工修改各个组件的jar包、war或ear包。
由于不同的环境,配置都可能不一样,而且是手工修改大量的配置文件。没错,程序设计成可配置的,不同程度的提高了程序的灵活性,但是换来的是,在联测或最终部署时,费很大力起去修改配置文件。一般情况下,写这些配置文件的都是各个不同功能模板的设计人员或者是开发人员。而部署人员不一定清楚每一个配置项的作用,即使用文档,这也是一个痛苦的过程。痛苦不说,最重要的是,大多数情况下,都会有配错的情况,包括健康性检查,都不是一件轻松的事。这就会导至很多问题,最简单的可能就是程序跑不起来,重要的就是有时程序能启动,但是运行中业务处理时,调用了错误的配置,比如,UAT时,某一项配置可能是配的生产环境的,如果UAT和生产环境不是隔离开的,就会出现致命的问题,甚至是灾难性的。
那么,废话了这么多,有什么好的办法或模式能解决这个问题呢。
现在我把我在项目中用到的一个方法列给大家,也许这不是最好的(其实我自己也是这么认为的),但是对一般的应用来就,足够了。
1,不使用配置文件,改为将配置信息放到数据库。有人会问,放到数据库?既然是配置文件,放到数据库我怎么配置?不急,请继续看下面。
这里先看我这个图:
上面这个图,没有太多的解释,简单说一下:
1, XAI server大家可以不理它,其实就是一个web service 服务器,功能是与数据库交互,相当于DAO层。
2, Config Database可以合并到Application Database(通常情况下也是这样)。
3, Property File是配置文件,这里主要放应用程序一航情况下不会去修改的那部份配置信息。
4, All J2EE Application就是各个不同的J2EE应用。
5, Common configuration manage Application实际上也是一个J2EE或者其它类型,提供UI界面的应用,作用是给配置人员用来修改,新增等操作配置信息的地方,也就是替换原来手工修改配置文件。
6, Common configuration manage Component 通用配置管理组件,一般情况下是一个jar包,提供给各个J2EE应用程序。
这里重点说下第6个:
他提供一些API接口给各个J2EE程序,其中必须的几个如下:
A: Config.initConfig(ServletContext servletContext ,String appName);//初始化配制文件
通过JDBC方法从数据库中查出所有appName的配置信息,然后set到servletContext中(configType做为 key,configValue作为Value)。
B: Config.refresh(ServletContext servletContext ,String appName);//刷新配制文件
重新从数据库中查一遍所有appName的配置信息,然后set到servletContext中(configType做为 key,configValue作为Value)。
C: JDBC.getConnection();//获得数据库连接,当程序用JDBC连数据库时。
提供一个通用的JDBC连接方法而已。
D:Security.encryption();或Security.decryption();提供加密解密算法。
提供一个通用的加密算法,因为有的配置的值可能是要加密的,比如说数据库密码。
其中A和B是必须的。也是这个组件的灵魂。这两个方法都会转进两个参数,这里有两种解决方案(相对于配置信息读取之后的存放位置),一种是ServletContext,另一种是用开源框架Ehcache,传进来的是Cache。这里只说第一第ServletContext,有关Ehcache的会在以后的文间中说细介绍。
另外他还提供一些管理这些配置信息的业务逻辑,比如说,增加配置信息,修改配置信息,删除配置信息,查询配置信息等。这些功能主要是给Common configuration manage Application用。
这里有三张数据库表:
以上Common configuration manage Component 通用配置管理组件开发完成后,会打成一个jar包(例如:application-config.jar),所有的的应用程序会引入这个jar包,以及这个jar包所依赖的其它jar包(比如:log4j.jar;servlet-api.jar;ehcache.jar等)。
至于Common configuration manage Application通用配置管理程序这里不多说,只能能实现增删改查配置信息及配置信息关联的配置为型及应用程序信息(以上三个数据库表)即可。
下面详细说一下在具体的J2EE应用中如何使用Common configuration manage Component 通用配置管理组件。
这里只说ServletContext这种方式:
1, 引入上面的Common configuration manage Component 通用配置管理组件jar包(application-config.jar)以及servlet-api.jar等。
2,新建一个过滤器Filter
/**********************************************
* @author Simon Hoo (simon@cottsoft.com)
* @contact QQ:9930323 MSN: simon_hoo@msn.com
* @OnlineSupport: http://www.CottSoft.com
* @Create 2011-4-8
* @version V1.0
********************************************/
package com.cottsoft.config.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.log4j.Logger;
import com.cottsoft.common.Config;
public class ConfigFilter implements Filter {
private static final Logger logger = Logger.getLogger(ConfigFilter.class);
private String appName = null;
private String refresh_parm = null;
private ServletContext servletContext=null;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.appName = filterConfig.getInitParameter("appname");
this.servletContext = filterConfig.getServletContext();
this.refresh_parm = filterConfig.getInitParameter("refresh_parm");
if(appName==null){
logger.info("Application is null!");
}else{
Config.init(servletContext, appName);
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String refresh = request.getParameter(refresh_parm);
if(refresh!=null && !"false".equals(refresh.toLowerCase())){
if(appName==null){
logger.info("Application is null!");
}else{
if(servletContext!=null){
Config.refresh(servletContext, appName);
}else{
logger.info("ServletContext is null!");
}
}
}
}
@Override
public void destroy() {
}
}
在Filter的init方法中初始化配置信息,在doFilter方法,根据请求参数来判断是否刷新配置信息(重新从数据库读取。)
3,在web.xml中配置过滤器
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <filter> <filter-name>config</filter-name> <filter-class> com.cottsoft.config.filter.ConfigFilter </filter-class> <init-param> <param-name>appname</param-name> <param-value>FOC</param-value> </init-param> <init-param> <param-name>refresh_parm</param-name> <param-value>r</param-value> </init-param> </filter> <filter-mapping> <filter-name>config</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
其中参数appname是应用程序的名字,在初始化或刷新时会根据这个名字到数据库中找出对应所有的配置信息。
参数refresh_parm是刷新配置信息时提供的参数,可以在URL后面跟上如http://10.10.10.10:8080/app/action.do?r或?r=true,其中,r就是该配置参数refresh_parm的值如果过滤器获取到这个值不为null或不是false就刷新配置信息。
4,在需要配置信息的地方使用。
比如,用到Struts框加的系统中,在Action里先得到ServletContext,然后用getAttribute("KEY").toString()得到配置信息的值。
/**********************************************
* @author Simon Hoo (simon@cottsoft.com)
* @contact QQ:9930323 MSN: simon_hoo@msn.com
* @OnlineSupport: http://www.CottSoft.com
* @Create 2011-4-8
* @version V1.0
********************************************/
package com.cottsoft.config.action;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class ConfigAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
ServletContext servletContext = request.getSession().getServletContext();
//Get configuration,ex: get jdbc_url,jdbc_driver,jdbc_db_user,jdbc_db_pwd
String jdbc_url = servletContext.getAttribute("jdbc_url").toString();
String jdbc_driver = servletContext.getAttribute("jdbc_driver").toString();
String jdbc_db_user = servletContext.getAttribute("jdbc_db_user").toString();
String jdbc_db_pwd = servletContext.getAttribute("jdbc_db_pwd").toString();
//do something...
return mapping.findForward("index");
}
}