问题来源
今天开发完毕了某个Web应用,自己部署自测,一切ok,提交给测试组同事,进行部署测试,结果发现,启动Tomcat时,报错,查看catalina.out日志中抛异常:
INFO: Illegal access: this web application instance has been stopped already. Could not load org.springframework.aop.framework.AdvisedSupport$MethodCacheKey. The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1750)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1709)
at org.springframework.aop.framework.AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport.java:486)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:193)
at com.sun.proxy.$Proxy44.buildRsponseData(Unknown Source)
at com.os.timertask.EplUpdateTimerTask.UpdateTimerTask(EplUpdateTimerTask.java:83)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Apr 26, 2016 11:31:23 AM org.apache.catalina.loader.WebappClassLoader loadClass
INFO: Illegal access: this web application instance has been stopped already. Could not load org.apache.log4j.spi.ThrowableInformation. The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1750)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1709)
at org.apache.log4j.spi.LoggingEvent.<init>(LoggingEvent.java:165)
at org.apache.log4j.Category.forcedLog(Category.java:391)
at org.apache.log4j.Category.log(Category.java:856)
at org.apache.commons.logging.impl.Log4JLogger.error(Log4JLogger.java:229)
at org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler.handleError(TaskUtils.java:95)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:60)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
从这里,只看到有spring和log4j的Class加载失败,但是没有引起问题的原因,再查看localhost日志,发现有以下
异常信息:
SEVERE: 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' = [/usr/apps/wag/] instead of [/usrapps/bas/] - Choose unique values for the 'webAppRootKey' context-param in your web.xml files!
at org.springframework.web.util.WebUtils.setWebAppRootSystemProperty(WebUtils.java:150)
at org.springframework.web.util.Log4jWebConfigurer.initLogging(Log4jWebConfigurer.java:117)
at org.springframework.web.util.Log4jConfigListener.contextInitialized(Log4jConfigListener.java:46)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5003)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5517)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:677)
at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1912)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
问题原因
从这里可以看出,是两个应用中的System Property:webAppRootKey在启动加载时的冲突导致的这个问题。
got the below information about the spring framework
“WARNING: Some containers (like Tomcat) do not keep system properties separate per web app.
You have to use unique “webAppRootKey” context-params per web app then, to avoid clashes.
Other containers like Resin do isolate each web app’s system properties:
Here you can use the default key (i.e. no “webAppRootKey” context-param at all) without worrying. ”
问题解决方案
在项目的web.xml中显示指定webAppRootKey参数值,以区分其他应用:
<!-- 应用路径 -->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>spring.webapp.root</param-value>
</context-param>
问题衍生出的知识点
在tomcat下部署两个或多个项目时,web.xml文件中最好定义webAppRootKey参数,如果不定义,将会缺省为“webapp.root”。
最好每个项目的webAppRootKey参数值不同,以免引起项目冲突,对多个项目要对webAppRootKey进行配置,这里主要是让log能将日志写到对应项目根目录下。
定义以后,在Web Container启动时将把ROOT的绝对路径写到系统变量里。然后log4j的配置文件里就可以用${webName.root }来表示Web目录的绝对路径,把log文件存放于webapp中。
<!--此参数用于后面的“Log4jConfigListener”-->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>webName.root</param-value>
</context-param>
<!--由Sprng载入的Log4j配置文件位置-->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<!--Spring默认刷新Log4j配置文件的间隔,单位为millisecond-->
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
<!-- Web 项目 Spring 加载 Log4j 的监听 -->
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
编写log4j.properties属性文件,使用web.xml配置的全局变量
log4j.appender.A_default.File=${webName.root}/WEB-INF/logs/log4j.log