因项目需求,将cloud项目的工作流模块迁移到springboot项目中。将中途遇到的一些问题整合到此处。
已处理部分
activiti依赖spring与项目spring不兼容。
解决
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring-boot-starter.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
MyBatis依赖冲突
启动报错:
***************************
APPLICATION FAILED TO START
***************************
Description:
An attempt was made to call a method that does not exist. The attempt was made from the following location:
org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:564)
The following method did not exist:
org.apache.ibatis.session.Configuration.setDefaultEnumTypeHandler(Ljava/lang/Class;)V
The method's class, org.apache.ibatis.session.Configuration, is available from the following locations:
jar:file:/D:/Program%20Files/maven/.m2/repository/org/mybatis/mybatis/3.4.2/mybatis-3.4.2.jar!/org/apache/ibatis/session/Configuration.class
It was loaded from the following location:
file:/D:/Program%20Files/maven/.m2/repository/org/mybatis/mybatis/3.4.2/mybatis-3.4.2.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of org.apache.ibatis.session.Configuration
Disconnected from the target VM, address: '127.0.0.1:64349', transport: 'socket'
Process finished with exit code 1
解决
引入activiti时排除MyBatis
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
activiti 的bean与项目中的bean重名。
注:已配置 spring.main.allow-bean-definition-overriding: true
允许项目存在重名bean。
通过注解创建的bean默认使用org.springframework.context.annotation.AnnotationBeanNameGenerator#generateBeanName
生成beanName。如果没有指定名字,则默认使用首字母小写的类名或者方法名。
通过beanFactory将activiti的TaskService、HistoryService等服务注入spring容器中。项目本身也有同名的service时,同名的只有一个service被创建。
// activiti Service创建:
@Bean
public ProcessEngineConfiguration processEngineConfiguration(DataSource dataSource, PlatformTransactionManager transactionManager) {
SpringProcessEngineConfiguration processEngineConfiguration = new SpringProcessEngineConfiguration();
processEngineConfiguration.setDataSource(dataSource);
processEngineConfiguration.setDatabaseType("mysql");
processEngineConfiguration.setTransactionManager(transactionManager);
return processEngineConfiguration;
}
/**
* 流程引擎,与spring整合使用factoryBean
*/
@Bean
public ProcessEngineFactoryBean processEngine(ProcessEngineConfiguration processEngineConfiguration) {
ProcessEngineFactoryBean processEngineFactoryBean = new ProcessEngineFactoryBean();
processEngineFactoryBean.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration);
return processEngineFactoryBean;
}
@Bean
public RepositoryService repositoryService(ProcessEngine processEngine) {
return processEngine.getRepositoryService();
}
@Bean
public RuntimeService runtimeService(ProcessEngine processEngine) {
return processEngine.getRuntimeService();
}
@Bean
public TaskService taskService(ProcessEngine processEngine) {
return processEngine.getTaskService();
}
@Bean
public HistoryService historyService(ProcessEngine processEngine) {
return processEngine.getHistoryService();
}
问题排查
spring默认的重名处理方法为org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
,直接覆盖先前的bean。
监控 org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
发现,通过ComponentScan
扫描的bean最先被创建,而所以导致通过@Service
创建的bean被覆盖,@Autowrite
无法注入。虽然可以通过修改类名或者指定bean名称解决,但是闲着也是闲着,不如折腾一下。
既然因为重名导致被覆盖,又不想手动改名字,那就修改生成beanName的规则。
继承AnnotationBeanNameGenerator
重写generateBeanName
方法,使用类的完整路径作为bean名称。
项目使用springboot2.2.13 需要通过@ComponentScan( basePackages = {"xxx"},nameGenerator = UniqueNameGenerator.class)
配置生效。
此处又有几个小知识点:@SpringBootApplication
默认扫描同级目录下的包。@ComponentScan
会覆盖@SpringBootApplication
中配置的路径,而@ComponentScans
不会。先前画蛇添足使用了@ComponentScans
,程序会把两种扫包规则都执行一遍,而@ComponentScans
使用的是自定义的命名规则@SpringBootApplication
使用的是默认的命名规则,导致部分包下的bean会被创建两遍。
报错:
implementations of CachingConfigurer were found when only 1 was expected. Refactor the configuration such that CachingConfigurer is implemented only once or not at all.
解决
使用自定义beanName生成规则,并通过ComponentScan
配置
public class UniqueNameGenerator extends AnnotationBeanNameGenerator {
@Override
@NonNull
public String generateBeanName(@NonNull BeanDefinition definition,@NonNull BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) {
//获取指定beanName
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
return beanName;
}
}
return Objects.requireNonNull(definition.getBeanClassName());
}
}
@ComponentScan(basePackages = {"com.xxx", "org.activiti.engine.impl"}, nameGenerator = UniqueNameGenerator.class)
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application{
调用接口报错,两个类加载器中有两个不同的对象
Handler dispatch failed; nested exception is java.lang.LinkageError: loader constraint violation: when resolving interface method \"org.activiti.engine.RepositoryService.createModelQuery()Lorg/activiti/engine/repository/ModelQuery;\" the class loader (instance of org/springframework/boot/devtools/restart/classloader/RestartClassLoader) of the current class, com/pms/workflow/service/ModelService, and the class loader (instance of sun/misc/Launcher$AppClassLoader) for the method's defining class, org/activiti/engine/RepositoryService, have different Class objects for the type org/activiti/engine/repository/ModelQuery used in the signature
错误内容大意是:调用RepositoryService.createModelQuery()Lorg
时发现两个类加载器RestartClassLoader
和AppClassLoader
中存在不同的class对象
AppClassLoader
是JVM的应用类加载器,RestartClassLoader
是个啥?
点进去一看路径org.springframework.boot.devtools.restart.classloader.RestartClassLoader
,
水平有限搞不懂问题的根源,删掉spring-boot-devtools
依赖就完事。
未处理部分
Spring
springboot多模块项目,新建的子模块内容无法引入
估计是IDE问题,删除后新建就好了
参考
《聊Spring的bean覆盖(存在同名name/id问题),介绍Spring名称生成策略接口BeanNameGenerator》
《SprintBoot devtools导致同一个类出现两个不同的Class类》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。