From 972f9bd5acae9f361c84ca044db936e6d069e465 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Dec 2022 20:57:35 +0800 Subject: [PATCH] =?UTF-8?q?fix(spring-boot-starter):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20#531?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #531 --- settings.gradle.kts | 1 + ...SpringBootBotAutoRegisterBuildConfigure.kt | 49 ++++++++++------- ...gBootListenerAutoRegisterBuildConfigure.kt | 55 +++++++++---------- .../core/application/BaseApplication.kt | 48 +++++++++++++++- .../core/application/SimpleApplication.kt | 38 ------------- .../build.gradle.kts | 22 ++++++++ .../src/main/java/demo/Main.java | 33 +++++++++++ .../src/main/resources/application.properties | 2 + 8 files changed, 160 insertions(+), 88 deletions(-) create mode 100644 simbot-project-tests/simbot-project-test-spring-boot-starter/build.gradle.kts create mode 100644 simbot-project-tests/simbot-project-test-spring-boot-starter/src/main/java/demo/Main.java create mode 100644 simbot-project-tests/simbot-project-test-spring-boot-starter/src/main/resources/application.properties diff --git a/settings.gradle.kts b/settings.gradle.kts index fc66811b3..22a77ec5a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -51,6 +51,7 @@ include( // project test include( projectTest("boot"), + projectTest("spring-boot-starter"), projectTest("jmh-duration"), ) diff --git a/simbot-boots/simboot-core-spring-boot-starter/src/main/kotlin/love/forte/simboot/spring/autoconfigure/SimbotSpringBootBotAutoRegisterBuildConfigure.kt b/simbot-boots/simboot-core-spring-boot-starter/src/main/kotlin/love/forte/simboot/spring/autoconfigure/SimbotSpringBootBotAutoRegisterBuildConfigure.kt index 98b533eed..073b517e3 100644 --- a/simbot-boots/simboot-core-spring-boot-starter/src/main/kotlin/love/forte/simboot/spring/autoconfigure/SimbotSpringBootBotAutoRegisterBuildConfigure.kt +++ b/simbot-boots/simboot-core-spring-boot-starter/src/main/kotlin/love/forte/simboot/spring/autoconfigure/SimbotSpringBootBotAutoRegisterBuildConfigure.kt @@ -22,6 +22,7 @@ import love.forte.simbot.ExperimentalSimbotApi import love.forte.simbot.application.Application import love.forte.simbot.application.EventProvider import love.forte.simbot.bot.* +import love.forte.simbot.utils.runInNoScopeBlocking import org.slf4j.LoggerFactory import org.springframework.beans.factory.config.BeanFactoryPostProcessor import org.springframework.beans.factory.config.ConfigurableListableBeanFactory @@ -47,21 +48,21 @@ public open class SimbotSpringBootBotAutoRegisterBuildConfigure : BeanFactoryPos logger.warn("No such bean (Application) definition, skip bot register.", nsbEx) return } - + val customDecoderFactories = beanFactory.getBeansOfType>() - + config(application, customDecoderFactories.values.toList()) - + } - + @OptIn(ExperimentalSerializationApi::class, ExperimentalSimbotApi::class) private fun config(application: Application, customDecoderFactories: List>) { val resolver = PathMatchingResourcePatternResolver() - + val decoderList = customDecoderFactories + StandardBotVerifyInfoDecoderFactory.supportDecoderFactories() - + val configuration = application.configuration as? BootApplicationConfiguration - + val botConfigResources = (configuration?.botConfigurationResources ?: emptyList()) .asSequence() .flatMap { @@ -80,7 +81,7 @@ public open class SimbotSpringBootBotAutoRegisterBuildConfigure : BeanFactoryPos if (it.filename == null) { logger.warn("Resource [{}]'s filename is null, skip.", it) } - + it.filename != null } .distinct() @@ -88,15 +89,15 @@ public open class SimbotSpringBootBotAutoRegisterBuildConfigure : BeanFactoryPos logger.debug("Resolved bot register resource: {}", it) val decoderFactory = decoderList.findLast { decoder -> decoder.match(it.filename!!) } // ?: null // err? warn? - + if (decoderFactory == null) { // 没有任何解码器能匹配此资源。 logger.warn("No decoders match bot resource [{}] in {}.", it, decoderList) return@mapNotNull null } - + var botVerifyInfo: BotVerifyInfo? = null - + if (it.isFile) { kotlin.runCatching { botVerifyInfo = @@ -104,16 +105,24 @@ public open class SimbotSpringBootBotAutoRegisterBuildConfigure : BeanFactoryPos ?: return@runCatching }.getOrNull() } - + botVerifyInfo ?: it.url.toBotVerifyInfo(decoderFactory) }.toList() - + if (botConfigResources.isNotEmpty()) { application.botManagers - + botConfigResources.forEach { res -> register(application.providers, res).also { bot -> - if (bot == null) { + if (bot != null) { + val isAutoStart = (application.configuration as? BootApplicationConfiguration)?.isAutoStartBots != false + if (isAutoStart) { + val startResult = runInNoScopeBlocking(application.coroutineContext) { bot.start() } + logger.info("Bot info [{}] successfully registered as Bot(component={}, id={}) and the result of auto-start is {}", res, bot.component, bot.id, startResult) + } else { + logger.info("Bot info [{}] successfully registered as Bot(component={}, id={}]) but it does not start automatically", res, bot.component, bot.id) + } + } else { logger.warn( "Bot verify info [{}] not registered by any registrars, skip. The botRegistrar: {}", res, @@ -123,16 +132,16 @@ public open class SimbotSpringBootBotAutoRegisterBuildConfigure : BeanFactoryPos } } } - + } - + private fun register(providers: List, botVerifyInfo: BotVerifyInfo): Bot? { logger.info("Registering bot with verify info [{}]", botVerifyInfo) for (manager in providers) { if (manager !is BotRegistrar) { continue } - + try { return manager.register(botVerifyInfo).also { bot -> logger.debug( @@ -147,11 +156,11 @@ public open class SimbotSpringBootBotAutoRegisterBuildConfigure : BeanFactoryPos // ignore this. } } - + logger.warn("Bot verify info [{}] is not matched by any manager, skip it.", botVerifyInfo) return null } - + public companion object { private val logger = LoggerFactory.getLogger(SimbotSpringBootBotAutoRegisterBuildConfigure::class.java) } diff --git a/simbot-boots/simboot-core-spring-boot-starter/src/main/kotlin/love/forte/simboot/spring/autoconfigure/SimbotSpringBootListenerAutoRegisterBuildConfigure.kt b/simbot-boots/simboot-core-spring-boot-starter/src/main/kotlin/love/forte/simboot/spring/autoconfigure/SimbotSpringBootListenerAutoRegisterBuildConfigure.kt index 5d9693d39..3612293a0 100644 --- a/simbot-boots/simboot-core-spring-boot-starter/src/main/kotlin/love/forte/simboot/spring/autoconfigure/SimbotSpringBootListenerAutoRegisterBuildConfigure.kt +++ b/simbot-boots/simboot-core-spring-boot-starter/src/main/kotlin/love/forte/simboot/spring/autoconfigure/SimbotSpringBootListenerAutoRegisterBuildConfigure.kt @@ -26,7 +26,6 @@ import love.forte.simbot.event.EventListenerRegistrationDescription import love.forte.simbot.event.EventListenerRegistrationDescriptionBuilder import org.slf4j.LoggerFactory import org.springframework.beans.factory.NoSuchBeanDefinitionException -import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.config.BeanFactoryPostProcessor import org.springframework.beans.factory.config.ConfigurableListableBeanFactory import org.springframework.beans.factory.getBean @@ -45,59 +44,59 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter @Suppress("SpringJavaAutowiredMembersInspection") @AutoConfigureAfter(SimbotSpringBootApplicationConfiguration::class) public open class SimbotSpringBootListenerAutoRegisterBuildConfigure : BeanFactoryPostProcessor { - @Autowired(required = false) - private var listeners: List? = null - - @Autowired(required = false) - private var listenerRegistrationDescriptions: List? = null - - @Autowired(required = false) - private var listenerBuilders: List? = null - override fun postProcessBeanFactory(beanFactory: ConfigurableListableBeanFactory) { logger.debug("Start listener auto register") try { val application = beanFactory.getBean() - config(application) + config(application, beanFactory) } catch (nsbEx: NoSuchBeanDefinitionException) { // ignore? logger.warn("No such bean (Application) definition, skip listener register.", nsbEx) } } - + @OptIn(ExperimentalSimbotApi::class) - private fun config(application: Application) { - logger.debug("The size of resolved listeners is {}", listeners?.size ?: -1) - logger.debug("The size of resolved listener builders is {}", listenerBuilders?.size ?: -1) - + private fun config(application: Application, beanFactory: ConfigurableListableBeanFactory) { + val listeners = beanFactory.getBeansOfType(EventListener::class.java) + val listenerRegistrationDescriptions = + beanFactory.getBeansOfType(EventListenerRegistrationDescription::class.java) + val listenerBuilders = beanFactory.getBeansOfType(EventListenerBuilder::class.java) + logger.debug("The size of resolved listeners is {}", listeners.size) + logger.debug( + "The size of resolved listenerRegistrationDescriptions is {}", + listenerRegistrationDescriptions.size + ) + logger.debug("The size of resolved listener builders is {}", listenerBuilders.size) + // listeners { - listeners?.forEach { - application.eventListenerManager.register(it) - logger.debug("Registered listener {}", it) + listeners.forEach { (name, listener) -> + application.eventListenerManager.register(listener) + logger.debug("Registered listener [{}] named {}", listener, name) } - listenerRegistrationDescriptions?.forEach { - application.eventListenerManager.register(it) - logger.debug("Registered listener description {}", it) + listenerRegistrationDescriptions.forEach { (name, listener) -> + application.eventListenerManager.register(listener) + logger.debug("Registered listener description [{}] named {}", listener, name) } - listenerBuilders?.forEach { builder -> + listenerBuilders.forEach { (name, builder) -> if (builder is EventListenerRegistrationDescriptionBuilder) { val description = builder.buildDescription() application.eventListenerManager.register(description) logger.debug( - "Registered listener registration description [{}] by builder [{}]", + "Registered listener registration description [{}] by builder [{}] named {}", description, - builder + builder, + name ) - + } else { val listener = builder.build() application.eventListenerManager.register(listener) - logger.debug("Registered listener [{}] by builder [{}]", listener, builder) + logger.debug("Registered listener [{}] by builder [{}] named {}", listener, builder, name) } } // } } - + private companion object { private val logger = LoggerFactory.getLogger(SimbotSpringBootListenerAutoRegisterBuildConfigure::class.java) } diff --git a/simbot-cores/simbot-core/src/main/kotlin/love/forte/simbot/core/application/BaseApplication.kt b/simbot-cores/simbot-core/src/main/kotlin/love/forte/simbot/core/application/BaseApplication.kt index c8ab62c6c..ce21b98e5 100644 --- a/simbot-cores/simbot-core/src/main/kotlin/love/forte/simbot/core/application/BaseApplication.kt +++ b/simbot-cores/simbot-core/src/main/kotlin/love/forte/simbot/core/application/BaseApplication.kt @@ -18,6 +18,12 @@ package love.forte.simbot.core.application import kotlinx.coroutines.Job import love.forte.simbot.application.Application +import love.forte.simbot.application.BotManagers +import love.forte.simbot.bot.Bot +import love.forte.simbot.bot.BotManager +import love.forte.simbot.bot.BotVerifyInfo +import love.forte.simbot.bot.ComponentMismatchException +import love.forte.simbot.logger.LoggerFactory import org.slf4j.Logger import kotlin.coroutines.CoroutineContext import kotlin.coroutines.cancellation.CancellationException @@ -30,8 +36,12 @@ import kotlin.coroutines.cancellation.CancellationException public abstract class BaseApplication : Application { abstract override val coroutineContext: CoroutineContext protected abstract val logger: Logger + + override val botManagers: BotManagers by lazy { BotManagersImpl(providers.filterIsInstance>()) } + + private val job: Job? get() = coroutineContext[Job] - + override suspend fun join() { job?.join() } @@ -50,4 +60,38 @@ public abstract class BaseApplication : Application { } } } -} \ No newline at end of file +} + + +private class BotManagersImpl(private val botManagers: List>) : BotManagers, + List> by botManagers { + + override fun register(botVerifyInfo: BotVerifyInfo): Bot? { + logger.info("Registering bot with verify info [{}]", botVerifyInfo) + for (manager in this) { + try { + return manager.register(botVerifyInfo).also { bot -> + logger.debug( + "Bot verify info [{}] is registered as [{}] via manager [{}]", + botVerifyInfo, + bot, + manager + ) + } + } catch (ignore: ComponentMismatchException) { + logger.debug("Bot verify info [{}] is not matched by manager {}, try next.", botVerifyInfo, manager) + + } + } + + return null + } + + override fun toString(): String { + return "BotManagersImpl(managers=$botManagers)" + } + + companion object { + private val logger = LoggerFactory.getLogger(BotManagersImpl::class) + } +} diff --git a/simbot-cores/simbot-core/src/main/kotlin/love/forte/simbot/core/application/SimpleApplication.kt b/simbot-cores/simbot-core/src/main/kotlin/love/forte/simbot/core/application/SimpleApplication.kt index abfd717db..e7f64df60 100644 --- a/simbot-cores/simbot-core/src/main/kotlin/love/forte/simbot/core/application/SimpleApplication.kt +++ b/simbot-cores/simbot-core/src/main/kotlin/love/forte/simbot/core/application/SimpleApplication.kt @@ -20,10 +20,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import love.forte.simbot.ExperimentalSimbotApi import love.forte.simbot.application.* -import love.forte.simbot.bot.Bot -import love.forte.simbot.bot.BotManager -import love.forte.simbot.bot.BotVerifyInfo -import love.forte.simbot.bot.ComponentMismatchException import love.forte.simbot.core.event.SimpleEventListenerManager import love.forte.simbot.logger.LoggerFactory import love.forte.simbot.logger.logger @@ -129,8 +125,6 @@ private class SimpleApplicationImpl( ) : SimpleApplication, BaseApplication() { override val providers: List = providerList.view() - override val botManagers: BotManagers = BotManagersImpl(providerList.filterIsInstance>()) - override val coroutineContext = environment.coroutineContext override val logger = environment.logger } @@ -189,36 +183,4 @@ private class SimpleApplicationBuilderImpl : SimpleApplicationBuilder, } } -private class BotManagersImpl(private val botManagers: List>) : BotManagers, - List> by botManagers { - - override fun register(botVerifyInfo: BotVerifyInfo): Bot? { - logger.info("Registering bot with verify info [{}]", botVerifyInfo) - for (manager in this) { - try { - return manager.register(botVerifyInfo).also { bot -> - logger.debug( - "Bot verify info [{}] is registered as [{}] via manager [{}]", - botVerifyInfo, - bot, - manager - ) - } - } catch (ignore: ComponentMismatchException) { - logger.debug("Bot verify info [{}] is not matched by manager {}, try next.", botVerifyInfo, manager) - - } - } - - return null - } - - override fun toString(): String { - return "BotManagersImpl(managers=$botManagers)" - } - - companion object { - private val logger = LoggerFactory.getLogger(BotManagersImpl::class) - } -} diff --git a/simbot-project-tests/simbot-project-test-spring-boot-starter/build.gradle.kts b/simbot-project-tests/simbot-project-test-spring-boot-starter/build.gradle.kts new file mode 100644 index 000000000..2ad88e1d9 --- /dev/null +++ b/simbot-project-tests/simbot-project-test-spring-boot-starter/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + java + kotlin("jvm") // version "1.7.21" + id("org.springframework.boot") version "2.7.5" + id("io.spring.dependency-management") version "1.1.0" +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web") + testImplementation("org.springframework.boot:spring-boot-starter-test") + + implementation(project(":simbot-boots:simboot-core-spring-boot-starter")) + implementation("love.forte.simbot.component:simbot-component-mirai-core:3.0.0.0-beta.5") +} + +tasks.withType { + useJUnitPlatform() +} diff --git a/simbot-project-tests/simbot-project-test-spring-boot-starter/src/main/java/demo/Main.java b/simbot-project-tests/simbot-project-test-spring-boot-starter/src/main/java/demo/Main.java new file mode 100644 index 000000000..e3306ad84 --- /dev/null +++ b/simbot-project-tests/simbot-project-test-spring-boot-starter/src/main/java/demo/Main.java @@ -0,0 +1,33 @@ +package demo; + +import love.forte.simboot.annotation.Listener; +import love.forte.simboot.spring.autoconfigure.EnableSimbot; +import love.forte.simbot.Identifies; +import love.forte.simbot.definition.Contact; +import love.forte.simbot.event.FriendMessageEvent; +import love.forte.simbot.event.internal.BotStartedEvent; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author ForteScarlet + */ +@EnableSimbot +@SpringBootApplication +public class Main { + public static void main(String[] args) { + SpringApplication.run(Main.class, args); + } + + + @Listener + public void onFriend(FriendMessageEvent event) { + System.out.println(event); + Contact contact = event.getBot().getContact(Identifies.ID(1149159218L)); + contact.sendBlocking("Hello!"); + } + @Listener + public void onStart(BotStartedEvent event) { + System.out.println(event); + } +} diff --git a/simbot-project-tests/simbot-project-test-spring-boot-starter/src/main/resources/application.properties b/simbot-project-tests/simbot-project-test-spring-boot-starter/src/main/resources/application.properties new file mode 100644 index 000000000..2973e409f --- /dev/null +++ b/simbot-project-tests/simbot-project-test-spring-boot-starter/src/main/resources/application.properties @@ -0,0 +1,2 @@ + +logging.level.love.forte=DEBUG