我正在尝试找到使用最新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中带注释的控制器上是否可以这样?
答案 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托管beangetApplicationContext().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
好,不要???