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阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容