就简洁地说一下吧。大多数crud都是在controller的一个个方法中进行的,一般的思路就是从request里取到当前user,或者userid,然后再通过这个user/userid去数据库里进行下一步操作。仔细想想的话,是不是做了一些重复的操作?能不能有一种方法可以从方法的的参数中直接取到当前user/userid?这样不仅可以直接根据其进行下一步操作,而且代码也减少了冗余。比如原先你可能要这样:
@RequestMapping("test")
@ResponseBody
public String test(HttpServletRequest request, HttpSession session) {
String user_uuid = (String)session.getAttribute(your_id_key); //假设userid是uuid,并且user_uuid在用户登录的时候就保存在session中
userService.doService(user_uuid);
.....
}
现在你只需要这样:
@RequestMapping("test")
@ResponseBody
public String test(HttpServletRequest request, String user_uuid) {
userService.doService(user_uuid);
.....
}
我们更希望像这样直接从参数中拿到当前会话下的user/userid,然后就直接根据其进行其他操作。那么有没有办法实现呢?答案是 有的,请继续看下面的思路
有关request的参数其实都保存在了一个parameterMap中,一般通过request.getParameterMap()得到,只要我们能在controller的方法之前得到user/userid,然后将其添加到request的parameterMap中,那么根据springMvc的参数绑定原则,在上面的test方法中String userid自然可以直接取到对应的值。但是由于tomcat的开发人员不想我们直接修改request的参数值(做了手脚),所以类似request.getParameterMap().put(xxx,xxx)是行不通的,所以需要另辟蹊径。这篇文章讲的挺清楚的https://blog.csdn.net/xieyuooo/article/details/8447301,这里就使用其中的一种方法。
继承HttpServletRequestWrapper包装一个自己的request
大致代码都是差不多的,如下
public class HttpRequest extends HttpServletRequestWrapper implements MyHttpRequest {
private Map<String, String[]> params = new HashMap<String, String[]>();
@SuppressWarnings("unchecked")
public HttpRequest(HttpServletRequest request) {
super(request);
params.putAll(request.getParameterMap());
}
@Override
public String getParameter(String name) {
String[] values = params.get(name);
return values == null || values.length == 0 ? null : values[0];
}
@Override
public String[] getParameterValues(String key) {
return params.get(key);
}
@Override
public void setParameter(String key, String value) {
if (value == null)
return;
params.put(key, new String[] { value });
}
@Override
public void setParameter(String key, String[] value) {
if (value == null)
return;
params.put(key, value);
}
@Override
public void setParameter(String key, Object value) {
if (value == null)
return;
params.put(key, new String[] { String.valueOf(value) });
}
@Override
public Map<String, String[]> getParameterMap() {
return params;
}
@Override
public Enumeration<String> getParameterNames() {
Enumeration<String> enumeration = new MyEnumeration<String>(params.keySet().iterator());
return enumeration;
}
}
对应的MyEnumeration重写下Enumeration的方法(My前缀可能有些人看的不舒服,认为有独占别人东西的感觉,但是并没有这个意思,当初加一个My前缀也没想那么多,仅仅是为了方便区分而已⊙﹏⊙)
public class MyEnumeration<E> implements Enumeration<E> {
private Iterator<E> iterator;
public MyEnumeration(Iterator<E> iterator) {
this.iterator = iterator;
}
@Override
public boolean hasMoreElements() {
return iterator.hasNext();
}
@Override
public E nextElement() {
return iterator.next();
}
}
好了,到这里你就拥有了一个可以自己设置参数的MyRequest对象了,然后思路就很清晰了,因为用户已经登录过,所以其user_uuid保存在session中,只要我们在filter中将user_uuid从session中取出来,并set到MyRequest对参数集合中,然后springMvc在接收你的MyRequest对象时就会调用getParameter方法,自然而然也就将user_uuid的值绑定到了controller方法的参数中。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
//取出登录时的user_uuid(凭证)
String user_uuid = (String)request.getSession().getAttribute(your_id_key);
//接口 自定义Request
MyHttpRequest mRequest = new HttpRequest(request);
//将取出的user_uuid set到自定义请求的参数集合中
mRequest.setParameter(your_id_key, user_uuid);
//注意这里传的是自定义的request
filter.doFilter(mRequest, response);
...
}
登录后再进行测试,直接在浏览器输入路径
后台打印,可以看到,成功地从方法的参数中拿到了session中保存的值
这样,就实现了直接从参数中拿到当前的user/userid,然后就可以直接根据其进行其他操作了,代码也不再那么冗余。有人看到最开始的两个例子可能会疑惑,这对比下来也就简化了一行代码呀,对,没错,但只简化了一行代码是因为这个业务逻辑本身就很简单,如果是一些特殊而复杂的业务逻辑,你可能需要写很多行代码才能得到一个结果,如果能将这个结果能提前在filter中保存到自定义请求的参数集合中,那么后台在编程时只需要在方法中声明一下就能拿到结果,显然要方便很多很多,尤其是对代码简洁强迫症患者来讲 (比如我自己),就是一个福音了。
说一些额外的(上面没问题了下面就可以忽略)
1> 为什么要使用filter,使用拦截器不更方便吗?
其实最开始我也是使用的Interceptor,但是后面发现不行,看了下dispatcher的源码,找到了原因所在
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
......
try {
ModelAndView mv = null;
......
// interceptor前置拦截
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// 真正执行handler的方法,返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
......
}
可以看到,真正传进去的参数都是processedRequest,所以说,即使你在interceptor的前置拦截中将processedRequest的引用指向了自定义的request,在真正处理handler的时候,传进去的仍然是processedRequest,而不是你自己的request。所以只能在调用doDispatch之前将HttpServletRequest的引用指向自定义的request,而filter恰好可以做到
2> 有关filter的配置,当然是使用spring的代理filter:DelegatingFilterProxy,web.xml如下
<filter>
<filter-name>DelegatingFilterProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>filterManager</param-value>
</init-param>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DelegatingFilterProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
然后在spring的配置中声明filter,如下
<bean id="convinientFilter" name="convinientFilter"
class="com.mine.support.web.filter.ConvinientFilter">
<property name="excludeMappingPath" value="/,/*.log,/*log,*.css,*.js,*.png,*.jpg,*.jpeg"></property>
</bean>
<bean id="filterManager" class="com.mine.support.web.filter.FilterManager">
<property name="filters">
<list>
<ref bean="convinientFilter"/>
</list>
</property>
</bean>
---------------------------------------------------------------------------------------------------------------------------------------------------------------
然后说一下自己的一些事,由于十月打了一整月比赛,所以十月没时间写博客,然后我的话基本上有问题我都会回复的,只是有些奇怪我记得11月1号我回复了一个问题了的(确信的眼神),但是今天发现我的回复莫名奇妙不见了,所以也不知道真实情况是什么,不过既然已经过了这么久,相信问题也已经自己解决了吧,就不再重复回复了( ̄▽ ̄)