场景:
在SpringMVC3.x 中一个http 请求被controller handler处理的时候,需要将请求中的某些参数根据特定的逻辑生成一些特定的数值供每个controller handler使用。这个特定的逻辑可以抽取到SpringMVC Interceptor中,然后将计算的特定数据以HttpServletRequest.attribute的方式传送到controller handler中。SpringMVC 3.x 没有提供类似@RequestParam的PARAMETER Annotation用来自动注入HttpServletRequest.attribute参数到handler method中。有2中选择:
1、将HttpServletRequest当做参数注入到handler method中,每个Controller中的每个方法都要注入request对象。。。这种方案就是一个大杯具!
2、自定义一个Annotation来实现HttpServletRequest.attribute自动注入
代码
1、定义Annotation:
/**
* get parameter from Request Attribute
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestAttribute {
/**
* The request attribute parameter to bind to.
*/
String value() ;
}
2、在目标类定义:
@Controller
@RequestMapping("/")
public class UserController{
@RequestMapping(value="/demo")
public @ResponseBody Map<String, String>> demo(@RequestAttribute("loginUserId") long loginUserId){
return Collections.emptyMap();
}
}
3、自定义Resolver
最重要的步骤,实现WebArgumentResolver接口来实现"特殊请求参数"的解析器Resolver,然后将这个Resolver加入到org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.customArgumentResolver。同时不能使用schema方式启用SpringMVC: <mvc:annotation-driven/>,使用xml定义的方式启用SpringMVC。
public class RequestAttributeArgumentResolver implements WebArgumentResolver {
public Object resolveArgument(MethodParameter param,
NativeWebRequest request) throws Exception {
Annotation[] paramAnns = param.getParameterAnnotations();
Class paramType = param.getParameterType();
for (Annotation paramAnn : paramAnns) {
if (RequestAttribute.class.isInstance(paramAnn)) {
RequestAttribute reqAttr = (RequestAttribute) paramAnn;
HttpServletRequest httprequest = (HttpServletRequest) request.getNativeRequest();
Object result = httprequest.getAttribute(reqAttr.value());
/**
* result 校验是否为null
*/
return result;
}
}
return WebArgumentResolver.UNRESOLVED;
}
}
4、启用Resolver方式:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="customArgumentResolver">
<bean class="com.netease.lovefront.web.extention.RequestAttributeArgumentResolver"></bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
</list>
</property>
</bean>
5、Interceptor for Http URL Request
生成loginUserId的Interceptor:
/**
* <li>init the user info, user id when {@code preHandle}.</li>
*/
@Component
public class UserInterceptor extends HandlerInterceptorAdapter {
@Resource
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String email = CookieUtils.getLoginEmail(request);
if(StringUtils.isNotBlank(email)) {
long userId = userService.getUserId(email);
request.setAttribute("loginUserId", userId);
}
return true;
}
}
UserInterceptor 会拦截每一个http 请求。
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="userInterceptor " />
</list>
</property>
</bean>
ps:
还有一种方法来类似的Interceptor逻辑:通过@ModelAttribute method方式来实现数据转换,然后在 handler method中使用@ModelAttribute 修饰的参数来实现参数注入。如果Controller太多,需要继承BaseController来抽取@ModelAttribute修饰的方法,这种也是一种可选方案。。。