在使用Spring Batch時,JobExplorer做爲查詢瀏覽job以及Step的入口,能夠方便的讓咱們及時掌握Batch的執行狀況,通常會使用MapJobExplorerFactoryBean,它須要一個MapJobRepositoryFactoryBean屬性,在進行xml配置時,若是配置成下面這種狀況,會獲得一個exceptionjava
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" > <property name="transactionManager" ref="batchTransactionManager" /> </bean> <bean id="jobExplorer" class="org.springframework.batch.core.explore.support.MapJobExplorerFactoryBean"> <property name="repositoryFactory" ref="jobRepository"/> </bean>
Caused By: java.lang.IllegalStateException: Cannot convert value of type [$Proxy123 implementing org.springframework.batch.core.repository.JobRepository,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean] for property 'repositoryFactory': no matching editors or conversion strategy found at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:267) at org.springframework.beans.BeanWrapperImpl.convertIfNecessary(BeanWrapperImpl.java:449) at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:495) at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:489) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.convertForProperty(AbstractAutowireCapableBeanFactory.java:1465) Truncated. see log file for complete stacktrace
究其緣由, MapJobRepositoryFactoryBean是一個FactoryBean接口的實現,因此在注入到MapJobExplorerFactoryBean時,獲得的是getObject()返回的對象實例。spring
針對這個問題,咱們能夠將配置改成這種形式:express
<bean id="jobExplorer" class="org.springframework.batch.core.explore.support.MapJobExplorerFactoryBean"> <property name="repositoryFactory" ref="&jobRepository"/> </bean>
在Spring的官方文檔中,關於FactoryBean有這樣一段描述,注意這句「When you need to ask a container for an actual FactoryBean instance itself instead of the bean it produces, preface the bean’s id with the ampersand symbol ( &) when calling the getBean() method of the ApplicationContext」,大意是說,當經過getBean()想得到一個FactoryBean對象的實際實例時,能夠在它的bean id前加上「&」符號。「&」符號在XML中應轉義爲&,因此,便有了上面的配置app
7.8.3 Customizing instantiation logic with a FactoryBean Implement the org.springframework.beans.factory.FactoryBean interface for objects that are themselves factories. The FactoryBean interface is a point of pluggability into the Spring IoC container’s instantiation logic. If you have complex initialization code that is better expressed in Java as opposed to a (potentially) verbose amount of XML, you can create your own FactoryBean, write the complex initialization inside that class, and then plug your custom FactoryBean into the container. The FactoryBean interface provides three methods: Object getObject(): returns an instance of the object this factory creates. The instance can possibly be shared, depending on whether this factory returns singletons or prototypes. boolean isSingleton(): returns true if this FactoryBean returns singletons, false otherwise. Class getObjectType(): returns the object type returned by the getObject() method or null if the type is not known in advance. The FactoryBean concept and interface is used in a number of places within the Spring Framework; more than 50 implementations of the FactoryBean interface ship with Spring itself. When you need to ask a container for an actual FactoryBean instance itself instead of the bean it produces, preface the bean’s id with the ampersand symbol ( &) when calling the getBean() method of the ApplicationContext. So for a given FactoryBean with an id of myBean, invoking getBean("myBean") on the container returns the product of the FactoryBean; whereas, invoking getBean("&myBean") returns the FactoryBean instance itself.
另外,經過Spring的源碼,也能夠看到,對這塊邏輯的處理:ide
protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object sharedInstance = getSingleton(beanName); Object bean; if ((sharedInstance != null) && (args == null)) { if (this.logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { this.logger.debug(new StringBuilder().append("Returning eagerly cached instance of singleton bean '").append(beanName).append("' that is not fully initialized yet - a consequence of a circular reference").toString()); } else { this.logger.debug(new StringBuilder().append("Returning cached instance of singleton bean '").append(beanName).append("'").toString()); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } //省略.... } protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) { if ((BeanFactoryUtils.isFactoryDereference(name)) && (!(beanInstance instanceof FactoryBean))) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } //BeanFactoryUtils.isFactoryDereference判斷name是否以"&"開始,若是以"&"開始 返回true if ((!(beanInstance instanceof FactoryBean)) || (BeanFactoryUtils.isFactoryDereference(name))) { return beanInstance; } Object object = null; if (mbd == null) { object = getCachedObjectForFactoryBean(beanName); } if (object == null) { FactoryBean factory = (FactoryBean)beanInstance; if ((mbd == null) && (containsBeanDefinition(beanName))) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null) && (mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }