springMVC


springMVC


springMVC简介

传统的servlet的不足

  • 每个请求,都需要定义一个Servlet。虽然可以在service方法中,根据业务标识进行业务分发,但是每个Servlet中的service方法的实现是重复。如果想要对service进行更高层次的封装,就可以解决service的重复代码问题。
  • 每个请求的参数,都需要根据request对象,从Map逐一的获取,单调且含量低。将所有的参数,自动封装映射,简化开发。
  • 每个Servlet,向客户端返回数据时,需要单独的处理。
  • 针对于Servlet在实际使用中的不便,Spring中提供了组件,SpringMVC,更进一步的简化了Servlet的开发。

springMVC的架构

mvc架构

  • DispatcherServlet

    • 核心控制器,本质上就是一个Servlet,处理所有的客户端的请求。根据请求的资源路径,在处理器映射器中查找对应的处理器。
  • HandlerMapping

  • 处理器映射器,存储所有当前程序中的处理器,如果在处理器映射器中查找不到资源路径,直接返回404。

  • HandlerAdapter

    • 处理器适配器,用于适配各种处理器,调用具体的处理器程序。
  • Handler

    • 具体处理器,开发者实现相应接口或者使用注解声明的程序。用于处理具体的请求。
  • ViewResolver

    • 视图解析器,根据处理器返回的数据,进行数据处理,将数据处理成相应的格式。

      JSP/JSON等等。

  • 完善请求响应流程图

    请求响应

    1. 用户通过浏览器发送请求到前端控制器DispatcherServlet。
    2. 前端控制器直接将请求转给处理器映射器HandleMapping。
    3. 处理器映射器HandleMapping会根据请求,找到负责处理该请求的处理器,并将其封装为处理器执行链HandlerExecutionChina后返回给前端控制器DispatcherServlet。
    4. 前端控制器DispatcherServlet根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器HandlerAdaptor。
    5. 处理器适配器HandlerAdaptor调用执行处理器Controller。
    6. 处理器Controller将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器HandlerAdaptor。
    7. 处理器适配器直接将结果返回给前端控制器DispatcherServlet。
    8. 前端控制器调用视图解析器,将 ModelAndView 中的视图名称封装为视图对象。
    9. 视图解析器ViewResolver将封装了的视图View对象返回给前端控制器DispatcherServlet。
    10. 前端控制器DispatcherServlet调用视图对象,让其自己进行渲染,即进行数据填充,形成响应对象。
    11. 前端控制器响应浏览器。

springMVC使用

springMVC配置文件

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

    <!-- 开启springmvc 注解 -->
    <!-- 开启组件扫描 -->
    <context:component-scan base-package="com.*" />
    <!-- 开启springmvc 注解驱动 --> 
    <mvc:annotation-driven />
    
    <!-- 处理静态资源 --> 
    <mvc:default-servlet-handler/>
    
    <!--配置视图解析器,完成模板的配置-->
       <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <property name="suffix" value=".jsp"/>
              <property name="prefix" value="/WEB-INF/"/>
       </bean>

</beans>

web.xml中配置springMVC

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">


    <servlet>
        <servlet-name>mvc</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>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

springMVC的数据绑定

 @Controller
public class Controller01 {

    // 映射路径 它可以用在方法上 也可以用在类上  映射路径必须要在整个项目中唯一
   // @RequestMapping("/register")
    // 返回值是String类型,一般表示 返回值的内容是 页面的路径,通过请求转发跳转到该页面上
    // 当使用了@ResponseBody这个注解 就变成了把返回值封装成json格式的对象直接响应到浏览器
  //  @ResponseBody
    /**
     * @RequestParam  处理 浏览器提交过来的参数的名称 和接受参数的名称不一致的情况
     */
    public String register(String name,Integer password,int sex, String hobby,@RequestParam("City") String city){
        System.out.println("name = " + name);
        System.out.println("password = " + password);
        System.out.println("sex = " + sex);
        System.out.println("hobby = " + hobby);
        System.out.println("city = " + city);

        return "/WEB-INF/register.jsp";
    }


    /**
     * 如果表单上的name属性的值和类中的属性的值一致,类型也一致,则,springMVC框架会自动将接收的表单的数据
     * 封装到对象中
     * @param user
     * @return
     */
    //@RequestMapping("/register")
    public String register2(User user){
        System.out.println("user = " + user);

        return "/WEB-INF/register.jsp";
    }

    /**
     * springMVC 框架也支持 数据封装到map中,但是必须使用@ReqeustParam注解 搭配使用
     * @param map
     * @return
     */
   // @RequestMapping("/register")
    public String register3(@RequestParam Map map ){

        map.forEach((x,y)->System.out.printf(x+":" + y));

        return "/WEB-INF/register.jsp";
    }

    /**
     * 针对表单提交的数据为一个name 对应多个value的情况,则单独接收可以使用数组接收
     * @param hobby
     * @return
     */
   // @RequestMapping("/register")
    public String register4(String[] hobby){
        System.out.println(Arrays.toString(hobby));

        return "/WEB-INF/register.jsp";
    }

    /**
     * 针对表单提交的数据为一个name 对应多个value的情况,则单独接收可以使用集合接收,但是
     * 必须搭配@RequestParam注解一起使用
     * @param hobby
     * @return
     */
    //@RequestMapping("/register")
    public String register5(@RequestParam("hobby") List hobby){
        for (String s : hobby) {
            System.out.println(s);
        }

        return "/WEB-INF/register.jsp";
    }

springMVC的跳转

  • 在SpringMVC中,默认的跳转方式内部转发,,每个URL前面默认有forward:

  • 默认会将方法的返回值当做视图的路径处理。并且,在SpringMVC中,/表示当前项目根目录。如果想要使用重定向,则使用关键字:redirect:/路径。

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class JumpController {
    
        @RequestMapping("register")
        public String forward(){
                   return  "forward:/success.jsp"; // 背后是直接去项目的根目录下面找success.jsp文件
        }
        @RequestMapping("register")
        public String forward1(){
                return  "success";   // 背后会使用模板 拼接跳转的路径 /WEB-INFO/success.jsp
        }
    
    /**
     *  如果想使用重定向的方式跳转到其它页面,那么则使用redirect关键字
     *  使用方式:
     *       redirect:绝对路径
     *
     * */
    @RequestMapping("/register")
    public String register7(@RequestParam("hobby") List hobby){
        for (String s : hobby) {
            System.out.println(s);
        }
    
        return "redirect:/other.jsp";
    }
    
    }
    

springMVC支持内置对象

  • 在SpringMVC中,支持为处理器中的方法注入内置的对象,如:HttpServletRequest、HttpServletResponse、HttpSession、Model等。

    /**
     *
     * @param request
     * @param resp
     * @param session
     * @param model  是springmvc中 推荐使用参数传递的容器 当参数发生覆盖 优先使用model中的参数
     * @throws IOException
     */
    @RequestMapping("servletParam.do")
    public void servletParam(HttpServletRequest request, HttpServletResponse resp, HttpSession session, Model model) throws IOException {
        String name = request.getParameter("name");
        System.out.println(name);
        System.out.println(request);
        System.out.println(resp);
        System.out.println(session);
        System.out.println(model);
        resp.sendRedirect("success.jsp");
        //return "/success.jsp";
    }
    

springMVC参数传递

  • 在SpringMVC中,可以获取内置HttpServletRequest对象,并且SpringMVC默认使用内部转发,所以可以使用HttpServletRequest进行数据传递,但是SpringMVC推荐使用Model进行数据传递,SpringMVC最终是将URL路径和Model进行结合产生ModelAndView对象。

    /**
     * 演示参数传递问题
     *  在springmvc中  默认的跳转是内部转发
     *  所以可以使用HttpServletRequest 传递参数
     *  但是  springmvc 推荐使用 model 传递参数  并且 model 参数的优先级最高
     *
     * @param request
     * @param model
     * @return
     */
    @RequestMapping("param2.do")
    public  String  param(HttpServletRequest request,Model model){
        model.addAttribute("age",25);
        model.addAttribute("sex","女");
        request.setAttribute("name","韩梅梅");
        request.setAttribute("age",18);
        return "/success.jsp";
    }
    

SpringMVC获取Servlet作用域

  • 代码

    import org.springframework.web.context.ContextLoader;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.ServletContext;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    
    /**
     * @Description: 获取Servlet 作用域对象工具类
     *      HttpServletRequest
     *      HttpSession
     *      ServletContext
     */
    public class WebScopeUtil {
    
        /**
         *  获取当前  HttpServletRequest 对象
         * @return
         */
        public static HttpServletRequest getRequest(){
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
            return  requestAttributes.getRequest();
        }
    
        /**
         * 获取当前请求绑定的session
         * @return
         */
        public static HttpSession getSession(){
            return  getRequest().getSession();
        }
    
        /**
         *  获取全局容器对象
         * @return
         */
        public static ServletContext getContext(){
            //getRequest().getServletContext(); // ServletContext 的生命周期 是早于 HttpServletRequest
            //所以不能通过 HttpServletRequest 获取ServletContext  可能发生 NullpointException
            return  ContextLoader.getCurrentWebApplicationContext().getServletContext();
        }
    }
    

springMVC内置的编码过滤器

  • 代码

    <!-- 编码过滤器 -->
    <filter>
        <filter-name>charsetFilter</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>charsetFilter</filter-name>
        <!-- 只对springmvc处理的请求进行编码过滤 -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

springMVC响应JSON

xml配置的形式

  • XML配置形式,是修改了默认结果处理,全局生效的。使用注解@ResponseBody,标识该方法返回的内容不是URL地址,而是一个消息,使用输出对象输出给客户端。

    @RequestMapping

    1. name : 方法映射名称

    2. value/path : 方法访问路径

    3. method : 支持的请求的方法类型:

    4. GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE

    5. params : 请求必须有的参数

    6. headers : 请求必须有的头信息

    7. consumes : 指定请求的数据格式类型 : 普通文本、表单格式、JSON

    8. produces : 指定返回数据的格式类型 :JSON 、HTML

注解

  • SpringMVC中,有内置的转换器,但是SpringMVC内置的转换器需要相关jar包,需要jackson的jar包。只需要导入jar包后,使用**@ResponseBody标识返回的数据是消息即可。SpringMVC自动将返回数据当做JSON字符串处理,并且消息按照UTF-8进行编码。如果@ResponseBody放在类上,类中所有的方法都返回的是消息,不是视图资源。**

    注意: @RestController

    是 @ResponseBody, @Controller 结合,标识该类是一个处理器,并且处理器中所有的方法都返回的是消息。

文件的上传和下载

文件上传

  • 导包

    <!-- 文件上传相关依赖jar  -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    </dependency>
    
  • 配置文件上传的解析器

    <!-- 文件上传的解析器 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
    
  • 处理器

     @Controller
    @RequestMapping("/fileUpload")
    public class FileUploadController {
    
           @RequestMapping("")
           @ResponseBody
           // MultipartFile 从servlet 3.0 以后支持文件上传 参数的名称 必须和 表单中的上传控件的名称 保持一致
           public String  upload(MultipartFile file){
    
               /**
                * 文件上传考虑的问题:
                *   文件上传的路径的问题
                *   上传文件的大小问题
                *   上传文件的类型
                *   上传文件的名称设置
                *
                * 问题解决方案:
                *    文件上传的路径的问题  (1.项目的根目录下面(使用少),2.使用第三方的图片服务器(阿里,七牛云...))
                *    上传文件的大小问题   通过上传的文件可以获取到文件的真实大小,可以达到限制的目的
                *    上传文件的类型问题   通过上传的文件可以获取到文件的类型。进而进行判断
                *    上传文件的名称问题   通过时间戳 或者 UUID 来随机生成文件的新的名称达到唯一的目的
                *
                */
               String originalFilename = file.getOriginalFilename();
               System.out.println("文件的原始名称 = " + originalFilename);
               System.out.println("文件的大小 (字节)= " + file.getSize());
               System.out.println("文件的类型 = " + file.getContentType());
               try {
                   File newFile = new File("D://" + file.getOriginalFilename());
                   Font font = new Font("微软雅黑", Font.BOLD, 15); // 字体的属性设置
                   // 文件上传的方法
                   ImgUtil.pressText(file.getInputStream(), new FileOutputStream(newFile), "我是水印", Color.RED, font, 1, 1, 0.5f);
    
               }catch (Exception e){
                    e.printStackTrace();
               }
    
               return "文件上传成功";
           }
    }
    

文件下载

  • 方法一

  • 文件下载的本质,就是获取服务器的文件数据信息,使用字节流将数据传递给客户端。

    1. 使用输入流,将磁盘文件读到内存中。

    2. 使用网络输出流,将数据输出给客户端。

    @Controller
    @RequestMapping("/download")
    public class DownloadController {
    
    
         @RequestMapping("")
        public void download(String fileName, HttpServletResponse response) throws Exception {
             // 准备要下载的文件
             File file = new File("d://" + fileName);
             //设置响应类型 通知浏览器 不要打开
             response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
             // 通过设置头信息 不让浏览器把图片的名称 后缀名进行修改
             String name = URLEncoder.encode(fileName, "UTF-8");
             System.out.println("编码后的名字:"+name);
             response.setHeader("Content-Disposition", "attachment; filename="+name);
    
             // 把下载的文件转成流对象
             FileInputStream fileInputStream = new FileInputStream(file);
             // 获取输出流
             ServletOutputStream outputStream = response.getOutputStream();
             int len = 0;
             byte[] buffer = new byte[1024];
             while ((len=fileInputStream.read(buffer))!=-1){
                  outputStream.write(buffer,0,len);
                  outputStream.flush();
             }
             outputStream.close();
             fileInputStream.close();
         }
        /**
         *  现实开发中,注意点:
         *  1. 下载需要限制
         *  2. 下载的源头是服务器地址 (文件上传的操作
         *   (把文件上传到云服务器以后,然后云服务器会返回一个url地址(图片在云服务器上的真实地址),
         *    后 台程序拿到图片的真实地址和图片的源文件名称 一同 存入到数据库中)
         *
         */
    
    }
    
  • 方法二

  • springMVC中为了简化文件的下载,封装了实体类:ResponseEntity,可以将文件数据封装在ResponseEntity中。

    package com.abc.controller;
    
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
      
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.net.URLEncoder;
    
    /**
     * @Description: 文件下载示例
     */
    @Controller
    @RequestMapping("download")
    public class DownLoadController {
    
        @RequestMapping("download2.do")
        public ResponseEntity download() throws Exception {
            File file = new File("E:\\猪图片.jpg");//本地磁盘文件
            //使用输入流  将文件读入内存中
            FileInputStream fis = new FileInputStream(file); // 找参照物   将程序运行的内存当做参照物
            //文件的大小
            long length = file.length();
            //创建一个和文件一样大小的字节数组  一次性将数据读入到内存中  如果文件量过大,请求比较频繁 存在 崩溃的风险
            byte[] fileByte = new byte[(int) length];//一个G 的   byte 数组  64 G    65个人下载   1分钟
            //一次性将文件数据读入到数组中
            fis.read(fileByte);
            // springmvc 中封装的 响应实体 :
            //设置响应码
            //设置响应的数据头信息
            //设置响应数据
            HttpHeaders headers = new HttpHeaders(); //响应头信息
            //设置响应的数据为流数据  告诉浏览器 不要解析
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            //设置下载的文件的名称  中文字符串要进行编码
            headers.setContentDispositionFormData("filename","猪图片.jpg");
            ResponseEntity responseEntity = new ResponseEntity(fileByte,headers, HttpStatus.OK);
            return responseEntity;
        }
    }
    

springMVC拦截器

使用拦截器

  • 创建类实现HandlerInteceptor接口

    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @Description: 自定义拦截器
     */
    public class MyInterceptor implements HandlerInterceptor {
        /**
         * 在执行具体的Handler中的方法前执行
         * @param request  当前的请求对象
         * @param response  当前的响应对象
         * @param handler   具体的处理器中将要执行的方法
         * @return   boolean  如果 返回 true 则执行Handler中的方法   false 则 不执行handler中的方法
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("我是拦截器中preHandle 方法");
            return true;
        }
        /**
         *  在具体的Handler中的方法执行完成 但是没有做具体的视图解析操作
         * @param request  当前 请求对象
         * @param response  当前响应对象
         * @param handler 具体的处理器中将要执行的方法
         * @param modelAndView 具体的处理器中将要执行的方法 返回的结果
         * @throws Exception
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("我是拦截器中postHandle 方法");
            //在具体的解析之前  修改视图模型中数据
            modelAndView.addObject("name","韩梅梅");
        }
        /**
         *  完成了视图解析后 整个方法执行完成调用的方法 在finally中调用 或者出现异常也会调用
         * @param request   当前请求对象
         * @param response  当前响应对象
         * @param handler   具体的处理器中将要执行的方法
         * @param ex         具体的处理器中抛出的异常
         * @throws Exception
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("我是拦截器中afterCompletion 方法");
            System.out.println(ex.getMessage());
            request.setAttribute("msg","网管去火星了!");
            request.getRequestDispatcher("/500.jsp").forward(request,response);
        }
    }
    
  • 配置拦截器

    <?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.bjpowernode.controller" />
        <!-- 开启 mvc注解 -->
        <mvc:annotation-driven></mvc:annotation-driven>
    
        <!-- 文件上传的解析器 -->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
            <!--<property name="maxUploadSize" value="1"></property>-->
        </bean>
        <!-- 配置springmvc的拦截器 -->
        <mvc:interceptors>
    
            <mvc:interceptor>
                <!-- 拦截器的资源路径  /** 拦截所有  ** 表示多层目录 -->
                <mvc:mapping path="/**"/>
                <!-- 不拦截的资源路径 -->
                <mvc:exclude-mapping path="/user/page.do"/>
                <!-- 自定义的拦截器 -->
                <bean id="myInterceptor" class="com.bjpowernode.interceptor.MyInterceptor" />
            </mvc:interceptor>
        </mvc:interceptors>
    </beans>
    

Interceptor和Filter的区别

  • Filter是Java Web的规范,拦截的是请求,任何JavaWeb项目都可以有Filter,但是Interceptor是SpringMVC提供的一套规范HandlerInterceptor,只适用SpringMVC自身,并且只能对DispatherServlet处理器的请求生效,拦截的方法。从范围上说,任何JavaWeb项目都有Filter,但是未必有Interceptor。

同一异常处理器

  • 在spring中,相对完善的异常处理器机制,spring可以自己定义处理异常的规则,这种处理异常规则的程序,就被称之为异常处理器。其实,异常处理器就是对controller的增强,因为异常是向上抛,controller调用service,service调用mapper,controller属于最上层,所以最终异常都会汇集到controller。因此,spring提供了@ControllerAdvice注解,表示对controller增强类。并且还提供了@ExceptionHandler这个注解,当发生异常时,该注解修饰的方法就会执行

  • 在实际开发中,异常主要分为两类:

    1. 系统异常,JDK中定义的异常
    2. 业务异常,开发者自己定义的异常
  • 一般是将系统异常转化为业务异常,开发者只处理业务异常。使用try…catch…将代码包裹起来,在catch中抛出自定义的异常,这种方案就是将系统异常转化为业务异常。因为很多数据操作,事务需要异常进行数据回滚。

  • 例子

     // 当密码出现问题的时候 ,使用
    public class MyPasswordException extends  RuntimeException{
    
        public MyPasswordException(String msg){
            super(msg);
        }
    }
    @Repository
    public class UserMapper {
    
    
        public boolean select(String name, String pwd) {
    
            int i = 1/0;
    
            if (name.equals("root")&&pwd.equals("123")) {
                 return true;
            }
            return false;
        }
    }
    @Service
    public class UserServiceImpl  implements UserService {
    
        @Autowired
        private UserMapper userMapper;
    
    //    @Override
    //    public boolean login(String name, String pwd) {
    //
    //        if (name==null || name.equals("")) {
    //              throw  new RuntimeException("账号不能为空");
    //        }
    //
    //        if (pwd==null || pwd.equals("")) {
    //            throw  new RuntimeException("密码不能为空");
    //        }
    //
    //       try {
    //          return userMapper.select(name,pwd);
    //       }catch (Exception e){
    //           e.printStackTrace();
    //           return false;
    //       }
    //
    //    }
    
        @Override
        public void login(String name, String pwd) {
    
            if (name==null || name.equals("")) {
                throw new MyAccountException("账号不能为空");
            }
    
            if (pwd==null || pwd.equals("")) {
                throw  new MyPasswordException("密码不能为空");
            }
            boolean result =  userMapper.select(name,pwd);
            if (!result){
                throw  new MyLogicException("账号或者密码错误");
            }
    
    
        }
    }
    public interface UserService {
        void login(String name, String pwd);
    }
    
    // 统一异常的处理类
    @ControllerAdvice
    public class MyControllerAdvice {
    
    
           @ExceptionHandler(MyAccountException.class)
           @ResponseBody
          public String accountException(MyAccountException exception){
                return  exception.getMessage();
           }
    
        @ExceptionHandler(MyPasswordException.class)
        public String pwdException(MyPasswordException exception){
            return  "500";
        }
    
        @ExceptionHandler(MyLogicException.class)
        public String logicException(MyLogicException exception){
            return  "redirect:/login.html";
        }
    
        @ExceptionHandler(RuntimeException.class)
        @ResponseBody
        public String runtimeException(RuntimeException exception){
            return  "程序崩溃";
        }
    
    
    
    }
    
    @Controller
    @RequestMapping("/loginUser")
    public class LoginController {
    
        @Autowired
        private UserService userService;
    
         @RequestMapping("")
         @ResponseBody
        public String login(String name,String pwd) {
    
             userService.login(name,pwd);
    
    
             return "登录失败";
         }
    }
    

Restful风格

Http协议设计的初衷

  • HTTP协议在设计时,期望使用一个URL表示一个资源。然后,根据不同的动作:GET、POST、PUT、DELETE等等表示对一个资源的各种操作。

    如:

    1. 获取这个资源就使用GET,

    2. 修改这个资源PUT,

    3. 删除这个资源用DELETE,

    4. 创建这个资源使用POST。

  • 但是在实际使用中,多个URL表示一个资源,例如:新增用户: addUser.do,修改用户:updateUser.do,删除用户:deleteUser.do,查询一个用户:getUser.do。这时候,出现一个资源存在多个URL。在一定程度声,违背了HTTP协议的设计初衷,并且命名也是个问题。

Restful设计思想

  1. 使用一个URL表示一种资源

  2. 客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源

  3. 通过操作资源的表现形式来操作资源

在SpringMVC中使用Restful

  • SpringMVC也支持Restful风格,但是目前存在一些问题。主要不是很好支持PUT请求,没有办法获取到PUT请求的数据。

将DispatherServlet的映射地址改为

  • 由于将DispatherServlet的映射路径,改为了/,则所有的请求都由DispatherServlet处理,静态的资源文件不在处理器映射器中,会出现404。并拦截器拦截DispatherServlet中调用Handler中的方法,改为/,则所有的请求都会被拦截。

    则需要在SpringMVC的核心配置文件中,新增启用默认的Servlet的处理器。

    <!-- 启用默认Servlet -->
    <mvc:default-servlet-handler/>
    

    并且注意,配置拦截器时,将静态资源不进行拦截,要排除:

    <mvc:exclude-mapping path="/resouces/**"/>
    

配置处理PUT请求的拦截器

  • 代码

    <!-- 处理put请求的拦截器 -->
    <filter>
      <filter-name>restful</filter-name>
      <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>restful</filter-name>
      <servlet-name>springmvc</servlet-name>
    </filter-mapping>
    

处理器代码

  • 代码

    package com.abc.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @Controller
    @RequestMapping("user")
    @ResponseBody
    public class UserController {
    
    //    @RequestMapping(method = RequestMethod.GET)
    //    //@GetMapping
    //    public Object get(Integer id){
    //        System.out.println("get请求");
    //        Map data = new HashMap<>();
    //        data.put("code",200);
    //        data.put("msg","get请求");
    //        return  data;
    //    }
        @GetMapping("{id}/{name}") //后台的获取方式 使用 {} 进行包裹  并且 在方法参数中 使用@PathVariable
        // 这种将参数拼接在URL上的方式 只支持 GET 请求和 DELETE请求
        public Object get1(@PathVariable("id") Integer id,@PathVariable("name") String name){
            System.out.println("get请求");
            System.out.println(id);
            System.out.println(name);
            Map data = new HashMap<>();
            data.put("code",200);
            data.put("msg","get请求");
            return  data;
        }
        //@RequestMapping(method = RequestMethod.PUT)
        @PutMapping
        public Object put(Integer id){
            System.out.println("PUT请求");
            Map data = new HashMap<>();
            data.put("code",200);
            data.put("msg","PUT请求");
            return  data;
        }
    
        @RequestMapping(method = RequestMethod.POST)
        //@PostMapping
        public Object post(Integer id){
            System.out.println("post请求");
            Map data = new HashMap<>();
            data.put("code",200);
            data.put("msg","post请求");
            return  data;
        }
    
        @RequestMapping(method = RequestMethod.DELETE)
        @DeleteMapping
        public Object delete(Integer id){
            System.out.println("delete请求");
            Map data = new HashMap<>();
            data.put("code",200);
            data.put("msg","delete请求");
            return  data;
        }
    }
    

页面代码

  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <button id="btn1">get</button>
        <button id="btn2">put</button>
        <button id="btn3">delete</button>
        <button id="btn4">post</button>
        <button id="btn5">get-1111</button>
        <script src="resources/jquery.js"></script>
        <script>
            $("#btn1").click(function () {
                $.get("user",{id:1001},function (rs) {
                    console.log(rs);
                })
            });
            $("#btn2").click(function () {
               $.ajax({
                   url:"user",
                   type:"PUT",
                   param:{id:1001},
                   success:function (rs) {
                       console.log(rs);
                   }
               })
            });
            $("#btn3").click(function () {
                $.ajax({
                    url:"user",
                    type:"DELETE",
                    param:{id:1001},
                    success:function (rs) {
                        console.log(rs);
                    }
                })
            });
            $("#btn4").click(function () {
                $.post("user",{id:1001},function (rs) {
                    console.log(rs);
                })
            });
            $("#btn5").click(function () {
                //将参数 拼接在URL上面
                $.get("user/11111/lucy",function (rs) {
                    console.log(rs);
                })
            });
        </script>
    </body>
    </html>
    

文章作者: 勾魂大猩猩
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 勾魂大猩猩 !
  目录