今天在做单元测试时候,出现了一个诡异的问题,异常如下:
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>



最近浏览
