三、SpringMVC

SpringMVC

  • MVC开发模式:人为的将Web项目分成三层,即Model数据模型层,Controller控制器层,View视图层,分层开发的好处是可以让项目各个组件之间解耦合,便于项目的维护

  • SpringMVC是Spring家族的一款专注于解决控制器层问题的框架产品,相比于Struts2做控制器,SpringMVC的开发效率更高,与Spring框架的集成更自然,SpringMVC天生倡导注解式开发

  • 控制器层解决的问题

    • 接受客户端请求参数
    • 进行数据类型的转换
    • 调用业务功能
    • 流程跳转
    • 使用作用域传递和管理数据
  • Struts2和StringMVC做控制器的不同

    Struts2SpringMVC
    入口不同监听器StrutsPrepareAndExecuFilterServlet的DispatcherServlet
    返回值类型必须为String,代表一个跳转视图多种返回值类型,还可以书写一个跳转的视图
    接收参数成员变量接收参数通过方法入参接收请求参数
  • SpringMVC做控制器

    @Controller
    @RequestMapping("")		//相当于namespace
    class XXXController{
    	@RequestMapping("/")	//相当于actionName
    	public String ma(方法接收请求参数){
    		//调用service
    		
    		//返回值完成视图跳转
    		return "index";	//index.jsp		也可以直接书写跳转视图
    	}
    }
    
  • SpringMVC开发控制器

    • 引入依赖:在Spring之前依赖基础上加上spring-webmvc

    • 引入配置文件:SpringMVC的配置文件,就是Spring的配置文件基础上引入mvc的文件头(xsd);文件名没有要求,放置位置也没有强制要求

      xmlns:mvc="http://www.springframework.org/schema/mvc"
      
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd
      
    • 初始化配置:配置一个核心入口的Servlet

    • 可以手动在web.xml中配置指定的servlet在服务器启动的时候初始化,提高效率

    • 一个Servlet实现类在服务器中只会创建一个对象,当请求一个Servlet的时候,服务器会检查一下看看当前内存中会否有当前请求的Servlet,如果有直接使用,没有则创建新的

      //web.xml
      <!--配置核心入口的Servlet-->
      <servlet>
              <servlet-name>mvc</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <!--指定springMVC配置文件的位置,param-name为定值,value为文件位置-->
              <init-param>
                  <param-name>contextConfigLocation</param-name>
                  <param-value>classpath:spring-mvc.xml</param-value>
              </init-param>
              <!--启动服务器创建入口Servlet
              非负数,启动服务器即创建,多个servlet在启动服务器创建时可以通过设置不同的整数值设置创建的优先级,值越小越先创建-->
              <load-on-startup>1</load-on-startup>
          </servlet>
          <servlet-mapping>
              <servlet-name>mvc</servlet-name>
              <!--url-pattern配置为*.do:来自客户端所有的以.do结尾的请求都会先经过DispatcherServlet进行处理-->
              <url-pattern>*.do</url-pattern>
      </servlet-mapping>
      
    • 编写控制器类

      @Controller //指定当前类是一个控制器类
      @RequestMapping("/hello") //相当于配置一个namespace
      public class HelloController {
          // 编写处理请求的方法
          //http://localhost:8989/项目名/hello/helloWorld.do
          @RequestMapping("/helloWorld.do")
          public String helloWorld() {
              System.out.println("hello world");
              return "index";		//index.jsp	如果仅仅以一个字符串而不是视图的路径方式跳转,需要配合视图解析器使用
          }
      }
      
    • 编写spring-mvc.xml

      <!--开启mvc注解驱动-->
      <mvc:annotation-driven />
      <!--开启Spring的注解扫描-->
      <context:component-scan base-package="com.mo.controller"></context:component-scan>
      <!--配置一个SpringMVC提供的视图解析器
      nternalResourceViewResolver:内部资源视图解析器,主要用于匹配和解析控制方法执行完毕跳转的
      视图解析器由SpringMVC调用,所以id必须是viewResolver-->
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
              <!--匹配成 / index 前缀-->
              <property name="prefix" value="/"></property>
              <!--匹配成 / index .jsp 后缀-->
              <property name="suffix" value=".jsp"></property>
      </bean>
      
  • @RequestMapping注解说明

    • 用在类上相当于namespace的含义,可以将不同类型的控制器进行分类管理

    • 用在类上相当于namespace,用在方法上相当于actionName

    • 请求方式的限定

      @RequestMapping(
      	value = "请求路径映射",
      	method = { RequestMethod.post / RequestMethod.get };	//可以约束当前控制器只能处理什么类型的请求,可以两个都写,用逗号隔开,可以同时处理get和post;与什么都不写的区别是,什么都不写是能处理所有的请求
      	
      )
      
  • 获取Servlet原生API

    • 方法入参注入

      @RequestMapping("/test0.do")
      public String test0(HttpServletRequest request){
              // 获取请求参数
              String name = request.getParameter("name");
              System.out.println("test0"+name);
              // 向request作用域存数据
              request.setAttribute("name",name);
              return "success";
      }
      
    • 成员变量自动注入

      public class GetServletAPI {
          @Autowired
          private HttpServletRequest request;
      }
      
    • 常用API:SpringMVC没有提供ServletContext方法入参的获取,需要手动获取

      @RequestMapping("/ma.do")
      public String ma(HttpServletRequest request, HttpServletResponse response,
                           HttpSession session) throws IOException {
              //使用request获取参数
              String name = request.getParameter("name");
              System.out.println(name);
              PrintWriter out = response.getWriter();
              System.out.println(out);
              // 向session作用域存入数据
              session.setAttribute("user","mo");
              //SpringMVC没有提供ServletContext方法入参的获取,需要手动获取
              ServletContext servletContext = session.getServletContext();
              servletContext.setAttribute("nickName","hang");
              //跳转
              return "index";
      }
      
  • SpringMVC的控制器类是单例的,开发中不允许定义保存数据状态的成员变量

SpringMVC中跳转控制
  • SpringMVC是通过控制器方法到返回值控制跳转

  • controller到jsp跳转

    • 请求转发:forward:/路径/xxx.jsp(相对于项目的根目录开始)

      @Controller
      @RequestMapping("/jump")
      public class JumpController {
          @RequestMapping("/test0.do")
          public String test0(){
              System.out.println("test0 controller");
              //转发跳转到一个jsp
              return "success";
          }
      }
      
      @Controller
      @RequestMapping("/jump")
      public class JumpController {
          @RequestMapping("/test0.do")
          public String test0(){
              System.out.println("test0 controller");
              //转发跳转到一个jsp,不需要匹配视图解析器
              return "forward:/success.jsp";
          }
      }
      
    • 请求重定向:redirect:/路径/xxx.jsp(相对于项目的根目录开始)

      @Controller
      @RequestMapping("/jump")
      public class JumpController {
          @RequestMapping("/test0.do")
          public String test0(){
              System.out.println("test0 controller");
              // 重定向跳转到jsp
              return "redirect:/success.jsp";
          }
      }
      
  • contorller到controller跳转

    • 请求转发:forward:/包名/xxx.do

      @RequestMapping("/test1.do")
      public String test1(){
              System.out.println("test1 controller");
              // 请求转发到另一个控制器
              return "forward:/jump/test2.do";		//同包跳转也必须加上包名
      }
      
    • 请求重定向:redirect:/包名/xxx.do

      @RequestMapping("/test1.do")
      public String test1(){
              System.out.println("test1 controller");
              // 请求重定向到另一个控制器
              return "redirect:/jump/test2.do";		//同包跳转也必须加上包名
      }
      
SpringMVC中的收参机制
  • 通过控制器方法的入参形式接收请求参数,基本的要求是请求参数的key与方法入参的变量名一致(八种基本数据类型和字符串)

  • 八种基本数据类型和字符串

    • 关于接收的请求参数:

      • 基本数据类型发送了对应的key,但没有赋值时为null;如果没有发送指定的请求参数,那么不管是基本数据类型还是字符串都是null
      • 发送了对应的key的请求但是没给值为空字符串
      <form action="${pageContext.request.contextPath }/param/test0.do" method="post">
          username:<input type="text" name="username"/><br />
          password:<input type="password" name="password"/><br/>
          age:<input type="text" name="age"><br />
          <input type="submit" value="regist" />
      </form>
      
      @Controller
      @RequestMapping("/param")
      public class ParamController {
          //八种基本数据类型和字符串类型
          @RequestMapping("/test0.do")	//当jsp页面参数name不同时,用注解RequestParam赋值
          public String test0(@RequestParam("username")String name, String password,Integer age) {
              System.out.println("name:" + name);
              System.out.println("password:" + password);
              System.out.println("age:" + age );
              return "redirect:/success.jsp";
          }
      }
      
      • @RequestParam注解

        @RequestParam(
        	value = "key",		//请求时参数name的值
        	required = "true",	//代表请求时参数中必须有一个参数的name为key,false时可以不传值,默认为true,此时必须传递参数name,不传参则报错
        	defaultValue = "mo"		//默认值,当传递的请求参数为空,没有赋值或根本就没有传递对应的请求参数时,取默认值,即使required=true时,没有发送也不会报错;传值时取传过来的
        )
        
  • 日期类型默认接收的格式:yyyy/MM/dd

    • 自定义接收日期的格式:@DateTimeFormat(pattern = “yyyy-MM-dd”)

      //自定义接收日期的格式
      //底层还是使用的new SimpleDateFormat("yyyy-MM-dd")
      @RequestMapping("/test1.do")
      public String test1(@DateTimeFormat(pattern = "yyyy-MM-dd")Date birthday){
              System.out.println(birthday);
              return "redirect:/success.jsp";
      }
      
  • 自定义对象类型参数

    public class User implements Serializable {
        private String username;
        private String password;
        @DateTimeFormat(pattern = "yyyy-MM-dd")	//自定义对象类型日期格式需要在实体类中加注解指定接收格式,也可以使用默认yyyy/MM/dd
        private Date birthday;
        private List<String> hobby;	//与前台jsp对应相同即可
    }
    //name对应实体类属性
    <form action="${pageContext.request.contextPath }/param/test2.do" method="post">
        username:<input type="text" name="username"/><br />
        password:<input type="password" name="password"/><br/>
        birthday:<input type="text" name="birthday"/><br />
          
        hobby:<input type="checkbox" value="eat" name="hobby">吃饭
        <input type="checkbox" value="sleep" name="hobby">睡觉
        <input type="checkbox" value="hitBee" name="hobby"/>打豆豆
        <input type="submit" value="regist" />
    </form>
    
    //自定义对象类型参数接收
    @RequestMapping("/test2.do")
    public String test2(User user,String username) {
            System.out.println("user:\t"+user);
            System.out.println("username:\t"+username);	//此时都给值
            return "redirect:/success.jsp";
    }
    
  • List集合类型

    • 泛型为八种基本数据类型和字符串

      <form action="${pageContext.request.contextPath }/param/test3.do" method="post">
          hobby:
          <input type="checkbox" value="eat" name="hobby">吃饭
          <input type="checkbox" value="sleep" name="hobby">睡觉
          <input type="checkbox" value="hitBee" name="hobby">打豆豆
          <br>
          Id:
          <input type="checkbox" value="1" name="ids">吃饭
          <input type="checkbox" value="2" name="ids">睡觉
          <input type="checkbox" value="3" name="ids">打豆豆
          <input type="submit" value="regist" />
      </form>
      
      
      //接收List集合
      @RequestMapping("/test3.do")
      public String test3(@RequestParam("hobby") List<String> strList,	//需要通过指定@RequestParam注解,让SpringMVC可以初始化list
              @RequestParam List<Integer> ids){
              System.out.println("=====字符串=====");
              if (strList!=null) {
                  strList.forEach(s -> System.out.println(s));
              }
              System.out.println("=====Integer类型=====");
              if (ids!=null) {
                  ids.forEach(i -> System.out.println(i));
              }
              return "redirect:/success.jsp";
      }
      
    • 泛型为自定义对象类型,用DTO封装为自定义对象类型来传参

    • DTO:data transfer object 数据传输对象

      public class UserDTO {
          private List<User> users;
          
      }
      //通过下标给每个对象封装值
      //成员变量名[index].集合泛型类型的属性名
      <form action="${pageContext.request.contextPath }/param/test4.do" method="post">
          注册:<br/>
          username:<input type="text" name="users[0].username">
          password:<input type="password" name="users[0].password"><br>
          <br>
          username:<input type="text" name="users[1].username">
          password:<input type="password" name="users[1].password"><br>
          <input type="submit" />
      </form>
      
      //接收泛型为自定义对象的List集合
      @RequestMapping("/test4.do")
      public String test4(UserDTO userDTO) {
              userDTO.getUsers().forEach(user -> System.out.println(user));
              return "redirect:/success.jsp";
      }
      
    • 处理POST请求提交的中文编码:使用SpringMVC中提供的编码过滤器

      <!-- web.xml -->
      <!--配置SpringMVC的编码过滤器-->
      <filter>
              <filter-name>encode</filter-name>
              <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
              <!--配置初始化参数,指定编码格式-->
              <init-param>
                  <!-- encoding为CharacterEncodingFilter中的一个参数名,不能更改 -->
                  <param-name>encoding</param-name>
                  <param-value>UTF-8</param-value>
              </init-param>
      </filter>
      <filter-mapping>
              <filter-name>encode</filter-name>
              <url-pattern>/*</url-pattern>
      </filter-mapping>
      
Spring+SpringMVC+mybatis整合
  • 搭建开发环境

    • 引入依赖
    • 引入配置文件
      • spring-core.xml
      • spring-mvc.xml
      • log4j.propertirs / jdbc.properties
      • mapper.xml
    • 初始化配置 - web.xml
      • 指定Spring工厂初始化的核心配置文件位置
      • 配置初始化Spring工厂的监听器
      • 配置SpringMVC核心入口Servler:DispatcherServlet
      • 配置编码过滤器
  • 编码

    • spring-core.xml中的配置信息:

      • 开启注解扫描,配置排除策略,暂时扫描除了控制器所在的包的其余全部

        <context:component-scan base-package="com.mo.*">
                <!--配置排除策略,spring核心配置文件不做Controller注解的扫描,由spring-mvc自己管理-->
                <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            </context:component-scan>
        
      • 引入小配置文件

      • 创建数据源

      • 创建SqlSessionFactory

      • 创建DAO实现类的对象

      • 开启事务注解驱动(DataSourceTransactionManager)

    • spring-mvc.xml中的配置信息

      • 扫描控制器的注释
      • 开启SpringMVC注释驱动
      • 配置视图解析器
    • 建表、封装实体类、编写DAO、编写Service、编写控制器

Spring和SpringMVC整合引发的事务失效
  • spring-core.xml属于父容器,不能使用子容器的对象,和继承是一样的思想
    • 管理dao对象
    • 管理service对象
    • 管理事务
  • spring-mvc.xml属于子容器,可以使用父容器的对象
    • 管理控制器对象
    • 此时如果在spring-mvc.xml中有dao和service时,就不会去获取父类器的对象,导致创建一个没有事务额外功能的原始类对象,不去获取代理类对象,使事务失效
SpringMVC的数据管理机制
  • 管理request作用域

    • Model(接口)机制:

      • model.addAttribute(String name,Object value);

      • model.addAllAttributes(Map<String,?> map); 将一个map中所有的键值对同时存入request作用域

        @RequestMapping("/test1.do")
        public String test1(Model model) {
                //使用model存数据到request中
                model.addAttribute("name", "yuan");
                
                // 同时向model中添加多个键值对
                Map<String, String> map = new HashMap<>();
                map.put("k1", "mo");
                map.put("k2", "yuan");
                model.addAllAttributes(map);
                return "success";
        }
        
      • model使用细节:重定向跳转时,model中的数据会自动拼接到地址栏传输

        @RequestMapping("/ma.do")
        public String ma(Model model) {
                Integer id = 10;
                model.addAttribute("id", id);
                //重定向跳转时,model中的数据会自动拼接到地址栏传输
                return "redirect:/emp/mb.do";
        }
        
    • ModelMap(类,本质为Map)机制,中间有一个转存:

      • modelMap.addAttribute(String name,Object value);

      • modelMap.addAlllAttributes(Map<String,?> attributes); 将一个map中所有的键值对同时存入request作用域

        @RequestMapping("/test2.do")
        public String test2(ModelMap modelMap) {
                //向request作用域中存入数据
                modelMap.addAttribute("name", "hang");
                
                // 同时向modelMap中添加多个键值对
                Map<String, String> map = new HashMap<>();
                map.put("k1", "mo");
                map.put("k2", "yuan");
                modelMap.addAllAttributes(map);
                return "success";
        }
        
  • 管理session作用域:常用原生的

    • @SessionAttributes:当次请求中model里面的命名属性,如果在@SessionAttributes里面指定了,那么会自动向session作用域存一份,此时reqeust作用域中也有

      @Controller
      @RequestMapping("/scope1")
      @SessionAttributes(value = {"name1","name2"})
      public class Scope1Controller {
          
      // 管理session作用域
      @RequestMapping("/test4.do")
      public String test4(Model model){
              // 向model中添加数据
              model.addAttribute("name1", "mo");
              model.addAttribute("name2", "yuan");
              return "redirect:/success.jsp";
      }
        
      //通过SessionAttributes与Model配合存入到session的数据,同包下的控制器方法中无法用invalidate或removeAttribute原生的清除,非同包可以使用invaludata或removeAttribute清除
      @RequestMapping("/test5.do")
      public String test5(SessionStatus sessionStatus){
              //session.removeAttribute(name)     移除session中的数据
              //session.invalidate();   //销毁session
              //使用sessionStatus去清理通过@SessionAttributes存入的数据
              sessionStatus.setComplete();
              return "ok";
      }
      
  • ModelAndView:可以同时管理数据和完成视图的跳转

    @RequestMapping(value="/test0.do")
    //@SessionAttributes({"name"})	//此时指定的name也会将modelAndView中的相同命名属性存入session作用域中一份
    public ModelAndView test0(){
            ModelAndView mv = new ModelAndView();
            //管理数据
            mv.addObject("name", "mo"); //存入了request作用域
            //设置跳转的视图
            // mv.setViewName("success");					//与之前方法返回值类型为String时,
            mv.setViewName("redirect:/success.jsp");	//返回值的写法格式一致
            return mv;
    }
    
    • request:存的事一次请求需要的数据
    • session:存的是一次会话需要的数据,通常只存储与用户相关的信息
SpringMVC与Ajax进行交互
  • SpringMVC天生支持jackson工具的使用

    • 使用@ResponseBody注解完成响应数据的格式转换;ResponseBody注解会自动使用jackson工具,将返回的java对象转换成json

      @Controller
      @RequestMapping("/ajax")
      public class AjaxController {
          @RequestMapping("/test0.do")
          @ResponseBody   //解析响应体的内容,此时返回的字符串不再是跳转的视图,而是个字符串响应到客户端
          public String test0(){
              return "ok";
          }
          
          @RequestMapping("/test1.do")
          //ResponseBody注解会自动使用jackson工具,将返回的java对象转换成json
          @ResponseBody
          public User test1(){
              User user = new User(1, "mo", "LoveMe");
              return user;		//返回json
          }
          @RequestMapping("/test2.do")
          public @ResponseBody List<User> test2(){
              List<User> users = Arrays.asList(
                      new User(1, "mo", "LoveMe"),
                      new User(2,"yuan","123456")
              );
              return users;		//返回json
          }
          //响应一个代表执行状态的字符串,这种方式响应结果描述性更强,可以响应回去状态码
          @RequestMapping("/test3.do")
          public @ResponseBody Map<String,String> test3(){
              Map<String,String> status = new HashMap<>();
              status.put("status","ok");
              return status;
          }
      }
      
      • @RequestBody接收json格式的数据,在参数处使用

        $(function () {
                    var jsObj = {username:'mo',password:'LoveMe'}//js对象
                    //将js对象转换成json字符串
                    var jsonStr = JSON.stringify(jsObj);
                    $('#btn').click(function () {
                        $.ajax({
                            url: '${pageContext.request.contextPath }/ajax/test4.do',
                            type: 'post',   //此时数据较多,不能用get
                            data: jsonStr,    //请求参数数据为json字符串
                            contentType:'application/json', //指定本次的请求参数为json字符串格式
                            dataType: 'json',  //如果使用这个属性,则服务端响应端的结果必须是JSON字符串
                            success: function (result) {
                                console.log(result);
                            }
                        });
                    });
        });
        //发送普通字符串,接收json
        $('#btn').click(function(){
                  $.ajax({
                            url:'${pageContext.request.contextPath }/ajax/test0.do',
                            type:'get',
                            data:'name=mo',
                            dataType: 'json',  //如果使用这个属性,则服务端响应端的结果必须是JSON字符串
                            success: function (result) {
                                console.log(result);
                            }
                   });
        });
        
        //接收json格式数据,自动使用Java对象接收
        @RequestMapping("/test4.do")
        @ResponseBody
        public String test4(@RequestBody User user){	//RequestBody
                System.out.println(user);
                return "ok";
        }
        
文件的上传
  • 编写客户端表单

    post:通常数据量大
    enctype:会将表单中传递的文件以二进制编码的方式发送到服务器;原始类型会以key-value字符串发送
    <form action="" method="post" enctype="multipart/form-data">
        <input type="file" name="upFile">
        <input type="submit" value="上传">
    </form>
    
  • 服务器接收上传的文件

  • MultipartFile upFile:上传的文件,所有的内容保存的对象

    @Controller
    @RequestMapping("file")
    public class FileController {
        /**
         * @param upload:获取上传文件的信息对象
         * @return
         */
        @RequestMapping("upload.do")
        public String upload(MultipartFile upload, HttpSession session) throws IOException {
            //控制台输出上传文件的几个原信息
            System.out.println("上传文件名:"+upload.getOriginalFilename());
            System.out.println("上传文件类型:"+upload.getContentType());
            System.out.println("上传文件字节数:"+upload.getBytes());
            
            //将上传文件保存到服务器的指定位置
            //获取绝对路径
            String realPath = session.getServletContext().getRealPath("/upload");
            //获取文件名后缀
            String extension = FilenameUtils.getExtension(upload.getOriginalFilename());
            //使用uuid+后缀拼接新名字
            UUID uuid = UUID.randomUUID();
            String newFileName = uuid.toString() + "." + extension;
            
            upload.transferTo(new File(realPath+"/"+newFileName));
        
            return "success";
        }
    }
    
  • 在spring-mvc.xml配置上传文件解析器

    <!--配置上传文件的解析器
    id需要让Spring获取,所有不能更改-->
    <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
            <!--设置上传文件大小,默认值sizeMax=-1,大小不限-->
            <property name="maxUploadSize" value="20971520"></property>
    </bean>
    
文件的下载
  • 客户端

    <a href="${pageContext.request.contextPath }/file/download.do">下载</a>
    
  • controller

    @RequestMapping("download.do")
    public void download(HttpServletResponse response,HttpSession session) throws IOException {
            //获取待下载的文件
            // FileInputStream in = new FileInputStream("/Users/hang/IdeaProjects/prepare/springMVC/springMVC_day2/target/springMVC_day2/upload/5.jpg");
            // 优化
            String realPath = session.getServletContext().getRealPath("/upload");
            FileInputStream in = new FileInputStream(realPath + "/a.jpg");
            //下载
            // 设置相应文件的类型
            response.setHeader("context-type","image/jpg");
            //设置下载文件在客户端打开的方式
            /**
             *  attachment;filename=5.jpg以附件的形式下载到客户端
             *   inline代表在浏览器窗口打开
             */
            response.setHeader("context-disposition","inline;filename=a.jpg");
            OutputStream out = response.getOutputStream();
            //调用工具类,把in的内容用out写出去
            IOUtils.copy(in,out);
    }
    
验证码
  • 判断操作的对象是否是一个真正的人

  • 使用工具类生成

    //用流响应结果,返回值void
    @RequestMapping("generate.do")
     public void generate(HttpSession session, HttpServletResponse response) throws IOException {
            // 获取验证码随机数
            String securityCode = SecurityCode.getSecurityCode();
            // 存入session作用域做验证使用
            session.setAttribute("securityCode",securityCode);
            // 生成验证码图片
            BufferedImage image = SecurityImage.createImage(securityCode);
            // 响应到客户端,专门用来处理图片的工具类
            ImageIO.write(image, "png", response.getOutputStream());
    }
    
拦截器实现
  • 主要作用:在请求到达目标控制器之前执行,可以拦截请求。通常会将多个控制器中的共性冗余的代码提取到拦截器中定义,提高代码的复用性

  • 实现接口HandlerInterceptor

    public class MyInterceptor1 implements HandlerInterceptor {
        /**
         * @param request:请求对象
         * @param response:响应对象
         * @param handler:处理器对象
         * @return  true:继续向下执行       false:拦截住请求,不继续向下执行,可以做重定向处理
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("从多个controller中提取到的冗余代码");
            //返回值代表请求是否继续向下执行
            //true:继续向下执行       false:不继续向下执行
            return true;
        }
    }
    
  • 配置spring-mvc.xml,同时排除不需要拦截的控制器(controller)

    <!--配置拦截器-->
    <mvc:interceptors>
          <!--一对<mvc:interceptor>标签代表配置一个拦截器-->
          <mvc:interceptor>
                <!--拦截所有的请求-->
                <!--<mvc:mapping path="/**"/>-->
    
                <!--拦截指定namespace下的-->
                <mvc:mapping path="/target/*"/>
            
                <!--在拦截的范围中,排除指定的不拦截的-->
                <mvc:exclude-mapping path="/target/test0"/>
                <!--指定拦截器实现类-->
                <bean class="com.mo.inteceptor.MyInterceptor1"></bean>
          </mvc:interceptor>
    </mvc:interceptors>
    
  • 拦截器使用技巧

    • 拦截器中做重定向

      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("从多个controller中提取到的冗余代码");
              String name = request.getParameter("name");
              if (name == null) {
                  //进行重定向
                  response.sendRedirect(request.getContextPath() + "/error.jsp");
                  return false;
              } else {
                  //返回值代表请求是否继续向下执行
                  //true:继续向下执行       false:不继续向下执行
                  return true;
              }
      }
      
全局异常的处理
  • 自定义异常的实现

    • 自定义非运行时异常,继承Exception
    • 自定义运行时异常,继承RuntimeException
  • 实现全局异常处理,把异常统一处理到一个位置,让开发人员专注业务开发

    public class UsernameErrorException extends RuntimeException {
        public UsernameErrorException(String message) {
            super(message);
        }
    }
    
    //当程序中抛出异常时,会自动执行这个全局异常处理器
    public class GlobalExceptionHandler implements HandlerExceptionResolver {
        //参数 e 为捕捉到的异常信息对象
        @Override
        public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
            ModelAndView mv = new ModelAndView();
            //在打印台输出,便于纠正
            e.printStackTrance();
            if (e instanceof UsernameErrorException) {
                //打印获取异常信息
                //System.out.println(e.getMessage());
                mv.setViewName("redirect:/error.jsp");
            } else if(e instanceof Exception){
                mv.setViewName("redirect:/error.jsp");
            }
            //注意返回值
            return mv;
        }
    }
    //spring-mvc.xml
    <!--配置全局异常处理的实现类-->
    <bean class="com.mo.exception_handler.GlobalExceptionHandler"></bean>
    
    //使用自定义异常
    @RequestMapping("login.do")
        public String login(String username,String password){
            if(!("mo".equals(username))){
                throw new UsernameErrorException("用户名错误");
            }
            return "success";
        }
    
web.xml的第二种开发方式
<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:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <!--匹配以 .do结尾的-->
        <!--<url-pattern>*.do</url-pattern>-->
        <!--匹配所有请求,处理不了html等静态资源,此时需要在配置文件中添加排除静态资源-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</servlet>

<!-- spring-mvc.xml -->
<!-- 排除静态资源不让入口Servlet处理(当路径风格为 / 时配置) -->
<mvc:default-servlet-handler />
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值