我们的Groovy应用程序通过在运行时加载代码来工作。它是一个多租户Web弹簧启动应用程序,包含一些控制器,一些简单的DI,通常的东西。
每个租户都通过外部代码添加到我们的应用程序,外部代码仅在运行时已知。它大致以“插件”方式工作。能够在运行时加载代码使我们能够随意添加,更新和删除这些插件/租户,这在我们的上下文中都是有意义的。这些租户经常被更改,每次我们更改它们时重新启动我们的主应用程序是不可行的。因此,在运行时加载代码的方法对我们来说非常有用。我会稍微交替使用tenant和plugin这两个词,其中插件只是指与租户相关的外部代码。
现在我们使用类似的东西加载这个Groovy代码:
GroovyClassLoader classLoader = new GroovyClassLoader()
classLoader.addClasspath(sourceDirectory)
Class clazz = classLoader.loadClass(pluginMainClass)
return clazz.newInstance()
返回的类型没有Spring的东西,没有注释,它工作得很好,每个人都很开心,生活也很美好。我们的应用程序知道如何存储和使用返回的实例。
但是,其中一个租户需要一个控制器。基本上我想将一些控制器从外部插件代码导入到主应用程序中。
主应用程序已经有一些控制器。我们需要的是在主应用程序中添加另一个控制器。包含这种控制器的Groovy代码只在运行时才知道(我们告诉应用程序在哪里找到源代码,因此我们不能只添加依赖项。
我尝试过的是在插件项目中声明一个具有以下代码的新类:
@Configuration
@Import(ThePluginController)
class ThePluginConfiguration {
}
控制器可能是正确的:
@Controller
@Slf4j
class PanvelController {
@RequestMapping(path="the-plugin/index", method = RequestMethod.GET)
def index(Model model){
return "index"
}
}
主项目会加载插件:
void loadConfiguration(ApplicationContext context, ...)
{
Class clazz = ... get ThePluginConfiguration class object
AutowireCapableBeanFactory autowire = context.getAutowireCapableBeanFactory()
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) autowire
BeanDefinition definition = new RootBeanDefinition(clazz, Autowire.BY_TYPE.value(), true)
registry.registerBeanDefinition(clazz.name, definition)
}
然而,没有任何反应。控制器根本没有注册,我可能对东西很困惑。可能我发布的代码没有做任何有用的事情,所以我正在寻求帮助。
应该发生的是,在注册控制器时,它应该开始接受对它们映射到的URL的请求。并在日志中显示类似的内容:
2017-11-17 14:52:42.561 INFO 1692 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/the-plugin/index],methods=[GET]}" onto (package/method) ....
此人正在尝试做同样的事情:Spring boot: Add new endpoints at runtime
我还看到其他一些建议像这样的答案,但对我不起作用:http://davidwelch.co/java-stack/programmatically-add-controllers-to-spring-mvc
我是Spring的新手,所以我不确切地知道我的问题是什么。是否有可能实现这一目标?