2016-11-05 3 views
3

Я только что попытался разработать систему плагинов для моего веб-приложения весной загрузки. Приложение развертывается на сервере tomcat с использованием корневого контекстного пути. Система плагинов позволяет загружать специально подготовленные файлы jar во время выполнения. Система также должна иметь возможность отключать плагины во время выполнения. Эти банки хранятся внутри папки плагинов в текущем рабочем каталоге. Я хотел, чтобы каждый плагин имел свой собственный весенний контекст для работы. Инъекция зависимостей работает так, как ожидалось, но весна не обнаруживает аннотацию @RequestMapping для контекста плагина. Поэтому мой вопрос: как я могу заставить весну обнаружить эти аннотации @RequestMapping для моих плагинов (во время выполнения)?Spring не обнаруживает аннотацию @RequestMapping для нового контекста во время выполнения

Я использую последнюю версию весной загрузки и следующий 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); 
    } 
} 

Когда я запускаю приложение и загружаю плагин, я вижу «Contr oller ready. " в консоли. Однако, когда я пытаюсь получить доступ к url ​​(localhost: 8080/test), я просто вижу страницу ошибки 404. Каждый URL-адрес контроллеров контекстных весов без плагинов правильно отображается (я могу получить доступ к localhost: 8080/index, например). Я узнал, что это может иметь какое-то отношение к RequestMappingHandlerMapping. Однако я действительно не понимаю, как использовать это, чтобы снова выполнить аннотацию.

Edit: я нашел способ сделать @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

Спасибо за чтение.

+0

можете ли вы разместить свои приложения.properties? – alexbt

+0

@Alex Я только что обновил сообщение, чтобы включить приложение application.yml –

+0

Я просто заметил, что ваш метод PluginTestController.doGet является закрытым. Я не могу проверить это на данный момент, но вы уверены, что это поддерживается? Хорошо, посмотрел, он поддерживается. (Https://sebastian.marsching.com/blog/archives/149-Springs-RequestMapping-annotation-works-on-private-methods.html) по-прежнему я бы попытался изменить методы на публичные – alexbt

ответ

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()); 
    } 

это RequestMappingHandlerMapping код spring3, в может быть, есть некоторые изменения в осущ spring4 в.

+0

, это действительно действительно полезно и похоже на код, который я уже выяснил прошлой ночью. На данный момент я использую аналогичный aproach, основанный на ответе на этот вопрос: http://stackoverflow.com/questions/27929965/find-method-level-custom-annotation-in-a-spring-context. я только что обновил свой ответ, чтобы включить код, который заставил контроллер работать для меня. Я не думаю, что мой aproach еще совершенен, но по крайней мере сейчас это работает. я посмотрю, как Spring4 реализует его. Спасибо за указание на код. –

+0

Я очень рад помочь вам, если у вас есть новый результат исследования, вы бы обновили этот пост? – BeeNoisy

+0

Я сделаю это и спасибо ^^ –

0

Вы добавили местоположение контроллера в контексте: компонент сканирования базы-пакет = «местоположение» в spring_servlet.xml

+0

Я использую весенний ботинок и rya java annotatiom управляемый способ настройки моих контроллеров. поэтому нет никакого spring_servlet.xml для этого проекта –

0

Я думаю, вам нужно добавить

<context:component-scan base-package="package_name" /> 
<mvc:annotation-driven /> 
<mvc:default-servlet-handler /> 

в файле ххх-servlet.xml.

+0

Я использую весеннюю загрузку и java annotatiom управляемый способ настройки моих контроллеров. поэтому для этого проекта нет spring_servlet.xml. этот вопрос уже был дан –