This is a cross post. I've also posted the same question to spring forums. http://forum.springsource.org/showthread.php?128579-Database-driven-Controller-Mapping
这是一个十字柱子。我也在spring论坛上发布了同样的问题。http://forum.springsource.org/showthread.php?128579-Database-driven-Controller-Mapping
Hi I'm trying to do database driven controller mappings so that they can change at runtime.
嗨,我正在尝试做数据库驱动的控制器映射,这样它们在运行时就可以改变。
So far what I have is as follows.
到目前为止,我所拥有的如下。
Custom Handler Adaptor which can always be optimized later.
自定义处理器适配器,可在以后进行优化。
@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 looks as follows (the relevant part)
我的webmvc-config如下(相关部分)
Code:
代码:
<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"/>
This appears to be picking up the correct controller. However I recieve an error when going to a database defined path (such as "/a")
这似乎找到了正确的控制器。然而,在访问数据库定义的路径(如“/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)
....
Do I need to define a custom annotation handler?
我需要定义自定义注释处理程序吗?
To be honest this whole process seems more difficult than it should. I want 1 controller to handle all requests to an externally defined url path is this the correct way of going arround it.
老实说,整个过程似乎比应该的要困难得多。我想要一个控制器来处理所有请求到一个外部定义的url路径这是正确的循环方式。
I'd also like to pass in the object which matched into the controller if this is possible rather than doing a fresh lookup in the controller. This will basically form my model for the view.
如果可能的话,我还希望传入与控制器匹配的对象,而不是在控制器中执行新的查找。这将基本形成我的视图模型。
Any advise on how to get this working?
有什么建议可以让它发挥作用吗?
EDIT For the record the NPE is here
编辑记录,NPE在这里
private boolean useTypeLevelMapping(HttpServletRequest request) {
if (!hasTypeLevelMapping() || ObjectUtils.isEmpty(getTypeLevelMapping().value())) {
return false;
}
return (Boolean) request.getAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING);
}
Another Edit version numbers from the pom.xml
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>
I've answered the question myself below but I'm still intrested in people weighing in on the correct way to do this.
我自己已经回答了下面的问题,但我仍然对那些正在权衡正确方法的人感兴趣。
2 个解决方案
#1
3
Apparently from the lack of answers to the contrary here and on the spring forums it appears that there is no simpler way to do this within the spring framework.
显然,由于在这里和在spring论坛上没有相反的答案,似乎在spring框架中没有更简单的方法来实现这一点。
I have however managed to get it working and I've shared a project at github that can be built with maven that add 4 classes to ease with the process of dynamically adding class. This project can be found at https://github.com/Athas1980/MvcBackingBean. I'll also share another project to prove that it works.
然而,我成功地让它运行起来了,并且我在github上共享了一个项目,这个项目可以通过maven来构建,maven可以添加4个类来简化动态添加类的过程。这个项目可以在https://github.com/Athas1980/MvcBackingBean中找到。我还将分享另一个项目来证明它是有效的。
Thanks to Marten Deinum, and Rossen Stoyanchev
感谢Marten Deinum和Rossen Stoyanchev
For those interested in how to achieve this yourselves you need to do the following
对于那些对如何实现这个目标感兴趣的人,你需要做以下的事情
-
Implement an instance of HandlerMapper This gives you the mapping between a controller class and the url that you are mapping to.
实现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; } } }
-
Implement a HandlerMethodArgumentResolver to fetch the value out of the session. (assuming you are intrested in setting in the session)
实现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); } }
-
Implement a custom WebArgumentResolver to fetch the instance of the Bean passed. Set this as a property to an instance of AnnotationMethodHandler.
实现自定义的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; } }
-
I also created a BackingBean annotation and an interface to pass to my handler addapters as I felt they were easier.
我还创建了一个BackingBean注释和一个接口来传递给我的处理程序addapter,因为我觉得它们更容易。
-
Create your controller. If you use my code you will want to inject the argument using the @BackingBean annotation. The request mapping on the controller itself must not match any good urls (This is because we bypass this step with our handler adapter and we don't want the default annotation handler to pick it up.
创建您的控制器。如果您使用我的代码,您将希望使用@BackingBean注释注入参数。控制器本身的请求映射本身不能匹配任何好的url(这是因为我们用我们的处理程序适配器绕过了这个步骤,并且我们不希望默认的注释处理程序来获取它)。
-
Wire up everything in spring. Here's an example file from my working example project.
在春天把一切都连接起来。这是我的工作示例项目中的一个示例文件。
<?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.<br /> It's only purpose is to check if <b>BackingBeans</b> 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>
#2
2
Just to get over this specific issue, let me recommend one way out for now -
为了解决这个具体的问题,让我向你推荐一种解决方法
Create your own handlerAdapter internally composing the AnnotationMethodHandlerAdapter:
创建自己的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>
This should solve the current issue, but you will likely encounter other issues
这应该可以解决当前的问题,但是您可能会遇到其他问题
#1
3
Apparently from the lack of answers to the contrary here and on the spring forums it appears that there is no simpler way to do this within the spring framework.
显然,由于在这里和在spring论坛上没有相反的答案,似乎在spring框架中没有更简单的方法来实现这一点。
I have however managed to get it working and I've shared a project at github that can be built with maven that add 4 classes to ease with the process of dynamically adding class. This project can be found at https://github.com/Athas1980/MvcBackingBean. I'll also share another project to prove that it works.
然而,我成功地让它运行起来了,并且我在github上共享了一个项目,这个项目可以通过maven来构建,maven可以添加4个类来简化动态添加类的过程。这个项目可以在https://github.com/Athas1980/MvcBackingBean中找到。我还将分享另一个项目来证明它是有效的。
Thanks to Marten Deinum, and Rossen Stoyanchev
感谢Marten Deinum和Rossen Stoyanchev
For those interested in how to achieve this yourselves you need to do the following
对于那些对如何实现这个目标感兴趣的人,你需要做以下的事情
-
Implement an instance of HandlerMapper This gives you the mapping between a controller class and the url that you are mapping to.
实现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; } } }
-
Implement a HandlerMethodArgumentResolver to fetch the value out of the session. (assuming you are intrested in setting in the session)
实现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); } }
-
Implement a custom WebArgumentResolver to fetch the instance of the Bean passed. Set this as a property to an instance of AnnotationMethodHandler.
实现自定义的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; } }
-
I also created a BackingBean annotation and an interface to pass to my handler addapters as I felt they were easier.
我还创建了一个BackingBean注释和一个接口来传递给我的处理程序addapter,因为我觉得它们更容易。
-
Create your controller. If you use my code you will want to inject the argument using the @BackingBean annotation. The request mapping on the controller itself must not match any good urls (This is because we bypass this step with our handler adapter and we don't want the default annotation handler to pick it up.
创建您的控制器。如果您使用我的代码,您将希望使用@BackingBean注释注入参数。控制器本身的请求映射本身不能匹配任何好的url(这是因为我们用我们的处理程序适配器绕过了这个步骤,并且我们不希望默认的注释处理程序来获取它)。
-
Wire up everything in spring. Here's an example file from my working example project.
在春天把一切都连接起来。这是我的工作示例项目中的一个示例文件。
<?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.<br /> It's only purpose is to check if <b>BackingBeans</b> 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>
#2
2
Just to get over this specific issue, let me recommend one way out for now -
为了解决这个具体的问题,让我向你推荐一种解决方法
Create your own handlerAdapter internally composing the AnnotationMethodHandlerAdapter:
创建自己的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>
This should solve the current issue, but you will likely encounter other issues
这应该可以解决当前的问题,但是您可能会遇到其他问题