2014-02-13 33 views
12

我們有一個Spring Web應用程序,我們將從Spring 3.2移植到Spring 4.我們的應用程序在Web應用程序啓動時將幾個子上下文組合到一個運行時上下文中。爲什麼Spring 4只允許一個TaskScheduler在上下文中?

我們在兩個子上下文中使用單獨的TaskScheduler。有了Spring 3.2,這個工作正常。使用Spring 4時,我們得到一個異常以下消息:

java.lang.IllegalStateException: More than one TaskScheduler and/or ScheduledExecutorService exist within the context. Remove all but one of the beans; or implement the SchedulingConfigurer interface and call ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback. Found the following beans: [commonScheduler, communicationTaskScheduler] 
     at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:289) ~[spring-context-4.0.1.RELEASE.jar:4.0.1.RELEASE] 
     at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:72) ~[spring-context-4.0.1.RELEASE.jar:4.0.1.RELEASE] 
     at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:98) ~[spring-context-4.0.1.RELEASE.jar:4.0.1.RELEASE] 
     at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:333) ~[spring-context-4.0.1.RELEASE.jar:4.0.1.RELEASE] 
     at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:776) ~[spring-context-4.0.1.RELEASE.jar:4.0.1.RELEASE] 
     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:485) ~[spring-context-4.0.1.RELEASE.jar:4.0.1.RELEASE] 
     at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403) ~[spring-web-4.0.1.RELEASE.jar:4.0.1.RELEASE] 
     at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306) ~[spring-web-4.0.1.RELEASE.jar:4.0.1.RELEASE] 
     at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106) ~[spring-web-4.0.1.RELEASE.jar:4.0.1.RELEASE] 
     at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4961) ~[catalina.jar:7.0.50] 
     at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5455) ~[catalina.jar:7.0.50] 
     at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) ~[catalina.jar:7.0.50] 
     at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901) ~[catalina.jar:7.0.50] 
     at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877) ~[catalina.jar:7.0.50] 
     at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:634) ~[catalina.jar:7.0.50] 
     at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1074) ~[catalina.jar:7.0.26] 
     at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1858) ~[catalina.jar:7.0.26] 
     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) ~[na:1.7.0_25] 
     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) ~[na:1.7.0_25] 
     at java.util.concurrent.FutureTask.run(FutureTask.java:166) ~[na:1.7.0_25] 
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) ~[na:1.7.0_25] 
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) ~[na:1.7.0_25] 
     at java.lang.Thread.run(Thread.java:724) ~[na:1.7.0_25] 

一個調度器通過定義:

<?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:task="http://www.springframework.org/schema/task" 
    xsi:schemaLocation=" 
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd"> 

    <!-- Enables annotation-driven task scheduling; detects @Scheduled- and 
     @Async-annotated process methods to be invoked via proxy --> 
    <task:annotation-driven mode="aspectj" /> 
    <task:scheduler id="commonScheduler" pool-size="5" /> 

</beans> 

其他調度器在(爲了清楚起見移除額外豆)中所定義:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" 
    xsi:schemaLocation=" 
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd 
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd 
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> 

    <context:spring-configured /> 

    <bean id="communicationExecutor" 
     class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> 
     <property name="corePoolSize" value="15" /> 
     <property name="maxPoolSize" value="20" /> 
     <property name="queueCapacity" value="20" /> 
    </bean> 
    <bean id="communicationTaskScheduler" 
     class="org.springframework.scheduling.concurrent.ConcurrentTaskScheduler"> 
     <property name="concurrentExecutor" ref="communicationExecutor" /> 
    </bean> 

</beans> 

上下文在運行時使用(爲了清楚起見刪除了其他上下文):

<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-4.0.xsd"> 

    <import resource="classpath:spring/tasks-context.xml" /> 
    <import resource="classpath:spring/collectors-context.xml" /> 
</beans> 

爲什麼Spring 4有這個限制?應該如何解決它?

+1

,你能否告訴堆棧和你實際的上下文的休息嗎? –

回答

15

是的,這是一個肯定的行爲改變。看起來ScheduledAnnotationBeanPostProcessor現在每個上下文有2個調度程序的限制。我在Spring 4的新WebSocket消息代理中遇到了這個問題,因爲它爲Simple STOMP代理和SockJS適配器分配了2個調度器。當我加入我自己的時候,它完全拋棄了你得到的同樣的信息。我發現它很煩人,我不得不通過錯誤而不是文檔來找出這個限制。這個問題似乎沒有在Spring 4文檔中描述。

解決方案是創建自己的SchedulingConfigurer,它管理自己的TaskScheduler。我懷疑,原因是隻有一個和額外的TaskScheduler實現需要添加以隔離它們彼此。我做了這樣的事情:

@Configuration 
@EnableScheduling 
public class MySchedulingConfigurer implements SchedulingConfigurer 
{ 
    @Bean 
    public TimedThingy timedThingy() 
    { 
     return new TimedThingy(); 
    } 

    @Bean() 
    public ThreadPoolTaskScheduler taskScheduler() { 
    return new ThreadPoolTaskScheduler(); 
    } 

    @Override 
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) 
    { 
     taskRegistrar.setTaskScheduler(taskScheduler()); 
     taskRegistrar.addFixedRateTask(new Runnable() 
     { 
     public void run() 
     { 
      timedThingy().sendIt(); 
     } 
     }, 1000); 
    } 
} 

一旦我這樣做,問題就消失了,事情按預期工作。不利的一面是,你不能使用像@Scheduled等註釋。但是你確實獲得了更多的控制權,而且事情起作用。希望這可以幫助。

+1

你提到的代碼有一個'schedulers.size()> = 2'的條件,所以它不允許2個調度器,它只允許一個。 – eis

+1

我能夠通過重寫taskScheduler來利用我的@Scheduled註釋(就像你所做的那樣)。無需在configureTasks()內添加任務。最初,我也對SockJS的東西也有同樣的問題。 – hoserdude

+0

hoserdude:你能提供一個例子嗎?這個解決方案非常好,謝謝.. – jvmvik

0

您需要明確指定使用哪個調度程序與@Scheduled註釋。只需添加scheduler屬性:

<task:annotation-driven scheduler="commonScheduler" mode="aspectj" /> 
<task:scheduler id="commonScheduler" pool-size="5" /> 
0

豆只需添加到您的配置:

@Bean() 
public ThreadPoolTaskScheduler taskScheduler() { 
    return new ThreadPoolTaskScheduler(); 
} 
相關問題