SpringMVC初探

SpringMVC

SpringMVC故名思議,是Spring提供的MVC框架,那么自然有其他MVC框架,例如Struts等等烫止。目前Struts正在陷入漏洞風(fēng)波,SpringMVC越來越受到大家的歡迎放航。

一個(gè)HTTP請求的旅程

要了解SpringMVC的原理首先我們從最基本的著手烈拒。一個(gè)HTTP請求到底要經(jīng)歷什么才能從請求發(fā)起到返回至瀏覽器中圆裕?一個(gè)請求被HTTP協(xié)議封裝之后广鳍,從OSI應(yīng)用層出發(fā),歷經(jīng)傳輸層吓妆,網(wǎng)絡(luò)層赊时,鏈路層,物理層行拢,直到與服務(wù)端Servlet容器(Tomcat)建立連接祖秒。這時(shí)候Tomcat管理的Servlet為該請求建立一個(gè)新線程或者從線程池中拿出線程做處理。如果請求是首次發(fā)出舟奠,我們需要為該請求創(chuàng)建一個(gè)Session竭缝;如果非首次訪問,且Session沒有過期沼瘫,那么就通過Cookies訪問對(duì)應(yīng)的Session抬纸。請求經(jīng)過一系列的處理,一般包括過濾器耿戚、監(jiān)聽器湿故,結(jié)果通過response返回阿趁。需要注意的是Servlet、過濾器坛猪、監(jiān)聽器脖阵,在一般情況下只有一個(gè)實(shí)例,但是如果你在web.xml中聲明了多個(gè)Servlet墅茉,或者一個(gè)Servlet實(shí)現(xiàn)了SingleThreadMode接口命黔,又或者在分布式的環(huán)境中,那么就存在多個(gè)實(shí)例躁锁。

服務(wù)端程序的進(jìn)化

JSP

起初我們只需要一個(gè)web.xml加上一個(gè)jsp文件纷铣,請求到達(dá)時(shí)直接返回url指定頁面。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
  <head>
    <title>Welcome战转!I'm a baby web</title>
  </head>
  <body>
  Hello, I'm still young.
  </body>
</html>

后來覺得靜態(tài)頁面不夠酷炫編程也不方便搜立,我們要把Java庫應(yīng)用到JSP中。

<%@ page contentType="text/html;charset=UTF-8" %>

<html>
  <head>
    <title>Welcome槐秧!I'm a baby web</title>
  </head>
  <body>
  Hello, I'm still young.<br/>
  現(xiàn)在時(shí)間為:<%= (new java.util.Date()).toString()%>
  <!--JSP中聲明變量-->
  <%!int i = 0;%>
  <!--聲明函數(shù)-->
  <%!public String f(int i) {
    if (i < 3)
     return "i小于3";
    else
    return "i大于等于3";
    }%>
  你的值是:<%= f(1)%><<br/>
  <!--使用Bean-->
  <jsp:useBean id="test" class="com.book.web.SimpleBean"/>
  <jsp:setProperty name="test" property="name" value="Baby"/>
  你叫什么:<jsp:getProperty name="test" property="name"/>
  <!--獲取Session-->
  <%! int number = 0;
    synchronized void countPeople(){
        number++;
    }
    %>
  <%
    if(session.isNew()){
        countPeople();
        String str=String.valueOf(number);
        session.setAttribute("count", str);
        application.setAttribute("count",str);
    }
  %>
  <p>
    你是第<%=(String) application.getAttribute("count")%>個(gè)訪問本網(wǎng)站的人啄踊。
  </p>
  <!--幾個(gè)默認(rèn)對(duì)象 application:應(yīng)用啟動(dòng)的時(shí)候存在,應(yīng)用停掉消失刁标,用戶在哥哥頁面瀏覽都是這一個(gè)對(duì)象颠通。
        pageContext范圍局限在本頁面內(nèi)。
        Request從請求到頁面之后膀懈。
        Session用戶持續(xù)連接服務(wù)器的時(shí)間顿锰,連接斷開則Session無效。
        -->
  </body>
</html>

java代碼嵌入在樣式代碼中启搂,雖然可用硼控,但是凌亂。因此有部分Java程序員提出胳赌,不用把java嵌入到網(wǎng)頁中牢撼,而要把網(wǎng)頁嵌入到j(luò)ava中,這種方案就是servlet疑苫。

Servlet

jsp出現(xiàn)以后出現(xiàn)了有兩種編程方式熏版,一種程序員啥代碼都往JSP里面堆積,為此擴(kuò)展了各種各樣的標(biāo)簽捍掺;一派覺得這種程序非常丑陋撼短,堅(jiān)持用Servlet技術(shù),在純粹的Java類中接收請求挺勿,填充網(wǎng)頁樣式曲横,并返回。

下面是Servlet的一個(gè)例子:

public class MyServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        out.println("<HTML>");
        out.println("<HEAD><TITLE>Servlet實(shí)例</TITLE></HEAD>");
        out.println("<body>");
        out.println("Servlet實(shí)例");
        out.println(this.getClass());
        out.println("</body>");
        out.println("</HTML");
        out.flush();
        out.close();
    }
}

我們將JSP用String的方式傳遞給response满钟,并在前端展現(xiàn)胜榔。這種方式可以在服務(wù)端做純粹的java編程胳喷,雖然有點(diǎn)奇怪,但是這個(gè)程序沒有涉及任何標(biāo)簽(額夭织,除了String里面的)吭露。然而,在構(gòu)建大型工程的時(shí)候尊惰,一方面樣式代碼會(huì)占用大量代碼且不可以重用讲竿,另一方面我們無法對(duì)樣式做擴(kuò)展了。

MVC

當(dāng)當(dāng)當(dāng)當(dāng)...MVC在這JSP方案和servlet方案中取得了平衡弄屡。MVC將網(wǎng)頁樣式和java代碼分離题禀,樣式我們叫“View”,java代碼我們叫“Controller”膀捷,兩者之間交換數(shù)據(jù)我們叫“Model”迈嘹。這樣樣式可以自由擴(kuò)展,用JSP全庸、HTML/CSS秀仲、甚至PHP都沒有問題;Controller只需要專心于業(yè)務(wù)邏輯實(shí)現(xiàn)壶笼;Model既可以用來做持久化也可以用來做接收表單的對(duì)象神僵。

SpringMVC來了

image

上文說,MVC是將Model覆劈、View保礼、Controller三者分離。那么如何做的分離呢责语?可以看一下上面的圖炮障,一個(gè)攜帶URL和表單信息的請求首先會(huì)發(fā)送到DispatcherServlet。DispatcherServlet是前端控制器鹦筹,在應(yīng)用中為一個(gè)單例铝阐。DispatcherServlet通過處理器映射得知請求的下一站是在哪個(gè)控制器址貌☆砉眨控制器接收請求,并將數(shù)據(jù)打包進(jìn)模型中练对,返回一個(gè)視圖名遍蟋,傳遞給DispatcherServlet。DispatcherServlet通過視圖解析器定位到視圖實(shí)現(xiàn)螟凭。請求到達(dá)視圖虚青,視圖用模型數(shù)據(jù)渲染輸出,寫入到response對(duì)象中螺男。

DispatcherServlet的配置

有兩種方法棒厘,第一種是直接按照Servlet的配置方法在web.xml中配置:

    <servlet>
        <servlet-name>servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>servlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

另一種是基于Servlet3.0提供的機(jī)制纵穿,Servlet3.0環(huán)境中容器會(huì)查找javax.servlet.ServletContainerInitialize的實(shí)現(xiàn)類,Spring實(shí)現(xiàn)了這個(gè)接口SpringServletContainerInitializer奢人,這個(gè)類查找WebApplicationInitializer類并將配置任務(wù)交給它谓媒,AbstractAnnotationConfigDispatcherServletInitializer是WebApplicationInitializer的一個(gè)基礎(chǔ)實(shí)現(xiàn),于是我們可以做出如下的實(shí)現(xiàn):

public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
  //用來配置ContextLoaderListener創(chuàng)建的應(yīng)用上下文中的bean
  @Override
  protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] { RootConfig.class };
  }
//定義DispatcherServlet應(yīng)用上下文中的bean
  @Override
  protected Class<?>[] getServletConfigClasses() {
    return new Class<?>[] { WebConfig.class };
  }
//所有請求都會(huì)被攔截
  @Override
  protected String[] getServletMappings() {
    return new String[] { "/" };
  }

}

實(shí)現(xiàn)webconfig類:

@Configuration
@EnableWebMvc
@ComponentScan("spittr.web")
public class WebConfig extends WebMvcConfigurerAdapter {

  @Bean
  public ViewResolver viewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    return resolver;
  }
  
  @Override
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
  }
  
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    // TODO Auto-generated method stub
    super.addResourceHandlers(registry);
  }

}

在Spring MVC中我們其實(shí)是可以創(chuàng)建出多個(gè)DispatcherServlet的(只要?jiǎng)?chuàng)建多個(gè)繼承自AbstractAnnotationConfigDispatcherServletInitializer的類即可)何乎。而每個(gè)DispatcherServlet有自己的應(yīng)用上下文(WebApplicationContext)句惯,這個(gè)應(yīng)用上下文只針對(duì)這個(gè)DispatcherServlet有用。這也就是getServletConfigClasses的作用支救,獲取這個(gè)DispatcherServlet的應(yīng)用上下文的配置類抢野。

而除了每個(gè)DispatcherServlet配置類的應(yīng)用上下文之外,還有一個(gè)根應(yīng)用上下文各墨,這個(gè)應(yīng)用上下文的作用是為了在多個(gè)DispatcherServlet之間共享Bean指孤,比如數(shù)據(jù)源Bean,這就是getRootConfigClasses的作用贬堵,用于返回根應(yīng)用上下文的配置類邓厕。Spring框架的機(jī)制會(huì)保證如果在當(dāng)前DispatcherServlet的應(yīng)用上下文中沒有找到想要的bean時(shí),會(huì)去根應(yīng)用上下文中去找扁瓢。

這里我們只需要一個(gè)DispatcherServlet配置详恼,因此可以不對(duì)RootConfig.class做復(fù)雜配置。

@Configuration
@Import(DataConfig.class)
@ComponentScan(basePackages={"spittr"}, 
    excludeFilters={
        @Filter(type=FilterType.CUSTOM, value=WebPackage.class)
    })
public class RootConfig {
  public static class WebPackage extends RegexPatternTypeFilter {
    public WebPackage() {
      super(Pattern.compile("spittr\\.web"));
    }    
  }
}

編寫控制器:

@Controller
@RequestMapping("/")
public class HomeController {

  @RequestMapping(method = GET)
  public String home(Model model) {
    return "home";
  }

}

home.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
  <head>
    <title>Spitter</title>
    <link rel="stylesheet" 
          type="text/css" 
          href="<c:url value="/resources/style.css" />" >
  </head>
  <body>
    <h1>Welcome to Spitter</h1>

    <a href="<c:url value="/spittles" />">Spittles</a> | 
    <a href="<c:url value="/spitter/register" />">Register</a>
  </body>
</html>

表單處理

Spring提供了數(shù)據(jù)驗(yàn)證功能引几。

public class Spitter {
  private Long id;
  
  @NotNull
  @Size(min=5, max=16)
  private String username;

  @NotNull
  @Size(min=5, max=25)
  private String password;
  
  @NotNull
  @Size(min=2, max=30)
  private String firstName;

  @NotNull
  @Size(min=2, max=30)
  private String lastName;
  
  @NotNull
  @Email
  private String email;
  //get set略
}

我們發(fā)起一個(gè)表單:

<html>
  <head>
    <title>Spitter</title>
    <link rel="stylesheet" type="text/css" 
          href="<c:url value="/resources/style.css" />" >
  </head>
  <body>
    <h1>Register</h1>

    <form method="POST">
      First Name: <input type="text" name="firstName" /><br/>
      Last Name: <input type="text" name="lastName" /><br/>
      Email: <input type="email" name="email" /><br/>
      Username: <input type="text" name="username" /><br/>
      Password: <input type="password" name="password" /><br/>
      <input type="submit" value="Register" />
    </form>
  </body>
</html>

后臺(tái)接收這個(gè)請求的Controller如下:

@Controller
@RequestMapping("/spitter")
public class SpitterController {

  private SpitterRepository spitterRepository;

  @Autowired
  public SpitterController(SpitterRepository spitterRepository) {
    this.spitterRepository = spitterRepository;
  }
  //接收Get請求的函數(shù)映射
  @RequestMapping(value="/register", method=GET)
  public String showRegistrationForm() {
    return "registerForm";
  }
  //接收Post請求的映射
  @RequestMapping(value="/register", method=POST)
  public String processRegistration(
      @Valid Spitter spitter, 
      Errors errors) {
    if (errors.hasErrors()) {
      return "registerForm";
    }
    
    spitterRepository.save(spitter);
    return "redirect:/spitter/" + spitter.getUsername();
  }
  //動(dòng)態(tài)映射地址
  @RequestMapping(value="/{username}", method=GET)
  public String showSpitterProfile(@PathVariable String username, Model model) {
    Spitter spitter = spitterRepository.findByUsername(username);
    model.addAttribute(spitter);
    return "profile";
  }
  
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昧互,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子伟桅,更是在濱河造成了極大的恐慌敞掘,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件楣铁,死亡現(xiàn)場離奇詭異玖雁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)盖腕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門赫冬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人溃列,你說我怎么就攤上這事劲厌。” “怎么了听隐?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵补鼻,是天一觀的道長。 經(jīng)常有香客問我,道長风范,這世上最難降的妖魔是什么咨跌? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮硼婿,結(jié)果婚禮上虑润,老公的妹妹穿的比我還像新娘。我一直安慰自己加酵,他們只是感情好拳喻,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著猪腕,像睡著了一般冗澈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陋葡,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天亚亲,我揣著相機(jī)與錄音,去河邊找鬼腐缤。 笑死捌归,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的岭粤。 我是一名探鬼主播惜索,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼剃浇!你這毒婦竟也來了巾兆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤虎囚,失蹤者是張志新(化名)和其女友劉穎角塑,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體淘讥,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡圃伶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蒲列。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窒朋。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嫉嘀,靈堂內(nèi)的尸體忽然破棺而出炼邀,到底是詐尸還是另有隱情魄揉,我是刑警寧澤剪侮,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響瓣俯,放射性物質(zhì)發(fā)生泄漏杰标。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一彩匕、第九天 我趴在偏房一處隱蔽的房頂上張望腔剂。 院中可真熱鬧,春花似錦驼仪、人聲如沸掸犬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽湾碎。三九已至,卻和暖如春奠货,著一層夾襖步出監(jiān)牢的瞬間介褥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國打工递惋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柔滔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓萍虽,卻偏偏與公主長得像睛廊,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杉编,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,778評(píng)論 6 342
  • 本章內(nèi)容: 映射請求到Spring控制器 透明地綁定表單參數(shù) 校驗(yàn)表單提交 狀態(tài)管理喉前、工作流以及驗(yàn)證都是Web 開...
    謝隨安閱讀 8,587評(píng)論 0 4
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司王财,掛了不少卵迂,但最終還是拿到小米、百度绒净、阿里见咒、京東、新浪挂疆、CVTE改览、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,218評(píng)論 11 349
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)缤言,斷路器宝当,智...
    卡卡羅2017閱讀 134,638評(píng)論 18 139
  • 第四章 水災(zāi) 淮德在刷牙,他的手機(jī)響了胆萧,嘉露伸手橫越大床到他的床頭柜庆揩。拿起來接聽俐东。 “哦,我明白了订晌÷脖瑁”她說,一邊爬...
    發(fā)現(xiàn)好物閱讀 643評(píng)論 0 0