SpringMVC一遍过

什么是SpringMVC?

SpringMVC是基于MVC开发模式的框架,用来优化控制器,属于Spring,在Spring3.0后发布的,也具备IoC跟AOP

SpringMVC负责优化控制器Controller

MyBatis负责优化数据访问层DAO

那Spring负责干什么呢?

  • Spring负责整合框架SpringMVC和MyBatis,何以见得?
    • 我们不用手动创建动态代理
    • Mybatis中的mybatis-config.xml都被整没了

SpringMVC有什么优点?

  1. 轻量级,基于MVC的框架
  2. 易于上手,容易理解,功能强大
  3. 具备AOP跟IoC
  4. 完全基于注解开发

SpringMVC的优化方向

在这里插入图片描述

SpringMVC执行流程

在这里插入图片描述

之前学的请求过程是:

  • index.jsp<------>servlet

  • 通过index.jsp发送请求到servlet

但是SpringMVC的处理器是普通类,并不是servlet,怎么办?

  • index.jsp<--------->DispatcherServlet<------------->SpringMVC
  • 通过一个servlet(核心处理器)转发请求,还是web请求到servlet的模式,只是这个servlet不负责具体的业务,只是它将请求转发出去了

具体实现过程

假设现在有个请求http://localhost:8080/login

  1. 进入DispatcherServlet进行请求转发

  2. 转发到HandleMapping映射器,判断请求路径

  3. 路径正确,进入HandleAdapter适配器,把请求/login,分发到具体的/login的方法上,比如:

    • @RequestMapping("/login")
      public String test(){xxxxxx; return "login";}
      
    • 返回视图

  4. 返回 "login"字符串,让InternalResourceViewResolver视图解析器解析(拼串):

    •         <property name="prefix" value="/admin/"/>
              <property name="suffix" value=".jsp"/>
      
  5. 将/admin/login.jsp的视图页面返回给用户


注意!

DispatcherServlet必须要在web.xml中注册才能用

DispatcherServlet不能使用注解*式配置,*因为这是spring的源码,我们无法修改


Controller方法的返回值

  1. String:客户端资源的地址,自动拼接前缀和后缀,还可以屏蔽自动拼接字符串,可以指定返回的路径
  2. Object:返回json格式的对象,自动将对象或集合转为json,使用的jackson工具进行转换,必须要添加jackson依赖,一般用于ajax请求
  3. void:无返回值,一般用于ajax请求
  4. 基本数据类型:用于ajax请求
  5. ModelAndView:返回数据和视图对象,用的很少

注解开发

@RequestMapping

@RequestMapping可以用在类和方法上,映射服务器访问的路径

这个注解可以区分get跟post请求

  • 用在类上,表示创建一个虚拟目录,用在什么情况下?
    • 一样的servlet名字,但是路径不同,用于区分servlet
  • 用在方法上,指定servlet的名字
//    action方法的规范
//    1.访问权限是public
//    2.方法的返回值任意
//    3.方法名称任意
//    4.方法可以没有参数,如果有可以是任意类型
//    5.要使用@RequestMappering注解来声明一个访问的路径(名称)

@RequestMapping下有几个子注解可以直接使用

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

可以代替@RequestMapping(name=“xxx” ,method=xxx)

案例

在web.xml中:

  • 注册springmvc框架
  • 注册的时候同时将springmvc.xml加入

在springmvc.xml中:

  • 包扫描
  • 添加视图解析器InternalResourceViewResolver

d路径下的demo Servlet方法

@Controller
@RequestMapping("d")
public class DemoAction {
//    action方法的规范
//    1.访问权限是public
//    2.方法的返回值任意
//    3.方法名称任意
//    4.方法可以没有参数,如果有可以是任意类型
//    5.要使用@RequestMappering注解来声明一个访问的路径(名称)
    @RequestMapping("demo")
    public String demo(){
        System.out.println("d服务器被访问到了");
        return "main";//可以跳到/admin/main.sp页面上
    }
}

d1路径下的demo Servlet方法

@Controller
@RequestMapping("d1")
public class DemoAction1 {
     @RequestMapping("/demo")
    public String demo(){
        return "main";
    }
    @RequestMapping(value = "/demo",method = RequestMethod.POST)
    public String demo2(){
        return "main";
    }
}

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.wzw.controller"/>
<!--    添加视图解析器-->
<!--    这个InternalResourceViewResolver视图解析器有啥用?
            这个视图解析器的作用就是保证我传值的时候只写main,即可到达这个admin/main.jsp
-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--        配置前缀-->
        <property name="prefix" value="/admin/"/>
<!--        配置后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
    

    <!--    让springmvc不处理静态资源:.js .css .html .mp3 .mp4-->
    <!--    在springMVC-servlet.xml中配置<mvc:default-servlet-handler />后,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。
    
    一般Web应用服务器默认的Servlet名称是"default",因此DefaultServletHttpRequestHandler可以找到它。如果你所有的Web应用服务器的默认Servlet名称不是"default",则需要通过default-servlet-name属性显示指定:
    
    <mvc:default-servlet-handler default-servlet-name="所使用的Web服务器默认使用的Servlet名称" />-->
        <mvc:default-servlet-handler/>


</beans>

html

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <a href="${pageContext.request.contextPath}/d/demo.action">访问服务器</a>
    <a href="${pageContext.request.contextPath}/d1/demo.action">访问服务器</a>
<form method="post" action="${pageContext.request.contextPath}/demo.action">
    <input type="submit" value="post提交">
</form>
</body>
</html>

web.xml,注册SpringMVC框架,并且设置拦截器路径

<?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>
<!--        注册的同时需要告诉DispatcherServletspringmvc做了什么-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
<!--        指定拦截什么样的请求-->
<!--        不拦截:http://localhost:8080/one.jsp-->
<!--        拦截:http://localhost:8080/one.action-->
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>
</web-app>

测试:

d服务器被访问到了
d1服务器被访问到了

注意

在springMVC的依赖中:如果版本大于5.2.5,使用@RequestMapping的时候需要写全名,小于等于5.2.5,可以只写名称

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.2.5.RELEASE</version>
</dependency>
<a href="${pageContext.request.contextPath}/demo.action">访问服务器</a>
@RequestMapping("demo")
public String demo(){
    System.out.println("服务器被访问到了");
    return "main";//可以跳到/admin/main.sp页面上
}

数据提交的方式

散提交

散提交的意思就是单个数据注入

不用再getParameter获取参数了,只要参数名称一致,就可以直接读取到

@RequestMapping("/submit")
public String submit(String myname,int age){
    System.out.println("访问get请求的submit");
    System.out.println(myname);
    System.out.println(age);
    return "main";
}
<form action="${pageContext.request.contextPath}/submit.action">
    <input type="text" name="myname"><br>
    <input type="text" name="age"><br>
    <input type="submit" value="提交">
</form>

对象注入

通过封装对象注入

不用再getParameter获取参数了,只要表单与bean名称一致,就可以直接读取到

@RequestMapping("/fzdx")
public String fzdx(User user){
    System.out.println(user);
    return "main";
}
//User{name='张三', age=12}
<h1>封装对象提交</h1>
<form method="get" action="${pageContext.request.contextPath}/fzdx.action">
    <input type="text" name="name"><br>
    <input type="text" name="age"><br>
    <input type="submit" value="提交">
</form>

动态占位符提交

RestFul风格

适用于超链接提交数据,请求路径后面加上变量,且在controller方法里接收,动态接收,而且也可以自动转化类型,比如提交的是一个int类型的数据,接收的时候可以直接接收int类型的。

@PathVariable 表示在这个动态路径里面,将哪个值赋值给参数,下面的这个例子就是表达:将dynamic后的第一个值传给name1

@RequestMapping("dynamic/{value1}/{value2}")
public String dynamic(@PathVariable("value1") String name1, @PathVariable("value2") String name2){
    System.out.println(name1);
    System.out.println(name2);
    return "main";
}
<h1>动态占位符提交</h1>
<a href="${pageContext.request.contextPath}/dynamic/张三/李四.action">动态占位符提交</a>

可以使用同一个URL,但是请求方式不同,进入不同的处理:

http://localhost:8080/add/1/2 get

http://localhost:8080/add/1/2 post

表面上是同一个url,但是根据请求方式的不同,可以请求到不同的方法

@GETMapping("/add")
public String test1(@PathVariable("a") int a,@PathVariable("b") int b){
    return "login"
}
@POSTMapping("/add")
public String test1(@PathVariable("a") int a,@PathVariable("b") int b){
    return "login"
}

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

@RequestMapping("param")
public String param(@RequestParam("name") String name1,@RequestParam("age") int age1){
    System.out.println(name1);
    System.out.println(age1);
    return "main";
}
<h1>解决请求参数跟接收参数不一样</h1>
<form method="get" action="${pageContext.request.contextPath}/param.action">
    <input type="text" name="name"><br>
    <input type="text" name="age"><br>
    <input type="submit" value="提交">
</form>

中文乱码问题

以上两种数据提交的方式,post请求都会有中文乱码的问题,那么为什么get请求没有?谁处理的?

tomcat8以后的版本tomcat自己解决了get请求中文乱码的问题,tomcat10已经解决了post请求中文乱码

解决办法

<!--    中文编码过滤器,拦截全部请求,强制转码-->
    <filter>
        <filter-name>encode</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>
        <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>

/ /* /**

<url-pattern>/</url-pattern><url-pattern>/*</url-pattern> 的区别

<servlet-mapping>
  <servlet-name>dispatcher</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>
要去掉*
spring用到forward("/WEB-INF/jsp/*.jsp")
而forward当然是又要经过web.xml的映射的,
然后,在URL匹配时,
 <url-pattern>/</url-pattern>  不会匹配到*.jsp,不会进入spring的DispatcherServlet类
 <url-pattern>/*</url-pattern> 会匹配*.jsp,导致进入spring的DispatcherServlet类,然后去寻找controller,接着找不到对应的controller所以报错。
2.访问的路径若是有错误,也会提示这个.
3.dispatcher-servlet.xml配置错误

/不会拦截页面,只会拦截路径

  • 当请求为/user/login的时候可以被拦截,当为/page/login.html页面的时候不拦截

/* 和 /*

  • /* 是拦截所有的文件夹,不包含子文件夹

  • /** 是拦截所有的文件夹及里面的子文件夹

    • 例如请求/usr/add/a,/*不会进行拦截,/*会拦截
    • 而请求/usr/add,/*会进行拦截,/**也会拦截
  • 相当于/*只有后面一级

  • /** 可以包含多级

/**可以拦截,其他的拦截不了

<a href="${pageContext.request.contextPath}/jsp2/jsp3">demo</a>
@RequestMapping("/jsp2/jsp3")
public String gojsp(){
    return "/jsp2/jsp3/a";
}

/**跟/*可以拦截,其他的拦截不了

<a href="${pageContext.request.contextPath}/jsp2">demo</a>
@RequestMapping("/jsp2")
public String gojsp(){
    return "/jsp2/jsp3/a";
}

v


json

服务器返回json格式的数据

@RequestMapping("/demo2")
    @ResponseBody
    public String demo2() throws ParseException, JsonProcessingException {
        String s = "zs"
        ObjectMapper objectMapper = new ObjectMapper();
        String str = objectMapper.writeValueAsString(s);
        return str;
    }

如果含有日期:

@RequestMapping("/demo2")
    @ResponseBody
    public String demo2() throws ParseException, JsonProcessingException {
        String date = "2000-10-2";
		Date parse = new SimpleDateFormat("yyyy-MM-dd").parse(date);
        ObjectMapper objectMapper = new ObjectMapper();
        String str = objectMapper.writeValueAsString(parse);
        return str;
    }

使用ajax发送请求

index.jsp使用ajax发送请求,需要jquery库

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="js/jquery-3.6.0.min.js"></script>
</head>
<body>
<a href="javascript:showStu()">访问服务器返回学生集合</a>
<div id="mydiv">
    等待服务器返回数据
</div>

<script>
    function showStu(){
        $.ajax({
            url:"${pageContext.request.contextPath}/list.action",
            type:"get",
            dataType:"json",
            success:function (stuList){
                var s = "";
                $.each(stuList,function (i,stu){
                    s+=stu.name + "----" + stu.age +"<br>";
                });
                $("#mydiv").html(s);
            }
        });
    }
</script>
</body>
</html>

配置web.xml:中午过滤器Filter,请求转发DispatcherServlet,导入springmvc.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">
    <filter>
        <filter-name>encode</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>
        <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>

    <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>

配置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"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">


    <context:component-scan base-package="com.wzw.controller"/>
<!--    添加注解驱动-->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>

bean对象

public class Student {
    private String name;
    private int age;
//此处省略set,get,tostring,构造器方法不写
}

action方法

@ResponseBody 返回字符串,不走视图解析器

@Controller
public class StudentListAction {
    @RequestMapping("/list")
    @ResponseBody//必须要在springmvc.xml钟添加注解驱动
    public List<Student> list(){
        List<Student> list = new ArrayList<>();
        Student student = new Student("张三", 12);
        Student student1 = new Student("张三1", 12);
        Student student2 = new Student("张三2", 12);
        list.add(student);
        list.add(student1);
        list.add(student2);
        return list;//springmvc框架负责讲集合转为json数组
    }
}

配置pom.xml文件

引入jackson依赖

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.8</version>
</dependency>


请求转发和重定向

<a href="${pageContext.request.contextPath}/one.action">请求转发页面(默认)</a><br><br>
<a href="${pageContext.request.contextPath}/two.action">请求转发action</a><br><br>
<a href="${pageContext.request.contextPath}/three.action">重定向页面</a><br><br>
<a href="${pageContext.request.contextPath}/four.action">重定向action</a><br><br>
<a href="${pageContext.request.contextPath}/five.action">重定向去login</a><br><br>
<a href="${pageContext.request.contextPath}/six.action">请求转发去login</a><br><br>
@Controller
public class JumpAction {
    @RequestMapping("/one")
    public String one(){
        System.out.println("one.action");
        return "main";//默认使用视图解析器拼接前缀后缀进行页面跳转
    }
    @RequestMapping("/two")
    public String two(){
        System.out.println("two.action");
        //forward:  这组字符串可以屏蔽前缀跟后缀的拼接
        return "forward:/other.action";//默认使用视图解析器拼接前缀后缀进行页面跳转
    }
    @RequestMapping("/other")
    public String other(){
        System.out.println("other.action");
        return "main";//默认使用视图解析器拼接前缀后缀进行页面跳转
    }
    @RequestMapping("three")
    public String three(){
        System.out.println("three.action");
        //redirect: 这组字符串可以屏蔽前缀跟后缀的拼接,实现重定向跳转
        return "redirect:/admin/main.jsp";
    }
    @RequestMapping("four")
    public String four(){
        System.out.println("three.action");
        //redirect: 这组字符串可以屏蔽前缀跟后缀的拼接,实现重定向跳转
        return "redirect:/other.action";
    }
    @RequestMapping("/five")
    public String five(){
        return "redirect:/fore/login.jsp";
    }
    @RequestMapping("/six")
    public String six(){
        return "forward:/fore/login.jsp";
    }
}
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/admin/"/>
    <property name="suffix" value=".jsp"/>
</bean>

视图解析器:

  • 使用视图解析器进行拼串,如果返回的是String,例如:

    • @RequestMapping("/one")
          public String one(){
              System.out.println("one.action");
              return "main";//默认使用视图解析器拼接前缀后缀进行页面跳转
          }
      
    • 没有使用forwardRedirect ,就自动拼串:/admin/main.jsp

    • 适用于很多访问同一个页面的时候,可以这么做

  • 返回结果带有forwardRedirect

    • 就不会自动拼串,就根据提供的字符串判断是转发还是重定向

    •  @RequestMapping("three")
          public String three(){
              System.out.println("three.action");
              //redirect: 这组字符串可以屏蔽前缀跟后缀的拼接,实现重定向跳转
              return "redirect:/admin/main.jsp";
          }
          @RequestMapping("four")
          public String four(){
              System.out.println("three.action");
              //redirect: 这组字符串可以屏蔽前缀跟后缀的拼接,实现重定向跳转
              return "redirect:/other.action";
          }	
       @RequestMapping("/five")
          public String five(){
              return "redirect:/fore/login.jsp";
          }
          @RequestMapping("/six")
          public String six(){
              return "forward:/fore/login.jsp";
          }
      

为什么带有forward跟redirect就不会自动拼串?

InternalResourceViewResolver 继承了 UrlBasedViewResolver

public class UrlBasedViewResolver extends AbstractCachingViewResolver implements Ordered {
    public static final String REDIRECT_URL_PREFIX = "redirect:";
    public static final String FORWARD_URL_PREFIX = "forward:";
    
public class InternalResourceViewResolver extends UrlBasedViewResolver {

数据传输

SpringMVC默认支持的内置对象:

  1. HttpServletRequest 对象

  2. HttpServletResponse 对象

  3. HttpSession 对象

  4. Model/ModelMap 对象

  5. Map<String,Object> 对象

注意

Model,Map,ModelMap都使用的是request请求作用域,意味着只能是请求转发后,页面才可以得到值。

<a href="${pageContext.request.contextPath}/data.action?name=李四">携带数据请求</a>
<a href="${pageContext.request.contextPath}/data2.action?name=李四">携带数据请求,重定向</a>
@RequestMapping("/data")
public String data(HttpServletRequest request,
                   HttpServletResponse response,
                   HttpSession session,
                   Map map,
                   Model model,
                   ModelMap modelMap){
    String name = "张三";
    request.setAttribute("requestName",name);
    session.setAttribute("sessionName",name);
    map.put("mapName",name);
    model.addAttribute("modelName",name);
    modelMap.addAttribute("modelMapName",name);
    return "forward:/fore/login.jsp";
}
@RequestMapping("/data2")
public String data2(HttpServletRequest request,
                   HttpServletResponse response,
                   HttpSession session,
                   Map map,
                   Model model,
                   ModelMap modelMap){
    String name = "张三";
    request.setAttribute("requestName",name);
    session.setAttribute("sessionName",name);
    map.put("mapName",name);
    model.addAttribute("modelName",name);
    modelMap.addAttribute("modelMapName",name);
    return "redirect:/fore/login.jsp";
}
<h1>requestName:${requestName}</h1>
<h1>sessionName:${sessionName}</h1>
<h1>mapName:${mapName}</h1>
<h1>modelName:${modelName}</h1>
<h1>modelMapName:${modelMapName}</h1>
<h1>携带数据:${param.name}</h1>

在这里插入图片描述

在这里插入图片描述


日期处理

日期类型不能自动注入到方法的参数中。需要单独做转换处理。使用@DateTimeFormat注解,需要在springmvc.xml文件中添加<mvc:annotation-driven/>标签。

因为form表单提交过来的日期是String类型的,需要将String转化成Date

单个类中处理

<form method="get" action="${pageContext.request.contextPath}/mydate.action">
    日期:<input type="date" name="mydate"><br><br>
    <input value="提交" type="submit">
</form>
@RequestMapping("/mydate")
public String mydate(@DateTimeFormat(pattern = "yyyy-MM-dd")@RequestParam("mydate") Date date, HttpServletRequest request){
    request.setAttribute("date",date);
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    String s = simpleDateFormat.format(date);
    request.setAttribute("date2",s);
    System.out.println(s);
    System.out.println(date);
    return "forward:/admin/show.jsp";
}
//2022-11-10
//Thu Nov 10 00:00:00 CST 2022

在这个例子中,参数的选择有两个

  • 因为form表单中的name是mydate
    • 所以可以采用@RequestParam("mydate")来接收数据,且定义自己的参数名
    • 也可以采用(@DateTimeFormat(pattern = "yyyy-MM-dd") Date mydate) 自动接收

@DateTimeFormat(pattern = "yyyy-MM-dd") 表示:

  • String类型的date数据提交过来转化成"yyyy-MM-dd"类型的date,但是好像没啥用,最后还是要通过SimpleDateFormat来进行转换

  • @DateTimeFormat(pattern = "yyyy-MM-dd") 单单用来接收date类型的数据,页面提交的是date数据,比如:

    • <input type="date">

类中全局处理

<form method="get" action="${pageContext.request.contextPath}/mydate.action">
    日期:<input type="date" name="mydate"><br><br>
    <input value="提交" type="submit">
</form>
  //注册一个全局的日期处理注解,
//    registerCustomEditor 注册自定义编辑器
//    CustomDateEditor 自定义日期编辑器
//    将Date类转化成SimpleDateFormat("yyyy-MM-dd")类型,可以为空,等于说代替了@DateTimeFormat(pattern = "yyyy-MM-dd")这个注解
    @InitBinder
    public void initBinder(WebDataBinder dateBinder){
        dateBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true));
    }

    @RequestMapping("/mydate")
    public String mydate(@RequestParam("mydate") Date date, HttpServletRequest request){
        request.setAttribute("date",date);
        String s = sf.format(date);
        request.setAttribute("date2",s);
        System.out.println(s);
        System.out.println(date);
        return "forward:/admin/show.jsp";
        //http://localhost:8080/springmvc_04/mydate.action?mydate=2022-11-03
    }

注册一个全局的日期处理注解 @InitBinder

registerCustomEditor 注册自定义编辑器

CustomDateEditor 自定义日期编辑器

将Date类转化成SimpleDateFormat("yyyy-MM-dd")类型,可以为空,等于说代替了@DateTimeFormat(pattern = "yyyy-MM-dd")这个注解,且这个是全局的整个类都可以用到

数据提交过来的时候直接就是已经转化好了的:http://localhost:8080/springmvc_04/mydate.action?mydate=2022-11-03,虽然单个的也是


案例

<form action="${pageContext.request.contextPath}/list.action">
    姓名:<input type="text" name="name"><br>
    生日:<input type="date" name="birthday"><br>
    <input type="submit" value="提交">
</form>
@Controller
public class StudentAction {
    @RequestMapping("/list")
    public String student(String name,String birthday,HttpServletRequest request) throws ParseException {
        Student student = new Student(name, new SimpleDateFormat("yyyy-MM-dd").parse(birthday));
        List<Student> list = new ArrayList<>();
        list.add(student);
        request.setAttribute("list",list);
        return "show";
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--jstl格式化标签库--%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>单个日期显示</h1>
<h1>date:${date}</h1>
<h1>date2:${date2}</h1>
<hr>
<c:forEach items="${list}" var="item">
    ${item.name}
    <br>
    ${item.birthday}---<fmt:formatDate value="${item.birthday}" pattern="yyyy-MM-dd"/>
</c:forEach>
</body>
</html>

mvc:annotation-driven/

mvc:annotation-driven/会自动注册两个bean,分别为DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter

是springmvc为@controller分发请求所必须的。除了注册了这两个bean,还提供了很多支持。

1)支持使用ConversionService 实例对表单参数进行类型转换;

2)支持使用 @NumberFormat 、@DateTimeFormat;

3)注解完成数据类型的格式化;

4)支持使用 @RequestBody 和 @ResponseBody 注解;

5)静态资源的分流也使用这个标签;


拦截器

拦截器是AOP思想的具体实现

SpringMVC 中的 Interceptor 拦截器,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。

其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。

当然,在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。


拦截器执行的时机

  1. preHandle() :在请求被处理之前进行操作,预处理

  2. postHandle(): 在请求被处理之后,但结果还没有渲染前进行

  3. afterCompletion(): 所有的请求相应结束后执行善后工作,清理对象,关闭资源,最终处理

拦截器实现的两种方式

  1. 继承HandlerInterceptorAdapter的父类
  2. 实现HandlerInterceptor接口
public class myInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("=======");
        HttpSession session = request.getSession(false);
        User user = (User)session.getAttribute("user");
        System.out.println(user);
        if (user !=null){
            return true;
        }
        if (request.getRequestURI().contains("login")){
            return true;
        }
        request.getRequestDispatcher("/index.jsp").forward(request,response);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

拦截器配置

<!--    注册拦截器驱动-->
    <mvc:interceptors>
        <mvc:interceptor>
<!--            拦截路径
				拦截这个请求下的所有请求
-->
            <mvc:mapping path="/**"/>
<!--            放行请求-->
            <mvc:exclude-mapping path="/login"/>
<!--            <mvc:exclude-mapping path="/success"/>-->
            <mvc:exclude-mapping path="/judgeLogin"/>
<!--            配置具体的拦截器实现功能的类-->
            <bean class="com.wzw.login.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

案例

这个案例写的不太好

bean

public class User {
    private String name;
    private String password;

controller

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/login")
    public String login(String name, String password, HttpSession session){
        System.out.println(name +","+password);
        if (!("张三".equals(name) && "123".equals(password))){
            return "redirect:/index.jsp";
        }
        User user = new User("张三", "123");
        session.setAttribute("user",user);
        return "main";
    }
    @RequestMapping("/goLogin")
    public String goLogin(){
        return "login";
    }
    @RequestMapping("/goMain")
    public String goMain(){
        return "main";
    }
}

HandlerInterceptor

public class myInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("=======");
        HttpSession session = request.getSession(false);
        User user = (User)session.getAttribute("user");
        System.out.println(user);
        if (user !=null){
            return true;
        }
        if (request.getRequestURI().contains("login")){
            return true;
        }
        request.getRequestDispatcher("/index.jsp").forward(request,response);
        return false;
    }

springmvc.xml

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/user/**"/>
<!--            <mvc:exclude-mapping path="/user/login"/>-->
            <mvc:exclude-mapping path="/user/goLogin"/>
            <bean class="com.wzw.Interceptor.myInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/user/goLogin">登录页面</a>
<a href="${pageContext.request.contextPath}/user/goMain">首页</a>
</body>
</html>

jsp/login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form method="post" action="${pageContext.request.contextPath}/user/login">
    用户名:<input type="text" name="name"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit" value="登录">
</form>
</body>
</html>

文件上传下载

文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。

如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。

前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。

只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;

对表单中的 enctype 属性做个详细的说明:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
<form action="" enctype="multipart/form-data" method="post">
    <input type="file" name="file"/>
    <input type="submit">
</form>

一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。

文件上传

<!--文件上传-->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>
<!--servlet-api导入高版本的-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>

配置bean:multipartResolver

<!--文件上传配置-->
<bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
    <property name="defaultEncoding" value="utf-8"/>
    <!-- 上传文件大小上限,单位为字节(10485760=10M) -->
    <property name="maxUploadSize" value="10485760"/>
    <property name="maxInMemorySize" value="40960"/>
</bean>

CommonsMultipartFile 的 常用方法:

  • String getOriginalFilename():获取上传文件的原名
  • InputStream getInputStream():获取文件流
  • void transferTo(File dest):将上传文件保存到一个目录文件中

案例

html

<form action="/upload" enctype="multipart/form-data" method="post">
  <input type="file" name="file"/>
  <input type="submit" value="upload">
</form>

controller

方法1

@Controller
public class FileController {
    //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
    //批量上传CommonsMultipartFile则为数组即可
    @RequestMapping("/upload")
    public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {
        //获取文件名 : file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();
        //如果文件名为空,直接回到首页!
        if ("".equals(uploadFileName)){
            return "redirect:/index.jsp";
        }
        System.out.println("上传文件名 : "+uploadFileName);
        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        //如果路径不存在,创建一个
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        System.out.println("上传文件保存地址:"+realPath);
        InputStream is = file.getInputStream(); //文件输入流
        OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //文件输出流
        //读取写出
        int len=0;
        byte[] buffer = new byte[1024];
        while ((len=is.read(buffer))!=-1){
            os.write(buffer,0,len);
            os.flush();
        }
        os.close();
        is.close();
        return "redirect:/index.jsp";
    }
}

方法2

采用file.Transto 来保存上传的文件

/*
 * 采用file.Transto 来保存上传的文件
 */
@RequestMapping("/upload2")
public String  fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
    //上传路径保存设置
    String path = request.getServletContext().getRealPath("/upload");
    File realPath = new File(path);
    if (!realPath.exists()){
        realPath.mkdir();
    }
    //上传文件地址
    System.out.println("上传文件保存地址:"+realPath);
    //通过CommonsMultipartFile的方法直接写文件(注意这个时候)
    file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
    return "redirect:/index.jsp";
}

文件下载

  1. 设置 response 响应头
  2. 读取文件 — InputStream
  3. 写出文件 — OutputStream
  4. 执行操作
  5. 关闭流 (先开后关)
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response ,HttpServletRequest request) throws Exception{
    //要下载的图片地址
    String  path = request.getServletContext().getRealPath("/upload");
    String  fileName = "基础语法.jpg";
    //1、设置response 响应头
    response.reset(); //设置页面不缓存,清空buffer
    response.setCharacterEncoding("UTF-8"); //字符编码
    response.setContentType("multipart/form-data"); //二进制传输数据
    //设置响应头
    response.setHeader("Content-Disposition",
            "attachment;fileName="+URLEncoder.encode(fileName, "UTF-8"));
    File file = new File(path,fileName);
    //2、 读取文件--输入流
    InputStream input=new FileInputStream(file);
    //3、 写出文件--输出流
    OutputStream out = response.getOutputStream();
    byte[] buff =new byte[1024];
    int index=0;
    //4、执行 写出操作
    while((index= input.read(buff))!= -1){
        out.write(buff, 0, index);
        out.flush();
    }
    out.close();
    input.close();
    return null;
}

html

<a href="/download">点击下载</a>

注解总结

@RequestMapping 请求注解,用于指定请求路径

  • @RequestMapping(value = "/demo",method = RequestMethod.POST)
    
  • 子注解

  • @GetMapping

  • @PostMapping

  • @PutMapping

  • @DeleteMapping

  • @PatchMapping

@RequestParam 参数注解,用于接收请求参数,参数名称不同的时候可以指定参数名称

  • public String submit(@RequestParam("myname") String name,int age)
    

@ResponseBody 返回字符串,不走视图解析器,用于返回json数据

  • @ResponseBody
    

@InitBinder 全局注解,可以用到处理日期

  • @InitBinder
    public void initBinder(WebDataBinder dateBinder){
        dateBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true));
    }
    

@ResponseBody 用在类上,意思是该类中所有的方法返回值都是以json格式返回给客户端

ervletRequest request) throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath(“/upload”);
String fileName = “基础语法.jpg”;
//1、设置response 响应头
response.reset(); //设置页面不缓存,清空buffer
response.setCharacterEncoding(“UTF-8”); //字符编码
response.setContentType(“multipart/form-data”); //二进制传输数据
//设置响应头
response.setHeader(“Content-Disposition”,
“attachment;fileName=”+URLEncoder.encode(fileName, “UTF-8”));
File file = new File(path,fileName);
//2、 读取文件–输入流
InputStream input=new FileInputStream(file);
//3、 写出文件–输出流
OutputStream out = response.getOutputStream();
byte[] buff =new byte[1024];
int index=0;
//4、执行 写出操作
while((index= input.read(buff))!= -1){
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return null;
}


html

```html
<a href="/download">点击下载</a>

注解总结

@RequestMapping 请求注解,用于指定请求路径

  • @RequestMapping(value = "/demo",method = RequestMethod.POST)
    
  • 子注解

  • @GetMapping

  • @PostMapping

  • @PutMapping

  • @DeleteMapping

  • @PatchMapping

@RequestParam 参数注解,用于接收请求参数,参数名称不同的时候可以指定参数名称

  • public String submit(@RequestParam("myname") String name,int age)
    

@ResponseBody 返回字符串,不走视图解析器,用于返回json数据

  • @ResponseBody
    

@InitBinder 全局注解,可以用到处理日期

  • @InitBinder
    public void initBinder(WebDataBinder dateBinder){
        dateBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true));
    }
    

@ResponseBody 用在类上,意思是该类中所有的方法返回值都是以json格式返回给客户端

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值