12

クロスポストです。同じ質問を春のフォーラムにも投稿しました。http://forum.springsource.org/showthread.php?128579-Database-driven-Controller-Mapping

こんにちは、実行時に変更できるように、データベース駆動型のコントローラー マッピングを実行しようとしています。

これまでのところ、私が持っているものは次のとおりです。

後でいつでも最適化できるカスタム ハンドラー アダプター。

@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-configは次のようになります(関連部分)

コード:

<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)
        ....

カスタム注釈ハンドラーを定義する必要がありますか?

正直なところ、このプロセス全体が必要以上に難しいように思えます。外部で定義された URL パスへのすべてのリクエストを 1 つのコントローラーで処理するようにしたいのですが、これが正しい回避方法です。

また、コントローラーで新しいルックアップを行うのではなく、可能であれば、コントローラーに一致するオブジェクトを渡したいと思います。これは基本的にビューのモデルを形成します。

これを機能させる方法についてアドバイスはありますか?

編集 記録のために、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>

私は以下の質問に自分で答えましたが、これを行う正しい方法について検討している人々にまだ興味があります.

4

2 に答える 2

3

どうやらここと春のフォーラムで反対の答えがないことから、春のフレームワーク内でこれを行う簡単な方法はないようです。

しかし、私はなんとかそれを機能させ、クラスを動的に追加するプロセスを容易にするために4つのクラスを追加するmavenで構築できるプロジェクトをgithubで共有しました。このプロジェクトはhttps://github.com/Athas1980/MvcBackingBeanにあります。また、別のプロジェクトを共有して、それが機能することを証明します。

Marten Deinum と Rossen 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 を実装して、セッションから値を取得します。(セッションの設定に興味があると仮定して)

    //   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 アノテーションとハンドラー アダプターに渡すインターフェイスも作成しました。簡単だと感じたからです。

  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>
    
于 2012-07-20T22:15:27.557 に答える
2

この特定の問題を乗り越えるために、今のところ 1 つの方法をお勧めします -

AnnotationMethodHandlerAdapter を内部的に構成する独自の handlerAdapter を作成します。

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>

これで現在の問題は解決するはずですが、他の問題が発生する可能性があります

于 2012-07-20T18:17:31.790 に答える