springmvc學習筆記

springmvc學習筆記

學習springmvc框架原理

包括哪些組件:

  • 前端控制器(中心)
  • 處理器映射器(三大組件)
  • 處理器適配器(三大組件)
  • 視圖解析器(三大組件)
  1. springmvc框架原理

springmvc框架原理.png

1.用戶發起請求,請求到DispatcherServlet前端控制器DispatcherServlet(中央調度),負責request和response,負責調用處理器映射器查找Handler,負責調用處理器適配器執行Handler,有了前端控制器降低了各各組件之間的耦合性,系統擴展性提高。

2.DispatcherServlet前端控制器請求處理器映射器HandlerMapping查找Handler根據用戶請求的url,根據一定的規則去查找(比如:xml配置,注解)

3.HandlerMapping處理器映射器將Handler返回給前端控制器

4.DispatcherServlet前端控制器調用HandlerAdapter處理器適配器執行Handler程序員編寫的Handler是按照適配器要求的規則去執行Handler

5.HandlerAdapter處理器適配器執行Handler

適配器按照一定規則去執行Handler

6.Handler執行完成,返回ModelAndView

ModelAndView是springmvc的封裝對象,將model和view封裝在一起。

7.HandlerAdapter處理器適配器將ModelAndView返回給前端 控制器

8.前端控制器調用視圖解析器,進行視圖解析,解析完成給前端控制器返回View

View是springmvc的封裝對象,是一個接口,實現類包括jspview,pdfview。。。。

9.前端控制器調用view進行視圖渲染

將模型數據填充到view(將model數據填充到request)響應給用戶

10、前端控制器響應給用戶。

創建一個springmvc

在web.xml中配置servlet。

指定 contextConfigLocation讓前端控制器找到springmvc的全局配置文件。

 <!-- 前端控制器 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- contextConfigLocation指定 springmvc的全局配置文件 如果 contextConfigLocation不指定,默認找配置文件名稱:servlet的name+"-servlet.xml" -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
    </servlet>

 <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>

在springmvc.xml中配置:

處理器映射器:

<!-- 處理器映射器 根據url匹配bean的name 處理器映射器實現了HandlerMapping接口 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />

處理器適配器:

<!-- 處理器適配器 實現了HandlerAdapter接口 action按照適配器要求開發 ,規則是實現Controller接口 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

視圖解析器:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 視圖的前綴 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 視圖的后綴 -->
        <property name="suffix" value=".jsp" />
    </bean>

編寫Handler

由于使用SimpleControllerHandlerAdapter適配器,要求編寫的Handler實現Controller接口。

需求:向頁面顯示一行信息“helloworld”

public class Hello implements Controller{

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView modelAndView = new ModelAndView();

        modelAndView.addObject("message", "HelloWorld");

        modelAndView.setViewName("hello");

        return modelAndView;
    }
}

編寫jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ${message}
</body>
</html>

配置Handler

由于使用處理器映射器BeanNameUrlHandlerMapping,要求在spring的容器配置Handler,Handler的name就是url。

在springmvc.xml中配置:

<!--配置 action-->
    <bean name="/helloworld.action" class="com.luoluo.springmvc.Hello" />

強化三大組件

處理器映射器:

作用:根據url找到Handler.

BeanNameUrlHandlerMapping:根據url匹配bean的name 處理器映射器實現了HandlerMapping接口

SimpleUrlHandlerMapping:將url進行集中配置。

<!-- 簡單url映射 -->
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hello1.action">hello_controller</prop>
                <prop key="/hello2.action">hello_controller</prop>
                <prop key="/hello3.action">hello_controller3</prop>
            </props>
        </property>
    </bean>

hello_controller是action這個bean的id。

處理器適配器:

作用:按照一定規則去執行Handler

SimpleControllerHandlerAdapter:規則是Handler要實現Controller接口

HttpRequestHandlerAdapter:規則是Handler要實現HttpRequestHandler接口。

注意:多個適配器可以共存!

使用注解開發

配置注解處理器映射器和適配器:

 <!-- 組件掃描 只掃描action -->
    <context:component-scan base-package="com.luoluo.springmvc" />

    <!-- 使用<mvc:annotation-driven />替換上邊定義的處理器映射器和適配器 -->
    <mvc:annotation-driven />
    <!--注解映射器 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
    <!--注解適配器 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />

編寫Handler:

package com.luoluo.springmvc;

import com.luoluo.domain.Student;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by luozhiyun on 17/2/11.
 */
@Controller
public class StudentAction  {

    @RequestMapping("/editstudent")
    public String editstudent(Model model) {

        Student student = new Student();
        student.setId("s001");
        student.setName("張三");
        student.setAge(32);
        student.setBirthday(new Date());

        model.addAttribute("student", student);

        return "student/editstudent";
    }

    @RequestMapping("/editstudentsubmit")
    public String editstudentsubmit(Student student) {
        System.out.println(student);

        return "success";
    }

    //這里需要進行日期轉換,一般的情況是放在父類里面,以提高代碼的復用性
    @InitBinder
    public void initBinder(HttpServletRequest request,
                           ServletRequestDataBinder binder) throws Exception {
        //將student類中的birthday屬性進行日期轉換,類型和birthday一致
        binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
    }

}

jsp頁面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>學生信息修改</title>
</head>
<body>

<form action="${pageContext.request.contextPath }/editstudentsubmit.action" method="post">
    <input name="id" value="${student.id}" type="hidden">
    姓名:<input name="name" value="${student.name}"/>
    <br/>
    年齡:<input name="age" value="${student.age}"/>
    <br/>
    出生日期:<input name="birthday" value="<fmt:formatDate value="${student.birthday}" pattern="yyyy-MM-dd"/>"/>

    <br/>
    <input type="submit" value="提交 "/>
</form>
</body>
</html>

與struts2的不同

1.springmvc的入口是servlet,struts是filter

2.springmvc是基于方法開發,struts是基于類開發action類中,一個url對應一個方法,對Handler對象,springmvc框架將一個方法封裝到Handler中(只有一個method)。Struts對action類進行實例化。

Springmvc中的action可以使用單例也可以使用多例,建議使用單例(對相同的方法只new一個)。Struts不能用單例,因為struts傳遞參數是通過成員變量。

Springmvc通過形參接收參數好處:更符合軟件開發的思想,和service接口類似。

3.Struts采用值棧存儲請求和響應的數據,通過OGNL存取數據, springmvc通過參數解析器是將request請求內容解析,并給方法形參賦值,將數據和視圖封裝成ModelAndView對象,最后又將ModelAndView中的模型數據通過reques域傳輸到頁面。Jsp視圖解析器默認使用jstl。

常用注解

@controller

標識該類為控制器類,@controller、@service、@repository分別對應了web應用三層架構的組件即控制器、服務接口、數據訪問接口

@RequestMapping

進行url映射,一個方法對應一個url,定義方法:在action方法上定義requestMapping

例如:定義在類上和方法上所代表的路徑就是根路徑+子路徑:/stu/querystudent.action

@Controller
@RequestMapping("/stu")
public class StudentAction  {
    @RequestMapping("/querystudent")
    public String querystudent(Model model) {
      
        return "student/querystudent";
    }
URI模版模式映射

定義在方法上:用{},然后通過方法進行傳參,

在RequestMapping上設置{},然后在方法上設置注解@PathVariable進行傳參

@RequestMapping(value = "/editstudent/{id}",method = RequestMethod.GET)
    public String editstudent(Model model, @PathVariable String id) {

        System.out.println("id="+id);

        Student student = new Student();
        student.setId("s001");
        student.setName("張三");
        student.setAge(32);
        student.setBirthday(new Date());

        model.addAttribute("student", student);

        return "student/editstudent";
    }
請求方法限定

在RequestMapping設定method參數來限制請求方式

@RequestMapping(value = "/editstudent/{id}",method = RequestMethod.GET)

可以限定為get、post、或兩者都可以

限定GET方法

@RequestMapping(method = RequestMethod.GET)

@RequestMapping(method = RequestMethod.POST)

@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})

@RequestParam綁定單個請求參數

value:參數名字,即入參的請求參數名字,如value=“studentid”表示請求的參數區中的名字為studentid的參數的值將傳入;

required:是否必須,默認是true,表示請求中一定要有相應的參數,否則將報400錯誤碼;

defaultValue:默認值,表示如果請求中沒有同名參數時的默認值

需求 :

學生查詢方法中必須要有一個參數group(學生分組)

@RequestMapping("/querystudent")
    public String querystudent(
            Model model,
            @RequestParam(value = "groupid", required = true, defaultValue = "g001") String group,
            UserVo userVo) throws Exception {
    
        //System.out.println("group="+group);
        
        //將學生信息列表在頁面上展示
        List<Student> list = new ArrayList<Student>();
        Student student = new Student();
        student.setId("s001");
        student.setName("張三");
        student.setAge(32);
        student.setBirthday(new Date());
        
        Student student2 = new Student();
        student2.setId("s002");
        student2.setName("李四");
        student2.setAge(32);
        student2.setBirthday(new Date());
        
        list.add(student);
        list.add(student2);
        
        model.addAttribute("list", list);
        
        return "student/querystudent";
        
    }

表單對象傳值

使用domain屬性名傳遞

jsp上的name屬性定義

<form method="post" action="${pageContext.request.contextPath}/loginsubmit.action">
    用戶名:<input type="text" name="name" /><br/>
    password:<input type="text" name="password" /><br/>
    <input type="submit" >
</form>

Action方法的定義:

@RequestMapping("/loginsubmit")
public String loginsubmit(User user){
  return "";
}

使用domain屬性名傳遞

jsp上的name屬性定義:

<form action="${pageContext.request.contextPath }/stu/editstudentsubmit.action" method="post">
    <input name="id" value="${student.id}" type="hidden">
    姓名:<input name="student.name" value="${student.name}"/>
    <br/>
    年齡:<input name="student.age" value="${student.age}"/>
    <br/>
    出生日期:<input name="student.birthday" value="<fmt:formatDate value="${student.birthday}" pattern="yyyy-MM-dd"/>"/>
    <br/>
    <input type="submit" value="提交 "/>
</form>

Action方法的定義:

在形參不能直接使用domain接受上面的參數,應該使用包裝對象接受上面的參數

public class StudentVo {

    private Student student;

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }
}

Action形參的定義:

@RequestMapping("/editstudentsubmit")
    public String editstudentsubmit(Student student, StudentVo studentVo) {
        System.out.println(studentVo);
        System.out.println(student);

        return "success";
    }

結果轉發

Redirect:請求重定向

? 瀏覽器中地址欄的url通過Redirect變了,重新定義了一個request。

Action方法通過Redirect重定向到另一個方法,方法形參無法帶到另一個方法。

定義方法:

return "redirect:querystudent.action";

Forward:頁面轉發

? 瀏覽器中地址欄的url通過Forward不變,沒有重新定義了一個request。

Action方法通過Forward轉發到另一個方法,方法形參可以帶到另一個方法。

定義方法:

return "forward:querystudent.action";

@RequestBody @ResponseBody實現json數據交互

需求一:請求json響應json

  1. 頁面傳入json格式的數據(json串)

  2. Action方法形參:

    使用java對象接收json串。使用@RequestBody

  3. Action返回一個json串,將java對象轉json

    使用@ResponseBody

@RequestMapping("/j2jAction")
    public @ResponseBody
    Student j2jAction(@RequestBody Student student) {

        System.out.println(student);

        return student;
    }

頁面:

function responseJson() {
           $.ajax({
               method:"POST",
               url:"${pageContext.request.contextPath}/j2jAction.action",
               contentType:"application/json;charset=utf-8",
               data:'{"name":"luoluo","age":13}',
               success:function (data) {
                   alert(data.name);
               }
           });
        }

需求二:請求key/value,響應json

  1. 頁面傳入普通表單數據:key/value
  2. Action方法接收到key/value數據,返回一個json串
@RequestMapping("/v2jAction")
    public
    @ResponseBody
    Student v2jAction(Student student) {

        System.out.println(student);

        return student;
    }

攔截器

配置

針對某一個handlerMapping配置攔截器

<bean
    class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="interceptors">
        <list>
            <ref bean="handlerInterceptor1"/>
            <ref bean="handlerInterceptor2"/>
        </list>
    </property>
</bean>
    <bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
    <bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>

間接配置全局攔截器:讓springmvc框架自動向每個handlerMapping中注冊攔截器

<!--攔截器 -->
<mvc:interceptors>
    <!--多個攔截器,順序執行 -->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>//攔截所有請求
        <bean class="cn.itcast.springmvc.interceptor.HandlerInterceptor1"></bean>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="cn.itcast.springmvc.interceptor.HandlerInterceptor2"></bean>
    </mvc:interceptor>
</mvc:interceptors>

定義攔截器:

package cn.itcast.springmvc.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * 
 * <p>Title: HandlerInterceptor1</p>
 * <p>Description: 攔截器1</p>
 * <p>Company: www.itcast.com</p> 
 * @author  傳智.燕青
 * @date    2014-12-15下午5:57:23
 * @version 1.0
 */
public class HandlerInterceptor1 implements HandlerInterceptor {

    //handler,springmvc根據url找到Handler(只有一個方法)
    //執行時機:進入Handler方法之前執行,如果返回false表示攔截,如果返回true表示放行
    //使用場景:用于用戶身份校驗,用戶權限攔截校驗
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        
        System.out.println("HandlerInterceptor1..preHandle");
        
        return false;
    }
    //執行時機:進入Handler方法之后 ,在返回modelAndView之前 
    //使用場景:使用modelAndView,向頁面傳遞通用數據,使用統一的view
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("HandlerInterceptor1..postHandle");
        
    }
    //執行時機:Handler方法執行完成,(modelAndView已經返回)
    //使用場景:統一異常處理,統一記錄系統日志,用于action方法執行監控(在preHandle記錄一個時間點,在afterCompletion記錄執行結束時間點,將結束時間點減去開始執行時間點,得到執行時長)
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("HandlerInterceptor1..afterCompletion");
        
    }

}

測試

  1. 兩個攔截器都放行

    HandlerInterceptor1..preHandle

    HandlerInterceptor2..preHandle

    HandlerInterceptor2..postHandle

    HandlerInterceptor1..postHandle

    HandlerInterceptor2..afterCompletion

    HandlerInterceptor1..afterCompletion

    結論:

    preHandle是按照攔截器定義順序執行,

    postHandle和afterCompletion是按照攔截器定義逆向執行。

  2. 第一個放行第二個不放

    HandlerInterceptor1..preHandle

    HandlerInterceptor2..preHandle

    HandlerInterceptor1..afterCompletion

    結論:

    只要有一個攔截器不放行,action方法無法完成。

    如果攔截器放行,afterCompletion才會執行。

    只要有一個攔截器不放行,postHandle不執行。

  3. 兩個都不放行

    HandlerInterceptor1..preHandle

    結論:

    只要有一個攔截器不放行,action方法無法完成。

    只要有一個攔截器不放行,postHandle不執行。

<mvc:annotation-driven />

<!-- 使用<mvc:annotation-driven />替換上邊定義的處理器映射器和適配器 -->
    <mvc:annotation-driven />

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容