加载中...

SpringMVC笔记

SpringMVC

一、SpringMVC介绍

1.什么是MVC

是一种软件架构的思想,将软件按照模型、视图、控制器来划分

  • M:Model,模型层,指工程中的JavaBean,作用是处理数据
    • JavaBean
      • 一类称为实体类Bean:专门存储业务数据的,如 Student、User 等
      • 一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。
  • V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
  • C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器

MVC工作流程: 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器

2.什么是SpringMVC

是Spring的一个后续产品,是Spring的一个子项目

SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层开发的首选。

三层架构:表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet

3.SpringMVC的优点

  • 轻量级,基于MVC框架
  • 易上手,易理解,功能强大
  • 具备IOC和AOP
  • 完全基于注解开发

二、工程创建

1.开发环境

idea:2019.3.5

tomcat:9.0.59

Spring:5.3.22

Maven:3.6.1

2.基于注解的SpringMVC框架开发步骤总结

  1. 新建项目(可以直接选择webapp的模板)
  2. 补全目录
  3. 修改pom.xml,添加SpringMVC和Servlet依赖
  4. 添加springmvc.xml配置文件,指定包扫描,添加视图解析器
  5. 删除web.xml文件,新建web.xml(版本过低有些功能不支持)
  6. web.xml中注册springmvc框架
  7. webapp下新建admin目录,在目录下新建main.jsp页面,删除index.jsp再添加index.jsp页面
  8. 开发控制器(Servlet)
  9. 添加tomcat测试功能

实例

img1

DemoAction.java

package com.xust.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class DemoAction {
    @RequestMapping("/demo.action")
    public String demo(){
        System.out.println("服务器被访问到了。。。。。");
        return "main";  //直接跳到/admin/mian.jsp页面
    }
}

springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--添加包扫描-->
    <context:component-scan base-package="com.xust.controller"></context:component-scan>
    <!--添加视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置前缀-->
        <property name="prefix" value="/admin/"></property>
        <!--配置后缀-->
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

web.xml

<?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_4_0.xsd"
         version="4.0">

    <!--注册SpringMVC框架-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <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>
</web-app>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.xust</groupId>
  <artifactId>spring_001_demo</artifactId>
  <version>1.0</version>
  <packaging>war</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--添加SpringMVC的依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.1</version>
    </dependency>
    <!--添加servlet依赖-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <!--资源文件-->
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
          <include>**/*.properties</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.xml</include>
          <include>**/*.properties</include>
        </includes>
      </resource>
    </resources>
  </build>
</project>

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/demo.action">访问服务器</a>
</body>
</html>

三、RequestMapping注解

将请求和处理请求的控制器方法关联起来,建立映射关系。

SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。

  1. 此注解加在方法前,可以为此方法注册一个可以访问的名称(路径)。

  2. 此注解加在类前,相当于虚拟路径,对不同类进行区分

如:我在前面例子中类名前加@RequestMapping("/zar"),那么index.jsp中就要改为

${pageContext.request.contextPath}/zar/demo.action

  1. 此注解可区分Get请求和Post请求

区分请求举例

<form action="${pageContext.request.contextPath}/req.action" method="get">
    <input type="submit" value="提交">
</form>
package com.xust.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class ReqAction {
    @RequestMapping(value = "/req.action",method = RequestMethod.GET)
    public String req(){
        System.out.println("我是处理get请求的。。。。。。。。。。");
        return "main";
    }

    @RequestMapping(value = "/req.action",method = RequestMethod.POST)
    public String req1(){
        System.out.println("我是处理post请求的。。。。。。。。。。");
        return "main";
    }
}

四、五种数据提交方式的优化

1.单个数据提交

自动注入并且类型转换

<form action="${pageContext.request.contextPath}/one.action">
    姓名:<input name="myname"><br>
    年龄:<input name="myage"><br>
    <input type="submit" value="提交">
</form>
package com.xust.controller;

import com.xust.Users;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class DataSubmitAction {
    @RequestMapping("/one.action")
    public String one(String myname,int myage){     //自动注入并且类型转换
        System.out.println("myname="+myname+",myage="+(myage+100));
        return "main";
    }
}

2.对象封装提交数据

保证jsp中的name和类中的成员变量名称相同

在提交请求中,保证请求参数的名称与实体类中成员变量名称一致,则可以自动提交数据,自动类型转换,自动封装数据到对象中。

<h2>2.对象封装数据提交</h2>
<form action="${pageContext.request.contextPath}/two.action">
    姓名:<input name="name"><br>
    年龄:<input name="age"><br>
    <input type="submit" value="提交">
</form>
package com.xust;

public class Users {
    private String name;
    private int age;

    public Users(){}

    public Users(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Users{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.xust.controller;

import com.xust.Users;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class DataSubmitAction {
    @RequestMapping("/two.action")
    public String two(Users u){
        System.out.println(u);
        return "main";
    }
}

3.动态占位符提交

仅限于超链接或地址栏提交数据

一杠一值,一杠一大括号,使用注解来解析

<h2>3.动态占位符提交</h2>
<a href="${pageContext.request.contextPath}/three/张三/22.action">动态提交</a>
package com.xust.controller;

import com.xust.Users;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class DataSubmitAction {
    @RequestMapping("/three/{name}/{age}.action")
    public String three(
            @PathVariable
            String name,
            @PathVariable
            int age){
        System.out.println("name="+name+",age="+(age+100));
        return "main";
    }
}
//如果@RequestMapping中是{uname},下边可以用@PathVariable(“uname)

4.映射名称不一致

提交请求参数与action方法的形参名称不一致,使用注解@RequestParam来解析

<h2>4.参数名称不一致</h2>
<form action="${pageContext.request.contextPath}/four.action">
    姓名:<input name="name"><br>
    年龄:<input name="age"><br>
    <input type="submit" value="提交">
</form>
package com.xust.controller;

import com.xust.Users;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class DataSubmitAction {
    @RequestMapping("/four.action")
    public String four(
            @RequestParam("name")
            String uname,
            @RequestParam("age")
            int uage){
        System.out.println("uname="+uname+",uage="+(uage+100));
        return "main";
    }
}

5.手工提取数据

<h2>5.手工提取数据</h2>
<form action="${pageContext.request.contextPath}/five.action">
    姓名:<input name="name"><br>
    年龄:<input name="age"><br>
    <input type="submit" value="提交">
</form>
package com.xust.controller;

import com.xust.Users;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;

@Controller
public class DataSubmitAction {
    @RequestMapping("/five.action")
    public String five(HttpServletRequest request){
        String name=request.getParameter("name");
        int age=Integer.parseInt(request.getParameter("age"));
        System.out.println("name="+name+",age="+(age+100));
        return "main";
    }
}

五、中文编码过滤器配置

在web.xml最前面加如下配置

<!--中文编码过滤器配置-->
<filter>
    <filter-name>encode</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--
        配置参数
        private String encoding;
        private boolean forceRequestEncoding;
        private boolean forceResponseEncoding;
    -->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encode</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

六、action方法返回值

  • String:客户端资源地址,自动拼接前缀和后缀,还可以屏蔽自动拼接字符串,可以指定返回路径。

  • Object:返回json格式对象,自动将对象或集合转为json,使用jackson工具进行转换,必须添加jackson依赖,一般用于ajax请求。

  • void:无返回值,一般用于ajax请求。

  • 基本数据类型:用于ajax请求。

  • ModelAndView:返回数据和视图对象,现在用的很少。

七、ajax请求返回学生集合

  1. 添加jackson依赖
  2. 在webapp目录下新建js目录,添加jQuery函数库
  3. 在index.jsp导入函数库
  4. 在action上添加注解@ResponseBody,用来处理ajax请求
  5. 在springmvc.xml添加注解驱动< mvc:annotationdriven/ >,它用来解析@ResponseBody注解

八、请求转发和重定向

转发可以携带数据,重定向不行

redirect和forward都可以屏蔽前后缀,但前者在重定向时使用,地址栏发生变化,后者在转发时使用,地址栏不变

1.请求转发页面(默认)

地址栏不变

<a href="${pageContext.request.contextPath}/one.action">1.请求转发页面(默认)</a>
@Controller
public class StudentListAction {
    @RequestMapping("/one.action")
    public String one(){
        return "main";      //默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转
    }
}

2.请求转发action

地址栏不变

<a href="${pageContext.request.contextPath}/two.action">2.请求转发action</a>
@Controller
public class StudentListAction {
    @RequestMapping("/two.action")
    public String two(){
        //forward可以屏蔽前缀和后缀字符串的拼接
        return "forward:/other.action";
    }
}
@Controller
public class OtherAction {
    @RequestMapping("/other.action")
    public String other(){
        return "main";
    }
}

3.重定向页面

地址栏变为.../admin/main.jsp

<a href="${pageContext.request.contextPath}/three.action">3.重定向页面</a>
@Controller
public class StudentListAction {
    @RequestMapping("/three.action")
    public String three(){
        //redirect可以屏蔽前缀和后缀字符串的拼接
        return "redirect:/admin/main.jsp";
    }
}

4.重定向action

地址栏变为.../other.action

<a href="${pageContext.request.contextPath}/four.action">4.重定向action</a>
@Controller
public class StudentListAction {
    @RequestMapping("/four.action")
    public String four(){
        //redirect可以屏蔽前缀和后缀字符串的拼接
        return "redirect:/other.action";
    }
}
@Controller
public class OtherAction {
    @RequestMapping("/other.action")
    public String other(){
        return "main";
    }
}

九、SpringMVC默认参数类型

不需要创建,直接拿来用

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • Model
  • Map
  • ModelMap

Model、Map、ModelMap和Request一样,都使用请求作用域进行数据传递,所以服务器端的跳转必须是请求转发。

默认参数传递实例

index.jsp

<a href="${pageContext.request.contextPath}/data.action?name=zar">访问服务器进行数据携带跳转</a>

main.jsp

<h2>显示数据</h2>
${requestStudent}<br>
${sessionStudent}<br>
${modelStudent}<br>
${mapStudent}<br>
${modelStudent}<br>
上面是从action传来的,从index.jsp传来的数据name为${param.name}

Student.java

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

DataAction.java

@Controller
public class DataAction {
    @RequestMapping("/data.action")
    public String data(HttpServletRequest request,
                       HttpServletResponse response,
                       HttpSession session,
                       Model model,
                       Map map,
                       ModelMap modelMap
                       ){
        //做一个数据传到main.jsp
        Student stu1=new Student("张三",21);
        //传递数据
        request.setAttribute("requestStudent",stu1);
        session.setAttribute("sessionStudent",stu1);
        model.addAttribute("modelStudent",stu1);
        map.put("mapStudent",stu1);
        modelMap.addAttribute("modelStudent",stu1);
        return "main";
    }
}

十、日期处理

1.日期的提交处理

(1)单个日期处理

要使用DataTimeFormat,此注解必须搭配springmvc.xml中的<mvc:annotationdriven >标签。

可以在方法参数上使用,也可以在类中成员变量的setXXX()方法或属性定义上使用,但是这种方法在每个用到日期类型的地方都要添加一次,比较麻烦,在下面只演示第一种情况。

<form action="${pageContext.request.contextPath}/mydate.action">
    日期:<input type="date" name="mydate"><br>
    <input type="submit" value="提交">
</form>
@Controller
public class MyDateAction {
    SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd");
    @RequestMapping("/mydate.action")
    public String mydate(
            @DateTimeFormat(pattern = "yyyy-MM-dd") //将String注入给Date
            Date mydate){
        System.out.println(mydate);
        System.out.println(sf.format(mydate));
        return "show";
    }
}

(2)类中全局日期处理

不使用@DateTimeFormat注解,使用@InitBinder注解,不需要绑定<mvc:annotationdriven >驱动

@Controller
public class MyDateAction {

    SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd");     //固定格式
    //注册一个全局的日期处理的注解
    @InitBinder
    public void intiBinder(WebDataBinder dataBinder){
        dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true));
    }
    @RequestMapping("/mydate.action")
    public String mydate(Date mydate){
        System.out.println(mydate);
        System.out.println(sf.format(mydate));
        return "show";
    }
}

2.日期的显示处理

如果是单个日期对象,直接转为好看的字符串进行显示

如果是List中的实体类对象的成员变量是日期类型,则必须用jstl进行显示

步骤:添加依赖Jstl,页面上导入标签库,使用标签显示数据

(1)在Json文件中日期显示

在类中成员getXXX()方法上使用注解@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")

(2)Jsp页面的日期显示

添加依赖

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

jsp导入标签库

<%--导入jstl核心标签库--%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--导入jstl格式化标签库--%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

实例

<h2>集合</h2>
<table width="800px" border="1">
    <tr>
        <th>姓名</th>
        <th>生日</th>
    </tr>
    <c:forEach items="${list}" var="stu">
        <tr>
            <th>${stu.name}</th>
            <th>${stu.birthday}----- <fmt:formatDate value="${stu.birthday}" pattern="yyyy-MM-dd"></fmt:formatDate></th>
        </tr>
    </c:forEach>
</table>
public class Users {
    private String name;
    private Date birthday;

    public Users() {
    }

    public Users(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Users{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}
@Controller
public class MyDateAction {

    SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd");     //固定格式

    @RequestMapping("/datelist.action")
    public String datelist(HttpServletRequest request) throws ParseException {
        Users u1=new Users("张三",sf.parse("2001-07-06"));
        Users u2=new Users("李四",sf.parse("2001-07-07"));
        Users u3=new Users("王五",sf.parse("2001-07-08"));
        List<Users> list=new ArrayList<>();
        list.add(u1);
        list.add(u2);
        list.add(u3);
        request.setAttribute("list",list);
        return "show";
    }
}

WEB-INF目录

此目录下动态资源,不可直接访问,只能通过请求转发的方式进行访问。

去后缀

在web.xml中将.action改为/,访问时网址栏可省略.action后缀

登录业务实现

<h2>登录</h2>
<form action="${pageContext.request.contextPath}/login">
    姓名:<input name="name"><br>
    密码:<input type="password" name="pwd"><br>
    <input type="submit" value="登录">
</form>
${msg}
@Controller
public class WebinfAction {
    @RequestMapping("/showLogin")
    public String showLogin(){
        System.out.println("访问login.jsp");
        return "login";
    }
    //登录的业务判断
    @RequestMapping("/login")
    public String login(String name, String pwd, HttpServletRequest request){
        if("zar".equalsIgnoreCase(name)&&"123".equalsIgnoreCase(pwd)){
            return "main";
        } else{
            request.setAttribute("msg","用户名或密码错误");
            return "login";
        }
    }
}

拦截器

上面虽然已经实现了登录功能,但是通过修改地址栏可以跳过登录,安全性不够。

拦截器就是针对请求和响应添加额外的处理。在请求和响应过程中添加预处理,后处理和最终处理。

拦截器执行时机

  • preHandle():请求被处理前操作
  • postHandle:在请求被处理后,但结果还没有被渲染之前操作,可以改变响应结果
  • afterCompletion:所有请求响应结束后执行善后工作,清理对象,关闭资源

拦截器实现的两种方式

  1. 继承HandlerInterceptorAdapter的父类

  2. 实现HandlerInterceptor接口,推荐使用实现接口的方式

拦截器实现步骤

  1. 改造登录方法,在session中存储用户信息,用于权限验证
  2. 开发拦截器功能,实现HandlerInterceptor接口,重写preHandle()方法
  3. 在springmvc.xml中注册拦截器

实例

<h2>登录</h2>
<form action="${pageContext.request.contextPath}/login">
    姓名:<input name="name"><br>
    密码:<input type="password" name="pwd"><br>
    <input type="submit" value="登录">
</form>
${msg}
@Controller
    @RequestMapping("/showMain")
    public String showMain(){
        System.out.println("访问main.jsp");
        return "main";
    }
    @RequestMapping("/showLogin")
    public String showLogin(){
        System.out.println("访问login.jsp");
        return "login";
    }
    //登录的业务判断
    @RequestMapping("/login")
    public String login(String name, String pwd, HttpServletRequest request){
        if("zar".equalsIgnoreCase(name)&&"123".equalsIgnoreCase(pwd)){
            //在session中存储用户信息,用于权限验证
            request.getSession().setAttribute("users",name);
            return "main";
        } else{
            request.setAttribute("msg","用户名或密码错误");
            return "login";
        }
    }
}
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //是否登录过的判断
        if (request.getSession().getAttribute("users")==null){
            //没登录
            request.setAttribute("msg","请先登录再访问");
            request.getRequestDispatcher("WEB-INF/jsp/login.jsp").forward(request,response);
            return false;
        }
        return true;//放行请求
    }
}
<!--注册拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--映射要拦截的请求-->
        <mvc:mapping path="/**"/>
        <!--设置要放行的请求-->
        <mvc:exclude-mapping path="/showLogin"/>
        <mvc:exclude-mapping path="/login"/>
        <!--配置具体拦截器功能的类-->
        <bean class="com.xust.interceptor.LoginInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>
posted @ 2022-09-10 15:59  我没有bug  阅读(64)  评论(0编辑  收藏  举报