2012-07-20 14 views
12

To jest krzyżowy post. Wysłałem to samo pytanie do wiosennych forów. http://forum.springsource.org/showthread.php?128579-Database-driven-Controller-MappingJak dynamicznie mapuje ścieżki spring-webmvc?

Hi Próbuję zrobić z bazy danych mapowania kontrolerów tak, że mogą się zmienić w wykonywania.

Do tej pory posiadam następujące informacje.

Adapter niestandardowego uchwytu, który można później zoptymalizować.

@Component 
public class DatabasePageUrlHandlerMapping extends AbstractUrlHandlerMapping implements PriorityOrdered { 


    @Override 
    protected Object getHandlerInternal(HttpServletRequest request) 
      throws Exception { 
     String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); 
     List<Page> pages = Page.findAllPages(); 
     for (Page page : pages) { 
      if (lookupPath.equals(page.getSeoPath())) { 
       Object handler = getApplicationContext().getBean("_pageViewController"); 
       return new HandlerExecutionChain(handler); 
      } 
     } 
     return super.getHandlerInternal(request); 
    } 

} 

mój webmvc-config wygląda następująco (odpowiednia część)

Kod:

<context:component-scan base-package="com.artiststogether" 
    use-default-filters="false"> 
    <context:include-filter expression="org.springframework.stereotype.Controller" 
     type="annotation" /> 
</context:component-scan> 

<!-- If I don't put an order into this it doesn't fail over to the implementation why? --> 
<bean class="com.artiststogether.web.DatabasePageUrlHandlerMapping" p:order="-1" /> 
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/> 

To wydaje się być picking prawidłowy sterownik. Jednakże I odbieranie błąd idąc do bazy danych określonej ścieżki (takie jak „/ a”)

java.lang.NullPointerException 
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodResolver.useTypeLevelMapping(AnnotationMethodHandlerAdapter.java:675) 
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodResolver.resolveHandlerMethod(AnnotationMethodHandlerAdapter.java:585) 
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:431) 
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424) 
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900) 
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827) 
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882) 
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722) 
     .... 

Czy muszę zdefiniować klasę obsługi adnotacji?

Szczerze mówiąc, cały ten proces wydaje się trudniejszy niż powinien. Chcę, aby jeden kontroler obsługiwał wszystkie żądania do zewnętrznie zdefiniowanej ścieżki URL-a, to jest właściwy sposób, aby go obsłużyć.

Chciałbym również przekazać obiekt, który został dopasowany do kontrolera, jeśli jest to możliwe, zamiast wykonywać świeże wyszukiwanie w kontrolerze. To zasadniczo stworzy mój model dla widoku.

Wszelkie porady dotyczące tego, jak to działa?

EDIT Dla przypomnienia NPE jest tutaj

private boolean useTypeLevelMapping(HttpServletRequest request) { 
     if (!hasTypeLevelMapping() || ObjectUtils.isEmpty(getTypeLevelMapping().value())) { 
      return false; 
     } 
     return (Boolean) request.getAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING); 
    } 

Kolejna Edycja wersja numery od pom.xml

<properties> 
    <aspectj.version>1.6.12</aspectj.version> 
    <java.version>6</java.version> 
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    <roo.version>1.2.1.RELEASE</roo.version> 
    <slf4j.version>1.6.4</slf4j.version> 
    <spring.version>3.1.0.RELEASE</spring.version> 
<spring-security.version>3.1.0.RELEASE</spring-security.version> 
</properties> 

Mam odpowiedział na pytanie myself poniżej ale nadal jestem zainteresowany osobami ważącymi się we właściwy sposób.

+0

@fmucar co dokładnie bazodanowych jazdy wzorców uRL? Jego 90% tam swoją znalezieniu właściwego kontrolera po prostu nie jest poprawna metoda na kontrolerze. Jeśli mówisz o przekazaniu zmiennej do kontrolera, to przypuszczam, że najgorszy przypadek bym to zrobił ct to jako atrybut żądania i pobierz w kontroler stamtąd, ale to wydaje się być niezręczne. – Wes

+0

Przepraszam, że źle zrozumiałem twoje q. Usunę powyższy komentarz – fmucar

+0

Czy mógłbyś potwierdzić swoją wersję Spring MVC –

Odpowiedz

2

Wystarczy przeboleć tej konkretnej kwestii, pozwól mi polecić jedno wyjście do teraz -

Stwórz własną handlerAdapter wewnętrznie komponowania AnnotationMethodHandlerAdapter:

public DBAnnotationMethodHandlerAdapter implements HandlerAdapter,{ 
    private AnnotationHandlerAdapter target; 

    @Override 
    public boolean supports(Object handler) { 
     return this.target.supports(handler); 
    } 

    @Override 
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 
     request.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, true); 
     return this.target.handle(request, response, handler); 
    } 

    public void setTarget(AnnotationHandlerAdapter target){ 
     this.target = target; 
    } 

} 

    <bean class="mypkg.DBAnnotationMethodHandlerAdapter"> 
     <property name="target"> 
      <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/> 
     </property> 
    </bean> 

To powinno rozwiązać aktualny problem, ale prawdopodobnie napotkasz inne problemy

+0

Nie mam innych odpowiedzi. Masz nagrodę. – Wes

3

Wygląda na to, że z braku odpowiedzi przeciwnych tutaj i na spring forums wynika, że ​​nie ma prostszego sposobu na zrobienie tego w ciągu ramy wiosenne.

Udało mi się jednak sprawić, że działa i udostępniłem projekt na githubie, który można zbudować za pomocą maven, który dodaje 4 klasy, aby ułatwić proces dynamicznego dodawania klasy. Ten projekt można znaleźć pod adresem https://github.com/Athas1980/MvcBackingBean. Podzielę się także innym projektem, aby udowodnić, że działa.

Dzięki Marten Deinum i Rossen Stoyanchev


Dla zainteresowanych jak to osiągnąć sami, co musisz zrobić następujące

  1. Wdrożenie wystąpienie HandlerMapper daje to mapowanie między klasą kontrolera a adresem URL, do którego mapujesz.

    // Copyright 2012 Wesley Acheson 
    // 
    // Licensed under the Apache License, Version 2.0 (the "License"); 
    // you may not use this file except in compliance with the License. 
    // You may obtain a copy of the License at 
    // 
    //  http://www.apache.org/licenses/LICENSE-2.0 
    // 
    // Unless required by applicable law or agreed to in writing, software 
    // distributed under the License is distributed on an "AS IS" BASIS, 
    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    // See the License for the specific language governing permissions and 
    // limitations under the License. 
    
    package com.wesley_acheson.spring; 
    
    import javax.servlet.http.HttpServletRequest; 
    import javax.servlet.http.HttpServletResponse; 
    
    import org.springframework.core.PriorityOrdered; 
    import org.springframework.web.servlet.HandlerExecutionChain; 
    import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping; 
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 
    
    /** 
    * A Handler mapper that delegates to a {@link UrlBackingBeanMapper} to know 
    * whether it should match a url. If it does match a url then it adds the bean 
    * which matches the url to the request. 
    * 
    * @author Wesley Acheson 
    * 
    */ 
    public class BackingBeanUrlHandlerMapper extends AbstractUrlHandlerMapping 
         implements PriorityOrdered { 
    
        private UrlBackingBeanMapper<?> urlMapper; 
    
        /** 
        * 
        * @param urlMapper 
        *   The bean which matches urls with other beans. 
        */ 
        public void setUrlMapper(UrlBackingBeanMapper<?> urlMapper) { 
         this.urlMapper = urlMapper; 
        } 
    
        protected UrlBackingBeanMapper<?> getUrlMapper() { 
         return urlMapper; 
        } 
    
        public static final String BACKING_BEAN_ATTRIBUTE = BackingBeanUrlHandlerMapper.class 
          .getName() + ".backingBean"; 
    
        /** 
        * The controller which control will be passed to if there is any beans 
        * matching in @{link {@link #setUrlMapper(UrlBackingBeanMapper)}. 
        */ 
        public Object controller; 
    
        /** 
        * @param controller 
        *   <p> 
        *   The controller which control will be passed to if there is any 
        *   beans matching in @{link 
        *   {@link #setUrlMapper(UrlBackingBeanMapper)}. 
        */ 
        public void setController(Object controller) { 
         this.controller = controller; 
        } 
    
        /* 
        * (non-Javadoc) 
        * 
        * @see org.springframework.web.servlet.handler.AbstractUrlHandlerMapping# 
        * lookupHandler(java.lang.String, javax.servlet.http.HttpServletRequest) 
        */ 
        @Override 
        protected Object lookupHandler(String urlPath, HttpServletRequest request) 
          throws Exception { 
    
         if (urlMapper.isPathMapped(urlPath)) { 
          Object bean = urlMapper.retrieveBackingBean(urlPath); 
          return buildChain(bean, urlPath); 
         } 
    
         return super.lookupHandler(urlPath, request); 
        } 
    
        /** 
        * Builds a handler execution chain that contains both a path exposing 
        * handler and a backing bean exposing handler. 
        * 
        * @param bean 
        *   The object to be wrapped in the handler execution chain. 
        * @param urlPath 
        *   The path which matched. In this case the full path. 
        * @return The handler execution chain that contains the backing bean. 
        * 
        * @see {@link AbstractUrlHandlerMapping#buildPathExposingHandler(Object, String, String, java.util.Map)} 
        *  
        */ 
        protected HandlerExecutionChain buildChain(Object bean, String urlPath) { 
         // I don't know why but the super class declares object but actually 
         // returns handlerExecution chain. 
         HandlerExecutionChain chain = (HandlerExecutionChain) buildPathExposingHandler(
           controller, urlPath, urlPath, null); 
         addBackingBeanInteceptor(chain, bean); 
         return chain; 
        } 
    
        /** 
        * Adds an inteceptor which adds the backing bean into the request to an 
        * existing HandlerExecutionChain. 
        * 
        * @param chain 
        *   The chain which the backing bean is being added to. 
        * @param bean 
        *   The object to pass through to the controller. 
        */ 
        protected void addBackingBeanInteceptor(HandlerExecutionChain chain, 
          Object bean) { 
         chain.addInterceptor(new BackingBeanExposingInteceptor(bean)); 
    
        } 
    
        /** 
        * An Interceptor which adds a bean to a request for later consumption by a 
        * controller. 
        * 
        * @author Wesley Acheson 
        * 
        */ 
        protected class BackingBeanExposingInteceptor extends 
          HandlerInterceptorAdapter { 
         private Object backingBean; 
    
         /** 
         * @param backingBean 
         *   the bean which is passed through to the controller. 
         */ 
         public BackingBeanExposingInteceptor(Object backingBean) { 
          this.backingBean = backingBean; 
         } 
    
         @Override 
         public boolean preHandle(HttpServletRequest request, 
           HttpServletResponse response, Object handler) throws Exception { 
          request.setAttribute(BACKING_BEAN_ATTRIBUTE, backingBean); 
          return true; 
         } 
        } 
    
    } 
    
  2. Wdrożenie HandlerMethodArgumentResolver do pobrania wartości z sesji. (Zakładając, że jesteś zainteresowany w tworzeniu w sesji)

    // Copyright 2012 Wesley Acheson 
    // 
    // Licensed under the Apache License, Version 2.0 (the "License"); 
    // you may not use this file except in compliance with the License. 
    // You may obtain a copy of the License at 
    // 
    //  http://www.apache.org/licenses/LICENSE-2.0 
    // 
    // Unless required by applicable law or agreed to in writing, software 
    // distributed under the License is distributed on an "AS IS" BASIS, 
    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    // See the License for the specific language governing permissions and 
    // limitations under the License. 
    
    package com.wesley_acheson.spring; 
    
    import javax.servlet.http.HttpServletRequest; 
    
    import org.springframework.core.MethodParameter; 
    import org.springframework.web.bind.support.WebDataBinderFactory; 
    import org.springframework.web.context.request.NativeWebRequest; 
    import org.springframework.web.method.support.HandlerMethodArgumentResolver; 
    import org.springframework.web.method.support.ModelAndViewContainer; 
    
    /** 
    * Resolves method parameters which are annotated with {@link BackingBean}. 
    * 
    * <b>Note:</b> Only works for Http requests. 
    * 
    * @author Wesley Acheson 
    * 
    */ 
    public class BackingBeanValueResolver implements HandlerMethodArgumentResolver { 
    
        /** 
        * Constructor. 
        */ 
        public BackingBeanValueResolver() { 
        } 
    
        /** 
        * Implementation of 
        * {@link HandlerMethodArgumentResolver#supportsParameter(MethodParameter)} 
        * that returns true if the method parameter is annotatated with 
        * {@link BackingBean}. 
        */ 
        @Override 
        public boolean supportsParameter(MethodParameter parameter) { 
         return parameter.hasParameterAnnotation(BackingBean.class); 
        } 
    
        @Override 
        public Object resolveArgument(MethodParameter parameter, 
          ModelAndViewContainer mavContainer, NativeWebRequest webRequest, 
          WebDataBinderFactory binderFactory) throws Exception { 
         return webRequest.getNativeRequest(HttpServletRequest.class) 
           .getAttribute(
             BackingBeanUrlHandlerMapper.BACKING_BEAN_ATTRIBUTE); 
        } 
    
    } 
    
  3. Wdrożenie niestandardowy WebArgumentResolver aby pobrać wystąpienie Bean minął. Ustaw tę właściwość jako instancję obiektu AnnotationMethodHandler.

    /** 
    * 
    */ 
    package com.wesley_acheson.spring; 
    
    import javax.servlet.http.HttpServletRequest; 
    
    import org.springframework.core.MethodParameter; 
    import org.springframework.web.bind.support.WebArgumentResolver; 
    import org.springframework.web.context.request.NativeWebRequest; 
    
    
    /** 
    * @author Wesley Acheson 
    * 
    */ 
    public class BackingBeanArgumentResolver implements WebArgumentResolver { 
    
        /* (non-Javadoc) 
        * @see org.springframework.web.bind.support.WebArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.context.request.NativeWebRequest) 
        */ 
        @Override 
        public Object resolveArgument(MethodParameter methodParameter, 
          NativeWebRequest webRequest) throws Exception { 
         if (methodParameter.hasParameterAnnotation(BackingBean.class)) 
         { 
          HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); 
          Object parameter = request.getAttribute(BackingBeanUrlHandlerMapper.BACKING_BEAN_ATTRIBUTE); 
          if (parameter == null) 
          { 
           return UNRESOLVED; 
          } 
          if (methodParameter.getParameterType().isAssignableFrom(parameter.getClass())) 
          { 
           return parameter; 
          } 
         } 
    
    
         return UNRESOLVED; 
        } 
    
    } 
    
  4. Stworzyłem również BackingBean adnotacji oraz interfejs przejść do moich addapters procedur obsługi, jak czułem, że były one łatwiejsze.

  5. Utwórz kontroler. Jeśli użyjesz mojego kodu, będziesz chciał wprowadzić argument za pomocą adnotacji @BackingBean. Mapowanie żądań na samym kontrolerze nie może się równać z żadnymi dobrymi adresami URL (Dzieje się tak dlatego, że pomijamy ten krok za pomocą naszego adaptera obsługi i nie chcemy, aby domyślny moduł do obsługi adnotacji go odebrał.)

  6. Podłącz wszystko na wiosnę . Oto przykładowy plik z moim przykładzie projektu roboczego.

    <?xml version="1.0" encoding="UTF-8" standalone="no"?> 
    <beans xmlns="http://www.springframework.org/schema/beans" 
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" 
        xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd 
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd 
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> 
    
        <!-- The controllers are autodetected POJOs labeled with the @Controller 
         annotation. --> 
        <context:component-scan base-package="com.wesley_acheson" 
         use-default-filters="false"> 
         <context:include-filter expression="org.springframework.stereotype.Controller" 
          type="annotation" /> 
        </context:component-scan> 
    
        <bean class="com.wesley_acheson.spring.BackingBeanUrlHandlerMapper" 
         p:order="-1"> 
         <property name="controller"> 
          <!-- A simple example controller. --> 
          <bean class="com.wesley_acheson.example.PageController" /> 
         </property> 
         <!-- A simple example mapper. --> 
         <property name="urlMapper"> 
          <bean class="com.wesley_acheson.example.PageBeanUrlMapper" /> 
         </property> 
        </bean> 
    
        <util:map id="pages"> 
         <entry key="/testPage1"> 
          <bean class="com.wesley_acheson.example.Page"> 
           <property name="title" value="Test Page 1 title" /> 
           <property name="contents" 
            value="This is the first test page.&lt;br /&gt; It's only purpose is to check 
            if &lt;b&gt;BackingBeans&lt;/b&gt; work." /> 
          </bean> 
         </entry> 
    
         <entry key="/test/nested"> 
          <bean class="com.wesley_acheson.example.Page"> 
           <property name="title" value="Nested Path" /> 
           <property name="contents" 
            value="This is another test page its purpose is to ensure nested pages work." /> 
          </bean> 
         </entry> 
        </util:map> 
    
    
        <bean 
         class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> 
         <property name="customArgumentResolver"> 
          <bean class="com.wesley_acheson.spring.BackingBeanArgumentResolver" /> 
         </property> 
        </bean> 
    
        <!-- Turns on support for mapping requests to Spring MVC @Controller methods 
         Also registers default Formatters and Validators for use across all @Controllers --> 
        <mvc:annotation-driven /> 
    
    
        <!-- Handles HTTP GET requests for /resources/** by efficiently serving 
         up static resources --> 
        <mvc:resources location="/, classpath:/META-INF/web-resources/" 
         mapping="/resources/**" /> 
    
        <!-- Allows for mapping the DispatcherServlet to "/" by forwarding static 
         resource requests to the container's default Servlet --> 
        <mvc:default-servlet-handler /> 
    
    </beans> 
    
+0

@Flexo dzięki za formatowanie. – Wes

+1

przykład użycia tego można znaleźć na github https://github.com/Athas1980/BackingBeanWebExample Uwaga to rozwiązanie wymaga mvc 3.1 + – Wes

Powiązane problemy