• 05-SpringMVC(2)


    视图解析

    概述

    不论控制器返回一个 String、ModelAndView、View 都会转换为 ModelAndView 对象,由视图解析器解析视图,然后,进行页面的跳转。

    重要的两个接口

    视图解析的源码分析

    Line 1204,Line 1225

    1. 一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图名处理
    2. 如果返回的字符串中带 forward:redirect: 前缀时,SpringMVC 会对他们进行特殊处理:将 forward:redirect: 当成指示符,其后的字符串作为 URL 来处理(不带前缀,则默认请求转发)
    3. redirect:success.jsp 会完成一个到 success.jsp 的重定向的操作
    4. forward:success.jsp 会完成一个到 success.jsp 的转发操作

    请求转发

    请求重定向

    视图和视图解析器

    1. 请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String,View 或 ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个 ModelAndView 对象,它包含了逻辑名和模型对象的视图
    2. Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP ,也可能是 Excel、JFreeChart 等各种表现形式的视图(Handler 返回值若为 void,则会默认将 @RequestMapping 的 value 属性值作为视图名称返回)
    3. 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现 MVC 的充分解耦

    视图

    • 视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户
    • 为了实现视图模型和具体实现技术的解耦,Spring 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口
    • 视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题

    常用的视图实现类

    视图解析器

    • SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在 SpringMVC 上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类
    • 视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象
    • 所有的视图解析器都必须实现 ViewResolver 接口
      public interface ViewResolver {
          View resolveViewName(String viewName, Locale locale) throws Exception;
      }
      

    常用的视图解析器实现类

    1. 可以选择一种视图解析器或混用多种视图解析器
    2. 每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可以通过 order 属性指定解析器的优先顺序,order 越小优先级越高
    3. SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出 ServletException 异常
    4. InternalResourceViewResolver:JSP 是最常见的视图技术,可以使用 InternalResourceViewResolve作为视图解析器

    mvc:view-controller

    1. 若希望直接响应通过 SpringMVC 渲染的页面,可以使用 <mvc:view-controller> 实现
      <!-- 直接配置响应的页面:无需经过控制器来执行结果 -->
      <mvc:view-controller path="/success" view-name="success"/>
      
    2. 请求的路径:http://localhost:8080/SpringMVC_02_View/success
    3. 配置 <mvc:view-controller> 会导致其他请求路径失效,解决办法:
      <!-- 在实际开发过程中都需要配置mvc:annotation-driven标签,后面讲,这里先配置上 -->
      <mvc:annotation-driven/>
      

    编码过滤器

    CharacterEncodingFilter 源码:

    public class CharacterEncodingFilter extends OncePerRequestFilter {
    
        private String encoding;
    
        private boolean forceEncoding = false;
    
        public void setEncoding(String encoding) {
            this.encoding = encoding;
        }
    
        public void setForceEncoding(boolean forceEncoding) {
            this.forceEncoding = forceEncoding;
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request
                , HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
    
            if (this.encoding != null && (this.forceEncoding
                    || request.getCharacterEncoding() == null)) {
                request.setCharacterEncoding(this.encoding);
                if (this.forceEncoding) {
                    response.setCharacterEncoding(this.encoding);
                }
            }
            filterChain.doFilter(request, response);
        }
    }
    

    配置 characterEncodingFilter,必须配置在 web.xml 的第 1 个:

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

    表单标签

    下文摘自:https://blog.csdn.net/hp_yangpeng/article/details/51906654 (做了删减)

    在使用 SpringMVC 的时候我们可以使用 Spring 封装的一系列表单标签,这些标签都可以访问到 ModelMap 中的内容。需要先在 JSP 中声明使用的标签,具体做法是在 JSP 文件的顶部加入以下指令:<%@taglib uri="http://www.springframework.org/tags/form" prefix="form" %>

    支持绑定表单对象

    使用 Spring 的 form 标签主要有两个作用,第一是它会自动的绑定来自 Model 中的一个属性值到当前 form 对应的实体对象,默认是 command 属性,这样我们就可以在 form 表单体里面方便的使用该对象的属性了;第二是它支持我们在提交表单的时候使用除 GET 和 POST 之外的其他方法进行提交,包括 DELETE 和 PUT 等。

    <form:form action="formTag/form.do" method="post">
        <table>
            <tr>
                <td>Name:</td>
                <td><form:input path="name"/></td>
            </tr>
            <tr>
                <td>Age:</td>
                <td><form:input path="age"/></td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交"/></td>
            </tr>
        </table>
    </form:form>
    

    这个时候如果 Model 中存在一个属性名称为 command 的 JavaBean,而且该 JavaBean 拥有属性 name 和 age 的时候,在渲染上面的代码时就会取 command 的对应属性值赋给对应标签的值。

    现假设 Model 中存在一个属性名称为 command 的 JavaBean,且它的 name 和 age 属性分别为 "Zhangsan" 和 "36" 时,那么它在渲染时就会生成如下一段代码:

    <form id="command" action="formTag/form.do" method="post">
        <table>
            <tr>
                <td>Name:</td>
                <td><input id="name" name="name" type="text" value="ZhangSan"/></td>
            </tr>
            <tr>
                <td>Age:</td>
                <td><input id="age" name="age" type="text" value="36"/></td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交"/></td>
            </tr>
        </table>
    </form>
    

    从上面生成的代码中,我们可以看出,当没有指定 form 标签的 id 时它会自动获取该 form 标签绑定的 Model 中对应属性名称作为 id,而对于 input 标签在没有指定 id 的情况下它会自动获取 path 指定的属性作为 id 和 name。

    我们指定 form 默认自动绑定的是 Model 的 command 属性值,那么当我的 form 对象对应的属性名称不是 command 的时候,应该怎么办呢?对于这种情况,Spring 给我们提供了一个 commandName 属性,我们可以通过该属性来指定我们将使用 Model 中的哪个属性作为 form 需要绑定的 command 对象。除了 commandName 属性外,指定 modelAttribute 属性也可以达到相同的效果。这里假设上面代码中我们存放在 Model 中的是 user 对象而不是默认的 command 对象,那么我们的代码就可以如下定义了:

    <form:form action="formTag/form.do" method="post" commandName="user">
        <table>
            <tr>
                <td>Name:</td>
                <td><form:input path="name"/></td>
            </tr>
            <tr>
                <td>Age:</td>
                <td><form:input path="age"/></td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交"/></td>
            </tr>
        </table>
    </form:form>
    

    支持全部的请求方法

    <form:form action="formTag/form.do" method="delete" modelAttribute="user">
        <table>
            <tr>
                <td>Name:</td>
                <td><form:input path="name"/></td>
            </tr>
            <tr>
                <td>Age:</td>
                <td><form:input path="age"/></td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交"/></td>
            </tr>
        </table>
    </form:form>
    

    在上面代码中我们设定了该 form 的提交方法是 delete,这样在后台我们就可以给对应的请求方法的 RequestMapping 加上 method 为 RequestMethod.DELETE 的限制。我们来看一下上面的代码在进行渲染的时候会生成怎样的 HTML 代码,其生成的代码如下所示:

    <form id="user" action="formTag/form.do" method="post">
        <input type="hidden" name="_method" value="delete"/>
        <table>
            <tr>
                <td>Name:</td>
                <td><input id="name" name="name" type="text" value="ZhangSan"/></td>
            </tr>
            <tr>
                <td>Age:</td>
                <td><input id="age" name="age" type="text" value="36"/></td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交"/></td>
            </tr>
        </table>
    </form>
    

    input 标签

    SpringMVC 的 input 标签会被渲染为一个 type 为 text 的普通 HTML input 标签。使用 SpringMVC 的 input 标签的唯一作用就是它能绑定表单数据。SpringMVC 表单标签最大的好处就是它支持数据绑定,当我们的表单标签不需要绑定的数据的时候,我们应该使用普通的 HTML 标签。关于 input 标签绑定表单数据的方法已经在介绍 form 标签的时候顺带介绍过了,这里就不再过多的赘述了。

    <form:form action="formTag/form.do" modelAttribute="user">
        <table>
            <tr>
                <td>Name:</td>
                <td><form:input path="name"/></td>
            </tr>
            <tr>
                <td>Age:</td>
                <td><form:input path="age"/></td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交"/></td>
            </tr>
        </table>
    </form:form>
    

    password 标签

    password 标签将会被渲染为一个 type 为 password 的普通 HTML input 标签。

    hidden 标签

    hidden 标签会被渲染为一个 type 为 hidden 的普通 HTML input 标签。用法跟 input 标签一样,也能绑定表单数据,只是它生成的是一个隐藏域。

    checkboxes 标签

    相对于一个 checkbox 标签只能生成一个对应的复选框而言,一个 checkboxes 标签将根据其绑定的数据生成 N 个复选框。

    checkboxes 绑定的数据可以是数组、集合和 Map。在使用 checkboxes 时我们有两个属性是必须指定的,一个是 path,另一个是 items。

    • items 表示当前要用来展现的项有哪些
    • path 所绑定的表单对象的属性表示当前表单对象拥有的项,即在 items 所展现的所有项中表单对象拥有的项会被设定为选中状态
    <form:form action="formTag/form.do" method="post" commandName="user">
        <table>
            <tr>
                <td>Roles:</td>
                <td>
                    <form:checkboxes path="roles" items="${roleList}"/>
                </td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交"/></td>
            </tr>
        </table>
    </form:form>
    
    @RequestMapping(value="form", method=RequestMethod.GET)
    public String formTag(Map<String, Object> map) {
        User user = new User();
        List<String> roles = new ArrayList<String>();
        roles.add("role1");
        roles.add("role3");
        user.setRoles(roles);
    
        List<String> roleList = new ArrayList<String>();
        roleList.add("role1");
        roleList.add("role2");
        roleList.add("role3");
        map.put("user", user);
        map.put("roleList", roleList);
        return "formTag/form";
    }
    

    从以上代码中我们可以看到我们放在 ModelMap 中的 roleList 对象有 3 个元素,分别是 role1、role2 和 role3,而我们的表单对象 User 的 roles 属性只拥有两个元素,分别是 role1 和 role3,,所以当我们访问该处理器方法返回如上所示的视图页面时,我们要展现的复选框项是 roleList,也就是 role1、role2 和 role3,而我们表单对象只拥有 role1 和 role3,所以在页面进行渲染的时候会展示 3 个复选框项,但只有 role1 和 role3 会被设定为选中状态。


    上面介绍的这种情况是使用 List 作为展现复选框项的数据源,这种情况我们已经看到了它所呈现出来的标签 Label 和它的值是一样的。使用 Array 和 Set 作为数据源也是这种情况。那么如果要让 checkboxes 呈现出来的 Label 和实际上送的 value 不同的话应该怎么做呢?这个时候我们就可以使用 Map 作为数据源了。使用 Map 作为 checkboxes 的 items 属性的数据源时 Key 将作为真正的复选框的 value,而 Map 的 value 将作为 Label 进行展示。当使用 Map 作为 checkboxes 的 items 属性的数据源时我们绑定的表单对象属性的类型可以是 Array、集合 和 Map,这种情况就是判断 items-Map 中是否含有对应的 key 来决定当前的复选框是否处于选中状态。我们来看如下一个处理器方法以及其对应的视图代码。

    @RequestMapping(value="form", method=RequestMethod.GET)
    public String formTag(Map<String, Object> map) {
        User user = new User();
        List<String> roles = new ArrayList<String>();
        roles.add("role1");
        roles.add("role3");
        user.setRoles(roles);
        Map<String, String> roleMap = new HashMap<String, String>();
        roleMap.put("role1", "角色1");
        roleMap.put("role2", "角色2");
        roleMap.put("role3", "角色3");
        map.put("user", user);
        map.put("roleMap", roleMap);
        return "formTag/form";
    }
    
    <form:form action="formTag/form.do" method="post" commandName="user">
        <table>
            <tr>
                <td>Roles:</td>
                <td>
                    <form:checkboxes path="roles" items="${roleMap}"/>
                </td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交"/></td>
            </tr>
        </table>
    </form:form>
    

    这个时候我们知道会呈现出 3 个复选框,而 checkboxes 绑定的表单对象 user 的 roles 属性是一个集合对象,其包含的 2 个元素都能在 checkboxes 的 items 数据源中找到对应的 Key,所以以这 2 个元素为 value 的 checkbox 将处于选中状态。效果如下:

    当我们使用 Array 或者集合作为数据源,且里面的元素都是一个一个 POJO 时,我们还可以使用 checkboxes 标签的 itemLabel 和 itemValue 属性来表示使用数组或者集合中元素对象的哪一个属性作为需要呈现的单选框的 label 和 value。

    radiobuttons 标签

    radiobuttons 标签跟 radiobutton 标签的区别如同 checkbox 标签对 checkboxes 标签的区别。使用 radiobuttons 标签的时候将生成多个单选按钮。使用 radiobuttons 有两个属性也是我们必须指定的,一个是 path 属性,表示绑定的表单对象对应的属性,另一个是 items 属性,表示用于生成单选按钮的数据源。跟 checkboxes 一样,radiobuttons 的 items 属性和 path 属性都可以是 Array、集合或者是 Map。现在我们假设 user 在篮球、足球、乒乓球、羽毛球和排球这 5 种运动中选择一种作为自己最喜欢的球类运动。处理器方法和返回的对应的视图代码如下:

    @RequestMapping(value="form", method=RequestMethod.GET)
    public String formTag(Map<String, Object> map) {
        User user = new User();
        user.setFavoriteBall(4);//设置我最喜爱的球类运动是4羽毛球
        Map<Integer, String> ballMap = new HashMap<Integer, String>();
        ballMap.put(1, "篮球");
        ballMap.put(2, "足球");
        ballMap.put(3, "乒乓球");
        ballMap.put(4, "羽毛球");
        ballMap.put(5, "排球");
        map.put("user", user);
        map.put("ballMap", ballMap);
        return "formTag/form";
    }
    
    <form:form action="formTag/form.do" method="post" commandName="user">
        <table>
        <tr>
            <td>最喜欢的球类:</td>
            <td>
                <form:radiobuttons path="favoriteBall" items="${ballMap}" delimiter="&nbsp;"/>
            </td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
        </table>
    </form:form>
    

    在上述代码中我们可以看到我们使用了 radiobuttons 的 delimiter 属性,该属性表示进行展示的 radiobutton 之间的分隔符。这里用的是一个空格。结果页面如下所示:

    select-option 标签

    select 标签将会被渲染为一个普通的 HTML select 标签。这里还拿前面的 user 最喜欢的球类运动来做示例,有如下这样一个处理器方法和对应的视图页面:

    @RequestMapping(value="form", method=RequestMethod.GET)
        public String formTag(Map<String, Object> map) {
        User user = new User();
        user.setFavoriteBall(4);//设置我最喜爱的球类运动是4羽毛球
        Map<Integer, String> ballMap = new HashMap<Integer, String>();
        ballMap.put(1, "篮球");
        ballMap.put(2, "足球");
        ballMap.put(3, "乒乓球");
        ballMap.put(4, "羽毛球");
        ballMap.put(5, "排球");
        map.put("user", user);
        map.put("ballMap", ballMap);
        return "formTag/form";
    }
    
    <form:form action="formTag/form.do" method="post" commandName="user">
        <table>
        <tr>
            <td>td>最喜欢的运动:</td>
            <td>
                <form:select path="favoriteBall" items="${ballMap}"/>
            </td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
        </table>
    </form:form>
    

    这个时候会渲染出如下结果:

    从上面示例我们可以看出,我们通过 items 属性给 select 标签指定了一个数据源,并且绑定了表单对象 user 的 favoriteBall 属性。items 属性是用于指定当前 select 的所有可选项的,但是它对于 select 标签而言不是必须的,因为我们还可以手动的在 select标签中间加上 option 标签来指定 select 可选的 option。select 标签支持的 items 属性的数据类型可以是 Array、Collection 和 Map,当数据类型为 Array 或 Collection 时且其中的元素为一个 POJO 时,我们可以通过属性 itemLabel 和 itemValue 来指定将用于呈现的 option Label 和 Value,其他情况下 Array 和 Collection 数据源中的元素将既作为可选项 option 的 value 又作为它的 Label。当 items 的数据类型为 Map 时,Map 的 key 将作为可选项 option 的 value,而 Map 的 value 将作为 option 的 Label 标签。

    textarea 标签

    SpringMVC textarea 标签将被渲染为普通 HTML textarea 标签。

    REST-CRUD

    需求分析

    • 查询操作:显示所有员工信息
      • URI:emps
      • 请求方式:GET
      • 显示效果
    • 添加操作1:去往添加页面
      • 显示添加页面
      • URI:emp
      • 请求方式:GET
      • 显示效果
    • 添加操作2:添加员工
      • 添加员工信息
      • URI:emp
      • 请求方式:POST
      • 显示效果:完成添加,重定向到 list 页面
    • 删除操作
      • URL:emp/{id}
      • 请求方式:DELETE
      • 删除后效果:对应记录从数据表中删除
    • 修改操作1:去往修改页面
      • URI:emp/{id}
      • 请求方式:GET
      • 显示效果:回显表单
    • 修改操作2:修改员工
      • URI:emp
      • 请求方式:PUT
      • 显示效果:完成修改,重定向到 list 页面

    相关类和页面

    • 相关类
      • 实体类:Employee、Department
      • Handler:EmployeeHandler
      • Dao:EmployeeDao、DepartmentDao
    • 相关页面
      • list.jsp
      • input.jsp
      • edit.jsp

    环境搭建

    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>
    
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <servlet>
        <servlet-name>springDispatcherServlet</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>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    springMVC.xml

    <!-- 配置扫描的包 -->
    <context:component-scan base-package="cn.edu.nuist"></context:component-scan>
    
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"></property>
        <property name="suffix" value=".jsp"></property>
        <property name="order" value="1"></property>
    </bean>
    
    <!--
        当请求静态资源,则交给 tomcat 中的缺省 Servlet 去处理
        DispatcherServlet 的 url-pattern 配置为 '/' [← 优先级高]
        DefaultServlet 的 url-pattern 的配置也是 '/'
        当客户端发送请求,先通过 DispatcherServlet 处理请求,找该请求
        是否有相对应的处理器。有则处理,无则再交给 DefaultServlet 处理
    -->
    <mvc:default-servlet-handler/>
    
    <!-- MVC 驱动 -->
    <mvc:annotation-driven/>
    

    JavaBean

    public class Employee {
        private Integer id;
        private String lastName;
        private String email;
        private Integer gender;
        private Department department;
    
        // ...
    }
    
    public class Department {
        private Integer deptId;
        private String deptName;
    
        // ...
    }
    

    DAO

    功能实现

    查询操作

    EmpController

    public class EmpController {
    
        @Autowired
        private EmployeeDao empDao;
    
        @Autowired
        private DepartmentDao deptDao;
    
        /**
         * 获取所有的员工信息
         * @param map request域
         * @return 要转发到的负责显示员工列表的视图名称
         */
        @RequestMapping(value="/emps")
        public String getAll(Map<String, Object> map) {
            Collection<Employee> emps = empDao.getAll();
            map.put("emps", emps);
            return "list";
        }
    

    list.jsp(1)

    <table border="1">
        <tr>
            <th>ID</th>
            <th>LASTNAME</th>
            <th>EMAIL</th>
            <th>GENDER</th>
            <th>DEPTNAME</th>
            <th>OPTION<a href="emp">[ADD]</a></th>
        </tr>
        <c:forEach items="${emps }" var="emp">
            <tr>
                <td>${emp.id }</td>
                <td>${emp.lastName }</td>
                <td>${emp.email }</td>
                <td>${emp.gender==1 ? 'male' : 'female'}</td>
                <td>${emp.department.deptName }</td>
                <td>
                    <a href="emp/${emp.id }">UPDATE</a>
                    <a class="del" href="emp/${emp.id }">DELETE</a>
                </td>
            </tr>
        </c:forEach>
    </table>
    

    添加操作

    EmpController

    @RequestMapping(value="/emp", method=RequestMethod.GET)
    public String toAdd(Map<String, Object> map) {
        // 获取所有部门信息
        map.put("depts", deptDao.getDepartments());
        // 创建存储 gender 的 Map
        Map<String, String> genders = new HashMap<>();
        genders.put("0", "女");
        genders.put("1", "男");
        map.put("genders", genders);
        // <form> 有自动回显的功能,表单会默认获取 requestArea 中的 command 属性的值
        // map.put("command", new Employee());
        // <form:form ... modelAttribute="emp"> 可手动设置回显对象的名字
        map.put("emp", new Employee()); // 添加操作,表单无需回显
        return "edit";
    }
    

    修改操作

    EmpController

    /**
     * 转发到修改页面
     * @param id 要求修改的员工的id
     * @param map 将要回显的员工信息放入 requestArea
     * @return
     */
    @RequestMapping(value="/emp/{id}", method=RequestMethod.GET)
    public String toUpdate(@PathVariable("id") Integer id, Map<String, Object> map) {
        // 获取要修改的员工信息
        Employee emp = empDao.get(id);
        map.put("emp", emp);
        Map<String, String> genders = new HashMap<>();
        genders.put("0", "女");
        genders.put("1", "男");
        map.put("genders", genders);
        map.put("depts", deptDao.getDepartments());
        return "edit";
    }
    
    /**
     * 修改员工
     * @param emp 封装修改过信息的员工 bean
     * @return
     */
    @RequestMapping(value="/emp", method=RequestMethod.PUT)
    public String update(Employee emp) {
        System.out.println(emp);
        empDao.save(emp); // 修改
        return "redirect:/emps";
    }
    

    edit.jsp

    <%-- modelAttribute 属性作用:自定义 RequestArea 中存放的要回显的对象的 key --%>
    <form:form action="${pageContext.request.contextPath }/emp"
            method="POST" modelAttribute="emp">
    
        <%-- add 和 update 共用这个 edit 来实现各自的功能;判断依据:emp.id -->
        <c:if test="${empty emp.id}" var="isAdd"></c:if>
        <c:if test="${!isAdd }">
            <form:hidden path="id"/>
            <input type="hidden" name="_method" value="PUT"/>
        </c:if>
    
        <table border="1">
            <tr>
                <th colspan="2">
                    <c:if test="${flag }">添加员工</c:if>
                    <c:if test="${!flag }">修改员工</c:if>
                </th>
            </tr>
            <tr>
                <td>LASTNAME</td>
                <td><form:input path="lastName"/></td>
            </tr>
            <tr>
                <td>EMAIL</td>
                <td><form:input path="email"/></td>
            </tr>
            <tr>
                <td>GENDER</td>
                <td>
                    <form:radiobuttons path="gender" items="${genders }"/>
                </td>
            </tr>
            <tr>
                <td>DEPTNAME</td>
                <td>
                    <form:select path="department.deptId" items="${depts }"
                        itemLabel="deptName" itemValue="deptId"></form:select>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="submit" value="edit">
                </td>
            </tr>
        </table>
    </form:form>
    

    删除操作

    EmpController

    @RequestMapping(value="/emp/{id}", method=RequestMethod.DELETE)
    public String delete(@PathVariable("id")Integer id) {
        empDao.delete(id);
        return "redirect:/emps";
    }
    

    list.jsp(2)

    <script src="${pageContext.request.contextPath }/js/jquery.js"></script>
    <script>
        $(function() { // 预加载函数/文档就绪函数
            $(".del").click(function() {
                if(confirm("确认删除吗?"))
                    $("form").attr("action", $(this).attr("href")).submit(); // this.href
                return false; // 将超链接的默认行为取消
            });
        });
    </script>
    
    <!-- 省略内容见查询操作贴的 list.jsp 代码
    <a class="del" href="emp/${emp.id }">DELETE</a>
     -->
    
    <form method="POST">
        <input type="hidden" name="_method" value="DELETE"/>
    </form>
    

    图解 Delete 操作

    处理静态资源

    出现的问题

    警告: No mapping found for HTTP request with URI [/REST-CRUD/js/jquery.js]
    in DispatcherServlet with name 'springDispatcherServlet'
    

    为什么会有这样的问题?
    REST 风格的资源 URL 不希望带 .html 或 .do 等后缀,若将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕获 WEB 容器的所有请求,包括静态资源的请求,SpringMVC 会将他们当成一个普通请求处理,因找不到对应处理器将导致错误。

    如何解决?

    在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/>,让 DefaultServlet 来处理对静态资源的请求。配置后,原来的请求又不好使了,还得配置 MVC 驱动 <mvc:annotation-driven />


    default-servlet-handler

    <mvc:default-servlet-handler/> 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理;如果不是静态资源的请求,才由 DispatcherServlet 继续处理。

    一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定。

    参考:CATALINA_HOME/config/web.xml

    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    

    springMVC.xml

    <!-- 该标签属性 default-servlet-name 默认值是"default",可以省略 -->
    <mvc:default-servlet-handler default-servlet-name="default"/>
    
  • 相关阅读:
    bzoj 1103
    [POI] 大都市meg
    [Luogu] 网络
    [Luogu] 1600
    [Luogu] 树状数组
    [Luogu] 软件包管理器
    [Luogu] 遥远的国度
    [USACO5.5] 矩形周长Picture
    [Luogu] 魔板
    【NOIP2015】斗地主
  • 原文地址:https://www.cnblogs.com/liujiaqi1101/p/13569784.html
Copyright © 2020-2023  润新知