kaka的gravatar头像
kaka 2017-04-27 16:52:29
spring Bean初始化的循环引用问题

今天在做单元测试时候,出现了一个诡异的问题,异常如下:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'incidentOrderService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.suninfo.workflow.WorkFlowService com.suninfo.itil.impl.IncidentOrderServiceImpl.workFlowService; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'workflowService': Bean with name 'workflowService' has been injected into other beans [operationApproveOrderService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example

从异常可以看出spring初始化bean的时候出现了循环引用的问题

场景还原:

项目大概结构:worflow(流程子工程),config(配置子工程),api(所有公共service子工程),其中config这个子工程放置了所有基础的、公有配置文件,比如spring-mybatis.xml,所有子工程都引用这个config子工程。

之前我所有activiti流程相关的配置都放在spring-mybatis.xml中的,我自己负责的工作流子工程(workflow)引用这个基础的配置文件,没有问题,但是别的子工程引用这个配置文件就会报错,因为我定义的流程相关的一个AOP的类是放在我流程(workflow)的子工程里的,别的子工程引用会报找不到这个类,如果这个子工程可以独立部署的话,这样肯定不行。

于是在workflow这个子工程里增加了spring-activiti.xml这个配置文件,同时这个配置文件也作为单元测试中spring的上下文,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	   xmlns:mvc="http://www.springframework.org/schema/mvc"
	   xmlns:aop="http://www.springframework.org/schema/aop" 
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context" 
       xsi:schemaLocation="
       	http://www.springframework.org/schema/beans
 		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-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/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.0.xsd">

	<!--后期放开,生产环境只需要引用spring-mybatis.xml-->
	<!--<import resource="spring-mybatis.xml"></import>-->
    <import resource="spring-context.xml"></import>
    <!-- 领取工单任务后的aop-->
    <bean id="takeWorkflowTaskAspect" class="com.suninfo.aop.TakeWorkflowTaskAspect"/>
    <aop:config>
        <aop:pointcut id="takeWorkflowTaskPointcut" expression="execution(*  com.suninfo.workflow.WorkFlowService.takeTask(..))" />
        <aop:advisor pointcut-ref="takeWorkflowTaskPointcut" advice-ref="takeWorkflowTaskAspect"   />
    </aop:config>
    <!--流程activiti相关配置-->
    <!-- spring负责创建流程引擎的配置文件 -->
	<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
		<property name="history" value="full"/>
		<!-- 数据源 -->
		<property name="dataSource" ref="dataSource" />
		<!-- 配置事务管理器,统一事务 -->
		<property name="transactionManager" ref="transactionManager" />
		<!-- 设置建表策略,如果没有表,自动创建表 -->
		<property name="databaseSchemaUpdate" value="true" />
		<!-- 是否启动jobExecutor -->
		<property name="jobExecutorActivate" value="false" />

		<property name="eventListeners">
			<list>
				<ref bean="globalEventListener"/>
			</list>
		</property>
		<!--自动部署流程定义 -->
        <!--<property name="deploymentResources" value="classpath*:/workflow/*.bpmn" />-->
	</bean>
	<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
		<property name="processEngineConfiguration" ref="processEngineConfiguration" />
	</bean>
    <!--流程通知listener -->
    <bean id="noticeTaskListener" class="com.suninfo.event.NoticeTaskListener"/>
    <!--流程业务处理listener -->
    <bean id="dealBusinessListener" class="com.suninfo.event.DealBusinessListener"/>
	<!-- 流程引擎全局事件处理器-->
	<bean id="globalEventListener" class="com.suninfo.event.GlobalEventListener">
		<property name="handlers">
			<map>
				<entry key="TASK_CREATED" value="noticeTaskListener"/>
				<entry key="TASK_COMPLETED" value="dealBusinessListener"/>
			</map>
		</property>
	</bean>

	<!-- 创建activiti提供的各种服务 -->
	<!-- 工作流仓储服务 -->
	<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
	<!-- 工作流运行服务 -->
	<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
	<!-- 工作流任务服务-->
	<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
	<!-- 工作流历史数据服务-->
	<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
	<!-- 工作流管理服务-->
	<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
	<bean id="formService" factory-bean="processEngine" factory-method="getFormService" />
	<!-- 工作流唯一服务 -->
	<bean id="IdentityService" factory-bean="processEngine" factory-method="getIdentityService"/>


</beans>

spring-context.xml引用了一些mybatis、memcached、api等配置,如下:

<?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-4.0.xsd">
        
	<!-- 引入属性文件 -->
	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
        <property name="locations">  
            <list>
                <value>classpath:jdbc.properties</value>
                <value>classpath:memcache.properties</value>
                <value>classpath:mongodb.properties</value>
            </list>
        </property>  
    </bean>


	<import resource="spring-mybatis.xml" />
	<import resource="spring-api.xml" />
	<import resource="spring-memcached.xml" />
	<import resource="spring-mongodb.xml" />
</beans>

spring-api.xml中只是简单的扫描包

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context" 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/aop
        http://www.springframework.org/schema/aop/spring-aop-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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

    <context:annotation-config />
    <!-- 自动扫描(自动注入) -->
    <context:component-scan base-package="com.suninfo" />
</beans>

 

问题分析:

出现这个错误肯定是在类初始化中,而且肯定是WorkflowService这个类导致的,根据spring配置文件的加载顺序。首先是要去加载spring-context.xml这个配置文件的,加载spring-api.xml,进行包扫描,初始化所有注解的bean,当扫描到WorkflowServie时,发现这个类配置了AOP,于是就会去初始化这个TakeWorkflowTaskAspect,而TakeWorkflowTaskAspect这类如下:

public class TakeWorkflowTaskAspect implements AfterReturningAdvice {

    public TakeWorkflowTaskAspect(){
        System.out.println("TakeWorkflowTaskAspect被初始化了");
    }

    @Autowired
    private OperationApproveOrderService opService;
    
     ............................................

OperationApproveOrderService类如下:

@Service("operationApproveOrderService")
public class OperationApproveOrderServiceImpl implements OperationApproveOrderService {

    public OperationApproveOrderServiceImpl(){
        System.out.println("OperationApproveOrderServiceImpl被初始化");
    }

    @Autowired
    private WorkflowOperationApproveDao workflowOperationApproveDao;

    @Autowired
    private WorkFlowService workFlowService;

OperationApproveOrderServiceImpl里面又引用了WorkflowService,看到这里就明白了初始化循环引用的异常。

 

解决办法:

将spring-context.xml引用放入spring-activit.xml的底部,调整加载顺序

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	   xmlns:mvc="http://www.springframework.org/schema/mvc"
	   xmlns:aop="http://www.springframework.org/schema/aop" 
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context" 
       xsi:schemaLocation="
       	http://www.springframework.org/schema/beans
 		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-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/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.0.xsd">

	<!--后期放开,生产环境只需要引用spring-mybatis.xml-->
	<!--<import resource="spring-mybatis.xml"></import>-->
    <!-- 领取工单任务后的aop-->
    <bean id="takeWorkflowTaskAspect" class="com.suninfo.aop.TakeWorkflowTaskAspect"/>
    <aop:config>
        <aop:pointcut id="takeWorkflowTaskPointcut" expression="execution(*  com.suninfo.workflow.WorkFlowService.takeTask(..))" />
        <aop:advisor pointcut-ref="takeWorkflowTaskPointcut" advice-ref="takeWorkflowTaskAspect"   />
    </aop:config>
    <!--流程activiti相关配置-->
    <!-- spring负责创建流程引擎的配置文件 -->
	<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
		<property name="history" value="full"/>
		<!-- 数据源 -->
		<property name="dataSource" ref="dataSource" />
		<!-- 配置事务管理器,统一事务 -->
		<property name="transactionManager" ref="transactionManager" />
		<!-- 设置建表策略,如果没有表,自动创建表 -->
		<property name="databaseSchemaUpdate" value="true" />
		<!-- 是否启动jobExecutor -->
		<property name="jobExecutorActivate" value="false" />

		<property name="eventListeners">
			<list>
				<ref bean="globalEventListener"/>
			</list>
		</property>
		<!--自动部署流程定义 -->
        <!--<property name="deploymentResources" value="classpath*:/workflow/*.bpmn" />-->
	</bean>
	<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
		<property name="processEngineConfiguration" ref="processEngineConfiguration" />
	</bean>
    <!--流程通知listener -->
    <bean id="noticeTaskListener" class="com.suninfo.event.NoticeTaskListener"/>
    <!--流程业务处理listener -->
    <bean id="dealBusinessListener" class="com.suninfo.event.DealBusinessListener"/>
	<!-- 流程引擎全局事件处理器-->
	<bean id="globalEventListener" class="com.suninfo.event.GlobalEventListener">
		<property name="handlers">
			<map>
				<entry key="TASK_CREATED" value="noticeTaskListener"/>
				<entry key="TASK_COMPLETED" value="dealBusinessListener"/>
			</map>
		</property>
	</bean>

	<!-- 创建activiti提供的各种服务 -->
	<!-- 工作流仓储服务 -->
	<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
	<!-- 工作流运行服务 -->
	<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
	<!-- 工作流任务服务-->
	<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
	<!-- 工作流历史数据服务-->
	<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
	<!-- 工作流管理服务-->
	<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
	<bean id="formService" factory-bean="processEngine" factory-method="getFormService" />
	<!-- 工作流唯一服务 -->
	<bean id="IdentityService" factory-bean="processEngine" factory-method="getIdentityService"/>

    <import resource="spring-context.xml"></import>

</beans>

打赏
最近浏览
shuini  LV4 2018年11月29日
紫玉天辰  LV11 2018年9月14日
dshannong  LV3 2018年4月21日
千里草  LV7 2017年7月21日
jialili  LV1 2017年7月10日
王思峰  LV8 2017年5月25日
小王wang  LV10 2017年5月16日
等不到天亮等黑夜  LV1 2017年5月15日
如果我是周星驰  LV2 2017年5月11日
wscf  LV14 2017年5月11日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友