DispatcherServlet 源码阅读(1)
有时间还是应该多看看源码。
We fear the thing we want most.
以前测试一个Contoller是否实现了相应的功能(包括DAO层是否正确工作),我们的做法是mvn clean package
然后启动tomcat启动应用,然后打开浏览器(或者使用curl)访问一个URL,然后看webapp的日志输出,据此判断功能是否实现了启。动tomcat好累。或者有时候写个Main方法测试有的功能,太low了。
是时候使用Spring test了。
使用mybatis时在Spring的配置文件中给sqlSessionFactory指定好dataSource,typeAliasesPackage,mapperLocations后,接下来写mappers xml及interface文件就好了,在数据层调用的时候很简单的两句:
1 | SpitterMapper spitterMapper = sqlSessionTemplate.getMapper(SpitterMapper.class); |
但是这并不够,虽然出了什么错,会在终端告诉我们(比如说操作的数据库表并不存在,SQL语法有问题等等),但是在实际Web环境中,我们往往需要更多的控制,不论是否有异常都需要自己明确的处理,而不是直接出错,所以我们把异常逐渐递交给上层。
所以我们在这里需要自己加入异常处理,并且在异常发生的时候有对应的动作(打印日志,或者回复客户端对应的消息)。像下面这样,在Service层声明我们自己的异常,主动捕获异常,然后把异常(内部包含底层具体的异常)传递下去,而且在日志输出中可以看到实际发生了什么。下面的System.out.println在实际中使用logger。
1 | @Service(value = "spitterService") |
Spring AOP需要的依赖:
1 | <dependency> |
Spring中定义切面的方法,就是在配置文件中声明pointcut,以及pointcut对应的advice(有before,after等),如下:
1 | <aop:config> |
Pointcut定义的是切点,pointcut表明针对哪些方法需要AOP,然后基于AOP可以定义切点之前,之后,返回值,剖出异常时调用的方法,其实就是代理模式和装饰模式。
AspectJ比Spring AOP更加强大,是运行时织入(weave)。需要的依赖有:
1 | <dependency> |
使用aspectj例子如下:
1 | @Aspect |
可以看到Around annotation注解使用的场景是在 Before annotation执行之前(进入方法之前)执行,然后 ProceedingPonintcut.proceed()执行完之后做一些统计处理,方法返回,然后执行After annotation。
如何对Spring MVC Controller进行AOP呢?见网上说:对于Spring MVC Controller实行AOP不能一般处理,因为Controller中的方法映射处理,其实都交给了AnnotationMethodHandlerAdapter.handle(),所以要针对其定义pointcut。然而并没有用!(亲测, 如下)而且AnnotationMethodHandlerAdapter现在Deprecated,as of Spring 3.2, in favor of RequestMappingHandlerAdapter
。
这样不行:
1 | @Around("execution(* org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(..))") |
所以如果有这样的场景的话,可以使用Spring的HandlerInterceptor,参见。
1 | Caused by: org.xml.sax.SAXParseException; lineNumber: 42; columnNumber: 29; cvc-complex-type.2.4.c: ?????????, ??????? 'aop:aspectj-autoproxy' ???? |
原因在xml配置文件中没有写完整,少了http://www.springframework.org/schema/aop/spring-aop.xsd
。
对于Servlet Filter,官方文档中说的很好, 并且给出了常见的应用场景。
A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both.
Filters perform filtering in the doFilter method. Every Filter has access to a FilterConfig object from which it can obtain its initialization parameters, and a reference to the ServletContext which it can use, for example, to load resources needed for filtering tasks.
Filters are configured in the deployment descriptor of a web application.
Examples that have been identified for this design are:
接下来用简单的例子进行理解。
实现Filter接口。
1 | public class SimpleServletFilter implements Filter{ |
配置部署文件, filter所有的请求(?有问题,后面会说)。
1 | <filter> |
通过运行日志输出,结合前文HandlerInterceptor的对比,可以很清楚的看出filter和interceptor大概的生命周期。filter比interceptor更早出生,更晚死去。
1 | 2016-07-23 14:07:18,581 INFO SimpleServletFilter:16 - SimpleServletFilter init.... |
<mvc:resources mapping="/resources/**" location="/resources/"/>
,那么如果访问的静态资源文件存在的话,filter并不会起作用(web.xml中的filter配置如下),即使把url-pattern指定为<url-pattern>/resources/*</url-pattern>
(其实/就匹配了所有的请求)。*说明了mvc:resources配置的静态文件并不会经过Servlet filter, 虽然说filter很强大。1 | <filter> |
1 | ➜ ~ curl http://localhost:8888/user/service\?id\=11 |
(1)The Essentials of Filters
(2) interface HandlerInterceptor