SpringMVC深入浅出完整学习笔记,附案例及框架原理(项目搭建,处理结果跳转,从前端接收数据,发送数据到前端,自定义拦截器以及文件上传与下载)

本篇学习笔记总结自bilibiliup主【狂神说Java】系列视频:【狂神说Java】SpringMVC最新教程IDEA版通俗易懂
视频作者公众号:狂神说

1、概述

1.1、什么是SpringMVC?

  • MVC是模型(Model),视图(View),控制器(Controller)的简写,是一种软件设计规范
  • MVC主要作用是降低了视图与业务逻辑间的双向耦合

职责分析:

Controller:控制器

  1. 取得表单数据
  2. 调用业务逻辑
  3. 转向指定的页面

Model:模型

  1. 业务逻辑
  2. 保存数据的状态

View:视图

  1. 显示页面

MVC框架要做哪些事情

  1. 将url映射到java类或java类的方法。
  2. 封装用户提交的数据 。
  3. 处理请求–调用相关的业务处理–封装响应数据 。
  4. 将响应的数据进行渲染 . jsp / html 等表示层数据。

1.2、中心控制器

Spring的web框架围绕DispatcherServlet设计。DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解的controller声明方式。

​ Spring MVC框架像许多其他MVC框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)

在这里插入图片描述

1.3、SpringMVC执行原理

在这里插入图片描述

  1. 用户请求
  2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。
  3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。
  4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
  5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
  6. Handler让具体的Controller执行。
  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。
  11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
  12. 最终视图呈现给用户。

大体分为三步

  1. 步骤2,3,4:适配url请求到底是干嘛的
  2. 步骤5,6,7,8:请求到底是干嘛的,传给controlller
  3. 步骤9,10,11:视图解析

2、第一个SpringMVC程序

2.1、使用配置搭建项目

  1. 导入一个maven项目

  2. 导入web支持

  3. 在web.xml注册DispatcherServlet

    <?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">
        <!--1.注册DispatcherServlet:这个是SpringMVC的核心:请求分发器,也是前端控制器-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!--启动级别-1:服务器启动就启动-->
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <!--/ 匹配所有的请求;(不包括.jsp)-->
        <!--/* 匹配所有的请求;(包括.jsp)-->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  4. 在resource目录下编写springmvc-servlet.xml,实质上是spring的配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--添加 处理映射器-->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> m
        <!--添加 处理器适配器-->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
        <!--添加 视图解析器-->
        <!--视图解析器:DispatcherServlet给他的ModelAndView-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <!--后缀-->
            <property name="suffix" value=".jsp"/>
        </bean>
    
        <!--Handler:将自己的类交给SpringIOC容器,注册bean-->
        <bean id="/hello" class="com.neil.controller.HelloController"/>
    </beans>
    
  5. Controller

    package com.neil.controller;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    //注意:这里我们先导入Controller接口
    public class HelloController implements Controller {
    
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            //ModelAndView 模型和视图
            ModelAndView mv = new ModelAndView();
    
            //封装对象,放在ModelAndView中。Model
            mv.addObject("msg","HelloSpringMVC!");
            //封装要跳转的视图,放在ModelAndView中
            mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
            return mv;
        }
    }
    
  6. 对应的jsp页面

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

缺点:

  • 一个控制器只能实现一个方法,定义多个方法需要多个Controller

2.2、使用注解搭建项目

  1. 导入一个maven项目

  2. 导入web支持

  3. 在web.xml配置DispatcherServlet

    <?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">
    
        <!--1.注册servlet-->
        <servlet>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!-- 启动顺序,数字越小,启动越早 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <!--所有请求都会被springmvc拦截 -->
        <servlet-mapping>
            <servlet-name>SpringMVC</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  4. 在resource目录下编写springmvc-servlet.xml,实质上是spring的配置文件

    <?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">
    
        <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
        <context:component-scan base-package="com.neil.controller"/>
        <!-- 让Spring MVC不处理静态资源 .css .js .mp3 .mp4资源不处理,过滤 -->
        <mvc:default-servlet-handler />
        <!--
        支持mvc注解驱动
            在spring中一般采用@RequestMapping注解来完成映射关系
            要想使@RequestMapping注解生效
            必须向上下文中注册DefaultAnnotationHandlerMapping
            和一个AnnotationMethodHandlerAdapter实例
            这两个实例分别在类级别和方法级别处理。
            而annotation-driven配置帮助我们自动完成上述两个实例的注入。
         -->
        <!--相对使用配置搭建,省略了映射器和适配器,使用注解进行-->
        <mvc:annotation-driven />
    
        <!-- 视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!-- 前缀 -->
            <property name="prefix" value="/WEB-INF/jsp/" />
            <!-- 后缀 -->
            <property name="suffix" value=".jsp" />
        </bean>
    </beans>
    
  5. controller

    package com.neil.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class HelloController {
    
        @RequestMapping("/MVC")
        public String hello(Model model){
            model.addAttribute("msg","Hello, this is SpringMVCAnnotation");
    
            //返回的字符串就是视图名字,会被视图解析器处理
            //会被拼接成/WEB-INF/jsp/hello.jsp
            return "hello";
        }
    }
    

2.3、小结

实现步骤其实非常的简单:

  1. 新建一个web项目
  2. 导入相关jar包
  3. 编写web.xml , 注册DispatcherServlet
  4. 编写springmvc配置文件
  5. 接下来就是去创建对应的控制类 , controller
  6. 最后完善前端视图和controller之间的对应
  7. 测试运行调试.

使用springMVC必须配置的三大件:

处理器映射器、处理器适配器、视图解析器

通常,我们只需要手动配置视图解析器,而处理器映射器处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置

3、RestFul 风格

3.1、概念

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

3.2、功能

资源:互联网所有的事物都可以被抽象为资源

资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。

分别对应 添加、 删除、修改、查询。

传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get

​ http://127.0.0.1/item/queryItem.action?id=1 查询,GET

​ http://127.0.0.1/item/saveItem.action 新增,POST

​ http://127.0.0.1/item/updateItem.action 更新,POST

​ http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST

使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!

​ http://127.0.0.1/item/1 查询,GET

​ http://127.0.0.1/item 新增,POST

​ http://127.0.0.1/item 更新,PUT

​ http://127.0.0.1/item/1 删除,DELETE

3.3、案例

在Spring MVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上。

@Controller
public class RestFulController {
   //映射访问路径
   @RequestMapping("/commit/{p1}/{p2}")
   public String index(@PathVariable int p1, @PathVariable int p2, Model model){
       
       int result = p1+p2;
       //Spring MVC会自动实例化一个Model对象用于向视图中传值
       model.addAttribute("msg", "结果:"+result);
       //返回视图位置
       return "test";       
  }   
}

3.4、小结

Spring MVC 的 @RequestMapping 注解能够处理 HTTP 请求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。

所有的地址栏请求默认都会是 HTTP GET 类型的。

方法级别的注解变体有如下几个:组合注解

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

@GetMapping 是一个组合注解,平时使用的会比较多!

它所扮演的是 @RequestMapping(method =RequestMethod.GET) 的一个快捷方式。

#4、SpringMVC结果跳转

4.1、ModelAndView

设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 .

页面 : {视图解析器前缀} + viewName +{视图解析器后缀}

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
     id="internalResourceViewResolver">
   <!-- 前缀 -->
   <property name="prefix" value="/WEB-INF/jsp/" />
   <!-- 后缀 -->
   <property name="suffix" value=".jsp" />
</bean>

对应的controller类

public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
       //返回一个模型视图对象
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","ControllerTest1");
       mv.setViewName("test");
       return mv;
  }
}

4.2、ServletAPI

通过设置ServletAPI , 不需要视图解析器 .

1、通过HttpServletResponse进行输出

2、通过HttpServletResponse实现重定向

3、通过HttpServletResponse实现转发

@Controller
public class ResultGo {

   @RequestMapping("/result/t1")
   public void test1(HttpServletRequest req, HttpServletResponse rsp) throwsIOException {
       rsp.getWriter().println("Hello,Spring BY servlet API");
  }

   @RequestMapping("/result/t2")
   public void test2(HttpServletRequest req, HttpServletResponse rsp) throwsIOException {
       rsp.sendRedirect("/index.jsp");
  }

   @RequestMapping("/result/t3")
   public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception{
       //转发
       req.setAttribute("msg","/result/t3");
       req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
  }

}

4.3、SpringMVC

通过SpringMVC来实现转发和重定向 - 有视图解析器;

重定向 , 不需要视图解析器 , 本质就是重新请求一个新地方嘛 , 所以注意路径问题.

可以重定向到另外一个请求实现 .

@Controller
public class ResultSpringMVC2 {
   @RequestMapping("/rsm2/t1")
   public String test1(){
       //转发
       return "test";
  }

   @RequestMapping("/rsm2/t2")
   public String test2(){
       //重定向
       return "redirect:/index.jsp";
       //return "redirect:hello.do"; //hello.do为另一个请求/
  }

}

5、处理前端数据

5.1、提交的域名称和处理方法的参数名一致

  • controller

    @Controller
    @RequestMapping("/user")
    public class UserController {
    
        @GetMapping("/t1")
        public String Test1(String name, Model model){
            //1. 接收前端参数
            //2。将返回的结果返回给前端
            System.out.println("接收到的参数为:" + name);
            model.addAttribute("msg",name);
            //跳转视图
            return "test";
        }
    }
    
  • 请求连接:http://localhost:8888/Springmvc_04_Controller_war_exploded/user/t1?name=neil

  • 后台,前端输出:neil

5.2、提交的域名称和处理方法的参数名不一致

  • controller

    @Controller
    @RequestMapping("/user")
    public class UserController {
    
        @GetMapping("/t1")
        public String Test1(@RequestParam("username") String name, Model model){
            //1. 接收前端参数
            //2。将返回的结果返回给前端
            System.out.println("接收到的参数为:" + name);
            model.addAttribute("msg",name);
            //跳转视图
            return "test";
        }
    }
    
  • 请求连接:http://localhost:8888/Springmvc_04_Controller_war_exploded/user/t1?username=neil

  • 后台,前端 输出:neil

5.3、前端接收一个对象

  • user

    //省略get set方法
    public class User {
        private int id;
        private String name;
        private int age;
    }
    
  • controller

    @Controller
    @RequestMapping("/user")
    public class UserController {
    
        @GetMapping("/t2")
        public String test2(User user){
            System.out.println("user = " + user.toString());
            return "test";
        }
    }
    
  • 请求连接:http://localhost:8888/Springmvc_04_Controller_war_exploded/user/t2?id=1&name=neil&age=22

  • 后台可以输出user

  • 必须保证匹配User对象的字段名和传参字段名一致!

6、数据返回到前端

6.1、通过ModelAndView

public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
       //返回一个模型视图对象
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","ControllerTest1");
       mv.setViewName("test");
       return mv;
  }
}

6.2、通过ModelMap

@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
   //封装要显示到视图中的数据
   //相当于req.setAttribute("name",name);
   model.addAttribute("name",name);
   System.out.println(name);
   return "hello";
}

6.3、通过Model

@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
   //封装要显示到视图中的数据
   //相当于req.setAttribute("name",name);
   model.addAttribute("msg",name);
   System.out.println(name);
   return "test";
}

6.4、区别

Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;

ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;

ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

6.5、过滤器解决乱码问题

  1. 在web.xml配置(一般可以解决)

    <!--过滤器解决乱码-->
    <filter>
      <filter-name>encoding</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>
    </filter>
    <filter-mapping>
      <filter-name>encoding</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>
    
  2. 极端情况下,自定义过滤器解决方法:

    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.util.Map;
    
    /**
    * 解决get和post请求 全部乱码的过滤器
    */
    public class GenericEncodingFilter implements Filter {
    
       @Override
       public void destroy() {
      }
    
       @Override
       public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
           //处理response的字符编码
           HttpServletResponse myResponse=(HttpServletResponse) response;
           myResponse.setContentType("text/html;charset=UTF-8");
    
           // 转型为与协议相关对象
           HttpServletRequest httpServletRequest = (HttpServletRequest) request;
           // 对request包装增强
           HttpServletRequest myrequest = new MyRequest(httpServletRequest);
           chain.doFilter(myrequest, response);
      }
    
       @Override
       public void init(FilterConfig filterConfig) throws ServletException {
      }
    
    }
    
    //自定义request对象,HttpServletRequest的包装类
    class MyRequest extends HttpServletRequestWrapper {
    
       private HttpServletRequest request;
       //是否编码的标记
       private boolean hasEncode;
       //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
       public MyRequest(HttpServletRequest request) {
           super(request);// super必须写
           this.request = request;
      }
    
       // 对需要增强方法 进行覆盖
       @Override
       public Map getParameterMap() {
           // 先获得请求方式
           String method = request.getMethod();
           if (method.equalsIgnoreCase("post")) {
               // post请求
               try {
                   // 处理post乱码
                   request.setCharacterEncoding("utf-8");
                   return request.getParameterMap();
              } catch (UnsupportedEncodingException e) {
                   e.printStackTrace();
              }
          } else if (method.equalsIgnoreCase("get")) {
               // get请求
               Map<String, String[]> parameterMap = request.getParameterMap();
               if (!hasEncode) { // 确保get手动编码逻辑只运行一次
                   for (String parameterName : parameterMap.keySet()) {
                       String[] values = parameterMap.get(parameterName);
                       if (values != null) {
                           for (int i = 0; i < values.length; i++) {
                               try {
                                   // 处理get乱码
                                   values[i] = new String(values[i]
                                          .getBytes("ISO-8859-1"), "utf-8");
                              } catch (UnsupportedEncodingException e) {
                                   e.printStackTrace();
                              }
                          }
                      }
                  }
                   hasEncode = true;
              }
               return parameterMap;
          }
           return super.getParameterMap();
      }
    
       //取一个值
       @Override
       public String getParameter(String name) {
           Map<String, String[]> parameterMap = getParameterMap();
           String[] values = parameterMap.get(name);
           if (values == null) {
               return null;
          }
           return values[0]; // 取回参数的第一个值
      }
    
       //取所有值
       @Override
       public String[] getParameterValues(String name) {
           Map<String, String[]> parameterMap = getParameterMap();
           String[] values = parameterMap.get(name);
           return values;
      }
    }
    

    之后在web.xml中配置这个过滤器即可(注意路径使用/* 而不是/ )

7、JSON

前端JSON 和 JavaScript 对象互转

要实现从JSON字符串转换为JavaScript 对象,使用 JSON.parse() 方法:

var obj = JSON.parse('{"a": "Hello", "b": "World"}');
//结果是 {a: 'Hello', b: 'World'}

要实现从JavaScript 对象转换为JSON字符串,使用 JSON.stringify() 方法:

var json = JSON.stringify({a: 'Hello', b: 'World'});
//结果是 '{"a": "Hello", "b": "World"}'

7.1、Controller返回JSON数据

导入Jackson包

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

controller

@Controller
@RequestMapping("/user")
public class UserController {

    //produces解决乱码问题
    @GetMapping(value = "/json1",produces = "application/json;charset=utf-8")
    @ResponseBody//该注解不会让接口走视图解析器,而是直接返回一个字符串
    public String Test1() throws JsonProcessingException {
        //创建一个jackson的对象映射器,用来解析数据
        ObjectMapper mapper = new ObjectMapper();
        //创建一个对象
        User user = new User(23, "张三", 11);
        //将我们的对象解析成为json格式
        //由于@ResponseBody注解,这里会将str转成json格式返回;十分方便
        return mapper.writeValueAsString(user);
    }

}

7.2、JSON乱码问题

可以进行统一在spring配置文件中配置

<mvc:annotation-driven>
   <mvc:message-converters register-defaults="true">
       <bean class="org.springframework.http.converter.StringHttpMessageConverter">
           <constructor-arg value="UTF-8"/>
       </bean>
       <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
           <property name="objectMapper">
               <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                   <property name="failOnEmptyBeans" value="false"/>
               </bean>
           </property>
       </bean>
   </mvc:message-converters>
</mvc:annotation-driven>

或者

@RequestMapping(value = "/user",produces = "text/html;charset=UTF-8")

关于注解:

@ResponseBody:作用在方法上,表示该方法会不走视图解析器,而是直接返回一个字符串

@RestController:作用在类上,表示该类所有方法都是返回字符串

8、Ajax技术

  • AJAX是一种无需重新加载整个网页的情况下,能够更新部分网页的技术

8.1、jQuery.ajax

  • jQuery 提供多个与 AJAX 有关的方法。
  • 通过 jQuery AJAX 方法,您能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON – 同时您能够把这些外部数据直接载入网页的被选元素中。
  • jQuery Ajax本质就是 XMLHttpRequest,对他进行了封装,方便调用!

8.2、案例一:失去焦点发送异步请求

  • 前端:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>主页</title>
        <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
        <script>
          function a(){
            $.post({
                url:"${pageContext.request.contextPath}/a1",
                data:{"name" : $("#username").val()},
                success:function (data) {
                  alert(data);
                }
              })
          }
    
        </script>
      </head>
      <body>
      <%--失去焦点的时候发起一个请求(携带信息)到后台--%>
      用户名:<input type="text" id="username" onblur="a()">
      </body>
    </html>
    
  • Controller

    @RestController
    public class AjaxController {
    
        @RequestMapping("/t1")
        public String test(){
            return "hello";
        }
    
        @RequestMapping("/a1")
        public void a1(String name, HttpServletResponse response) throws IOException {
            System.out.println("name = " + name);
            if ("Neil".equals(name)){
                response.getWriter().print("true");
            }else {
                response.getWriter().print("false");
            }
        }
    }
    

8.2、案例二:点击按钮返回pojo数组并显示

  • 前端代码

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title></title>
        <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
        <script>
            $(function () {
                $('#btn').click(function () {
                    /*
                    * $.post(url, param[可以省略], success)
                    *
                    * */
                    $.post("${pageContext.request.contextPath}/a2",function (data) {
                        console.log(data);
                        var append="";
                        for (let i = 0 ; i < data.length ; i++){
                            append += "<tr>" +
                                "<td>"+  data[i].name + "</td>"+
                                "<td>"+  data[i].age + "</td>"+
                                "<td>"+  data[i].sex + "</td>"+
                                "</tr>"
                        }
                        $("#content").html(append);
                    });
                })
            })
        </script>
    </head>
    <input type="button" value="获取数据" id="btn">
    <body>
        <table>
            <tr>
                <td>姓名:</td>
                <td>年龄:</td>
                <td>姓名:</td>
            </tr>
    
            <tbody id="content">
                <%--数据:后台--%>
            </tbody>
        </table>
    </body>
    </html>
    
  • controller

    package com.neil.controller;
    
    import com.neil.pojo.User;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    @RestController
    public class AjaxController {
        @RequestMapping("/a2")
        public List<User> a2(){
            List<User> userList= new ArrayList<>();
            //添加数据
            userList.add(new User("Neill",1 ,"女"));
            userList.add(new User("张三",2 ,"男"));
            userList.add(new User("王五",3 ,"女"));
            return userList;
        }
    }
    
    

8.3、案例三:登陆时异步判断用户名密码

  • 前端代码

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
        <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
        <script>
            function a1() {
                $.post({
                    url:"${pageContext.request.contextPath}/a3",
                    data:{"name":$("#name").val()},
                    success:function (data) {
                        if(data.toString()==="ok"){
                            $("#userInfo").css("color","green");
                        }
                        $("#userInfo").html(data);
    
                    }
                })
            }
            function a2() {
                $.post({
                    url:"${pageContext.request.contextPath}/a3",
                    data:{"pwd":$("#pwd").val()},
                    success:function (data) {
                        if(data.toString()==="ok"){
                            $("#userPwd").css("color","green");
                        }
                        $("#userPwd").html(data);
                    }
                })
            }
        </script>
    </head>
    <body>
        <p>
            用户名:<input type="text" id="name" onblur="a1()">
            <span id="userInfo"></span>
    
        </p>
        <p>
            密码:<input type="text" id="pwd" onblur="a2()">
            <span id="userPwd"></span>
        </p>
    
    </body>
    </html>
    
    
    
  • controller

    @RestController
    public class AjaxController {
        @RequestMapping(value = "/a3",produces = "text/html;charset=UTF-8")
        public String a3(String name, String pwd){
            String msg="";
            if(name!=null){
                //这些数据应该在数据库中查
                if ("admin".equals(name)){
                    msg = "ok";
                }else {
                    msg = "用户名有误";
                }
            }
            if(pwd!=null){
                if ("123".equals(pwd)){
                    msg = "ok";
                }else {
                    msg = "密码有误";
                }
            }
            return msg;
        }
    }
    
    

9、SpringMVC拦截器

9.1、概述

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

**过滤器与拦截器的区别:**拦截器是AOP思想的具体应用。

过滤器

  • servlet规范中的一部分,任何java web工程都可以使用
  • 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截

拦截器

  • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
  • 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的

9.2、自定义拦截器

  • MyInterceptor

    package com.neil.config;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class MyInterceptor implements HandlerInterceptor {
        @Override
        // return true:执行下一个拦截器,放行
        // return false:并执行下一个拦截器,不放行
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("=================处理前=================");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("=================处理后=================");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("=================清理=================");
        }
    }
    
    
  • ApplicationContext.xml

    <!--拦截器-->
    <mvc:interceptors>
      <mvc:interceptor>
        <!--包括这个请求下面的所有请求/admin/user/jsp-->
        <mvc:mapping path="/**"/>
        <bean class="com.neil.config.MyInterceptor"/>
      </mvc:interceptor>
    </mvc:interceptors>
    
  • controller

    package com.neil.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    @RestController
    public class TestController {
        @RequestMapping("/t1")
        public String test(){
            System.out.println("执行了");
            return "ok";
        }
    }
    

9.3、登陆拦截案例

  • ApplicationContext.xml 配置拦截器

    <!--拦截器-->
    <mvc:interceptors>
      <mvc:interceptor>
        <!--包括这个请求下面的所有请求/admin/user/jsp-->
        <mvc:mapping path="/**"/>
        <bean class="com.neil.config.MyInterceptor"/>
      </mvc:interceptor>
      <mvc:interceptor>
        <!--包括这个请求下面的所有请求/admin/user/jsp-->
        <mvc:mapping path="/user/**"/>
        <mvc:exclude-mapping path="/user/login"/>
        <bean class="com.neil.config.LoginInterceptor"/>
      </mvc:interceptor>
    </mvc:interceptors>
    
  • 拦截器类

    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //判断Session如果存在就没登陆
            HttpSession session = request.getSession();
            //放行的判断:判断什么情况下登陆了: 1:登陆页面放行,2有session放行
            if(request.getRequestURI().contains("goLogin")){
                return true;
            }
    
            if(session.getAttribute("userLoginInfo")!=null){
                return true;
            }
    
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
            return false;
        }
    }
    
  • 在WEB-INF下的login.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <%--在web-inf下所有页面和资源只能在controller或者servlet进行访问--%>
        <h1>登陆页面</h1>
        <form action="${pageContext.request.contextPath}/user/login" method="post">
            用户名:<input type="text" name="username">
            密码:<input type="text" name="pwd">
            <input type="submit" value="提交">
        </form>
    </body>
    </html>
    
  • 在WEB-INF下的main.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>首页</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>${username}</h2>
        <a href="${pageContext.request.contextPath}/user/logout">注销</a>
    </body>
    </html>
    
  • 首页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/main">主页面</a>
      </body>
    </html>
    

10、文件上传与下载

10.1、准备工作

文件上传是项目开发中最常见的功能之一 ,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响应。在2003年,Apache Software Foundation发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。

  • Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。
  • 而Spring MVC则提供了更简单的封装。
  • Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
  • Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:
  • CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。

导入包

<!--文件上传-->
<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>

10.2、文件上传

  1. 前端页面

    <form action="/upload" enctype="multipart/form-data" method="post">
     <input type="file" name="file"/>
     <input type="submit" value="upload">
    </form>
    
  2. ApplicationContext.xml配置bean

    注意!!!这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!在这里栽过坑,教训!

    <!--文件上传配置-->
    <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):将上传文件保存到一个目录文件中
  3. Controller

    • 方法一:正常方法上传

      @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";
        }
      }
      
    • 方法二:采用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";
      }
      

10.3、文件下载

文件下载步骤:

1、设置 response 响应头

2、读取文件 – InputStream

3、写出文件 – OutputStream

4、执行操作

5、关闭流 (先开后关)

  • controller

    @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;
    }
    
  • 前端

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

本篇学习笔记总结自bilibiliup主【狂神说Java】系列视频:【狂神说Java】SpringMVC最新教程IDEA版通俗易懂
视频作者公众号:狂神说

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值