java - Spring 不会在运行时发现新上下文的 @RequestMapping 注释

标签 java spring spring-mvc spring-boot request-mapping

我刚刚尝试为我的 spring boot web 应用程序开发一个插件系统。该应用程序使用根上下文路径部署在 tomcat 服务器上。插件系统允许我在运行时加载专门准备的 jar 文件。系统还应该能够在运行时取消部署插件。这些 jar 包含在当前工作目录的插件文件夹中。我希望每个插件都有自己的 spring 上下文来操作。依赖注入(inject)按预期工作,但 spring 没有发现我的插件上下文的 @RequestMapping 注释。所以我的问题是:如何让 spring 发现我的插件的那些 @RequestMapping 注释(在运行时)?

我正在使用最新的 spring boot 版本和以下 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 代码(在我的插件中):

@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 准备就绪”。在控制台中。但是,当我尝试访问 url (localhost:8080/test) 时,我只能看到 404 错误页面。非插件 spring 上下文 Controller 的每个 url 都被正确映射(例如,我可以访问 localhost:8080/index)。我发现它可能与 RequestMappingHandlerMapping 有关。但是我真的不明白如何利用它来使注释再次工作。

编辑:我找到了一种使用以下代码使 @RequestMapping 注释适用于我的 Controller 的方法:

// 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 http://stackoverflow.com/questions/27929965/find-method-level-custom-annotation-in-a-spring-context
    // 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

感谢阅读。

最佳答案

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有一些改动。

关于java - Spring 不会在运行时发现新上下文的 @RequestMapping 注释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40441028/

相关文章:

java - 为什么Spring MVC会以404响应并报告“在DispatcherServlet中未找到带有URI […]的HTTP请求的映射”?

java - 在对 Map 进行排序时确定某些字符串优先级的最佳方法

java - @Secured 服务方法和 spring ws

java - Spring 集成: start JPA polling only when all the results of last polling has been processed

spring - spring boot 在哪里配置默认的application.properties

java - Bean 属性不可读或具有无效的 getter 方法

java - Spring Mvc 提交后获取 HTTP 状态 [404] – [Not Found]

java - java中使用质因数分解的最大公约数

java - 无法从 Android 中的 Web 服务解析

java - 无法解析 Java 中的 newDate()