Spring 未在运行时发现新上下文的 @RequestMapping 注释
Spring does not discover @RequestMapping annotation for a new context at runtime
我刚刚尝试为我的 spring 引导 Web 应用程序开发一个插件系统。该应用程序使用根上下文路径部署在 tomcat 服务器上。插件系统允许我在运行时加载专门准备的 jar 文件。系统还应该能够在运行时取消部署插件。这些罐子包含在当前工作目录的插件文件夹中。我希望每个插件都有自己的 spring 上下文来操作。依赖注入按预期工作,但 spring 没有发现我的插件上下文的 @RequestMapping 注释。所以我的问题是:如何让 spring 发现我的插件的那些 @RequestMapping 注释(在运行时)?
我正在使用最新的 spring 引导版本和以下 application.yml:
# Server
server:
error:
whitelabel:
enabled: true
session:
persistent: true
tomcat:
uri-encoding: UTF-8
# Spring
spring:
application:
name: Plugins
mvc:
favicon:
enabled: false
favicon:
enabled: false
thymeleaf:
encoding: UTF-8
# Logging
logging:
file: application.log
level.: error
这是加载插件的代码:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
URLClassLoader urlClassLoader = URLClassLoader.newInstance(new URL[] { plugin.getPluginURL() }, getClass().getClassLoader()); // plugin.getPluginURL will refer to a jar file with the plugin code (see below).
context.setClassLoader(urlClassLoader);
context.setParent(applicationContext); // applicationContext is the the context of the original spring application. It was autowired.
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, true);
scanner.scan("my.plugin.package");
context.refresh();
控制器代码(在我的插件中):
@Controller
public class PluginTestController {
@PostConstruct
private void postContruct() {
System.out.println("Controller ready.");
}
@RequestMapping(value = "/test", method = RequestMethod.GET)
public ResponseEntity<String> doGet() {
return new ResponseEntity<>("Hello!", HttpStatus.OK);
}
}
当我启动应用程序并加载插件时,我可以在控制台中看到 "Controller ready."。但是,当我尝试访问 url (localhost:8080/test) 时,我只能看到 404 错误页面。非插件 spring 上下文控制器的每个 url 都被正确映射(例如,我可以访问 localhost:8080/index)。我发现它可能与 RequestMappingHandlerMapping 有关。但是我真的不明白如何利用它来使注释再次工作。
编辑:我找到了一种使用以下代码使 @RequestMapping
注释适用于我的控制器的方法:
// context is the Plugins context, that i just created earlier.
for (Map.Entry < String, Object > bean: context.getBeansWithAnnotation(Controller.class).entrySet()) {
Object obj = bean.getValue();
// From
// As you are using AOP check for AOP proxying. If you are proxying with Spring CGLIB (not via Spring AOP)
// Use org.springframework.cglib.proxy.Proxy#isProxyClass to detect proxy If you are proxying using JDK
// Proxy use java.lang.reflect.Proxy#isProxyClass
Class < ? > objClz = obj.getClass();
if (org.springframework.aop.support.AopUtils.isAopProxy(obj)) {
objClz = org.springframework.aop.support.AopUtils.getTargetClass(obj);
}
for (Method m: objClz.getDeclaredMethods()) {
if (m.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(m, RequestMapping.class);
RequestMappingInfo requestMappingInfo = RequestMappingInfo
.paths(requestMapping.path())
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name())
.customCondition(null)
.build();
// This will register the actual mapping, so that the Controller can handle the Request
requestMappingHandlerMapping.registerMapping(requestMappingInfo, obj, m);
}
}
}
但是我仍在寻找一种使用 spring 方式使其工作的方法:https://github.com/spring-projects/spring-framework/blob/fb7ae010c867ae48ab51f48cce97fe2c07f44115/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java
感谢阅读。
您是否在 context:component-scan base-package="location" 中添加了控制器位置 spring_servlet.xml
SpringMvc有自己的@RequestMapping
缓存,详细代码是RequestMappingHandlerMapping
。如您所见,显示了init方法,也许您可以在加载新插件后调用init方法。
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
这是spring3的RequestMappingHandlerMapping
代码,可能spring4的impl有一些改动
我认为,您需要添加
<context:component-scan base-package="package_name" />
<mvc:annotation-driven />
<mvc:default-servlet-handler />
在你的 xxx-servlet.xml 文件中。
我刚刚尝试为我的 spring 引导 Web 应用程序开发一个插件系统。该应用程序使用根上下文路径部署在 tomcat 服务器上。插件系统允许我在运行时加载专门准备的 jar 文件。系统还应该能够在运行时取消部署插件。这些罐子包含在当前工作目录的插件文件夹中。我希望每个插件都有自己的 spring 上下文来操作。依赖注入按预期工作,但 spring 没有发现我的插件上下文的 @RequestMapping 注释。所以我的问题是:如何让 spring 发现我的插件的那些 @RequestMapping 注释(在运行时)?
我正在使用最新的 spring 引导版本和以下 application.yml:
# Server
server:
error:
whitelabel:
enabled: true
session:
persistent: true
tomcat:
uri-encoding: UTF-8
# Spring
spring:
application:
name: Plugins
mvc:
favicon:
enabled: false
favicon:
enabled: false
thymeleaf:
encoding: UTF-8
# Logging
logging:
file: application.log
level.: error
这是加载插件的代码:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
URLClassLoader urlClassLoader = URLClassLoader.newInstance(new URL[] { plugin.getPluginURL() }, getClass().getClassLoader()); // plugin.getPluginURL will refer to a jar file with the plugin code (see below).
context.setClassLoader(urlClassLoader);
context.setParent(applicationContext); // applicationContext is the the context of the original spring application. It was autowired.
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, true);
scanner.scan("my.plugin.package");
context.refresh();
控制器代码(在我的插件中):
@Controller
public class PluginTestController {
@PostConstruct
private void postContruct() {
System.out.println("Controller ready.");
}
@RequestMapping(value = "/test", method = RequestMethod.GET)
public ResponseEntity<String> doGet() {
return new ResponseEntity<>("Hello!", HttpStatus.OK);
}
}
当我启动应用程序并加载插件时,我可以在控制台中看到 "Controller ready."。但是,当我尝试访问 url (localhost:8080/test) 时,我只能看到 404 错误页面。非插件 spring 上下文控制器的每个 url 都被正确映射(例如,我可以访问 localhost:8080/index)。我发现它可能与 RequestMappingHandlerMapping 有关。但是我真的不明白如何利用它来使注释再次工作。
编辑:我找到了一种使用以下代码使 @RequestMapping
注释适用于我的控制器的方法:
// context is the Plugins context, that i just created earlier.
for (Map.Entry < String, Object > bean: context.getBeansWithAnnotation(Controller.class).entrySet()) {
Object obj = bean.getValue();
// From
// As you are using AOP check for AOP proxying. If you are proxying with Spring CGLIB (not via Spring AOP)
// Use org.springframework.cglib.proxy.Proxy#isProxyClass to detect proxy If you are proxying using JDK
// Proxy use java.lang.reflect.Proxy#isProxyClass
Class < ? > objClz = obj.getClass();
if (org.springframework.aop.support.AopUtils.isAopProxy(obj)) {
objClz = org.springframework.aop.support.AopUtils.getTargetClass(obj);
}
for (Method m: objClz.getDeclaredMethods()) {
if (m.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(m, RequestMapping.class);
RequestMappingInfo requestMappingInfo = RequestMappingInfo
.paths(requestMapping.path())
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name())
.customCondition(null)
.build();
// This will register the actual mapping, so that the Controller can handle the Request
requestMappingHandlerMapping.registerMapping(requestMappingInfo, obj, m);
}
}
}
但是我仍在寻找一种使用 spring 方式使其工作的方法:https://github.com/spring-projects/spring-framework/blob/fb7ae010c867ae48ab51f48cce97fe2c07f44115/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java
感谢阅读。
您是否在 context:component-scan base-package="location" 中添加了控制器位置 spring_servlet.xml
SpringMvc有自己的@RequestMapping
缓存,详细代码是RequestMappingHandlerMapping
。如您所见,显示了init方法,也许您可以在加载新插件后调用init方法。
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
这是spring3的RequestMappingHandlerMapping
代码,可能spring4的impl有一些改动
我认为,您需要添加
<context:component-scan base-package="package_name" />
<mvc:annotation-driven />
<mvc:default-servlet-handler />
在你的 xxx-servlet.xml 文件中。