【WEB】初探Spring MVC框架


 

Spring MVC框架算是當下比較流行的Java開源框架。但實話實說,做了幾年WEB項目,完全沒有SpringMVC實戰經驗,乃至在某些交流場合下被同行嚴重鄙視“奧特曼”了。“心塞”的同時,只好默默的打開IDE從HelloWorld開始。

 

初步認識

 

宏觀視野決定微觀實現的質量,首先對Spring MVC框架組件及其流程做一個簡單的認識。以下是從互聯網中某Spring MVC教材扣來一張介紹圖(懶得重復造輪子了):

從上圖可以看出,Spring MVC框架的核心組件有DispatcherServlet、HandlerMapping、HandlerAdapter、Handler、ModelAndView、Model、View以及ViewResolver。既然是核心組件,怎么也得結合組件源碼來探索個究竟吧:

 

DispatcherServlet

 

從名字可以看出,這就是一個Servlet實例,既然是Servlet,那當然是Srping MVC框架入口了,也是web.xml的一個Spring MVC配置項:

<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>

其中springmvc為servlet的自定義命名名稱,其中Spring MVC配置文件也是默認名稱為[servletName]-servlet.xml。

 

從DispatcherServlet源碼看到到,DispatcherServlet的基礎結構是:

DispatcherServlet extend FrameworkServlet

FrameworkServlet extend HttpServletBean

HttpServletBean extend HttpServlet

初略的看了一下DispatcherServlet的干系源碼,主要做了兩大部分,其一是初始化WEB容器的上下文信息和一些Spring MVC策略容器(如HandlerMapping、HandlerAdapter等),在啟動WEB容器時可以通過控制台輸出看到Spring MVC的一些初始化操作:

……
信息: Starting Servlet Engine: Apache Tomcat/6.0.13
2016-4-12 10:51:54 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring FrameworkServlet 'springmvc'
2016-4-12 10:51:54 org.springframework.web.servlet.FrameworkServlet initServletBean
信息: FrameworkServlet 'springmvc': initialization started
2016-4-12 10:51:54 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing WebApplicationContext for namespace 'springmvc-servlet': startup date [Tue Apr 12 10:51:54 CST 2016]; root of context hierarchy
2016-4-12 10:51:54 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [springmvc-servlet.xml]
2016-4-12 10:51:55 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@c7f06: defining beans [helloWorldAnnotation,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping#0,org.springframework.format.support.FormattingConversionServiceFactoryBean#0,org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#0,org.springframework.web.servlet.handler.MappedInterceptor#0,org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping#0,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter#0,org.springframework.web.servlet.view.InternalResourceViewResolver#0,/helloWorldController]; root of factory hierarchy
2016-4-12 10:51:55 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/helloWorldAnnotation] onto handler [com.maventest.springmvc.HelloWorldAnnotation@9c393d]
2016-4-12 10:51:55 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/helloWorldAnnotation.*] onto handler [com.maventest.springmvc.HelloWorldAnnotation@9c393d]
2016-4-12 10:51:55 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/helloWorldAnnotation/] onto handler [com.maventest.springmvc.HelloWorldAnnotation@9c393d]
2016-4-12 10:51:55 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping registerHandler
信息: Mapped URL path [/helloWorldController] onto handler [com.maventest.springmvc.HelloWorldController@85ce5a]
2016-4-12 10:51:55 org.springframework.web.servlet.FrameworkServlet initServletBean
信息: FrameworkServlet 'springmvc': initialization completed in 1222 ms
……

其二就是對MVC容器的流程控制,其主要流程控制方法是doDispatch,接下來結合源碼針對此方法的一些重要操作進行分析和學習:

//檢查請求是否是multipart(如文件上傳),如果是則通過MultipartResolver解析
processedRequest = checkMultipart(request); multipartRequestParsed = processedRequest != request; //獲取請求對應的mappedHandler
mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } //獲取請求對應的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //由適配器執行處理器(調用處理器相應功能處理方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //如果HandlerAdapter沒有對應的ModelAndView響應,怎通過上下文獲取默認對應的view,接着
applyDefaultViewName(request, mv); //看applyPostHandle得知,這是定義攔截器的處理方法
mappedHandler.applyPostHandle(processedRequest, response, mv); //解析視圖並進行視圖的渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

從doDispatch方法流程分析可以看出,跟以上Spring MVC框架流程圖的處理流程是一致的,整個DispatcherServlet組件就是Spring MVC的總流程控制器,再形象一點就如下圖所示:

 

HandlerMapping

 

察人先察色,HandlerMapping中文意思就是“處理映射”,作為一個強大的開源框架,命名自然不會亂來,通過名稱就大概知其所以。看看getHandle這個方法:

protected HandlerExecutionChain getHandler(HttpServletRequest request)…

先不看源碼,就大概可以猜個一二,這是通過request參數,獲取一個對應的的處理類,而這個HandlerExecutionChain就是這個返回的處理類。這個HandlerMapping已經在項目啟動的時候跟隨Servlet一同初始化了:

initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);

而getHandler方法可以通過request獲取請求的所有信息,包括請求方法、URL路徑等,就可以通過這個映射容器找出對應的處理類了。下面再看看這個HandlerExecutionChain響應類屬性:

private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList; private int interceptorIndex = -1;

它包括了請求處理的所有攔截實例和核心處理handler實例,這都會在DispatcherServlet往下幾個步驟會使用到的,具體可以往上回看DispatcherServlet的處理流程。

 

HandlerAdapter

 

還是從名稱理解開始,HandlerAdapter中文意思就是處理對象適配器,按意思就是說Spring MVC有很多個Handler處理對象,這個處理器實際就是一個Handler代理。那么如果不自己定義Handler代理的話,那默認有多少個呢,那就可以看看DispatcherServlet.properties這個配置文件了:

Name:org.springframework.web.servlet.HandlerAdapter
Value:org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

哦,原來默認有HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter一共三個默認的Handler代理。那他們分別有什么用呢,看看我自己親自動手做過Spring MVC HelloWorld實例就很明白了,我通過兩種方式實現了兩個HelloWorld Handler,一個在配置文件配置的bean:

<bean name="/helloWorldController" class="com.maventest.springmvc.HelloWorldController"/>
public class HelloWorldController implements Controller{ public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("message", "Hello World!,i am HelloWorldController."); mv.setViewName("hello"); return mv; } }

而另一個是通過注解實現的HelloWorld Handler:

@Controller public class HelloWorldAnnotation{ @RequestMapping(value="/helloWorldAnnotation") public String hello(ModelMap model){ model.addAttribute("message", "Hello, World!I am HelloWorldAnnotation."); return "hello"; } }

這兩種方式就是分別通過SimpleControllerHandlerAdapte和AnnotationMethodHandlerAdapter處理的,那這樣一說就很明白了。另外這三個個Handler代理都實現了HandlerAdapter接口,就是Spring MVC規定了Handler代理的規則,分別有以下定義方法:

public interface HandlerAdapter { //判斷處理適配器是不是支持該Handler
    boolean supports(Object handler); //調用對應的Handler中適配到的方法,並返回一個ModelView
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; //這個暫時還沒看懂具體想干什么(不是重點,暫時放下)
    long getLastModified(HttpServletRequest request, Object handler); }

其中判斷是否找到合適的Handler代理就靠這個supports方法的具體實現,如果適配成功,這個代理會替這個Handler實現業務路基處理。再簡單這三個代理的supports實現:

//HttpRequestHandlerAdapter @Override public boolean supports(Object handler) { return (handler instanceof HttpRequestHandler); } //SimpleControllerHandlerAdapte @Override public boolean supports(Object handler) { return (handler instanceof Controller); } //AnnotationMethodHandlerAdapter @Override public boolean supports(Object handler) { return getMethodResolver(handler).hasHandlerMethods(); }

再來簡單分別說說以上三個代理對handle方法的實現:

HttpRequestHandlerAdapter和SimpleControllerHandlerAdapte都是直接調用handler的handleRequest方法,而AnnotationMethodHandlerAdapter稍微復雜一點,它是通過注釋和反射獲取相關自定義信息,進行匹配和封裝,具體可自行參考其源碼。

 

Handler

 

這就是自己實現的具體業務處理類了,上文提到很多,不用多說了。

 

ModelAndView

 

通過handler代理完成業務流程后返回一個ModelAndView對象,從名稱就大概可以知道這是一個裝載的數據模型(Model)和數據視圖的對象(View)。

 

Model

 

從源碼可以看出,model集成了LinkedHashMap<String,Object>類,這個model對象裝載了所有在Handler響應給頁面的數據。例如在我自己例子中的message數據:

model.addAttribute("message", "Hello, World!I am HelloWorldAnnotation.");

這些數據將會在頁面上通過JSTL獲取。

 

View

View接口表示一個響應給用戶的視圖,例如jsp文件,pdf文件,html文件等,該接口定義如下:

public interface View { //HttpServletRequest中的屬性名,其值為響應狀態碼
    String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus"; //HttpServletRequest中的屬性名,前一篇文章用到了該變量,它的對應值是請求路徑中的變量,及@PathVariable注解的變量
    String PATH_VARIABLES = View.class.getName() + ".pathVariables"; //該視圖的ContentType
    String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType"; //獲取該視圖ContentType
 String getContentType(); //渲染該視圖
    void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception; }

該接口只有兩個方法定義,分別表明該視圖的ContentType和如何被渲染。Spring中提供了豐富的視圖支持,並且可以自定義視圖。

 

ViewResolver

ViewResolver接口定義了如何通過view 名稱來解析對應View實例的行為。例如在我自己的一個注解Handler實現里面,我返回的是“hello”view name字符串,意思就是響應到對應的hello.jsp視圖(在springmvc-servlet.xml配置文件定義了):

//controller
@RequestMapping(value="/helloWorldAnnotation") public String hello(ModelMap model){ … return "hello"; }

springmvc-servlet.xml:

<!-- ViewResolver -->  
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
</bean>

在這里,我選擇了默認Spring MVC JSP的實現類InternalResourceViewResolver。再來看看ViewResolver的接口定義:

public interface ViewResolver { View resolveViewName(String viewName, Locale locale) throws Exception; }

該接口只有一個方法,通過view name 解析出View。還是以我例子為准,通過“hello”view name字符串,通過ViewResolver. resolveViewName方法生成View實例。再通過View實例的render方法渲染該視圖,剩下的具體細節可自行學習。

 

總結

 

兩天學習下來,終於對Spring MVC有個大概的了解。畢竟是一個通用的框架,除了默認的實現,Spring MVC框架還定義了大量的標准可供用戶自定義實現,整體也算是采用了Open-Closed原則,擴展性好,但有不失整體優雅。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM