gpt4 book ai didi

java - 如何动态映射 spring-webmvc 路径?

转载 作者:搜寻专家 更新时间:2023-10-30 21:11:46 24 4
gpt4 key购买 nike

这是一个交叉帖子。我也在 Spring 论坛上发布了同样的问题。 http://forum.springsource.org/showthread.php?128579-Database-driven-Controller-Mapping

您好,我正在尝试进行数据库驱动的 Controller 映射,以便它们可以在运行时 进行更改。

到目前为止,我所拥有的如下。

可以在以后随时优化的自定义处理程序适配器。

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

这似乎是在选择正确的 Controller 。但是,当我转到数据库定义的路径(例如“/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 个 Controller 来处理对外部定义的 url 路径的所有请求,这是绕过它的正确方法。

如果可能的话,我还想将匹配的对象传递给 Controller ​​,而不是在 Controller 中进行新的查找。这将基本上形成我的 View 模型。

关于如何让它工作有什么建议吗?

编辑为了记录 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>

我自己在下面回答了这个问题,但我仍然希望人们能权衡正确的方法来做到这一点。

最佳答案

显然这里和 spring forums 上都没有相反的答案看来在 spring 框架内没有更简单的方法可以做到这一点。

不过,我已经设法让它工作,并且我在 github 上共享了一个项目,该项目可以使用添加 4 个类的 Maven 构建,以简化动态添加类的过程。这个项目可以在 https://github.com/Athas1980/MvcBackingBean 找到。 .我还将分享另一个项目来证明它有效。

感谢 Marten Deinum 和 Rossen Stoyanchev


对于那些对如何自己实现这一目标感兴趣的人,您需要执行以下操作

  1. 实现 HandlerMapper 的实例这为您提供了 Controller 类和您要映射到的 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 以从 session 中获取值。 (假设您对 session 中的设置感兴趣)

    //   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 注释和一个接口(interface)来传递给我的处理程序适配器,因为我觉得它们更容易。

  5. 创建您的 Controller 。如果您使用我的代码,您将需要使用 @BackingBean 注释注入(inject)参数。 Controller 本身上的请求映射不能匹配任何好的 url(这是因为我们使用处理程序适配器绕过了这一步,我们不希望默认的注释处理程序接收它。

  6. 在 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'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>

关于java - 如何动态映射 spring-webmvc 路径?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11581767/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com