Весенняя аннотация с проблемой смешивания XML - PullRequest
2 голосов
/ 21 июня 2010

Я пытаюсь найти оптимальный способ использовать последнюю версию Spring 3.0.Мне очень нравится аннотация @RequestMapping со всеми примененными к ней функциями.Однако что мне не нравится, так это то, что URL-адрес, связанный с действием, должен быть полностью указан в java-файле.

Лучше всего как-то отправить всю конфигурацию привязки URL-адреса в контекст xmlфайл.Тем не менее, это также будет сделано, если это URL-привязка может быть перемещен в XML хотя бы частично.

Это то, что я имею в виду:

Текущий код:

@Controller
@RequestMapping("myController")
class MyController {
    @RequestMapping("**/someMethod")
    String someMethod(...) {
    }
}

Этот код связывает myController / someMethod с MyController :: someMethod.Что мне здесь не нравится, так это то, что привязка части myController также находится в этом java-файле.Я хочу сделать его как можно более модульным, и эта роль для меня играет очень плохо.

Я бы хотел увидеть что-то вроде этого для достижения того же результата:

context.xml

<mapping>
    <url>myController</url>
    <controller>MyController</controller>
</mapping>    

java

@Controller
//-- No request mapping here --// @RequestMapping("myController")
class MyController {
    @RequestMapping("**/someMethod")
    String someMethod(...) {
    }
}

Возможно ли что-то подобное на аннотированных контроллерах в Spring 3?

Ответы [ 3 ]

4 голосов
/ 30 июня 2010

По запросу.Вы хотите создать свой собственный шаблон URL без аннотаций контроллеров Spring .

Прежде всего, создайте аннотацию CustomController Чтобы избежать обнаружения @Controller HandlerMapping

package br.com.ar.web.stereotype;

@Target(value=TYPE)
@Retention(value=RUNTIME)
@Component
public @interface CustomController {}

Здесь идет наш AccountController

@CustomController
public class AccountController {

    public void form(Long id) {
        // do something
    }

}

Наш HandlerAdapter - Он заботится о вызове нашего контроллера - Что-то похожее на интерфейс Spring Validator

package br.com.ar.web.support;

public class CustomHandlerAdapter implements HandlerAdapter {

    public boolean supports(Object handler) {
        Annotation [] annotationArray = handler.getClass().getAnnotations();

        for(Annotation annotation: annotationArray) {
           /**
             * Make sure your annotation contains @SomeController
             */
        }
    }

    /**
      * Third parameter is our CustomController
      */
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Method[] methods = handler.getClass().getMethods();

        /**
          * Logic To verify whether Target method fullfil request goes here
          */            

        /**
          * It can be useful To see MultiActionController.invokeNamedMethod and MultiActionController.isHandlerMethod              
          */
        method.invoke(// parameters goes here);
    }

    public long getLastModified(HttpServletRequest request, Object handler) {
        return -1;
    }
}

И, наконец, наше HandlerMapping.Убедитесь, что ваш HandlerMapping расширяет WebApplicationObjectSupport.Он позволяет вам извлекать любой управляемый bean-компонент Spring, вызывая

getApplicationContext().getBean(beanName);

package br.com.ar.web.servlet.handler;

public class CustomeHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {

    private static final String CUSTOM_HANDLER_ADAPTER_NAME = "CUSTOM_HANDLER_ADAPTER_NAME";

    /**
      * Bind each URL path-CustomController bean name
      */
    private final Map handlerMap = new LinkedHashMap();

    /**
      * Ordered interface will make sure your HandlerMapping should be intercepted BEFORE or AFTER DefaultAnnotationHandlerMapping
      */
    public final void setOrder(int order) {
        this.order = order;
    }

    public final int getOrder() {
        return this.order;
    }

    /**
      * HandlerMapping interface method
      */
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        String url = extractUrl(request);

        if(handlerMap.get(url) == null) {
            /**
              * Because Spring 3.0 controller is stateful
              * Let's just store CustomController class (Not an instance) in ApplicationContext
              *
              * Or use a FactoryBean to retrieve your CustomController
              */
            handlerMap.put(url, getApplicationContext().getBean(beanName));
        }

        /**
          * instantiateClass needs no-arg constructor
          */
        Object handler = BeanUtils.instantiateClass(handlerMap.get(url));

        return new HandlerExecutionChain(handler);
    }

    private String extractUrl(HttpServletRequest request) {
        /**
          * Here goes code needed To retrieve URL path from request
          *
          * Take a look at AntPathMatcher, UrlPathHelper and PathMatcher
          *
          * It can be useful To see AbstractUrlHandlerMapping.getHandlerInternal method
          */ 
    }

}

Не забудьте зарегистрировать и HandlerAdapter, и HandlerMapping

<bean id="br.com.ar.web.servlet.handler.CustomHandlerMapping"/>
<bean id="br.com.ar.web.support.CustomHandlerAdapter"/>
<!--To allow Spring 3.0 controller-->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>

Надеюсь, это может дать вам хороший старт

Последовательность ( За кулисами ) Spring DispatcherServlet будет называть наши объекты

/**
  * Our HandlerMapping goes here
  */
HandlerMapping handlerMapping = getHandler(request);

HandlerExecutionChain handlerExecutionChain = handlerMapping.getHandler(request);

for(HandlerInterceptor interceptor: handlerExecutionChain.getInterceptors) {
    interceptor.preHandle(request, response, handlerExecutionChain.getHandler());
}

/**
  * Our CustomController goes here
  */
Object handler = handlerExecutionChain.getHandler();

/**
  * Our CustomHandlerAdapter goes here
  */
HandlerAdapter handlerAdapter = getHandlerAdapter(handler);

ModelAndView mav = handlerAdapter.handle(request, response, handler);

for(HandlerInterceptor interceptor: handlerExecutionChain.getInterceptors) {
    interceptor.postHandle(request, response, handlerExecutionChain.getHandler());
}
0 голосов
/ 29 июня 2010

Однако, что мне не нравится, - это то, что URL, привязанный к действию, должен быть полностью указан в java-файле

Так что полагайтесь на ControllerClassNameHandlerMapping

<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>

Помните, что ControllerNameHandlerMapping удалить Суффикс контроллера , если он существует и вернуть оставшийся текст в нижнем регистре Если вы просто хотите, чтобы первая буква в нижнем регистре установите для свойства caseSensitive значение true

Предположим, здесь идет ваш контроллер

package br.com.ar.view.resources;

@Controller
public class UserController {

    /**
      * mapped To /user/form
      */
    @RequestMapping(method=RequesMethod.GET)
    public void form(Model model) {
        model.add(categoryRepository().getCategoryList());
    }

    /**
      * mapped To user/form
      */
    @RequestMapping(method=RequesMethod.POST)
    public void form(User user) {
        userRepository.add(user);
    }

}

Существует больше: если вы используете модульное приложение, вы можете положиться на свойство basePackage . Предположим, у вас есть модуль финансовых и человеческих ресурсов, например

br.com.ar.view.financial.AccountController;
br.com.ar.view.resources.ManagementController;

Вы определяете свой базовый пакет

<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
    <property name="basePackage" value="br.com.ar.view"/>
</bean>

Вы можете вызвать свой AccountController form метод как

/financial/account/form

И вы можете вызвать свой метод ManagementController form как

/resources/management/form

Поскольку я уверен, что вы используете соглашение TranslateToViewName по умолчанию для конфигурации, ваша структура каталогов должна выглядеть как

/WEB-INF
    /view
        /financial
            /user
                form.jsp

        /resources
            /management
                form.jsp

Не забудьте определить свой InternalResourceViewResolver

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/view/"/>
    <property name="suffix" value=".jsp"/>
</bean>

И, наконец, если ваш запрос не нуждается в контроллере . Нет проблем, определите ваше свойство defaultHandler

<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
    <property name="basePackage" value="br.com.ar.view"/>
    <property name="caseSensitive" value="true"/>
    <property name="defaultHandler">
        <bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
    </property>
</bean>

Теперь, если вы вызываете, например, /index.htm (я полагаю, ваш DispatcherServlet перехватывает расширение htm) и у вас нет IndexController, Spring будет искать

/WEB-INF/view/index.jsp

Хорошо, нет ???

0 голосов
/ 21 июня 2010

Вы можете комбинировать сопоставления в стиле XML и аннотации со значительной гибкостью.Оба могут использовать сопоставление с подстановочными знаками в стиле Ant, так что вы можете делать такие вещи (не проверено, но дает общее представление):

<bean class="SimpleUrlHandlerMapping">
   <property name="mappings">
      <map>
         <entry key="myController/**" value-ref="myController"/>
      </map>
   </property>
</bean>

<bean id="myController" class="MyController"/>

А затем

@Controller
class MyController {
    @RequestMapping("**/someMethod")
    String someMethod(...) {
    }
}

URL/myController/someMethod должен затем соответствовать этому методу.

Возможно, вам придется немного поиграть, чтобы заставить его работать, но в этом суть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...