淺析 SpringMVC 原理和配置.

来源:https://www.cnblogs.com/jmcui/archive/2017/12/31/8124204.html
-Advertisement-
Play Games

一、原理 Spring MVC基於模型-視圖-控制器(Model-View-Controller,MVC)模式實現,它能夠幫你構建像Spring框架那樣靈活和松耦合的Web應用程式,將請求處理的邏輯和視圖中的渲染實現解耦。 1、DispatcherServlet是Spring MVC的核心 。Spr ...


一、原理

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

      

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

2、DispatcherServlet需要知道將請求發送給哪個控制器,所以DispatcherServlet會查詢一個或多個處理器映射(handler mapping) 來確定請求的下一站在哪裡。

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

4、控制器在處理完成後,通常會產生一些信息,這些信息稱為模型(model)。但是這個模型到底是渲染哪個頁面的呢?所以控制器還會返回視圖相關的東西。Spring 有個思想就是前後端分離,為了和視圖解耦,所以控制器只返回了視圖名。即,這裡控制器返回了模型和視圖名(modelAndViews)。

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

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

6、視圖解析器會返回一個視圖(view),並將模型數據填充到對應的視圖中。

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

tips實際上,設計良好的控制器本身只處理很少甚至不處理工作,而是將業務邏輯委托給一個或多個服務對象進行處理。

二、使用 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

測試:

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

 添加自定義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的繼承上添加...

三、使用 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 的順序進行調用的。

四、結語 

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

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

    祝大家新年快樂!2018!我來了......


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 單例模式的作用就是在整個應用程式的生命周期中,單例類的實例都只存在一個,同時這個類還必須提供一個訪問該類的全局訪問點。 首先創建一個單例類,可以直接使用這個單例類獲得唯一的實例對象,也可以繼承該類,使用子類實例化對象。 下麵的代碼使用子類進行實例對象創建 Singleton.php文件 Single ...
  • Python 目錄 Mode one :開發基礎 第一章 基礎語法 1.python的起源 1.1.python的應用 1.2.python版本的選擇 1.3.python安裝 1.4.hello world 2.開啟python之路 2.1.變數 2.2數據類型 2.3.while迴圈 2.4.c ...
  • 今天想到了一個問題的演算法,就自己去敲了代碼運行,這個問題是經典的斐波那契數列求值。用了for迴圈、遞歸、尾遞歸運行了出來,突然想到一個問題for迴圈的時間複雜度、遞歸的時間複雜度、尾遞歸的時間複雜度那個更小呢?也就是那個演算法更加節省時間效率更高呢? 於是我就把這三個做了比較,發現for迴圈的效率更高 ...
  • #配置模塊#創建import configparserconfig = configparser.ConfigParser()#添加config["DEFAULT"] = {'ServerAliveInterval':'45', 'Compression':'yes', 'CompressionLe ...
  • 如果一個程式只包含固定數量的且其生命期都是己知的對象. 那麼這是一個非常簡單的程式。 通常,程式總是根據運行時才知道的某些條件去創建新對象。在此之前,不會知道所需對象的數量,甚至不知道確切的類型。為解決這個普遍的編程問題,需要在任意時刻和任意位置創建任意數量的對象。所以,就不能依靠創建命名的引用來持 ...
  • 現在正式開始第一篇博客。 先看一個式子: x+y+z=5 2*x+3*y+z=11 x+4*y+z=11 如果問人怎麼解,人家肯定會告訴你,消元啦~ 實際上消元有兩種:加減消元和帶入消元 在電腦上編程實現的話,加減消元會簡單一些。 這樣就有了我們的高斯消元法。 高斯消元就是有多個加減消元構成的,能夠 ...
  • 1.Java I/O 概述 2.Java I/O File類 3.Java I/O 獲取文件目錄並寫入到文本 4.Java I/O 輸入與輸出 5.Java I/O 複製文本文件 6.Java I/O 添加屬性和有用的介面 7.Java I/O Reader & Writer(字元流) 8.Java ...
  • 位元組流、字元流涉及的類比較多,比較容易混淆。因此,有必要針對何時使用位元組流、何時使用字元流、何時使用Buffer類的流做一個歸納。要歸納它們,無需過多的語言,只需抓住它們的重點和特性即可。 在決定何時使用何種類時,以下幾個問題需要考慮清楚。 數據源:表示輸入,或稱為讀。可提供使用的兩個父類為Inpu ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...