2012-07-20 52 views
12

這是一個交叉帖子。我也向春季論壇發佈了同樣的問題。 http://forum.springsource.org/showthread.php?128579-Database-driven-Controller-Mapping如何動態地映射spring-webmvc路徑?

您好我想做數據庫驅動器映射,使他們能夠在運行改變。

到目前爲止我所擁有的如下。

自定義處理程序適配器,可隨時進行優化。

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

} 

我webmvc,配置如下所示(相關部分)

代碼:

<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"/> 

這似乎是撿了正確的控制器。不過,我要一個數據庫中定義的路徑時(如「/ 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) 
     .... 

我需要定義一個自定義註釋處理程序?

說實話,這整個過程似乎比它應該更困難。我想要1個控制器處理所有請求到一個外部定義的url路徑,這是圍繞它的正確方式。

我也想傳入匹配到控制器的對象,如果這是可能的,而不是在控制器中進行新的查找。這將基本上形成我的觀點模型。

有關如何使其正常工作的建議?

編輯 備案NPE在這裏

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

從另一個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> 

我已經回答了我自己以下問題。但我仍然對那些正確地認真對待這個問題的人感興趣。

+0

@fmucar究竟數據庫驅動的URL模式?它的90%有其找到正確的控制器只是沒有在控制器上的正確方法。 如果你談論把變量傳遞給控制器​​,我想最壞的情況是我會注入把它作爲請求屬性並從控制器中取出,但感覺笨重。 – Wes 2012-07-20 15:00:20

+0

對不起,我一定誤解了你的q。我將刪除上述評論 – fmucar 2012-07-20 15:40:33

+0

您能否確認您的Spring MVC版本 – 2012-07-20 17:39:21

回答

2

只是爲了克服這個具體問題,讓我出去推薦的一種方式,現在 -

創建您自己的HandlerAdapter內部爲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> 

這應該解決目前的問題,但你可能會遇到其他問題

+0

我沒有得到其他答案。你有賞金。 – Wes 2012-07-29 23:00:46

3

顯然從這裏和spring forums缺乏相反的答案,似乎沒有更簡單的方法來做到這一點春天框架。

然而,我設法讓它工作,我已經在github上共享了一個項目,可以使用maven構建,添加4個類來緩解動態添加類的過程。該項目可在https://github.com/Athas1980/MvcBackingBean找到。我還會分享另一個項目來證明它的工作原理。

由於貂Deinum和羅森Stoyanchev


對於那些有興趣在如何實現這一目標自己,你需要做以下

  1. 實施HandlerMapper的實例,這使你是控制器類和你映射到的url之間的映射。

    // 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. 實現一個HandlerMethodArgumentResolver來獲取值了會議。 (假設你是在會話設置intrested)

    // 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. 實現自定義WebArgumentResolver來獲取 Bean的實例通過。將此屬性設置爲 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. 我還創建了一個BackingBean註釋和接口傳遞給我的處理程序addapters,因爲我覺得他們比較容易。

  5. 創建您的控制器。如果你使用我的代碼,你會想使用@BackingBean註解來注入參數。控制器上的請求映射本身不能匹配任何好的url(這是因爲我們用我們的處理器適配器繞過了這一步,我們不希望默認註釋處理程序將它拿起)。

  6. 在春天。下面是我的工作示例項目的例子文件。

    <?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感謝格式。 – Wes 2012-07-29 21:06:25

+1

使用此示例可以在github上找到https://github.com/Athas1980/BackingBeanWebExample注意此解決方案需要mvc 3.1 + – Wes 2012-07-29 23:05:22