一、經過在web.xml中添加配置,引入spring mvc框架,相似於Struts2引入須要在web.xml中配置過濾器StrutsPrepareAndExecuteFilter同樣,可是Spring MVC是經過servlet實現的,須要配置一個Servlet。前端
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springConfig/springmvx-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
二、在springmvc中主要是配置Controller,靜態文件的映射策略,視圖的配置等(即後面原理要講的幾個組件)。java
<!-- 自動掃描的包名 --> <context:component-scan base-package="com.test"/> <!-- 默認的註解映射的支持,自動註冊RequestMappingHandlerMapping和RequestMappingHandlerAdapter (spring 3.2以上版本已經再也不是DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter) --> <mvc:annotation-driven /> <!-- 視圖解釋類 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> </bean>
三、編寫controllerios
@Controller @RequestMapping("/test") public class IndexController { @RequestMapping("/index") public ModelAndView index() { ModelAndView view = new ModelAndView("index"); view.addObject("welcome", "hello"); return view; } }
四、訪問地址:xxx/contexpath/test/index。web
說明:關於mvc:annotation-driven配置的詳細說明能夠參考:https://my.oschina.net/HeliosFly/blog/205343spring
Spring的MVC框架主要由DispatcherServlet、處理器映射(HandlerMapping)、處理器(Controller)、視圖解析器(ViewResolver)、視圖(View)組成。DispatcherServlet是整個Spring MVC的核心。它負責接收HTTP請求組織協調Spring MVC的各個組成部分。編程
一、客戶端發出一個http請求給web服務器,web服務器對http請求進行解析,若是匹配DispatcherServlet的請求映射路徑(在web.xml中指定),web容器將請求轉交給DispatcherServlet.設計模式
二、DipatcherServlet接收到這個請求以後將根據請求的信息(包括URL、Http方法、請求報文頭和請求參數Cookie等)以及HandlerMapping的配置找處處理請求的處理器(Handler)。tomcat
3-四、DispatcherServlet根據HandlerMapping找到對應的Handler,將處理權交給Handler(Handler將具體的處理進行封裝),再由具體的HandlerAdapter對Handler進行具體的調用。服務器
五、Handler對數據處理完成之後將返回一個ModelAndView()對象給DispatcherServlet。mvc
六、Handler返回的ModelAndView()只是一個邏輯視圖並非一個正式的視圖,DispatcherSevlet經過ViewResolver將邏輯視圖轉化爲真正的視圖View。
七、Dispatcher經過model解析出ModelAndView()中的參數進行解析最終展示出完整的view並返回給客戶端。
Spring提供的前端控制器,全部的請求都有通過它來統一分發。在DispatcherServlet將請求分發給Spring Controller以前,須要藉助於Spring提供的HandlerMapping定位到具體的Controller。
Spring mvc 使用HandlerMapping來找到並保存url請求和處理函數間的mapping關係。
以RequestMappingHandlerMapping爲例來具體看HandlerMapping的做用
DefaultAnnotationHandlerMapping將掃描當前全部已經註冊的spring beans中的@requestmapping標註以找出url 和 handler method處理函數的關係並予以關聯。
Spring MVC經過HandlerAdapter來實際調用處理函數。
以RequestMappingHandlerAdapter爲例
DispatcherServlet中根據handlermapping找到對應的handler method後,首先檢查當前工程中註冊的全部可用的handlerAdapter,根據handlerAdapter中的supports方法找到可使用的handlerAdapter。經過調用handlerAdapter中的handle方法來處理及準備handler method中的參數及annotation(這就是spring mvc如何將reqeust中的參數變成handle method中的輸入參數的地方),最終調用實際的handle method。
Spring提供的視圖解析器(ViewResolver)在Web應用中查找View對象,從而將相應結果渲染給客戶。
DispatcherServlet的繼承體系如:
看到它們繼承自HttpServlet,你就知道初始化過程應該是從init方法開始了,整個初始化的流程爲:
一、服務器啓動的時候,tomcat容器執行init()方法,主要作兩個事情:加載web.xml配置的初始化參數、讓子類回調同時覆寫方法initServletBean()。
/** * Map config parameters onto bean properties of this servlet, and * invoke subclass initialization. * @throws ServletException if bean properties are invalid (or required * properties are missing), or if subclass initialization fails. */ @Override public final void init() throws ServletException { // Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); // Let subclasses do whatever initialization they like. initServletBean(); }
二、對於第一點的initServletBean(),由子類FrameworkServlet繼承,覆寫該方法,所以接下來執行該方法。
/** * Overridden method of {@link HttpServletBean}, invoked after any bean properties * have been set. Creates this servlet's WebApplicationContext. */ @Override protected final void initServletBean() throws ServletException { this.webApplicationContext = initWebApplicationContext(); //此方法本類沒有代碼邏輯,由子類繼承覆寫該方法, //可是DispatcherServlet並無覆寫,一次此方法沒有作任何事。 initFrameworkServlet(); }
第一行代碼的做用是:初始化FrameworkServlet的屬性webApplicationContext,這個屬性表明SpringMVC上下文,它有個父類上下文,既web.xml中配置的ContextLoaderListener監聽器初始化的容器上下文,而後提供onRefresh方法給DispatcherServlet覆寫。
進入initWebApplicationContext()方法,咱們能夠發現代碼分紅兩塊,一塊是獲取WebApplicationContext ,另外一塊是拿到這個類的實例傳遞給onfresh()方法。對於onRefresh方法本類並無任何的邏輯,只是提供了一個模版供子類覆寫。
protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
三、對於第二點提到的onfresh方法,由子類DispatcherServlet繼承覆寫,所以接下來執行DispatcherServlet類中的onRefresh()方法。
/** * This implementation calls {@link #initStrategies}. */ @Override protected void onRefresh(ApplicationContext context) { initStrategies(context); }
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
從代碼能夠清晰地知道,這個方法就是根據拿到的上下文初始化一些列的策略,即初始化什麼樣的handlerMappings、handlerAdapters等等,這裏咱們看initHandlerMappings這個方法,簡化後的代碼以下:
/** * Initialize the HandlerMappings used by this class. * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace, * we default to BeanNameUrlHandlerMapping. */ private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. OrderComparator.sort(this.handlerMappings); } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); } }
先根據前面獲取好的ApplicationContext獲取到全部的handlerMappings,而後排序,這裏調試後的結果是這裏獲取到了兩個handlerMapping:RequestMappingHandlerMapping、=和BeanNameUrlHandlerMapping,若是獲取到的 handlerMappings 集合爲空,則按照默認去加載,這個默認的規則,這個默認的規則是配置在DispatcherServlet.properties中,看下圖代碼結構便可知道。
回顧整個SpringMVC的初始化流程,咱們看到,經過HttpServletBean、FrameworkServlet、DispatcherServlet三個不一樣的類層次,SpringMVC的設計者將三種不一樣的職責分別抽象,運用模版方法設計模式分別固定在三個類層次中。其中HttpServletBean完成的是<init-param>配置元素的依賴注入,FrameworkServlet完成的是容器上下文的創建,DispatcherServlet完成的是SpringMVC具體編程元素的初始化策略。