论坛首页 Java企业应用论坛

定时任务的声明性事务问题

浏览 9238 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2006-05-17  
使用这个标题是为了“害人听闻”也。

我把它贴出来,可能有人已经遇到过了,不知道有没有人解决过这样的问题。

问题来源:
对于一个声明性事务的bean配置,其Spring配置文件可以是以下格式:
1.首先是声明target bean的声明:
<bean id="sampleServiceTarget"  classs="com.xxx.service.SampleServiceImpl"/>
2.其次是事务代理bean声明:
<bean id="sampleService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
   <property name="transactionManager">
	<ref bean="transactionManager" />
    </property>
    <property name="target">
	<ref bean="sampleServiceTarget" />
    </property>
	<property name="transactionAttributes">
	   <props>
             <prop key="business_method">PROPAGATION_REQUIRED</prop>
	   </props>
	</property>
 </bean>

经过这样的声明之后,调用从Spring容器中查找出该bean的实例,并且调用sampleService的business_method方法时,该方法就被纳入Spring
的容器管理的事务中,一旦该方法发生RuntimeException,事务会自动的回滚。

现在遇到的问题是,假如这个sampleService的business_method是定时触发的任务(或者定时调用)则如何配置呢?

Spring定时调度依赖于quartz.jar,例如给上面的business_method配置定时任务的spring配置文件可以是如下的格式:

<bean id="sampleServiceScheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
     <property name="delay">
	<value>120000</value>
     </property>		
     <property name="period">
	<value>60000</value>
     </property>
     <property name="timerTask">
	<ref bean="sampleServiceOnTimeJob" />
     </property>
</bean>
<bean id="sampleServiceOnTimeJob" class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
    <property name="targetObject">
	<ref bean="sampleServiceTarget" />
     </property>
     <property name="targetMethod">
	<value>business_method</value>
     </property>
</bean>
<bean id="sampleServiceTimerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
     <property name="scheduledTimerTasks">
	<list>
	   <ref bean="sampleServiceScheduledTask" />
	</list>
     </property>
</bean>	


配置的文件的重要部分在于第二个bean的声明,定时调度代理使用的targetObject是sampleServiceTarget,定时触发的方法targetMethod是business_method,经过这些声明配置之后,容器一旦启动,延时120000毫秒后,business_method会自动运行,且每隔60000毫秒运行一次。

现在的问题是,被代理的business_method没有纳入Spring容器管理的事务中,一旦出现异常,事务无法回滚。

把targetObject设置为sampleService不就可以了吗?也不可以,sampleService是一个ProxyFactoryBean,一旦把targetObject设置为sampleService,spring容器提示无法找到business_method的方法,因为这个方法是在运行的时候有Spring动态生成的。
   发表时间:2006-05-17  
大愚弱智 写道
把targetObject设置为sampleService不就可以了吗?也不可以,sampleService是一个ProxyFactoryBean,一旦把targetObject设置为sampleService,spring容器提示无法找到business_method的方法,因为这个方法是在运行的时候有Spring动态生成的。

BS标题党,偶们一直用targetObject设置为TransactionProxyFactoryBean构建出来的bean,从来没有遇到你说的无法找到方法的问题。要么是你的spring版本太低,要么就是你的配置有问题。
0 请登录后投票
   发表时间:2006-05-17  
大愚弱智 写道


把targetObject设置为sampleService不就可以了吗?也不可以,sampleService是一个ProxyFactoryBean,一旦把targetObject设置为sampleService,spring容器提示无法找到business_method的方法,因为这个方法是在运行的时候有Spring动态生成的。


大老失手阴沟里翻船的说?
就是绑到sampleService上啊,ProxyFactoryBean是FactoryBean,容器里的是getObject的返回值啊.

本人已经证明可以解决






btw,是不是classpath里没有cglib而且service implements了某个interface结果被jdk DynamicProxy代理了?
0 请登录后投票
   发表时间:2006-05-17  
不好意思啊,其实我很少到技术论坛发贴的,不到崩溃的边缘是不会Q助于论坛。
因为我觉得与其用心良苦的写完这么一个诱惑别人回复的帖子,我还不如上百度,而且大部分的问题都是这么解决的。

说明一下,我这个问题是一年前遇到的,可能是使用的spring版本可能比较低,或者是粗心大意吧,所以定时任务没有配置事务,我们的产品快发布了,我检查代码漏洞的时候检查出来这个bug。

我折腾一下,想出来一个很变态的方法,使用ApplicationContextAware。还是拿刚才那个例子来说:

1.事务bean仍是原来的声明:
<bean id="sampleServiceTarget" classs="com.xxx.service.SampleServiceImpl"/> 
<bean id="sampleService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
   <property name="transactionManager"> 
        <ref bean="transactionManager" /> 
    </property> 
    <property name="target"> 
        <ref bean="sampleServiceTarget" /> 
    </property> 
        <property name="transactionAttributes"> 
           <props> 
             <prop key="business_method">PROPAGATION_REQUIRED</prop> 
           </props> 
        </property> 
</bean> 

2.定时任务配置的targetObject配置为另外一个bean:
<bean id="sampleServiceScheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask"> 
     <property name="delay"> 
        <value>120000</value> 
     </property>                
     <property name="period"> 
        <value>60000</value> 
     </property> 
     <property name="timerTask"> 
        <ref bean="sampleServiceOnTimeJob" /> 
     </property> 
</bean> 
<bean id="sampleServiceOnTimeJob" class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean"> 
    <property name="targetObject"> 
        <ref bean="anotherSampleServiceTarget" /> 
     </property> 
     <property name="targetMethod"> 
        <value>anotherBusiness_method</value> 
     </property> 
</bean> 
<bean id="sampleServiceTimerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean"> 
     <property name="scheduledTimerTasks"> 
        <list> 
           <ref bean="sampleServiceScheduledTask" /> 
        </list> 
     </property> 
</bean>      
<bean id="anotherSampleServiceTarget" classs="com.xxx.service.AnotherSampleServiceImpl"/>   

AnotherSampleServiceImpl.java的定义如下:
import org.apache.log4j.Logger;
public class AnotherSampleServiceImpl implements ApplicationContextAware {
	ApplicationContext ctx = null;
	protected final Logger logger = Logger.getLogger(this.getClass(););;
	public void anotherBusiness_method(); {
		System.out.println("Auto run job");;
		ISampleService sampleService = (ISampleService ); ctx
				.getBean("sampleService");;
		sampleService.business_method();;
	}
	public void setApplicationContext(ApplicationContext ctx);
			throws BeansException {
		this.ctx = ctx;
	}
}


定时任务的事务的问题终于可以解决了!
0 请登录后投票
   发表时间:2006-05-17  
非常感谢你们的回答,我有时间检查一下以前的做法到底对不对,也升级一下spring的版本,
谢谢!
0 请登录后投票
   发表时间:2006-05-17  
为什么不单独写一个class,定义一个method做任务调度,把你的Service注入给这个class呢?这样岂不是没有你所说的事务失效的问题了?

用那么恐怖一个标题,有招人眼球的嫌疑。
0 请登录后投票
   发表时间:2006-05-19  
昨天我也遇到类似楼主说的找不到method的情况,把我吓了一跳:因为昨天运行的时候都没有出现这个问题。检查发现我在被调用的函数多加了一个参数,觉得可能是这个引起的问题。我查看了Spring的MethodInvoker的源代码,验证了我的想法。

	public void prepare(); throws ClassNotFoundException, NoSuchMethodException {
		...

		if (this.arguments == null); {
			this.arguments = new Object[0];
		}

		Class[] argTypes = new Class[this.arguments.length];
		for (int i = 0; i < this.arguments.length; ++i); {
			argTypes[i] = (this.arguments[i] != null ? this.arguments[i].getClass(); : Object.class);;
		}

		// Try to get the exact method first.
		try {
			this.methodObject = this.targetClass.getMethod(this.targetMethod, argTypes);;
		}
		catch (NoSuchMethodException ex); {
			// Just rethrow exception if we can't get any match.
			this.methodObject = findMatchingMethod();;
			if (this.methodObject == null); {
				throw ex;
			}
		}

		...
	}


而在Spring的配置中Job的定义是:
<bean id="settlementJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
		<property name="targetObject" ref="settlementBizFacade"/>
		<property name="targetMethod" value="doSettlement"/>
		<property name="concurrent" value="false"/>
		<property name="name" value="settlement"/>
</bean>


被调用的targetObject的接口
public interface SettlementBizFacade {	
	void doSettlement(UserDto byUser);;	//原来是doSettlement();
	...
}


可以看到由于没有设置arguments,所以Spring会在启动时去找不带参数的doSettlement方法,所以肯定找不到,在启动的时候就会报错。

如果一定要这样要调用这个函数,必须设置arguments, Spring会根据arguments的Class来找被调用方法。

所以我在我的接口中多加了一个“doSettlement()”方法....... -_-!
0 请登录后投票
   发表时间:2006-08-03  
用spring的quartz支持就是为了直接使用配置的方式解决定时任务方法,如果为了解决persistent jobs的问题推而使用编码方式定制触发类,然后再去配置一个这个类的实例,还不如直接使用quartz的api写一个定时任务。

关于楼主的问题我也遇到过,我用的是spring1.2.6, 不过是在初始化的时候遇到了不能把经过spring代理的bean造型成org.quartz.Trigger类型(我的业务类实现了自己的业务接口并且有一个事务增强器被自动代理创建器加载)。

是不是用jdk proxy代理的带有事务支持的业务对象就不能作为Trgger注入到SchedulerFactoryBean了?
谢谢
0 请登录后投票
   发表时间:2006-08-03  
"大弱智"好久不见

~~~~~~~

我闪.............
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics