2

数か月前、データベースに特定の基準が見つかった場合に、ユーザーにメールを送信するために毎晩実行するだけの簡単なアプリケーションを作成しました。これを実現するために、Spring3.1.0とQuartz1.8.5を使用しました。ここまでは順調ですね。

アプリはweblogicサーバーにデプロイされます。

先週、URLを介して実行するように手動でコマンドできるようにアプリを変更するように依頼されました。

サーブレットを追加しましたが、正常に機能しました。誰かがURLを呼び出すたびに、アプリが実行されます。

しかし、1つの付随的な効果が現れました。各サーブレットの呼び出しは、Springコンテキストのリロードも引き起こし、Quartzの別のインスタンスが作成されます。したがって、上記のサーブレットがn回呼び出された場合、Quartzがジョブを起動すると、アプリは1回だけではなく、n+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

サーブレットが呼び出されるたびに発生するログファイルの抜粋:

<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の情報に従って、サーブレットを少し変更しました。私のコンテキストはすでにロードされているので、それにアクセスする必要がありました:

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());

    }

}
4

1 に答える 1

2

コードでは、すべてのGETメソッドで新しいアプリケーションコンテキストを作成します。代わりに、サーブレットinit()mehodでコンテキストを作成するか、Web Approotcontetを使用する必要があります。

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がシングルトンであるかどうかは明らかではないので、私の答えでは、自動配線の代わりにgetbean()を使用しました。すべての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の使用を検討してください。

したがって、スケジューリングAPIでのみルートWebアプリケーションコンテキストのみを使用するのが最善の方法だと思います。春のライフサイクル豆もご覧ください。

于 2012-11-29T12:49:15.673 に答える