Подтвердить что ты не робот

Как можно динамически сопоставлять пути spring -webmvc?

Это перекрестный столбец. Я также опубликовал тот же вопрос на форумах spring. 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);
    }

}

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

Нужно ли определять пользовательский обработчик аннотации?

Честно говоря, весь этот процесс кажется сложнее, чем нужно. Я хочу, чтобы 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>

Я ответил на вопрос сам ниже, но меня все еще интересуют люди, которые взвешивают правильный способ сделать это.

4b9b3361

Ответ 1

По-видимому, из-за отсутствия ответов на обратное здесь и на spring форумах кажется, что нет более простого способа сделать это в рамках spring.

Мне все же удалось заставить его работать, и я поделился проектом в github, который можно построить с помощью maven, который добавит 4 класса, чтобы облегчить процесс динамического добавления класса. Этот проект можно найти на https://github.com/Athas1980/MvcBackingBean. Я также расскажу о другом проекте, чтобы доказать, что он работает.

Благодаря Мартену Дейнуму и Россен Стоянчеву


Для тех, кто заинтересован в том, как достичь этого, вам нужно сделать следующее

  • Реализация экземпляра 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;
            }
        }
    
    }
    
  • Внедрите 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);
        }
    
    }
    
  • Внедрение настраиваемого 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;
        }
    
    }
    
  • Я также создал аннотацию BackingBean и интерфейс, чтобы перейти к дополнительным элементам моего обработчика, поскольку я чувствовал, что они были легче.

  • Создайте свой контроллер. Если вы используете мой код, вам нужно будет ввести аргумент, используя аннотацию @BackingBean. Отображение запроса на самом контроллере не должно соответствовать хорошим URL-адресам (это связано с тем, что мы обходим этот шаг с помощью нашего адаптера обработчика, и мы не хотим, чтобы обработчик аннотации по умолчанию его подбирал.

  • Подключите все в spring. Вот пример файла из моего рабочего проекта проекта.

    <?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 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 default Servlet -->
        <mvc:default-servlet-handler />
    
    </beans>
    

Ответ 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>

Это должно решить текущую проблему, но вы, вероятно, столкнетесь с другими проблемами.