编程之路

We fear the thing we want most.


  • 首页

  • 关于

  • 归档

  • 标签

Spring HandlerInterceptor 学习实践

发表于 2016-07-23   |  

Spring HandlerInterceptor 学习实践

这两天花了很多时间在折腾使用AOP对Spring MVC Controller进行拦截,但是没有效果。然后尝试了下Spring的HandlerInterceptor,使用起来比较简单,思想也容易理解。下面是Spring Doc对HandlerInterceptor接口及相关方法的说明。

HandlerInterceptor 接口

Workflow interface that allows for customized handler execution chains. Applications can register any number of existing or custom interceptors for certain groups of handlers, to add common pre-processing behavior without needing to modify each handler implementation.
A HandlerInterceptor gets called before the appropriate HandlerAdapter triggers the execution of the handler itself. This mechanism can be used for a large field of preprocessing aspects, e.g. for authorization checks, or common handler behavior like locale or theme changes. Its main purpose is to permit the factoring out of otherwise repetitive handler code.

Typically an interceptor chain is defined per HandlerMapping bean, sharing its granularity. To be able to apply a certain interceptor chain to a group of handlers, one needs to map the desired handlers via one HandlerMapping bean. The interceptors themselves are defined as beans in the application context, referenced by the mapping bean definition via its “interceptors” property (in XML: a of elements).

A HandlerInterceptor is basically similar to a Servlet Filter, but in contrast to the latter it allows custom pre-processing with the option to prohibit the execution of the handler itself, and custom post-processing. Filters are more powerful; for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor in the application context.

As a basic guideline, fine-grained handler-related pre-processing tasks are candidates for HandlerInterceptor implementations, especially factored-out common handler code and authorization checks. On the other hand, a Filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.

preHandle 方法

Intercept the execution of a handler. Called after HandlerMapping determined an appropriate handler object, but before HandlerAdapter invokes the handler.
DispatcherServlet processes a handler in an execution chain, consisting of any number of interceptors, with the handler itself at the end. With this method, each interceptor can decide to abort the execution chain, typically sending a HTTP error or writing a custom response.

postHandle 方法

Intercept the execution of a handler. Called after HandlerAdapter actually invoked the handler, but before the DispatcherServlet renders the view. Can expose additional model objects to the view via the given ModelAndView.
DispatcherServlet processes a handler in an execution chain, consisting of any number of interceptors, with the handler itself at the end. With this method, each interceptor can post-process an execution, getting applied in inverse order of the execution chain.

afterCompletion 方法

Callback after completion of request processing, that is, after rendering the view. Will be called on any outcome of handler execution, thus allows for proper resource cleanup.
Note: Will only be called if this interceptor’s preHandle method has successfully completed and returned true!
As with the postHandle method, the method will be invoked on each interceptor in the chain in reverse order, so the first interceptor will be the last to be invoked.

实践

接下来是一个具体实例,interceptor代码是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class LogInterceptor extends HandlerInterceptorAdapter {

private static Logger logger = Logger.getLogger(LogInterceptor.class);

// Intercept the execution of a handler.
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception
{


if(!isValidUser()){
logger.info("Invalid user from " + request.getRemoteAddr());
response.sendRedirect("http://localhost:8888/user/error");
return false;
}

long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
logger.info("Will call " + handler.toString());
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("before return view page, i can get model = " + modelAndView.getModelMap().get("hello"));
}

// Callback after completion of request processing, that is, after rendering the view.
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// Our Log Func
long curTime = System.currentTimeMillis();
long startTime = (Long)request.getAttribute("startTime");
long cost = curTime - startTime;
logger.info("Call cost time " + cost + " mills!");
}

// FAKE
private boolean isValidUser(){
return true;
}
}

application context中对拦截器的配置:

1
2
3
4
5
6
7
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/admin/**"/>
<bean class="com.vonzhou.learning.interceptor.LogInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

所以当我们访问对应的URL时就会触发拦截器的执行,如下:

1
2
3
4
5
6
7
➜  ~ curl http://localhost:8888/user/service



You are our user, Welcome!

➜ ~

server端的日志输出:

1
2
3
2016-07-23 10:25:37,374  INFO LogInterceptor:32 - Will call public java.lang.String com.vonzhou.learning.controller.UserController.serveUser(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,java.util.Locale,org.springframework.ui.ModelMap)
2016-07-23 10:25:38,379 INFO LogInterceptor:38 - before return view page, i can get model = Some Data
2016-07-23 10:25:38,381 INFO LogInterceptor:50 - Call cost time 1007 mills!

完整代码

参考资料

  • HandlerInterceptor

使用commons-fileupload处理上传文件FileItemStream$ItemSkippedException

发表于 2016-07-21   |  

问题:org.apache.commons.fileupload.FileItemStream$ItemSkippedException

因为解析post request的时候把inputstream放在了集合中,打算事后统一处理,如下,这样就出现了这个问题。

1
2
3
4
5
6
7
8
9
10
11
12

while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();

if (item.isFormField()) {
fields.put(name, Streams.asString(stream));
} else {
streamList.add(stream);
}
}

官网给出的解释是:

This exception is thrown, if an attempt is made to read data from the InputStream, which has been returned by FileItemStream.openStream(), after Iterator.hasNext() has been invoked on the iterator, which created the FileItemStream.

最后我的解决方法是:把inputstream先转为bytearray,然后再转化为InputStream,如下。会不会太耗内存?

1
2
byte[] bytes = IOUtils.toByteArray(stream);
streamList.add(new ByteArrayInputStream(bytes));

使用公钥认证登录远程服务器(Mac)

发表于 2016-07-21   |  

在登录远程主机的时候通常是使用SSH,而不是使用密码登录。这种情况下使用的是公钥认证(Public Key Authentication),就是在你的主机上使用ssh-keygen生成密钥对(公钥和私钥),私钥是在主机的主机上,把公钥拷贝到你需要登录的远程服务器上,这样你就可以ssh无密码登录到那个server了。Public Key Authentication的基本原理是当有登录请求达到我们的ssh server(远程主机)时,ssh server会使用公钥对一个消息加密,然后回复,只要拥有私钥的机器才能解密这个消息,进而达到认证的目的。

1
ssh root@serverhost

这两天遇到的情况是,申请虚拟主机的时候,会让我们选择是否使用ssh密钥,此时创建的密钥文件就是我们的私钥,公钥已经被放到了虚拟主机的authorized-keys文件中。这个密钥文件在Windows下面使用xshell远程连接工具的话,选择公钥认证,导入这个文件。在Linux系统下面更简单,只是一直不知道ssh-add这个命令。

1
ssh-add -- adds private key identities to the authentication agent

实例如下:

Spring MVC国际化中文乱码问题

发表于 2016-07-16   |  

Spring MVC国际化中文乱码问题

在国际化Spring MVC应用的时候,是将语言区域(Locale)相关的文本元素单独存在一个独立的属性文件中,然后不同的Locale对应的属性文件就构成了一个ResourceBundle,在Spring MVC显示本地化的消息时就会根据所在的区域显示对应的文本。

今天实际操作后,发现页面显示中文正常,但是通过国际化显示的中文出现乱码,那是因为我们在属性文件XX_zh_CN.properties中写的是汉字,这个属性文件还需要通过jdk自带的native2ascii工具进行转化,这样文件中存的是汉字对应的Unicode,像下面这样。

转换之前

1
2
greeting=你好啊
book.name=追风筝的人

转换之后

1
2
greeting=\u4f60\u597d\u554a
book.name=\u8ffd\u98ce\u7b5d\u7684\u4eba

native2ascii的使用方法是 把一个输入文件转换输出到另一个文件,比如下面我要转换labels_zh_CN.properties这个文件。

1
➜  native2ascii -encoding UTF-8 src/main/webapp/WEB-INF/resource/labels_zh_CN.properties src/main/webapp/WEB-INF/resource/labels_zh_CN.properties

Nginx学习0 - Hello World

发表于 2016-05-18   |  

Nginx学习0 - Hello World

简介

NGINX is a free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server. NGINX is known for its high performance, stability, rich feature set, simple configuration, and low resource consumption.

NGINX is one of a handful of servers written to address the C10K problem. Unlike traditional servers, NGINX doesn’t rely on threads to handle requests. Instead it uses a much more scalable event-driven (asynchronous) architecture. This architecture uses small, but more importantly, predictable amounts of memory under load. Even if you don’t expect to handle thousands of simultaneous requests, you can still benefit from NGINX’s high-performance and small memory footprint. NGINX scales in all directions: from the smallest VPS all the way up to large clusters of servers.

–官网

阅读全文 »
123…5
vonzhou

vonzhou

A Programmer's Notes

21 日志
8 标签
GitHub 知乎
© 2017 vonzhou
由 Hexo 强力驱动
主题 - NexT.Mist