Ответ 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.<br /> It 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 default Servlet --> <mvc:default-servlet-handler /> </beans>