由一个面试题引发的基础知识的回顾

140 阅读2分钟

最近本人在找工作,作为一个java菜狗,两次面试面试官都问到了我SpringMVC 的执行流程,我三下五除二简单了说了一下。事实上对这个已经很模糊了。 于是本人回来之后打开了IDE看了下org.springframework.web.servlet.DispatcherServlet的源码。

众所周知,客户端发送的Http请求都会走**doService()方法,因此我看了看。主要是调用doDispatch()**方法

        ModelAndView mv = null;
		Exception dispatchException = null;

			try {
			    // 检查请求类型 
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

			    // 寻找是否有请求对应的handler,没有返回404
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 根据handler找到对应的适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

其实以上代码就是整个MVC请求接口最核心的代码。 如果想深入细节建议先了解责任链模式,不然会一头雾水。 其实流程并不复杂,主要是在阅读的过程中引发了自己的一点思考,也算是对基础的回顾吧。

主要是以下两个问题:

1.springMVC的handler/handlerMapping/handlerAdapter是什么时候存在于服务中的?

2.springMVC为什么是线程不安全的?

针对第一个问题,其实是很基础的一个问题,我们在学习servlet的时候知道有三个核心方法:init()/doService()/destroy().

init()方法会在容器启动的时候执行,并且只会执行一次,看DispatcherServlet的源码可知它的结构是这样的:

其中在HttpServletBean这个类中进行了init()方法的实现,然后在DispatcherServletFrameWorkServlet对init()方法进行了增强,主要就是加载spring容器初始化的一些信息,就包含了我所思考的第一个问题的内容,具体代码不一一赘述。

然后第二个问题,其实很简单因为在容器启动的时侯通过扫描@RestController注解将他注入到spring容器中,默认是单例的。只要是单例就会涉及线程安全的问题,假若我们在controller中存在一个静态字段,那么如果使用原子容器或者ThreadLocal那么就会涉及线程 安全问题。

其实这本来都是很简单的问题,但是却引发了我对spring的设计的理解加深。spring大量的单例模式的运用其实通过面向对象的思维来理解是很精妙的,并且在客户端与服务端的交互中性能的确会好很多。其实还引发了一切其他的思考,例如容器上下文ApplicationContext等等,暂时先放放。