IllegalStateException : Web app root system property already set to different value问题详解

时间:2023-01-21 23:33:33

一、问题描述

    最近公司有了一个新项目,这个项目最近部署到测试服务器上的时候出现了一个问题。

严重: Exception sending context initialized event to listener instance of class org.springframework.web.util.Log4jConfigListener
java.lang.IllegalStateException: Web app root system property already set to different value: 'webapp.root' = [ ] instead of [ ] - Choose unique values for the 'webAppRootKey' context-param in your web.xml files! 

在eclipse下面启动是没问题的,打包的时候也没有报错。唯一的区别是测试服务器上tomcat里起了多个项目。

二、解决方法

从网上找了各种资料,解决方法是在web.xml中加一段代码

<context-param>
<param-name>webAppRootKey</param-name>
<param-value>projectName.root</param-value> <!-- 建议用"工程名字.root"-->
</context-param>

加了之后,果然解决了。

三、具体原因

    我是那种不找到具体原因就不甘心的人。

首先,这个项目用到了log4j(貌似现在大部分项目都在用)。log4j启动时,默认会寻找source folder(src)下的log4j.xml配置文件,若没有,会寻找log4j.properties文件。如果log4j放到别的位置,则需要在web.xml中配置。通过log4jConfigLocation指定文件的位置。

  <context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:config/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>6000</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

问题来了,问题就出在这个org.springframework.web.util.Log4jConfigListener类里面,点进去看这个class。

public class Log4jConfigListener implements ServletContextListener {

    @Override
public void contextInitialized(ServletContextEvent event) {
Log4jWebConfigurer.initLogging(event.getServletContext());
} @Override
public void contextDestroyed(ServletContextEvent event) {
Log4jWebConfigurer.shutdownLogging(event.getServletContext());
} }

再点进去initLogging这个方法。

public static void initLogging(ServletContext servletContext) {
// Expose the web app root system property.
if (exposeWebAppRoot(servletContext)) {
WebUtils.setWebAppRootSystemProperty(servletContext);
} // Only perform custom log4j initialization in case of a config file.
String location = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
if (location != null) {
// Perform actual log4j initialization; else rely on log4j's default initialization.
try {
// Resolve property placeholders before potentially resolving a real path.
location = ServletContextPropertyUtils.resolvePlaceholders(location, servletContext); // Leave a URL (e.g. "classpath:" or "file:") as-is.........
      .........

再点进去setWebAppRootSystemProperty这个方法。

public static void setWebAppRootSystemProperty(ServletContext servletContext) throws IllegalStateException {
Assert.notNull(servletContext, "ServletContext must not be null");
String root = servletContext.getRealPath("/");
if (root == null) {
throw new IllegalStateException(
"Cannot set web app root system property when WAR file is not expanded");
}
String param = servletContext.getInitParameter(WEB_APP_ROOT_KEY_PARAM);
String key = (param != null ? param : DEFAULT_WEB_APP_ROOT_KEY);
String oldValue = System.getProperty(key);
if (oldValue != null && !StringUtils.pathEquals(oldValue, root)) {
throw new IllegalStateException(
"Web app root system property already set to different value: '" +
key + "' = [" + oldValue + "] instead of [" + root + "] - " +
"Choose unique values for the 'webAppRootKey' context-param in your web.xml files!");
}
System.setProperty(key, root);
servletContext.log("Set web app root system property: '" + key + "' = [" + root + "]");
}

在这个类里面就能看到拋异常的语句以及拋异常之前处理的逻辑了。

如果两个工程都不配webAppRootKey,第一个工程启动运行到这个ok,但是第二个工程启动的时候运行到这里,if那里就报错了。

其实归根究底不是log4j的原因,只要涉及到这个方法,并且没有设置webAppRootKey都会报错。当启动多个工程的时候,应该要配置一下这个webAppRootKey。