mamoe / mirai

高效率 QQ 机器人支持库
https://mirai.mamoe.net
GNU Affero General Public License v3.0
14.35k stars 2.53k forks source link

mirai-console插件开发:根据包名路径动态获取类失败 #2720

Closed 954L closed 1 year ago

954L commented 1 year ago

问题描述

gradle + java + mybatis 用main函数右键启动mybatis的配置一切正常 用mirai的runConsole指令则提示class路径内找不到任何java类导致配置失败

image image

用runConsole启动后提示如下错误:

2023-07-01 15:59:09 W/stderr: org.apache.ibatis.binding.BindingException: Type interface com.w954l.bot.mapper.BotDoMapper is not known to the MybatisPlusMapperRegistry.
2023-07-01 15:59:09 W/stderr:   at mirai-console-954l-dev.mirai2.jar[private]//com.baomidou.mybatisplus.core.MybatisMapperRegistry.lambda$getMapper$1(MybatisMapperRegistry.java:54)
2023-07-01 15:59:09 W/stderr:   at java.base/java.util.Optional.orElseThrow(Optional.java:403)
2023-07-01 15:59:09 W/stderr:   at mirai-console-954l-dev.mirai2.jar[private]//com.baomidou.mybatisplus.core.MybatisMapperRegistry.getMapper(MybatisMapperRegistry.java:54)
2023-07-01 15:59:09 W/stderr:   at mirai-console-954l-dev.mirai2.jar[private]//com.baomidou.mybatisplus.core.MybatisConfiguration.getMapper(MybatisConfiguration.java:192)
2023-07-01 15:59:09 W/stderr:   at mirai-console-954l-dev.mirai2.jar[private]//org.apache.ibatis.session.defaults.DefaultSqlSession.getMapper(DefaultSqlSession.java:288)
2023-07-01 15:59:09 W/stderr:   at mirai-console-954l-dev.mirai2.jar//com.w954l.bot.config.DbConfig.init(DbConfig.java:78)
2023-07-01 15:59:09 W/stderr:   at mirai-console-954l-dev.mirai2.jar//com.w954l.bot.BotApplication.systemInit(BotApplication.java:77)
2023-07-01 15:59:09 W/stderr:   at mirai-console-954l-dev.mirai2.jar//com.w954l.bot.BotApplication.onEnable(BotApplication.java:50)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.internal.plugin.JvmPluginInternal.internalOnEnable$mirai_console(JvmPluginInternal.kt:237)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.internal.plugin.BuiltInJvmPluginLoaderImpl.enable(BuiltInJvmPluginLoaderImpl.kt:308)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.internal.plugin.BuiltInJvmPluginLoaderImpl.enable(BuiltInJvmPluginLoaderImpl.kt:39)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.plugin.jvm.JvmPluginLoader$BuiltIn.enable(JvmPluginLoader.kt)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.plugin.jvm.JvmPluginLoader$BuiltIn.enable(JvmPluginLoader.kt:54)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.plugin.PluginManager.enablePlugin(PluginManager.kt:173)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.internal.plugin.PluginManagerImpl.enableAllLoadedPlugins$mirai_console(PluginManagerImpl.kt:181)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge.doStart$mirai_console(MiraiConsoleImplementationBridge.kt:359)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.MiraiConsoleImplementation$Companion.start(MiraiConsoleImplementation.kt:512)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader.startAsDaemon(MiraiConsoleTerminalLoader.kt:182)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader.startAsDaemon$default(MiraiConsoleTerminalLoader.kt:181)
2023-07-01 15:59:09 W/stderr:   at net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader.main(MiraiConsoleTerminalLoader.kt:59)

build.gradle.kts

plugins {
    val kotlinVersion = "1.7.10"
    kotlin("jvm") version kotlinVersion
    kotlin("plugin.serialization") version kotlinVersion
    id("net.mamoe.mirai-console") version "2.15.0-RC"
    id("io.freefair.lombok") version "5.3.0"
}

group = "com.w954l.bot"
version = "1.0.0"

repositories {
    if (System.getenv("CI")?.toBoolean() != true) {
       maven("https://maven.aliyun.com/repository/public")
    }
    mavenCentral()
}

dependencies {
    compileOnly("org.projectlombok:lombok:1.18.22")
    annotationProcessor("org.projectlombok:lombok:1.18.22")

    implementation(fileTree(mapOf("dir" to "lib", "include" to listOf("*.jar"))))
    implementation("org.jsoup:jsoup:1.16.1")
    implementation("com.zaxxer:HikariCP:5.0.1")
    implementation("org.redisson:redisson:3.22.1")
    implementation("mysql:mysql-connector-java:8.0.33")
    implementation("com.squareup.okhttp3:okhttp:4.9.3")
    implementation("com.baomidou:mybatis-plus:3.5.3.1")
    implementation("com.baomidou:mybatis-plus-extension:3.5.3.1")
    implementation("com.alibaba.fastjson2:fastjson2:2.0.34")
    implementation("org.apache.commons:commons-lang3:3.12.0")
    implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2")
}

复现

完整DbConfig类

package com.w954l.bot.config;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.w954l.bot.entity.BotDo;
import com.w954l.bot.mapper.BotDoMapper;
import com.w954l.bot.utils.GenericUtil;
import com.w954l.bot.utils.LogUtils;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author 954L
 * @create 2023/06/28 22:29
 */
public class DbConfig {

    public static void main(String[] args) {
        init();
    }

    private static SqlSessionFactory sqlSessionFactory;

    public <T> T openSession(Class<T> mapperClazz) {
        return sqlSessionFactory.openSession().getMapper(mapperClazz); }

    public static  void init() {
        try {
            // 自定义泛型实现
            GenericTypeUtils.setGenericTypeResolver(((clazz, genericIfc) ->
                GenericUtil.resolveTypeArguments(clazz, genericIfc)));
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            MybatisConfiguration configuration = initConfiguration();
            // 如mybatis-plus的分页插件
            configuration.addInterceptor(initInterceptor());
            configuration.setLogImpl(Slf4jImpl.class);
            configuration.addMappers("com.w954l.bot.mapper");
            GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
            // sql默认映射
            globalConfig.setSqlInjector(new DefaultSqlInjector());
            globalConfig.setIdentifierGenerator(new DefaultIdentifierGenerator());
            globalConfig.setSuperMapperClass(BaseMapper.class);
            // 数据源
            configuration.setEnvironment(new Environment("Production",
                    new JdbcTransactionFactory(), initDataSource()));
            registryMapperXml(configuration, "mapper");
            sqlSessionFactory = builder.build(configuration);

            try {
                BotDoMapper botDoMapper = sqlSessionFactory.openSession().getMapper(BotDoMapper.class);
                BotDo botDo = botDoMapper.abcById();
                System.out.println(JSON.toJSONString(botDo));
            } catch (Exception e) {
                e.printStackTrace();
            }

        } catch (Exception e) { LogUtils.error("数据库初始化失败", e); System.exit(0); }
    }

    private static  MybatisConfiguration initConfiguration() {
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setUseGeneratedKeys(true); return configuration;
    }

    private static  DataSource initDataSource() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/qq-bot?useUnicode=true&useSSL=false&characterEncoding=utf-8&serverTimeZone=UTC&allowMultiQueries=true");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setIdleTimeout(60000);
        dataSource.setAutoCommit(true);
        dataSource.setMaximumPoolSize(128);
        dataSource.setMinimumIdle(12);
        dataSource.setMaxLifetime(60000 * 10);
        dataSource.setConnectionTestQuery("SELECT 1");
        return dataSource;
    }

    private static  Interceptor initInterceptor() {
        // 创建mybatis-plus插件对象
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 构建分页插件
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        paginationInnerInterceptor.setOverflow(true);
        paginationInnerInterceptor.setMaxLimit(500L);
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }

    /**
     * 解析mapper.xml文件
     * @param configuration
     * @param classPath
     * @throws IOException
     */
    private static  void registryMapperXml(MybatisConfiguration configuration, String classPath) throws IOException {
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> mapper = contextClassLoader.getResources(classPath);
        while (mapper.hasMoreElements()) {
            URL url = mapper.nextElement();
            if (url.getProtocol().equals("file")) {
                File[] files = new File(url.getPath()).listFiles();
                for (File f: files) {
                    FileInputStream in = new FileInputStream(f);
                    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(in,
                        configuration, f.getPath(), configuration.getSqlFragments());
                    xmlMapperBuilder.parse(); in.close();
                }
            } else {
                JarFile jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry jarEntry = entries.nextElement();
                    if (jarEntry.getName().endsWith(".xml")) {
                        InputStream in = jarFile.getInputStream(jarEntry);
                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(in, configuration, jarEntry.getName(), configuration.getSqlFragments());
                        xmlMapperBuilder.parse(); in.close();
                    }
                }
            }
        }
    }

}

mirai-core 版本

2.15.0-RC

bot-protocol

ANDROID_PHONE

其他组件版本

> /status
Running MiraiConsole v2.15.0-RC, built on 2023-06-20 22:34:50.
Frontend Terminal: version 2.15.0-RC, provided by Mamoe Technologies

Permission Service: Built In Permission Service

Plugins: com.w954l.bot v1.0.0

Object Pending Finalization Count: 0
                 committed |  init   |  used   |  max  
    Heap Memory:  100.0MB  | 254.0MB | 53.46MB | 3.96GB
Non-Heap Memory:  61.06MB  | 7.31MB  | 59.44MB |   -1  

系统日志

No response

网络日志

No response

补充信息

No response

Karlatemp commented 1 year ago

自行检查 classloader 配置传递

Karlatemp commented 1 year ago

mirai-console 并没有限制这些反射获取路径的框架的使用, 而你并没有把插件的 classloader 传递给相关框架, 这是你的配置失误, 与 mirai-console 无关, 你应该查找你所使用的框架是否有指定 classloader 的选项, 如果没有指定的方法, 你应该去你所使用的框架开 issue