一、问题描述
WEB项目在继承Spring时候,默认的配置的是ContextLoaderListener
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
这种配置会把Spring-context.xml配置的所有文件都加载进来,当spring-context.xml文件嵌入很多xml文件时候,加载非常的内容非常庞大,特别是涉及到后台定时任务时候,对开发来说非常不方便,还打印一大堆日志,影响开发效率,而且当发布项目的时候,可能在配置文件的修改忘记修改回来,导致发布出现BUG等。
二、问题分析:
在本地开发的大多时候,是不需要启动一些配置的,例如:quartz的配置,此种情况下,系统可以在配置文件中设置一个开关器,当配置的时候设置值的时候启动,不配置不启动,方便开发,提升效率。
通过开关器空值加载的spring文件,通过阅读ContextLoader源码发现,Spring在初始化Context的时候,获取到配置文件,只需要对:String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);这个地方修改即可:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any.
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getServletContextName()));
}
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
}
wac.setServletContext(sc);
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (initParameter != null) {
wac.setConfigLocation(initParameter);
}
customizeContext(sc, wac);
wac.refresh();
}
自定义ContextLoaderListener关系如下:
三、解决方案:
1、在web.xml配置文件时候,对配置文件拆分,分多个配置
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring/wms-jdbc.xml,
classpath:spring/wms-job.xml
</param-value>
</context-param>
2、自定义ContextLoaderListener:
自定义的ContextLoaderListener,重写configureAndRefreshWebApplicationContext方法,通过配置文件控制获取配置XML的内容:
package com.oneplus.wms.mvc;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ContextLoaderListener;
import javax.servlet.ServletContext;
import java.io.IOException;
import java.util.List;
import java.util.Properties;
/**
* 功能描述:Spring启动时, WMS控制加载Spring配置文件
*
* @author: Zhenbin.Li
* Date: 16/1/8 Time: 10:45
*/
public class OneplusContextLoaderListener extends ContextLoaderListener {
/**
* sl4j
*/
private static final Logger LOGGER = LoggerFactory.getLogger(OneplusContextLoaderListener.class);
/**
* 配置文件名称
*/
private static final String LOADER_CONFIG_FILE = "env-config.properties";
@Override
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
} else {
// Generate default id...
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any.
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getServletContextName()));
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
}
wac.setServletContext(sc);
String initParameter = assemblyInitParameter(sc.getInitParameter(CONFIG_LOCATION_PARAM));
if (initParameter != null) {
wac.setConfigLocation(initParameter);
}
customizeContext(sc, wac);
wac.refresh();
}
/**
* WMS对Spring启动设置开关加载配置文件
*
* @param webXmlConfigInitParameter web.xml配置的Spring文件
* @return
*/
protected String assemblyInitParameter(String webXmlConfigInitParameter) {
String initParameter = webXmlConfigInitParameter;
ClassPathResource resource = new ClassPathResource(LOADER_CONFIG_FILE);
Properties configureProps;
try {
configureProps = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException e) {
throw new RuntimeException("加载" + LOADER_CONFIG_FILE + "配置文件失败!", e);
}
Object configJobDebug = configureProps.get(ContextLoaderListenerConfigType.JOB_DEBUG.getType());
if (configJobDebug == null) {
LOGGER.debug("当前未配置加载Spring定时任务启动参数" + ContextLoaderListenerConfigType.JOB_DEBUG.getType() + ", 系统直接启动.");
return initParameter;
}
String jobDebug = String.valueOf(configJobDebug).toUpperCase();
ContextLoaderListenerStatus listenerStatus = ContextLoaderListenerStatus.getByStatus(jobDebug);
if (listenerStatus == null) {
throw new RuntimeException("配置Spring定时任务启动参数" + ContextLoaderListenerConfigType.JOB_DEBUG.getType() + ", ON或者OFF, 当前配置:" + jobDebug);
}
String[] configures = StringUtils.split(initParameter, ",");
if (ArrayUtils.isEmpty(configures)) {
throw new RuntimeException("配置Spring启动文件为空!");
}
LOGGER.debug(LOADER_CONFIG_FILE + "配置" + ContextLoaderListenerConfigType.JOB_DEBUG.getType() + "设置为:" + jobDebug);
// 校验配置文件是否存在
Object jobFileName = configureProps.get(ContextLoaderListenerConfigType.JOB_CONFIG_FILE.getType());
if (jobFileName == null) {
throw new RuntimeException(LOADER_CONFIG_FILE + "配置" + ContextLoaderListenerConfigType.JOB_CONFIG_FILE.getType() + "设置为空!");
}
if (!StringUtils.contains(webXmlConfigInitParameter, String.valueOf(jobFileName))) {
throw new RuntimeException("web.xml无" + jobFileName + "配置信息!");
}
if (StringUtils.equals(jobDebug, ContextLoaderListenerStatus.OFF.getStatus())) {
LOGGER.debug(LOADER_CONFIG_FILE + "配置" + ContextLoaderListenerConfigType.JOB_DEBUG.getType() + "设置为:" + jobDebug + ", WMS不加载配置文件" + jobFileName);
// 获取过滤之后的配置文件
List<String> filterConfigs = Lists.newArrayList();
for (String configure : configures) {
if (!StringUtils.contains(configure, String.valueOf(jobFileName))) {
filterConfigs.add(configure);
}
}
initParameter = Joiner.on(",").join(filterConfigs);
}
return initParameter;
}
/**
* 启动过滤配置文件类型
*/
protected enum ContextLoaderListenerConfigType {
JOB_DEBUG("JOB.DEBUG"),
JOB_CONFIG_FILE("JOB.CONFIG.FILE");
private String type;
ContextLoaderListenerConfigType(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
/**
* 开关状态值
*/
protected enum ContextLoaderListenerStatus {
ON("ON"),
OFF("OFF");
private String status;
ContextLoaderListenerStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
public static ContextLoaderListenerStatus getByStatus(String statusCode) {
if (StringUtils.isEmpty(statusCode)) {
return null;
}
for (ContextLoaderListenerStatus status : ContextLoaderListenerStatus.values()) {
if (status.getStatus().equals(statusCode)) {
return status;
}
}
return null;
}
}
}
3、配置文件中开关配置:
# 开启定时任务调试,ON:打开,OFF:关闭
JOB.DEBUG=OFF
JOB.CONFIG.FILE=wms-job.xml
四、系统启动加载结果
1、设置为OFF
2016-01-12 18:56:25.827 [main] INFO /-Initializing Spring root WebApplicationContext
2016-01-12 18:56:25.869 [main] DEBUG com.oneplus.wms.mvc.OneplusContextLoaderListener-env-config.properties配置JOB.DEBUG设置为:OFF
2016-01-12 18:56:25.869 [main] DEBUG com.oneplus.wms.mvc.OneplusContextLoaderListener-env-config.properties配置JOB.DEBUG设置为:OFF, WMS不加载配置文件wms-job.xml
2、设置为ON
2016-01-12 18:57:22.081 [main] INFO /-Initializing Spring root WebApplicationContext
2016-01-12 18:57:22.125 [main] DEBUG com.oneplus.wms.mvc.OneplusContextLoaderListener-env-config.properties配置JOB.DEBUG设置为:ON
...
2016-01-12 19:00:00.105 [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1] DEBUG org.mybatis.spring.SqlSessionUtils-Creating a new SqlSession
20