乐趣区

关于springboot:activiti整合springboot问题总结

因我的项目需要,将 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 时发现两个类加载器 RestartClassLoaderAppClassLoader中存在不同的 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 类》

退出移动版