为什么每次访问servlet时,我的Spring应用程序上下文都会重新加载



几个月前,我制作了一个简单的应用程序,只要在数据库中找到某些条件,就可以每晚运行它向用户发送电子邮件。我使用Spring 3.1.0和Quartz 1.8.5来实现这一点;到目前为止,一切都很好。

该应用程序部署在weblogic服务器中。

上周,我被要求修改该应用程序,以便手动命令它通过URL运行。

我添加了一个servlet,它运行得很好:每次有人调用URL时,应用程序都会运行。

但出现了一个附带的影响——每次servlet调用都会引发spring上下文的重新加载,并创建Quartz的另一个实例。所以,如果所说的servlet被调用n次,当Quartz解雇我的工作时,我的应用程序将重复发送n+1封电子邮件,而不是只有1封。

我想了解为什么会发生这种情况,以及如何解决这个问题。你能帮我吗?

以下是我的问题的相关档案。

日志文件摘录,包括应用程序的启动:

28/11/2012 16:37:48 org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
28/11/2012 16:37:48 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Wed Nov 28 16:37:48 BRST 2012]; root of context hierarchy
28/11/2012 16:37:48 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from URL [file:/C:/bea/user_projects/workspaces/app/target/app-1.0.0/WEB-INF/classes/app-context.xml]
28/11/2012 16:37:48 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-data-context.xml]
28/11/2012 16:37:48 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-scheduler.xml]
28/11/2012 16:37:48 org.springframework.core.io.support.PropertiesLoaderSupport loadProperties
INFO: Loading properties file from class path resource [app.properties]
28/11/2012 16:37:48 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@599bcd: defining beans [org.springframework.beans.factory.config.PropertyPlaceholderConfigurer#0,appDS,job,cronTrigger,org.springframework.scheduling.quartz.SchedulerFactoryBean#0,appDAO,appService,mailerService,appTask, (...)]; root of factory hierarchy
28/11/2012 16:37:50 org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup start
INFO: Starting beans in phase 2147483647
**28/11/2012 16:37:50 org.springframework.scheduling.quartz.SchedulerFactoryBean startScheduler
INFO: Starting Quartz Scheduler now**
28/11/2012 16:37:50 org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 2109 ms

每次调用servlet时发生的日志文件摘录:

<28/11/2012 16h37min50s BRST> <Notice> <WebLogicServer> <BEA-000360> <Server started in RUNNING mode> 
28/11/2012 16:39:06 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@11f7458: startup date [Wed Nov 28 16:39:06 BRST 2012]; root of context hierarchy
28/11/2012 16:39:06 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
28/11/2012 16:39:06 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-data-context.xml]
28/11/2012 16:39:06 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-scheduler.xml]
28/11/2012 16:39:06 org.springframework.core.io.support.PropertiesLoaderSupport loadProperties
INFO: Loading properties file from class path resource [app.properties]
28/11/2012 16:39:06 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@10ca001: defining beans [(...)]; root of factory hierarchy
28/11/2012 16:39:06 org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup start
INFO: Starting beans in phase 2147483647
**28/11/2012 16:39:06 org.springframework.scheduling.quartz.SchedulerFactoryBean startScheduler
INFO: Starting Quartz Scheduler now**

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"
version="2.5">
<description>generic web.xml</description>
<!-- ====================================== --> 
<!--               SPRING                   -->
<!-- ====================================== --> 
<!-- Loading Application Bean's. -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:app-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<display-name>appServlet</display-name>
<servlet-name>appServlet</servlet-name>
<servlet-class>br.com.app.publicInterface.appServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/appServlet</url-pattern>
</servlet-mapping>
</web-app>

app-context.xml摘录:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations">
<list>
<value>classpath:app.properties</value>
</list>
</property>
</bean>
<import resource="classpath:app-data-context.xml"/>
<import resource="classpath:app-scheduler.xml"/>
<!-- DAOs -->
<!-- Services -->   
<!-- tasks -->
</beans>

app-scheduler.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean name="job" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="br.com.app.job.QuartzJob" />
<property name="jobDataAsMap">
<map>
<entry key="gerenciadorTasks">
<ref bean="gerenciadorTasks" />
</entry>
</map>
</property>
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="job" />
<property name="cronExpression" value="${quartz.cronExpression}" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.skipUpdateCheck">true</prop>
<prop key="org.quartz.jobStore.isClustered">true</prop> 
<prop key="org.quartz.scheduler.instanceName">NotificadorQuartzScheduler</prop>  
<prop key="org.quartz.scheduler.instanceId">AUTO</prop> 
</props>
</property>
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
</beans>

AppServlet源代码:

public class AppServlet extends HttpServlet   {
private static final long serialVersionUID = 7213474106234238692L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter out = resp.getWriter();
out.println("execução iniciada em: " + new Date());
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:app-context.xml");   
GerenciadorTasks gt = (GerenciadorTasks) context.getBean(SpringNameBeans.GERENCIADOR_TASKS);
gt.init();
out.println("execução terminada em: " + new Date());
}
}

非常感谢您抽出时间。

解决方案

---根据@Boris的信息,我对servlet进行了一些更改。我的上下文已经加载,我只需要访问它:

public class AppServlet extends HttpServlet   {
private static final long serialVersionUID = 7213474106234238692L;
ApplicationContext applicationContext = null;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter out = resp.getWriter();
out.println("execução iniciada em: " + new Date());
if (applicationContext == null){
applicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
}
GerenciadorTasks gt = (GerenciadorTasks) applicationContext.getBean(SpringNameBeans.GERENCIADOR_TASKS);
gt.init();
out.println("execução terminada em: " + new Date());
}
}

在您的代码中,您要在每个GET方法上创建一个新的应用程序上下文,您应该在servlet init()mehod中创建上下文,或者使用Web App根目录:

public class AppServlet extends HttpServlet   {
@Autowired
ApplicationContext applicationContext;
@Override
public void init(ServletConfig arg0) throws ServletException {
WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext())
.getAutowireCapableBeanFactory().autowireBean(this);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter out = resp.getWriter();
out.println("execução iniciada em: " + new Date());
GerenciadorTasks gt = (GerenciadorTasks) this.applicationContext.getBean(SpringNameBeans.GERENCIADOR_TASKS);
gt.init();
out.println("execução terminada em: " + new Date());
}
}

目前还不清楚您的GerenciadorTasks是否是singleton,所以在我的回答中,我使用了getbean()而不是autowiring在每个GET请求上调用init()不太可能是您想要做的事情。另外请注意,根web应用程序上下文已经在您的配置中创建

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:app-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

如果您想在春季使用调度,请考虑使用相应的API。

因此,我认为只使用根web应用程序上下文和调度api是可行的。还可以看看Spring lifecyle beans。

最新更新