由Spring Quartz 实例化时抛出SQL Exception的问题来看spring的autowire(自动装配)

项目使用Spring Quartz,声明如下schedule:
<beans default-autowire="byName">
...
<bean id="schedulerBackend" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="schedulerName">
<value>quartz_backend</value>
</property>
<property name="triggers">
<list>
<!-- 自定义Cron trigger bean -->
<ref local="Customer_CronTriggerBean"/>
</list>
</property>
</bean>
</beans>

OAS环境下部署应用抛出如下异常:
[ERROR][2010-03-02 15:06:25,549] Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'backEndService' defined in class path resource [service-appContext.xml]: Cannot resolve reference to bean 'schedulerBackend' while setting bean property 'scheduler'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schedulerBackend' defined in class path resource [service-appContext.xml]: Invocation of init method failed; nested exception is org.quartz.SchedulerConfigException: Failure occured during job recovery. [See nested exception: org.quartz.impl.jdbcjobstore.LockException: Failure obtaining db row lock: ORA-00942: table or view does not exist
[See nested exception: java.sql.SQLException: ORA-00942: table or view does not exist
]]
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:275)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:104)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1210)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:978)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:462)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:404)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:375)
... ...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schedulerBackend' defined in class path resource [service-appContext.xml]: Invocation of init method failed; nested exception is org.quartz.SchedulerConfigException: Failure occured during job recovery. [See nested exception: org.quartz.impl.jdbcjobstore.LockException: Failure obtaining db row lock: ORA-00942: table or view does not exist
[See nested exception: java.sql.SQLException: ORA-00942: table or view does not exist
]]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1302)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:463)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:404)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:375)
... 35 more
Caused by: org.quartz.SchedulerConfigException: Failure occured during job recovery. [See nested exception: org.quartz.impl.jdbcjobstore.LockException: Failure obtaining db row lock: ORA-00942: table or view does not exist
[See nested exception: java.sql.SQLException: ORA-00942: table or view does not exist
]]
at org.quartz.impl.jdbcjobstore.JobStoreSupport.initialize(JobStoreSupport.java:493)
at org.quartz.impl.jdbcjobstore.JobStoreCMT.initialize(JobStoreCMT.java:144)
... 45 more

本身定义的Quartz部分的应用根本涉及不到DB, 怎么会抛出SQL Exception呢,
查看一下出问题的源码

org.springframework.scheduling.quartz.SchedulerFactoryBean

if(dataSource != null)mergedProps.put("org.quartz.jobStore.class", (org.springframework.scheduling.quartz.LocalDataSourceJobStore.class).getName());

看来异常就抛出在这个地方,但是SchedulerFactoryBean的dataSource属性并没有指定啊,为什么不为空呢?
上网搜了一下,原来问题出在Spring配置文件的autowire上。
spring提供了一个通过配置文件进行注入的很简便的功能,就是<beans default-autowire="byName">这段,翻了一下Spring的官方文档,autowire可以配置5个值:
[list]
[b]no [/b]-- (Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system.
默认值,不自动装配,给Bean注入属性必须通过<ref />标签,基于较大的spring配置文件,推荐使用这个默认值而不是其他的值,对每一个bean的注入都单独指定,这样比较清晰切易读,一个bean被注入了哪些属性,每个属性都注入的是什么值 都明确指定,一定程度上体现了一个系统的层次结构。
[/list]
[list]
[b]byName [/b]-- Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired.
按照属性的名称装配,spring根据bean的属性名自动在指定范围内搜索,如果找到名称匹配的的bean描述,就自动注入给定义的bean。而之前启动quartz失败,也正是因为spring的配置文件指定了default-autowire="byName",而项目中其他部分用的datasource正好命名成通用的“dataSource”,这样spring就“自作聪明”把项目里用的dataSource也注入给了本不想要它的SchedulerFactoryBean。
[/list]
[list]
[b]byType [/b]-- Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set. If this is not desirable, setting the dependency-check="objects" attribute value specifies that an error should be thrown in this case.
按照类型装配,按照文档的描述,还是不用它为妙,举例:一个bean1有一个属性是A类型的,而上下文中有一个bean2是A类型的,这样spring就自动把bean2的实例赋值给bean1的这个属性。当上下文中有多个A类型的bean时,这个配置会导致抛出致命异常,而那个A类型的属性也不会set值。
[/list]
[list]
[b]constructor [/b]-- Analogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.
byType的一个变通,只是把byType情况的属性替换为构造函数的参数。相当于上例中的这个bean1的属性改成bean1的构造方法的一个参数。spring把相同类型的bean赋值给这个参数。同类型的参数多于一个时也会抛出致命异常
[/list]
[list]
[b]autodetect [/b]-- Chooses constructor or byType through introspection of the bean class. If a default constructor is found, the byType mode is applied.
自动指定,按照类型和按照构造器的一个折衷,如果bean1有默认构造器(无参),则按照byType处理,否则,按照constructor处理。

[/list]
理解了上面的描述,再回头来看这个问题,解决方案就明朗了:
方法一:去掉default-autowire="byName"配置,也就是用默认配置,这样spring就不会隐式的注入值给属性
方法二:如果想保留byName配置,可以显式的注入null给bean,这样spring也不会把不期望的值再注入给属性,比如上面的配置文件可以改成如下

<beans default-autowire="byName">
...
<bean id="schedulerBackend" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource">
<null /><!-- null 标签,强制把dataSource属性设置为空-->
</property>
<property name="schedulerName">
<value>quartz_backend</value>
</property>
<property name="triggers">
<list>
<!-- 自定义Cron trigger bean -->
<ref local="Customer_CronTriggerBean"/>
</list>
</property>
</bean>
</beans>
这里要注意,如果设置成<value><null/></value>,是不对的,<null />标签直接配置给<property>标签。

方法三: 使byName的配置对这个bean失效,这就有好多实现方式了,例如官方文档上说
Avoid autowiring for a bean definition by setting its autowire-candidate attributes to false ..
也就是把这个bean的autowire-candidate设置为false

<beans default-autowire="byName">
...
<bean id="schedulerBackend" autowire-candidate="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="schedulerName">
<value>quartz_backend</value>
</property>
<property name="triggers">
<list>
<!-- 自定义Cron trigger bean -->
<ref local="Customer_CronTriggerBean"/>
</list>
</property>
</bean>
</beans>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值