淺析 SpringMVC 原理和配置.

1、原理

      Spring MVC基於模型-視圖-控制器(Model-View-Controller,MVC)模式實現,它可以幫你構建像Spring框架那樣靈活和鬆耦合的Web應用程序,將請求處理的邏輯和視圖中的渲染實現解耦。html

      

一、DispatcherServletSpring MVC的核心 。Spring MVC 中的請求頁面都會委託給DispatcherServlet來執行處理。java

二、DispatcherServlet須要知道將請求發送給哪一個控制器,因此DispatcherServlet會查詢一個或多個處理器映射(handler mapping) 來肯定請求的下一站在哪裏。web

三、到了控制器(controller),請求會卸下其負載(用戶提交的信息)並耐心等待控制器處理這些信息。spring

四、控制器在處理完成後,一般會產生一些信息,這些信息稱爲模型(model)。可是這個模型究竟是渲染哪一個頁面的呢?因此控制器還會返回視圖相關的東西。Spring 有個思想就是先後端分離,爲了和視圖解耦,因此控制器只返回了視圖名。即,這裏控制器返回了模型和視圖名(modelAndViews)。json

tips:Model 實際上就是一個Map(也就是key-value對的集合),它會傳遞給視圖,這樣數據就能渲染到客戶端了,當調用addAttribute()方法而且不指定key的時候,那麼key會根據值的對象類型推斷肯定,好比 List<Spittle>,那麼推斷他的 key 就是 spittleList。若是你但願使用非Spring類型的話,那麼能夠用java.util.Map來代替Model。後端

五、MVC 要怎麼依靠一個視圖名找到對應的視圖呢?答案就是 視圖解析器(view resolver)。瀏覽器

六、視圖解析器(ViewResolver )接口會根據試圖名和Locale對象返回一個View實例。View 接口的任務就是接受Model 以及Servlet的request和response對象,並將輸出結果渲染到response中。安全

七、視圖 (好比 JSP)。最終會被相應的容器(好比Tomcat)解析成 HTML 頁面,並響應用戶的請求。服務器

tips實際上,設計良好的控制器自己只處理不多甚至不處理工做,而是將業務邏輯委託給一個或多個服務對象進行處理。session

2、使用 Java 配置

    按照傳統的方式,像 DispatcherServlet 這樣的Servlet會配置在web.xml文件中 ,可是,藉助於Servlet 3規範和Spring 3.1的功能加強,這種方式已經不是惟一的方案了 。咱們會使用Java將DispatcherServlet配置在Servlet容器中。開始前,咱們先來理解下 DispatcherServlet 和 Servlet 監聽器(也就是ContextLoaderListener) 這兩個應用上下文 。

DispatcherServlet 上下文:當DispatcherServlet啓動的時候,它會建立Spring應用上下文,並加載配置文件或配置類(即帶有@configuration註解的配置類)中所聲明的bean,主要是Web 組件中的 bean, 包括 控制器(controller)、映射器(handler mapping)、視圖解析器(view resolver)等。

ContextLoaderListener 上下文:這個上下文 由 ContextLoaderListener  建立,主要負責加載應用中的其餘 bean 。這些bean一般是驅動應用後端的中間層和數據層組件。

一、實現:
    咱們經過繼承 AbstractAnnotationConfigDispatcherServletInitializer 類來配置SpringMVC,以做爲傳統 XML 配置的替代方案。實際上,AbstractAnnotationConfigDispatcherServletInitializer  會 同時建立 DispatcherServlet 和 ContextLoaderListener 。固然,咱們須要手動配置咱們的映射路徑、視圖解析器 並啓用組件掃描 以及一系列咱們能夠自定義的配置。固然,若是咱們沒有配置視圖解析器,SpringMVC 會啓用默認的視圖解析器(經過查找 ID 與視圖名稱相匹配的Bean,而且這個Bena 要實現View 接口)。若是沒有配置路徑映射,DispatcherServlet會映射爲應用的默認Servlet,因此它會處理全部的請求,包括對靜態資源的請求,如圖片和樣式表等。

public class SplittrWebAppInitailzer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    /*返回會建立ContextLoaderListener 上下文*/
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{RootConfig.class};
    }

    /*返回會建立 DispatcherServlet 上下文*/
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{WebConfig.class};
    }

    /*配置路徑映射*/
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}
最小但可用的SpringMVC配置
@Configuration
@ComponentScan(basePackages = {"com"},
        excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION,value = EnableWebMvc.class)
        })
public class RootConfig {
}
RootConfig.java
@Configuration
@EnableWebMvc //啓用SpringMVC,固然也可使用 <mvc:annotation-driven /> 註解驅動
@ComponentScan(basePackages = "com.controller")
public class WebConfig extends WebMvcConfigurerAdapter {

    /**
     * 在查找的時候,它會在視圖名稱上加一個特定的前綴和後綴
     * (例如,名爲home的視圖將會解析爲/WEB-INF/pages/home.jsp)。
     *
     * @return
     */
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/pages/");
        resolver.setSuffix(".jsp");
        /*設置是否把全部在上下文中定義的bean做爲request屬性可公開訪問。
          這樣在JSP 2.0中可以使用${}來存取,JSTL中使用c:out。
          默認爲false。*/
        resolver.setExposeContextBeansAsAttributes(true);
        resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class); //設置解析JSTL
        return resolver;
    }

    /**
     * 經過調用DefaultServlet-HandlerConfigurer的enable()方法,
     * 咱們要求DispatcherServlet將對靜態資源的請求轉發到Servlet容器
     * 中默認的Servlet上,而不是使用DispatcherServlet自己來處理此類請求
     *
     * @param configurer
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}
WebConfig.java

    InternalResourceViewResolver所採起的方式並不那麼直接。它遵循一種約定,會在視圖名上添加前綴和後綴,進而肯定一個Web應用中視圖資源的物理路徑。當邏輯視圖中包含斜線時,這個斜線也會帶到資源的路徑名中。
    經過  resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class)  配置了視圖解析器的ViewClass後,能夠保證 JSTL的格式化和信息標籤可以得到Locale對象以及Spring中配置的信息資源。

二、測試:

    @RequestMapping(value = {"/","/home"},method = RequestMethod.GET)
    public String getHome(Model model){
        return "home";
    }
controller

       

三、請求參數說明

A、處理requet URL 部分(不含queryString)的註解: @PathVariable;
B、處理request header部分的註解: @RequestHeader, @CookieValue;
C、處理request body部分的註解:@RequestParam, @RequestBody;
D、處理attribute類型是註解: @SessionAttributes, @ModelAttribute;

@RequestParam:能夠處理get方式中的queryString的值,也能夠處理post方式的body data 的值。用來處理Content-Type 爲 application/x-www-form-urlencoded 編碼的內容,提交方式GET、POST。

@RequestBody:

該註解經常使用來處理Content-Type: 不是application/x-www-form-urlencoded編碼的內容,例如application/json, application/xml等;
特殊狀況下,也能夠用來處理 application/x-www-form-urlencoded的內容,處理完的結果放在一個MultiValueMap<String, String>裏。
返回結果不會被解析爲跳轉路徑,而是直接寫入HTTP response body中。好比異步獲取json數據,加上@responsebody後,會直接返回json數據

@ModelAttribute
該註解有兩個用法,一個是用於方法上,一個是用於參數上;
用於方法上時: 一般用來在處理@RequestMapping以前,爲請求綁定須要從後臺查詢的model;
用於參數上時: 用來經過名稱對應,把相應名稱的值綁定到註解的參數bean上;要綁定的值來源於:
    A) @SessionAttributes 啓用的attribute 對象上;
    B) @ModelAttribute 用於方法上時指定的model對象;(會綁定須要的對象,好比model.addAttribute("pet", pet);
    C) 上述兩種狀況都沒有時,new一個須要綁定的bean對象,而後把request中按名稱對應的方式把值綁定到bean 對象的各個屬性中。

(1) SpringMVC 在 處理表單的時候,能夠接受一個POJO對象(不用添加任何註解)做爲參數。對象中的屬性會使用請求中同名的參數進行補充,默認調用@ModelAttribute。
                                                        能夠接受一個基本數據類型(不用添加任何註解)做爲參數。會使用請求中同名的參數進行補充,默認調用@RequestParam。

(2) 當InternalResourceViewResolver看到視圖格式中的「redirect:」前綴時,它就知道要將其解析爲重定向的規則,而不是視圖的名稱。InternalResourceViewResolver還能識別「forward:」前綴。當它發現視圖格式中以「forward:」做爲前綴時,請求將會前往(forward)指定的URL路徑,而再也不是重定向。

分享一篇這方面講得特別好的博客:http://blog.csdn.net/truong/article/details/28097837

四、添加自定義Servlet、Filter、Listener

public class MyServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("這是新建的Servlet");
    }
}
自定義Servlet類
public class MyServletInitializer implements WebApplicationInitializer {
    public void onStartup(ServletContext servletContext) throws ServletException {
        ServletRegistration.Dynamic myServlet = servletContext.addServlet("MyServlet", MyServlet.class);
        myServlet.addMapping("/myServlet");
    }
}
註冊Servlet

    註冊Filter、Listener 也能夠用相似的方式。可是,若是你只是註冊Filter,而且該Filter只會映射到DispatcherServlet上的話,那麼在AbstractAnnotationConfigDispatcherServletInitializer中還有一種快捷方式。 

public class MyFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("過濾器的工做");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

}
自定義Filter類
    protected Filter[] getServletFilters() {
        return new Filter[]{new MyFilter()};
    }
在AbstractAnnotationConfigDispatcherServletInitializer的繼承上添加...

3、使用 XML 配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app
        version="3.0"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <!--Web應用圖標:指出IDE和GUI工具用來表示Web應用的大圖標和小圖標-->
    <icon>
        <small-icon>/images/small.gif</small-icon>
        <large-icon>/images/large.gif</large-icon>
    </icon>
    
<!--定義了WEB應用的名字--> <display-name>mvc</display-name>
<!--聲明WEB應用的描述信息--> <description>mvc test</description>
<!--上下文參數:在servlet裏面能夠經過 getServletContext().getInitParameter("name")獲得--> <!--設置根上下文配置文件位置--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
<!--配置過濾器--> <filter> <filter-name>encoding-filter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding-filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<!--配置監聽器 註冊ContextLoaderListener--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
<!--配置Servlet 註冊DispatcherServlet--> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
<!--會話超時配置(單位爲分鐘)--> <session-config> <session-timeout>120</session-timeout> </session-config>
<!--mime類型配置,用來指定對應的格式的瀏覽器處理方式--> <!--配置靜態頁面的打開編碼--> <mime-mapping> <extension>htm</extension> <mime-type>text/html;charset=gb2312</mime-type> </mime-mapping> <mime-mapping> <extension>html</extension> <mime-type>text/html;charset=gb2312</mime-type> </mime-mapping> <!--歡迎文件頁配置--> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list>
<!--錯誤頁面配置--> <!--配置了當系統發生404錯誤時,跳轉到錯誤處理頁面NotFound.jsp--> <error-page> <error-code>404</error-code> <location>/NotFound.jsp</location> </error-page> <!--配置了當系統發生java.lang.NullException(即空指針異常)時,跳轉到錯誤處理頁面error.jsp--> <error-page> <exception-type>java.lang.NullException</exception-type> <location>/error.jsp</location> </error-page> <!--以上是常見的配置,如下的東西也沒搞懂怎麼用,特別是 security-role 的含義指的是?--> <!--安全限制配置--> <!--與login-config元素聯合使用,指定服務器應該怎樣給試圖訪問受保護頁面的用戶受權--> <security-constraint> <web-resource-collection> <web-resource-name>ProtectedArea</web-resource-name> <url-pattern>/resources/*</url-pattern> <!--若是沒有<http-method>方法,表示禁止全部的HTTP方法訪問對應的資源--> <http-method>GET</http-method> </web-resource-collection> <!--哪些用戶應該具備受保護資源的訪問權 若是沒有 <auth-constraint> ,配置其實是不起做用的。 若是內容爲空,表示全部的身份都被禁止訪問--> <auth-constraint> <role-name>ALL Role</role-name> </auth-constraint> </security-constraint> <!--登陸驗證配置四種認證類型 --> <!-- BASIC:HTTP規範,Base64 這種方式被認爲是最不安全的認證,由於它沒有提供強烈的加密措施 --> <login-config> <auth-method>BASIC</auth-method> </login-config> <!-- DIGEST:HTTP規範,數據完整性強一些,但不是SSL 相比於BASIC認證,它是種比較安全的認證,它在認證時將請求數據 經過MD5的加密方式進行認證 --> <login-config> <auth-method>DIGEST</auth-method> </login-config> <!-- CLIENT-CERT:J2EE規範,數據完整性很強,公共鑰匙(PKC) 這是一種基於客戶端證書的認證方式,比較安全。但缺陷是在沒有安全證書的客戶端沒法使用 --> <login-config> <auth-method>CLIENT-CERT</auth-method> </login-config> <!-- FORM:J2EE規範,數據完整性很是弱,沒有加密,容許有定製的登陸界面 這是種基礎自定義表單的認證,你能夠指定登陸時的驗證表單 --> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login.html</form-login-page> <form-error-page>/error.jsp</form-error-page> </form-login-config> </login-config> <!--安全角色--> <!--這些角色將出如今servlet元素內的security-role-ref元素的role-name子元素中。分別地聲明角色可以使高級IDE處理安全信息更爲容易(沒看懂這句話)--> <security-role> <role-name>ALL Role</role-name> </security-role> </web-app>

tipsweb.xml 的加載順序是:ServletContext -> context-param -> listener -> filter -> servlet ,而同個類型之間的實際程序調用的時候的順序是根據對應的 mapping 的順序進行調用的。

4、結語 

    2017年最後一篇博文了,堅持在2017年的最後一個晚上寫完。畢竟2017的事總很差意思拖一年呀!堅持寫博客真是個好習慣,好記性畢竟不如白紙黑字來的牢靠啊,若是把記性比做網上搜索的話,博客就是本身的一份離線存儲。

    原本想好好回顧下2017,打一大堆滿滿的文字,裝一個文藝的青年。真到落筆的時候,什麼都不想寫。敬往事一杯酒,悠悠歲月不回頭!

    祝你們新年快樂!2018!我來了......

相關文章
相關標籤/搜索