小组的一次技术交流中提到手动实现一个mvc框架练练手,对于这个话题我很是的感兴趣。因此写着玩尝试尝试。web
说到mvc框架不得不得说当下最流行的spirngmvc了。下面就来分析下实现思路spring
首先,web项目确定都是基于servlet,咱们先来看看spirng是怎么作的。mvc
随便打开一个spirng的web项目启动来看看app
这段日志咱们能够看出spring在刚启动时正在初始化spirng的上下文。那么初始化上下文又是在作什么呢。咱们一步一步的来分析框架
首先spirng在ContextLoader中开始进行了初始化,那么为何ContextLoader会在应用启动时初始化呢?this
缘由是在web.xml中有这样一段配置url
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
点开里面的源码咱们能够看到这个监听器实现了ServletContextListener接口,实现了这个接口就能在应用启动时自动执行contextInitialized方法,在这里进行spring上下文的初始化spa
public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } this.contextLoader.initWebApplicationContext(event.getServletContext()); }
能够在代码中看到spring上下文初始化时传入了servletContext。在刚开始学servlet时咱们知道servletContext是servlet的上下文,为何要传入这个下面再说日志
读取application.xml配置文件初始化WebApplicationContextxml
而后接着往下读源码发现WebApplicationContext保存在了servletContext,方便咱们随时获取WebApplicationContext,这就是为何要在上图方法中传入servletContext。
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
初始化完WebApplicationContext接下来就是初始化springDispatcherServlet了。springDispatcherServlet是咱们springmvc的核心控制器,它其实就是个sevlet,配置在咱们的web.xml中
<servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-servlet.xml</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet>
springDispatcherServlet是咱们spirngmvc的整个核心控件。下面是一个请求的整个过程。具体再也不阐述
在接下来的日志中咱们能够看到来自AbstractUrlHandlerMapping的一堆咱们请求映射的打印。打开这个类看看它到底作了什么。在registerHandler这个方法中咱们能够看到
this.handlerMap.put(urlPath, resolvedHandler);
handlerMap是这个类中定义的一个map,而resolvedHandler则是这个请求的处理类对象。看到这里思路就大概清晰了。springDispatcherServlet接到请求后,根据url找到对应的已经实例化好的处理对象,而后再执行相应的方法。
说到这里大概的思路就已经有了。作个总结。
实现思路(只是个大概)
1.应用启动初始化操做
建立一个contextLoader实现ServletContextListener接口
建立applicationContext上下文,存储在servletContext中
读取xml配置文件获取扫描bean的包路径
经过配置路径获取带有注解的类
经过反射将类实例化,以类名首字母小写或注解上的值为key,实例化的对象为value,将bean存储在上下文中
2.初始化dispatchservlet
在web.xml中配置dispatchservlet,拦截全部请求
将全部类中的@requestMapping存储在一个map中,key为url,value为class
将class(controller)反射实例化存储在dispatchservlet的上下文中
从applicationContext获取bean,为filed注入实例
3.解析请求
dispatchservlet收到请求
经过threadLocal设置request和response方便在controller中使用。
根据url匹配到对应的class,根据class在dispatchservlet上下文中获取实例。
根据注解value找到对应的方法执行,返回视图。
本人道行尚浅,文中若有错误,欢迎指正。