【Java EE 学习 83 上】【SpringMVC】【基本使用方法】

发布时间:2019-05-12 | 分类:SpringMVC

标签: Java EE,SpringMVC

一、SpringMVC框架概述

  什么是SpringMVC?SpringMVC是一个和Struts2差不多的东西,他们的作用和性质几乎是相同的,甚至开发效率上也差不多,但是在运行效率上SpringMVC要高于Struts2;注意这里的SpringMVC很明确的指明了使用了MVC框架,Struts2也使用了MVC框架。

  1.环境准备

    和之前使用的spring环境几乎是差不多的,但是需要增加两个核心包:

org.springframework.web.servlet-3.0.0.RELEASE.jar

org.springframework.web-3.0.0.RELEASE.jar

    这是在Spring3.0的环境下,Spring3.2有了重大的升级,不再使用org.springframework.web.servlet-3.0.0.RELEASE.jar,而是取而代之的使用了下面的jar包:

spring-webmvc-3.2.0.RELEASE.jar

  当然,还是推荐使用Spring3.2的版本,根据spring官方文档中的声明,自3.0版本之后一直到3.2版本的所有版本都存在已知的而且很明显的bug;在spring3.2中则解决了相当数量的bug。

  2.需要的完整jar包列表

    由于我使用的是Eclipse,所以相关jar包IDE并没有提供支持,所以只能手动添加jstl.jar以及standard.jar了:

aopalliance.jar
commons-logging.jar
jstl.jar
spring-aop-3.2.0.RELEASE.jar
spring-aspects-3.2.0.RELEASE.jar
spring-beans-3.2.0.RELEASE.jar
spring-context-3.2.0.RELEASE.jar
spring-context-support-3.2.0.RELEASE.jar
spring-core-3.2.0.RELEASE.jar
spring-expression-3.2.0.RELEASE.jar
spring-web-3.2.0.RELEASE.jar
spring-webmvc-3.2.0.RELEASE.jar
standard.jar

  很明显了,使用SpringMVC的另外一个最大的好处就是不用像在Struts2中那样再引入一大批的jar包了。

二、第一个SpringMVC程序

  1.首先在web.xml配置文件中配置一个Servlet

<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

  2.配置spring配置文件

  默认的Servlet将会读取WEB-INF文件夹下的相应配置文件;当然该配置文件的命名还需要满足规则才行:$servletName-servlet.xml,否则Servlet也找不到相应的配置文件;根据上述的配置中的servlet-name标签的值,我们将配置文件的名字命名为:action-servlet.xml,这个配置文件实际上就是Spring的配置文件

  首先配置“内部资源视图解析器”

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    <!-- 内部资源视图解析器 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/jsps/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
<beans>

  内部视图资源解析器确定了一种规则,那就是将Controller返回的ModelAndView对象进行解析,并将viewName提取出来和"prefix"以及"suffix"进行拼接,得到需要跳转的资源位置,并执行跳转,具体是重定向还是转发,需要根据ModelAndView的内容进行确定。

  这里则是假设所有jsp页面都保存到了/jsps目录下,并且都是以.jsp结尾。

  3.新建控制器

  所有控制器的超类都是AbstractController,这里直接继承该类,并重写该类的核心方法:handleRequestInternal,该方法返回值是ModelAndView对象,该对象必须赋予一个字符串代表资源的位置。

package com.kdyzm.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

public class HomeController extends AbstractController {
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        System.out.println("你好,这是第一个SpringMVC程序!");
        return new ModelAndView("index");
    }
}

  这里的index经过"内部视图资源解析器"解析之后,就转变成了真正的地址:/jsps/index.jsp,在浏览器上就需要使用http://localhost:8080/项目名称/jsps/index.jsp来访问了。

  新建控制器之后需要将控制器注入到spring容器管理。

<bean name="/home.action" class="com.kdyzm.controller.HomeController"></bean>

  4.在/jsp/文件夹下新建jsp页面index.jsp

 1 <%@ page language="java" pageEncoding="utf-8"%>
 2 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 3 <html>
 4 <head>
 5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 6 <title>Insert title here</title>
 7 </head>
 8 <body>
 9     你好,这是第一个SpringMVC程序!
10 </body>
11 </html>

  5.在浏览器上输入http://localhost:8080/springmvc/home.action进行测试

  

  测试成功。

三、三种不同的URL处理器映射

  所谓的处理器映射就是一种Map对象,它储存着请求的url到控制器的映射,它能够将我们的请求转交给某一个指定的控制器处理。

  1.Bean名url处理器映射

    这是默认的url处理器映射,也是最常使用的url处理器映射;所谓默认,就是不配置也会自动加载,这里显式声明一下意思意思:

<bean
    class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="order" value="3"></property>
</bean>

    它有一个serOrder方法,该方法能够确定该url处理器的优先级,如果配置了多个url处理器映射,那么该值越大的越有限匹配;之前的例子由于没有明确的配置url处理器映射,所以使用的是默认的url处理器映射。

  2.简单url处理器映射

    这种url处理器映射基本上没有人使用,因为使用这种方式需要将key和值一一列举出来,这种工作量即使在一个中型项目中也是难以承受的。

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="order" value="2"></property>
    <property name="mappings">
        <map>
            <entry key="/abc.action" value-ref="homeAction"></entry>
            <entry key="/xiaozhang.action" value-ref="homeAction"></entry>
        </map>
    </property>
</bean>

  使用这种形式的访问方式就是使用key值部分,比如上述的例子中定义了两个可以都指向了同一个资源:homeAction,那么使用这两种方式都能够访问到同一个页面:

  http://localhost:8080/springmvc1/abc.action或者http://localhost:8080/springmvc1/xiaozhang.action

  

  

  3.控制器类名处理器映射

    顾名思义,使用这种方式能够直接通过控制器的类名访问控制器,使用这种方式虽然简单但是使用这种凡是有着很明显的弊端,那就是如果不同的包中如果定义了相同类名的控制器,那么使用这种方式肯定会出错。所以也不推荐使用这种方式。

<bean
    class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
    <property name="order" value="1"></property>
</bean>

    使用控制器类名直接访问控制器,但是注意需要将类名首字母编程小写:

  

  4.综上所述,这三种控制器映射中,只有第一种配置起来最简单,而且最灵活,是强烈推荐的使用方式。

四、三种不同的控制器

  SpringMVC中控制器的概念和Struts2中的Action的概念几乎是差不多的,尝试着将控制器看做Action会发现理解起来就会简单的多。

  1.最基本的控制器

    直接继承AbstractController,该抽象类是所有控制器的超类,直接继承该抽象类也能够实现简单的数据处理功能。

 1 package com.kdyzm.controller;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 import javax.servlet.http.HttpServletResponse;
 5 
 6 import org.springframework.web.servlet.ModelAndView;
 7 import org.springframework.web.servlet.mvc.AbstractController;
 8 
 9 public class HomeController extends AbstractController {
10     @Override
11     protected ModelAndView handleRequestInternal(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
12         System.out.println("这是第二个SpringMVC程序!");
13         return new ModelAndView("index");
14     }
15 }

  在XML文件中的配置:

<bean id="homeAction" name="/home.action" class="com.kdyzm.controller.HomeController"></bean>

  在讲解接下来的几个控制器之前首先需要创建一个JavaBean:

 1 package com.kdyzm.domain;
 2 
 3 import java.io.Serializable;
 4 
 5 public class Person implements Serializable {
 6     private static final long serialVersionUID = 1298989747316274828L;
 7     private int id;
 8     private String name;
 9     private String address;
10     private int age;
11 
12     /****************** 华丽的分割线 **************************/
13     public int getId() {
14         return id;
15     }
16 
17     public void setId(int id) {
18         this.id = id;
19     }
20 
21     public String getName() {
22         return name;
23     }
24 
25     public void setName(String name) {
26         this.name = name;
27     }
28 
29     public String getAddress() {
30         return address;
31     }
32 
33     public void setAddress(String address) {
34         this.address = address;
35     }
36 
37     public int getAge() {
38         return age;
39     }
40 
41     public void setAge(int age) {
42         this.age = age;
43     }
44 
45     @Override
46     public String toString() {
47         return "Person [id=" + id + ", name=" + name + ", address=" + address + ", age=" + age + "]";
48     }
49 }
com.kdyzm.domain.Person

  2.命令控制器

  使用这种控制器能够实现简单的数据的收发,并且能够实现自动封装成Bean对象;但是需要通过构造方法注册命令类和命令名称。

 1 package com.kdyzm.controller;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 import javax.servlet.http.HttpServletResponse;
 5 
 6 import org.springframework.validation.BindException;
 7 import org.springframework.web.servlet.ModelAndView;
 8 import org.springframework.web.servlet.mvc.AbstractCommandController;
 9 
10 import com.kdyzm.domain.Person;
11 
12 /**
13  * 命令控制器
14  * 
15  * @author kdyzm
16  *         访问形式:http://localhost:8080/springmvc1/commandController.action?id=1&
17  *         name=xiaozhang&age=12&address=shandong
18  */
19 @SuppressWarnings("deprecation")
20 public class MyCommandController extends AbstractCommandController {
21     /**
22      * 需要通过构造方法注册命令类和命令名称
23      */
24     public MyCommandController() {
25         this.setCommandClass(Person.class);
26         this.setCommandName("person");
27     }
28 
29     @Override
30     protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object command,
31             BindException errors) throws Exception {
32         System.out.println("使用了CommandController控制器!" + command);
33         return new ModelAndView("index");
34     }
35 
36 }

  需要注意的是使用这种方式必须继承AbstractCommandController抽象类,但是这种方式已经过时了,也就是不推荐使用了,查看javadoc:

  

  在上面画圈的部分可以看出,在spring3.0之后这种方式就已经过时了,推荐使用注解控制器的方式;注解控制器的方式之后再说。

  先看XML文件中的配置,XML文件中的配置也十分简单,只需要注入到Spring容器中即可:

<bean id="commandController" name="/commandController.action"
        class="com.kdyzm.controller.MyCommandController"></bean>  

  3.表单控制器

  表单控制器的特点是:

    (1)能够自动将获取的参数封装成Bean对象

    (2)可以自动识别请求的方式,并根据请求方式的不同使用不同方式进行处理

      如果是GET方式的请求,则会自动转发到显示表单的页面;如果是POST请求,就会对数据进行封装并跳转到程序中定义好的页面。

 1 package com.kdyzm.controller;
 2 
 3 import org.springframework.web.servlet.mvc.SimpleFormController;
 4 
 5 import com.kdyzm.domain.Person;
 6 /**
 7  * 简单表单控制器
 8  * 访问方法:http://localhost:8080/springmvc1/formController.action
 9  * 表单页面显示方法和访问的表单提交的对象都是相同的url地址
10  * @author kdyzm
11  *
12  */
13 @SuppressWarnings("deprecation")
14 public class MyFormController extends SimpleFormController {
15     /**
16      * 在构造方法中实现注册命令类和命令名称
17      */
18     public MyFormController() {
19         this.setCommandClass(Person.class);
20         this.setCommandName("person");
21         System.out.println("执行了formController的构造方法!");
22     }
23     /**
24      * 执行set、get方法的时候会出问题!
25      * 表单内容必须填写正确,否则不能正确提交!最后执行不了该方法!
26      */
27     @Override
28     protected void doSubmitAction(Object command) throws Exception {
29         System.out.println("MyFormController:"+command);
30         super.doSubmitAction(command);
31     }
32 }

    注意上面的方法的返回值是void,该方法的返回值为什么不是ModelAndView对象呢?因为成功之后需要跳转的页面是由配置文件决定的。

    XML文件中的配置和之前相比有些不同:

<bean id="formController" name="/formController.action"
    class="com.kdyzm.controller.MyFormController">
    <property name="formView" value="formView"></property>
    <property name="successView" value="index"></property>
</bean>

    注意上面红色背景部分的内容,formView部分配置的是如果接收到了GET方式的请求,就会重定向到formView.jsp页面上,如果接收到了POST请求,那么就会执行上面的doSubmitAction方法,如果执行该方法的过程中并没有出现异常,执行该方法之后就会跳转到配置的成功页面上去。

  注意,在执行该doSubmitAction方法之前就已经将javaBean封装好了,如果封装的过程中出现异常,Spring并不会报错提醒,只是将页面跳转到显示form表单的页面上去而已,这会让人百思不得其解到底发生了什么事情,实际上就是封装javaBean失败而已;最常见的错误信息就是"类型不匹配",比如javaBean中定义的类型是int类型,但是在前端页面上传递过来的数据不能转换成整型类型,这种情况下就会发生转换失败的情况,这样就不能跳转到成功页面上去了。

  这种方式也已经过时了,过时的原因和之前相同,在spring3.0之后就推荐使用注解控制器实现了。

  4.向导表单控制器

  这种控制器的特点:

    (1)能够跨页面实现表单提交,比如调查问卷,一页显示不完,需要多个页面才能显示完成,但是提交的时候就需要提交所有的信息

    (2)能够自动封装提交的数据,当然这种功能之前的表单控制器也能够实现

    (3)使用这种方式需要特别注意数据回显的问题

  

 1 package com.kdyzm.controller;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 import javax.servlet.http.HttpServletResponse;
 5 
 6 import org.springframework.validation.BindException;
 7 import org.springframework.web.servlet.ModelAndView;
 8 import org.springframework.web.servlet.mvc.AbstractWizardFormController;
 9 
10 import com.kdyzm.domain.Person;
11 /**
12  * 向导表单控制器
13  * @author kdyzm
14  *
15  */
16 @SuppressWarnings("deprecation")
17 public class MyWizardController extends AbstractWizardFormController{
18     /**
19      * 还是需要通过构造方法注册命令类和命令方法
20      */
21     public MyWizardController() {
22         this.setCommandClass(Person.class);
23         this.setCommandName("person");
24     }
25     /**
26      * 当最终提交了表单之后将会访问该方法
27      */
28     @Override
29     protected ModelAndView processFinish(HttpServletRequest request, HttpServletResponse response, Object command,
30             BindException errors) throws Exception {
31         System.out.println("访问了WizardController类:"+command);
32         return new ModelAndView("index");
33     }
34     @Override
35     protected ModelAndView processCancel(HttpServletRequest request, HttpServletResponse response, Object command,
36             BindException errors) throws Exception {
37         System.out.println("访问了processCancel方法!"+command);
38         return new ModelAndView("index");
39     }
40 }

  当然,使用这种方式也得通过构造方法实现注册命令类和命令方法;关键是得重写上面红色背景部分的方法,这两个方法分别对应着提交的方法和取消的方法,而且返回值都是ModelAndView,这就意味着不需要我们通过配置文件的方式实现页面的跳转了,这时候就使用ModelAndView的方式实现页面的跳转。

  配置文件和之前的配置文件也有比较大的区别:

<bean id="wizardFormController" name="/wizard.action"
    class="com.kdyzm.controller.MyWizardController">
    <property name="pages">
        <list>
            <value>wizard/1</value>
            <value>wizard/2</value>
            <value>wizard/3</value>
        </list>
    </property>
</bean>

  这里的pages属性指的就是所有的分页;wizard/1实际上就是/jsps/wizard/1.jsp,也就是说我在/jsps/wizard文件夹中新建了三个jsp文件,这三个jsp文件组成了"提交"表单这一事件。

  在每个jsp页面中,需要视情况给它们分配几个"提交"按钮,比如上一页、下一页、退出、提交,但是每个提交按钮都需要给它们分配一个预先定义好的名字;这个名字不是根据我们的规则定义的,而是根据AbstractWizardFormController类的规则定义的。

 org.springframework.web.servlet.mvc.AbstractWizardFormController

Deprecated. as of Spring 3.0, in favor of annotated controllers

Form controller for typical wizard-style workflows.

In contrast to classic forms, wizards have more than one form view page. Therefore, there are various actions instead of one single submit action:

  • finish: trying to leave the wizard successfully, that is, perform its final action, and thus requiring a valid state;
  • cancel: leaving the wizard without performing its final action, and thus without regard to the validity of its current state;
  • page change: showing another wizard page, e.g. the next or previous one, with regard to "dirty back" and "dirty forward".

Finish and cancel actions can be triggered by request parameters, named PARAM_FINISH ("_finish") and PARAM_CANCEL ("_cancel"), ignoring parameter values to allow for HTML buttons. The target page for page changes can be specified by PARAM_TARGET, appending the page number to the parameter name (e.g. "_target1"). The action parameters are recognized when triggered by image buttons too (via "_finish.x", "_abort.x", or "_target1.x").

The current page number will be stored in the session. It can also be specified as request parameter PARAM_PAGE ("_page") in order to properly handle usage of the back button in a browser: In this case, a submission will always contain the correct page number, even if the user submitted from an old view.

The page can only be changed if it validates correctly, except if a "dirty back" or "dirty forward" is allowed. At finish, all pages get validated again to guarantee a consistent state.

Note that a validator's default validate method is not executed when using this class! Rather, the validatePage implementation should call special validateXXX methods that the validator needs to provide, validating certain pieces of the object. These can be combined to validate the elements of individual pages.

Note: Page numbering starts with 0, to be able to pass an array consisting of the corresponding view names to the "pages" bean property.

   上述文档说明了规则:

    (1)如果是"提交"按钮,名字必须是"_finish"

    (2)如果是"退出"按钮,名字必须是"_cancle"

    (3)如果是"上一页"或者"下一页"按钮,名字的前缀必须是"_target",后面跟着目标页的页码,规律是如果是第一页,那么页码就是0;如果是第二页,页码就是1,以此类推。

  另外之前配置文件中的

    <value>wizard/1</value>
    <value>wizard/2</value>
    <value>wizard/3</value>

   并没有什么特别的含义,并非写了1就是第一页,其实任何名字也无所谓,实际上摆放的位置决定了它真实的页序。

  注意:所有过程中访问的url地址都是:http://localhost:8080/springmvc1/wizard.action,至于向导表单控制器是如何进行逻辑处理的,这种事情就不需要我们操心了。

五、项目练习源代码地址

https://github.com/kdyzm/springmvc1

 

 

  

java编程学习网专注于java编程技术分享