Spring在运行时

时间:2016-11-05 17:13:24

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

我刚尝试为我的Spring启动Web应用程序开发一个插件系统。应用程序使用根上下文路径部署在tomcat服务器上。插件系统允许我在运行时加载专门准备的jar文件。系统还应该能够在运行时取消部署插件。这些jar包含在当前工作目录的插件文件夹中。我希望每个插件都有自己的弹簧上下文来操作。依赖注入按预期工作,但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
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 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);
        }
    }
}

然而,我仍在寻找一种方法,使用弹簧方式使其工作:https://github.com/spring-projects/spring-framework/blob/fb7ae010c867ae48ab51f48cce97fe2c07f44115/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java

感谢阅读。

3 个答案:

答案 0 :(得分:2)

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&lt; c}}中有一些变化。

答案 1 :(得分:0)

您是否在上下文中添加了控制器位置:spring_servlet.xml中的component-scan base-package =“location”

答案 2 :(得分:0)

我想,你需要添加

ask min-one-of (turtles-on patch 1 0) [who]
[ set flag-1 TRUE
]
在xxx-servlet.xml文件中。