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