带有xml混合问题的Spring注释

时间:2010-06-21 14:27:47

标签: java spring spring-mvc annotations

我正在尝试找到使用最新Spring 3.0的最佳方式。我非常喜欢@RequestMapping注释,并且应用了所有功能。但是,我不喜欢的是,应该在java文件中完全指定绑定到操作的URL。

以某种方式将整个url绑定配置发送到上下文xml文件是最好的。但是,如果可以将url-binding至少部分地移动到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 个答案:

答案 0 :(得分:4)

根据要求。您希望在没有Spring控制器注释的情况下创建自己的URL模式

首先,创建一个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。它允许您通过调用

来检索任何Spring托管bean
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());
}

答案 1 :(得分:0)

您可以将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(...) {
    }
}

网址/myController/someMethod应与该方法匹配。

你可能需要玩一点才能让它发挥作用,但这就是它的要点。

答案 2 :(得分:0)

  

然而,我不喜欢的是,是在java文件中应该完全指定绑定到操作的URL

所以依靠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 表单方法调用为

/financial/account/form

您可以将ManagementController 表单方法调用为

/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

好,不要???