2011-04-22 16 views
10

He estado usando Spring MVC durante tres meses. Estaba considerando una buena forma de agregar dinámicamente RequestMapping. Esto proviene de la necesidad de colocar partes del controlador en una biblioteca y luego agregarlas dinámicamente. De todos modos, la única manera que se me ocurre es declarar un controlador de la siguiente manera:¿Es posible establecer dinámicamente RequestMappings en Spring MVC?

@Controller 
@RequestMapping("/mypage") 
public class MyController { 

@RequestMapping(method = RequestMethod.GET) 
    public ModelAndView mainHandler(HttpServletRequest req) { 
     return handleTheRest(req); 
    } 

} 

cual no es bueno porque en el fondo no estoy usando la primavera. Entonces no puedo usar encuadernación de formularios, anotaciones, etc. Me gustaría agregar requestMappings dinámicamente a los métodos de las clases que podrían anotarse como los controladores MVC usuales, con la autoenlace, para poder evitar el procesamiento manual de HttpServletRequest.

¿Alguna idea? }

Respuesta

23

Spring MVC realiza asignaciones de URL mediante implementaciones de la interfaz HandlerMapping. Los que generalmente se usan de fábrica son las implementaciones predeterminadas, a saber, SimpleUrlHandlerMapping, BeanNameUrlHandlerMapping y DefaultAnnotationHandlerMapping.

Si desea implementar su propio mecanismo de mapeo, esto es bastante fácil de hacer: simplemente implemente esa interfaz (o, quizás más probablemente, extienda AbstractUrlHandlerMapping), declare la clase como un frijol en su contexto, y será consultado por DispatcherServlet cuando una solicitud necesita ser mapeada.

Tenga en cuenta que puede tener tantas implementaciones HandlerMapping como desee en un contexto. Ellos serán consultados por turno hasta que uno de ellos tenga una coincidencia.

+0

Gracias Skaff, que siempre da buenos consejos. De todos modos, ¿cómo puedo manipular el contexto por código? Quiero decir, ¿hay alguna forma de agregar dinámicamente un bean HandlerMapping o cualquier otro bean? – gotch4

+0

@ gotch4: no necesita agregar dinámicamente un 'HandlerMapping'. Usted configura * one * custom 'HandlerMapping', y luego le agrega dinámicamente mapeos. Ya que está escribiendo el 'HandlerMapping' usted mismo, cómo funciona eso depende de usted. – skaffman

2

Sé que esto es muy viejo, pero pensé que lo arrojaría en caso de que alguien más tenga la misma experiencia áspera que hice tratando de hacer que esto funcione. Terminé aprovechando dos características de Spring: la capacidad de registrar dinámicamente beans después de que se haya iniciado el contexto y el método afterPropertiesSet() en el objeto RequestMappingHandlerMapping.

Cuando se inicializa RequestMappingHandlerMapping, escanea el contexto y crea un mapa de todos los @RequestMapping que necesita para servir (presumiblemente por razones de rendimiento). Si registra dinámicamente beans anotados con @Controller, no se recogerán. Para reactivar este escaneo, solo necesita llamar al afterPropertiesSet() después de haber agregado sus frijoles.

En mi caso particular de uso, instalé los nuevos objetos @Controller en un contexto de Spring separado y necesité cablearlos en mi contexto de WebMvc. Los detalles de cómo los objetos no son importantes para esto, sin embargo, todo lo que necesita es una referencia de objeto:

//register all @Controller beans from separateContext into webappContext 
separateContext.getBeansWithAnnotation(Controller.class) 
    .forEach((k, v) -> webappContext.getBeanFactory().registerSingleton(k, v)); 

//find all RequestMappingHandlerMappings in webappContext and refresh them 
webappContext.getBeansOfType(RequestMappingHandlerMapping.class) 
    .forEach((k, v) -> v.afterPropertiesSet()); 

Por ejemplo, también se puede hacer esto:

//class annotated with @Controller 
MyController controller = new MyController 

//register new controller object 
webappContext.getBeanFactory().registerSingleton("myController", controller); 

//find all RequestMappingHandlerMappings in webappContext and refresh them 
webappContext.getBeansOfType(RequestMappingHandlerMapping.class) 
    .forEach((k, v) -> v.afterPropertiesSet()); 
3

Por favor, mire mi solución . No crea @RequestMapping dinámico en su código, pero proporciona un HandlerMapping y Controller que maneja todas las solicitudes. Si ejecuta esa aplicación, obtendrá el mensaje de hello world en json.

clase de aplicación:

@SpringBootApplication 
public class Application { 
    public static void main(String[] args) { 
    SpringApplication.run(Application.class, args); 
    } 

    @Bean 
    public MyCustomHandler myCustomHandler(MyCustomController myCustomController) { 
    MyCustomHandler myCustomHandler = new MyCustomHandler(myCustomController); 
    myCustomHandler.setOrder(Ordered.HIGHEST_PRECEDENCE); 
    return myCustomHandler; 
    } 
} 

MyCustomController

@Component 
public class MyCustomController extends AbstractController { 

    @Override 
    protected ModelAndView handleRequestInternal(HttpServletRequest request, 
     HttpServletResponse response) throws Exception { 
    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); 
    response.getWriter().println("{\"hello\":\"world\"}"); 
    return null; 
    } 
} 

MyCustomHandler

public class MyCustomHandler extends AbstractHandlerMapping { 

    private MyCustomController myCustomController; 

    public MyCustomHandler(MyCustomController myCustomController) { 
    this.myCustomController = myCustomController; 
    } 

    @Override 
    protected Object getHandlerInternal(HttpServletRequest request) throws Exception { 
    return myCustomController; 
    } 
} 

https://github.com/nowszy94/spring-mvc-dynamic-controller

1

Pasé un largo tiempo tratando de hacer que esto funcione, pero finalmente logré encontrar una solución que devuelva ResponseEntity en lugar de la anterior . Esta solución también tiene el beneficio adicional de evitar cualquier interacción explícita con Application Context.

extremo de servicio

@Service 
public class EndpointService { 

    @Autowired 
    private QueryController queryController; 

    @Autowired 
    private RequestMappingHandlerMapping requestMappingHandlerMapping; 

    public void addMapping(String urlPath) throws NoSuchMethodException { 

    RequestMappingInfo requestMappingInfo = RequestMappingInfo 
      .paths(urlPath) 
      .methods(RequestMethod.GET) 
      .produces(MediaType.APPLICATION_JSON_VALUE) 
      .build(); 

    requestMappingHandlerMapping. 
      registerMapping(requestMappingInfo, queryController, 
        QueryController.class.getDeclaredMethod("handleRequests") 
      ); 
    } 

} 

controlador para manejar las peticiones de nueva asignadas

@Controller 
public class QueryController { 

    public ResponseEntity<String> handleRequests() throws Exception { 

    //Do clever stuff here 

    return new ResponseEntity<>(HttpStatus.OK); 
    } 

} 
+0

¿podría agregar cuándo y dónde se invoca 'addMapping'? – Tiago

+0

Puede invocarse desde donde desee. En mi caso, mi aplicación se envió a un punto final separado que analizó la nueva solicitud y finalmente se llamó 'addMapping' – kaybee99

Cuestiones relacionadas