Spring MVC 复习

概念

三层架构 将整个业务应用划分为三层

​ 表现层:用来和客户端进行数据交互,一般采用MVC设计模式

​ 业务层:处理公司具体业务逻辑

​ 持久层:用来操作数据库

MVC模型 Model View Controller模型视图控制器

​ Model:数据模型,JavaBean的类,用来封装数据

​ View:通过jsp, html等展示数据

​ Controller:接收用户请求,整个流程的控制器

Spring MVC spring提供的mvc框架

​ 与struts2的区别:前者入口为servlet,后者用filter接收请求;前者基于方法,后者基于类(每次执行都会新建一个对象,效率低);前者使用更方便;前者使用JSTL表达式执行效率高,后者使用OGNL表达式开发效率高


应用

环境搭建

  1. 新建maven项目(不使用骨架)

  2. 完善目录结构

    在main目录下新建webapp/WEB-INF/web.xml文件并导入约束和新建jsp.index文件

    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    <web-app>
        <display-name>Web Application</display-name>
    </web-app>
            main
       /     |      \
    java  resources  webapp
                     /     \
                WEB-INF   index.jsp
                   |
                web.xml
  3. 添加web moudules

    • 打开project structure的Modules添加Web

    • 设置Deployment Descriptors的Path为web.xml文件的路径

    • 设置Web Resource Directories的为webapp目录的路径

  4. 配置pom.xml

    <properties>
      <!--  版本锁定 -->
      <spring.version>5.0.2.RELEASE</spring.version>
    </properties>
    
    <dependencies>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
      </dependency>
    
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
      </dependency>
    
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
      </dependency>
    
      <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
      </dependency>
    
      <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
      </dependency>
    </dependencies>
  5. 配置Tomcat

    • 打开Add Configuration添加Tomcat local
    • 随意取Name,在Deployment中添加当前Artifact并设置访问路径
    • 在Server的On'update'action中可以选择Redeploy
  6. 创建springmvc.xml(resources目录下,ioc下的bean.xml)

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

    <web-app>
        <!-- 配置前端控制器 -->
        <servlet>
            <servlet-name>dispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- 指定springmvc配置文件路径 -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc.xml</param-value>
            </init-param>
            <!-- 启动服务器就加载此servlet,默认发请求时才创建 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>dispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>

入门程序

  1. 编写View

    在WEB-INF下编写index.jsp(用a或form发送请求)和返回页面pages/success.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Start Program</title>
    </head>
    <body>
        <a href="hello">Start Program</a>
    </body>
    </html>
  2. 配置springmvc.xml

    <bean ...>
        <!-- 开启扫描注解 -->
        <context:component-scan base-package="com.whteway"/>
    
        <!-- 视图解析器 -->
        <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- 文件所在路径 -->
            <property name="prefix" value="/pages/"/>
            <!-- 文件后缀名 -->
            <property name="suffix" value=".jsp"/>
        </bean>
    
        <!-- 开启SpringMVC框架注解支持,自动加载处理映射器、处理适配器和注解处理器 -->
        <mvc:annotation-driven/>
    </bean>
  3. 编写控制器类

    @Controller //放入IOC容器
    public class HelloController{
        @RequestMapping("/hello")   //处理路径为hello的请求
        public String sayHello(){
            //TODO
            return "success";   //根据视图解析器找到对应success的页面并返回
        }
    }
  • @RequestMapping

    可以放在类或方法上,放在类上时可以定义路径公共前缀(分模块)

    属性path:用于指定请求的URL

    属性value(常用):与path一致,单用value的时候可以省略掉value=

    属性method(常用):定义能接收的请求方法,method={RequestMethod.POST}

    属性params:用于指定限制请求参数的条件

    ​ 如@RequestMapping(value="/hello", params={"username"})则请求的参数必须有username

    ​ 如params={"username=abc"}则传过来的username必须为abc,还可以用!表示不等于

    属性headers:用于指定限制请求消息头的条件

    ​ 如headers={"Accept"}则请求中必须包含有Accept请求头

    多个属性同时出现,关系为与

请求参数绑定

  • 绑定机制(名字相同,自动注入)

    • 表单提交的数据都是键值对形式的k=v
    • SpringMVC把表单提交的参数作为控制器中方法的参数
    • 要求表单参数的name和方法参数的的名称相同
  • 支持的数据类型

    • 基本数据类型和String:区分大小写

    • 实体类型(JavaBean)

      • 表单数据的name要与JavaBean的属性名相同,方法参数中加一个JavaBean类型的参数,自动封装名称随意

      • 若JavaBean类中包含其他的引用类型,则表单name为对象.属性,如address.name

    • 集合数据类型

      • 设JavaBean中存在myList或myMap,则表彰数据的name=myList[0],myMap['one']
  • 解决中文乱码(web.xml中配置)

        <!-- 配置解决中文乱码的过滤器 -->
        <filter>
            <filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
  • 自定义类型转换器

    • 页面提交的参数都是字符串形式
    • 正常情况下框架自动把传过来的字符串参数转换为对应类型,但有时可能会异常,如不同格式的日期
    1. 编写转换类实现Converter接口

      import org.springframework.core.convert.converter.Converter;
      public class StringToDateConvert implements Converter<String,Date>{
          @Override
          public Date convert(String source){
              if(source == null){
                  throw new RuntimeException("请传入数据");
              }
              DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
              try{
                  return df.parse(source);
              } catch(Exception e){
                  throw new RuntimeException("数据类型转换出现错误");
              }
          }
      }
    2. 配置自定义类型转换器(springmvc.xml中)

          <!-- 配置自定义类型转换器 -->
          <bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
              <property name="converters">
                  <set>
                      <bean class="包路径.StringToDateConverter"/>
                  </set>
              </property>
          </bean>
          <!--修改注解支持使转换器生效-->
          <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
  • 获取servlet原生API

    在方法参数中加对应类型的参数,如HttpServletRequest request, HttpServletResponse response

    request.getSession()可以拿到HttpSession

  • Model(最常用)

    方法参数接收一个Model model(map结构),向model中存值,值会自动存入request域中,可以代替获取原生request方式,存用set(),取用get(),取值时可以用实现类ModelMap

常用注解

  • @RequestParam

    • 用在方法的参数前,把请求中指定名称的参数给控制器中的形参赋值,当页面参数与方法参数名不一致时可以用

    • 属性value:请求参数中的名称

      属性required:是否必须提供此参数,默认true

  • @RequestBody

    • 用在方法的参数前,用于获取请求体的内容,直接使用得到的是k=v&k=v...结构的数据,get方式不适用
    • 属性required:默认true,为true时用get方式会报错,false时get方式得到null
  • @PathVariable

    • restful编程风格,简单来说就是通过不同请求方式访问同一请求路径的不同方法(用@RequestMapping的method属性指定),或路径上加参数来区分方法如 @RequestMapping(path="/user/{id}")
    • 用于取请求路径上的参数 如 public String test(@PathVariable(name="id") String id){}
    • 请求路径写 user/10
  • @RequestHeader(不常用)

    • 用在方法的参数前,用于取请求头
  • @CookieValue(不常用)

    • 用在方法的参数前,用于取指定名称Cookie的值,属性value
  • @ModelAttribute

    • 在控制器方法执行前对参数进行预处理

    • 用在方法上,使该方法会在别的控制器的方法执行之前执行,方法的返回值会作为之后实际控制器方法上的参数
    • 用在参数上,获取指定的数据给参数赋值,当表单提交的数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据

    @RequestMapping("/user")    //此方法的参数user是findUser方法的返回值
    public String test(User user){}
    
    @ModelAttribute
    public User findUser(String name){
        //数据库中根据传入参数name查找user
        return user;
    }
    
    @ModelAttribute     //无返回值写法,取参时参数上要用@ModelAttribute("u")
    public void findUser(String name, Map<String, User> map){
        //数据库中根据传入参数name查找user
        map.put("u", user);
    }
  • @SessionAttribute

    • 用于多次制造控制器方法(多次请求)间的参数共享,代替request.getSession()...方式
    • 用在类上,@SessionAttributes(value={"key"}) 用于将model中的键值对存入session域中
    • 清空session用SessionStatus(参数接收)中setComplete()方法

响应方式

  • 页面跳转及存值

    • 字符串:根据前端控制器配置好的前缀和后缀找到要跳转的页面,存值用model,底层用ModelAndView实现

    • void

      • 默认跳转以路径名为名的jsp文件

      • 使用request.getRequestDispatcher("/WEB-INF开始路径全名").forward(request,response); return;进行请求转发

      • 使用response.sendRedirect(request.getContextPath+"/index.jsp");进行重定向

      • 直接进行响应

        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().print("hello");
    • ModelAndView

      • 方法内用new创建,最后返回
      • ModelAndView可以当作Model存数据
      • 使用setViewName("success")指定跳转页面,使用视图解析器
    • 使用关键字进行转发和重定向(不能使用视图解析器)

      • return "forward:/WEB-INF/pages/success.jsp";
      • return "redirect:/index.jsp"; 框架自动添加项目名
  • 响应json数据

    • springmvc.xml中配置前端控制器不拦截的静态资源

      <mvc:resources mapping="/js/**" location="/js/**" />
    • 添加坐标,导入jackson包

      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.9.0</version>
      </dependency>
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-core</artifactId>
          <version>2.9.0</version>
      </dependency>
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-annotations</artifactId>
          <version>2.9.0</version>
      </dependency>
    • 接收json数据,用JavaBean作为参数接收,参数上用@RequestBody,后台自动将json按照名称封装进JavaBean

    • 响应json数据,在返回值类型前加@ResponseBody,后台自动将JavaBean转换为json

      @RequestMapping("/test")
      public @ResponseBody User test(@RequestBody User user){
          //TODO
          return user;
      }
  • 文件上传

    1. 导入坐标

      <dependency>
          <groupId>commons-fileupload</groupId>
          <artifactId>commons-fileupload</artifactId>
          <version>1.3.1</version>
      </dependency>
      <dependency>
          <groupId>commons-io</groupId>
          <artifactId>commons-io</artifactId>
          <version>1.3.1</version>
      </dependency>
    2. 编写前端代码

      • form表单的enctype取值必须是multipart/form-data
      • method必须是Post
      • 需要提供一个文件选择域《input type="file"/》
    3. 配置文件解析器(beanId为固定写法,springmvc.xml)

      <bean id="multipartResolver" class="org.springframework.multipart.commons.CommonsMultipartResolver">
          <property name="maxUploadSize" value="10485760"/>
      </bean>
    4. 编写控制器方法(参数中的upload必须对应表单中文件的name)

          @RequestMapping("/upload")
          public String fileupload(HttpServletRequest request, MultipartFile upload) throws Exception{
              //上传位置
              String path = request.getSession().getServletContext().getRealPath("/uploads");
              File file = new File(path);
              if(!file.exists()){file.mkdirs();}
              //说明上传文件项
              //获取文件名称
              String filename = upload.getOriginalFilename();
              //把文件的名称设置唯一值
              String uuid = UUID.randomUUID().toString().replace("-", "");
              filename = uuid + "_" + filename;
              //完成文件上传
              upload.transferTo(new File(path, filename));
              return "success";
          }
    • 传统文件上传的控制器方法的写法

          @RequestMapping("/traUpload")
          public String traFileupload(HttpServletRequest request) throws Exception{
              //使用fileupload组件完成文件上传
              //上传位置
              String path = request.getSession().getServletContext().getRealPath("/uploads/");
              File file = new File(path);
              if(!file.exists()){file.mkdirs();}
              //解析request对象,获取上传文件项
              DiskFileItemFactory factory = new DiskFileItemFactory();
              ServletFileUpload upload = new ServletFileUpload(factory);
              //解析request
              List<FileItem> items = upload.parseRequest(request);
              for (FileItem item : items) {
                  //判断对象是否是上传文件项
                  if(item.isFormField()){
                      //为普通表单项
                  }else{
                      //为上传文件项
                      //获取文件名称
                      String filename = item.getName();
                      //完成文件上传
                      item.write(new File(path, filename));
                      //删除临时文件
                      item.delete();
                  }
              }
    • 跨服务器上传文件

      准备另一个文件上传专用项目,按照路径写好文件上传的控制器方法

          @RequestMapping("/upload")
          public String fileupload(HttpServletRequest request, MultipartFile upload) throws Exception{
              //定义上传文件服务器路径
              String path = "http://localhost:9090/uploads/";
              //说明上传文件项
              //获取文件名称
              String filename = upload.getOriginalFilename();
              //把文件的名称设置唯一值
              String uuid = UUID.randomUUID().toString().replace("-", "");
              filename = uuid + "_" + filename;
              //完成文件上传
              //创建客户端的对象
              Client client = Client.create();
              //和图片服务器进行连接
              WebResource webResource = client.resource(path+filename);
              //上传文件
              webResource.put(upload.getBytes());
              return "success";
          }
  • 异常处理

    出现异常后跳转到自定义的友好页面,默认情况下各层出现异常后都向上一级抛出,最终会到视图层,可以在前端控制器处配置一个异常处理器,在异常处理器中处理异常

    1. 编写自定义异常类(做提示信息)

      public class SysException extends Exception{
          //存储提示信息的
          private String message;
          public String getMessage(){
              return message;
          }
          public String setMessage(String message){
              this.message = message;
          }
          public SysException(String message){
              this.message = message;
          }
      }
    2. 编写异常处理器

      public class SysExceptionResolver implements HandlerExceptionResolver{
          @Override
          public ModelAndView resolveException(...){
              //获取异常对象
              SysException e = null;
              if(e instanceof SysException)
                  e = (SysException)e;
              else
                  e = new SysException("系统正在维护...");
              //创建ModelAndView对象
              ModelAndView mv = new ModelAndView();
              mv.addObject("errorMsg", e.getMessage());
              mv.setViewName("error");    //提前写好error.jsp页面
              return nmv;
          }
      }
    3. 配置异常处理器(跳转到提示页面,springmvc.xml)

      <bean id="sysExceptionResolver" class="异常处理器全限定类名"/>
    4. 在可能出现异常的地方catch到异常后new一个新的自定义异常对象存入提示信息并throw

      try{
          int a = 10/0;
      } catch(Exception e){
          e.printStackTrace();
          throw new SysException("赋值失败...");
      }
  • 拦截器

    SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理,也是AOP思想的具体应用

    • 拦截器与过滤器的区别

      • 过滤器是servlet规范中的一部分,任何java web项目都能用

        拦截器是SpringMVC自己的,只有使用了SpringMVC框架的工程才能用

      • 过滤器在url-pattern中配置了/*后可以对所有要访问的资源 拦截

        拦截器只会拦截访问的控制器方法,不能拦截jsp,html,css,image,js等静态资源

    1. 创建自定义拦截器类(接口方法有默认实现,可以不重写)

      public class MyInterceptor implements HandlerInterceptor{
          @Override
          public boolean preHandle(...){
              //TODO before controller, 返回true表示放行,不放行可以request转发
              return true;
          }
          @Override
          public boolean postHandle(...){
              //TODO after controller
              return true;
          }
          @Override
          public void afterCompletion(...){
              //TODO after showing page
          }
      }
    2. 配置拦截器(springmvc.xml)

      <mvc:interceptors>
          <mvc:interceptor>
              <!-- 要拦截的方法(二选一) -->
              <mvc:mapping path="/user/*"/>
              <!-- 不拦截的方法(二选一) -->
              <mvc:exclude-mapping path=""/>
              <!-- 配置拦截器对象 -->
              <bean class="拦截器全限定类名"/>
          </mvc:interceptor>
      </mvc:interceptors>

猜你喜欢

转载自www.cnblogs.com/whteway/p/12037374.html