第3章 Java Web开发 随着网络技术的飞速发展,Web程序应用领域越来越多,涵盖各种社交、电子商务、网上银行等。Java语言具有跨多平台、可移植性高的优点,通过不断优化,使得其非常适合Web应用的开发。本章将介绍Web程序开发过程中应该掌握的各种关键技术,包括开发环境搭建、MySQL数据库技术、Spring和MyBatis框架等内容。通过本章的学习,读者及相关开发者应能掌握Java Web应用的各个关键开发技术。 3.1Java Web开发环境的搭建 开发环境的搭建是Java Web应用开发的基础,包括安装Java开发工具包JDK、Web服务器(Tomcat)、数据库和IDE开发工具(IntelliJ IDEA),下面将介绍在IDE开发工具中如何配置Web服务器,最后介绍简单Web项目的发布及运行。 3.1.1Java Tomcat安装 首先介绍Web服务器(Tomcat)的安装。Tomcat服务器是Apache软件基金会(Apache Software Foundation)的Jakarta项目组的产品,目前Tomcat最新版本为10.0.2,同时它能支持Servlet和JSP规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java爱好者的喜爱并得到了软件开发商的认可,成为目前比较流行的Web应用服务器。本节将介绍Tomcat服务器的安装与配置。 本书使用Tomcat 10.0.2版本,读者可自行去Apache官网下载最新版本。下面将介绍Tomcat 10.0.2的具体下载及安装步骤。 官网网址为http://tomcat.apache.org/,其首页如图31所示。 图31Apache官方网站 左侧Download栏有各种版本供下载,单击Tomcat 10超链接,进入Tomcat 10下载页面,如图32所示。在图32中,包含了Tomcat服务器安装文件的不同平台的不同版本。本书以Windows 10 64位系统为例,选择“64bit Windows zip (pgp , sha512)”超链接进行下载。下载完成后,得到一个zip格式压缩包,将其解压后即可使用。 单击文件夹bin下的startup.bat图标,即可启动Tomcat,如图33所示。单击shutdown.bat图标,即可关闭Tomcat。 图32Tomcat下载链接 图33启动Tomcat 安装完成后,还需进行Tomcat环境变量配置,整个步骤如下: 右击“我的电脑”,单击“属性”命令,选择“高级系统设置”→“环境变量”,如图34所示。 图34Tomcat环境变量配置 在“系统变量”中添加系统变量CATALINA_BASE和CATALINA_HOME; 对应的变量值都是Tomcat的安装路径(本例中为D:\apachetomcat10.0.2),如图35所示。 图35系统变量CATALINA_BASE和CATALINA_HOME设置 此处还需修改Path的变量值。选中Path变量,单击“编辑”按钮,变量名为Path,在变量值的原有值后面填入“;%CATALINA_HOME%\bin; %CATALINA_HOME%\lib”,如图36所示。 图36系统变量Path设置 最后需要验证配置是否成功。选择“开始”→“运行”,输入cmd(或按快捷键Win+R),输入命令: startup,若出现如图37所示信息,则说明环境变量配置成功。 图37Tomcat环境变量配置成功 此外,在网页端输入地址http://localhost:8080/,若打开页面如图38所示,则说明安装Web服务器成功。 图38Tomcat安装验证 3.1.2在IntelliJ IDEA中配置Tomcat 接下来将介绍如何在IDE开发工具中配置Tomcat。进行Java Web开发,可选择常用的IDE开发工具IntelliJ IDEA。IntelliJ IDEA的下载及安装详见本书第1章。需要注意的是,IntelliJ IDEA旗舰版才可以进行Tomcat服务器的配置,而IntelliJ IDEA社区版没有此项功能。 首先启动IntelliJ IDEA,在菜单栏单击Run→Edit Configurations,如图39所示。 在弹出的Configurations对话框中,单击左侧“+”图标,在下拉菜单中选择Tomcat Server→Local,如图310所示。 图39IntelliJ IDEA中Edit Configurations路径 图310IntelliJ IDEA中Tomcat服务器Local路径 在弹出的窗口中,选择Tomcat Server→Unnamed→Server→Application server,并单击Configuration按钮,如图311所示。 图311IntelliJ IDEA中Tomcat服务器Configuration路径 在弹出的窗口中,在Tomcat Home选项框选择本地Tomcat服务器的安装路径,再单击OK按钮,如图312所示。至此,便完成了在IntelliJ IDEA中配置Tomcat服务器。 图312在IntelliJ IDEA中配置Tomcat服务器 3.1.3发布并运行Web项目 在IntelliJ IDEA中完成配置Tomcat服务器后,就可以进行Web应用的开发了。下面将通过发布并运行一个简单的Web实例项目,来介绍IntelliJ IDEA中开发Web应用的具体方法。 首先创建项目,启动IntelliJ IDEA,按照File→New Project→Java Enterprise→Web Application的流程来生成一个新的项目,如图313所示。同时指定项目名称及存放路径,如图314所示。 图313新建IntelliJ IDEA的Web项目 图314Web项目命名及存放路径 新建项目后,在左侧的子文件夹下找到默认生成的JSP文件index.jsp,双击打开后,可以对里面的默认代码进行修改。如图315所示,代码所实现的任务是输出Hello World和Hello Servlet两行字符。 图315创建的JSP文件 此外,检查Web项目设置是否正确。依次选择File→Project Structure→Project Setting,即可打开Web项目设置,如图316所示。 图316Web项目设置 在发布和运行项目前,还需要先配置Web服务器,由于前面已经配置好了Web服务器Tomcat,此处省略具体配置过程。如图317所示,在配置好Web服务器页面,Server标签页下的URL网址为项目发布地址,复制此地址以备之后在浏览器输入。 图317Web项目的发布路径 项目创建完成之后,即可将项目发布到Tomcat并运行项目。在创建项目中,单击Run或者 图标,项目运行完成后,将之前复制的URL地址粘贴到浏览器地址栏,并按下回车键运行即可。项目成功运行效果如图318所示,至此,便完成了一个简单Web项目的开发。 图318Web项目运行效果图 3.2MySQL基础 根据之前介绍的步骤,可以创建Web应用。但一个有价值的Web应用必然需要进行数据的存储和处理。Web应用最常见的数据操作就是对数据进行增删改查。比如一个图书馆管理系统应用,最核心的功能就是对图书和读者数据进行创建、修改、删除和查询,而这些功能都离不开Web服务器背后的数据库系统。数据库系统包含结构化查询语言(SQL)、数据库管理系统(常见的有MySQL、Oracle等)和数据,接下来将对每一部分内容进行详细介绍。 3.2.1SQL基础语法 SQL是用于访问和处理数据库的标准的计算机程序设计语言,是一种高级的非过程化编程语言,允许用户在高层数据结构上工作。它不要求用户指定对数据的存放方法,也不需要用户了解具体的数据存放方式,所以能应用到不同底层结构的数据库管理系统。SQL用户能对数据库进行执行查询,取回数据,插入数据,更新数据,删除记录,创建新数据库,创建新表,创建视图以及设置表、存储过程和视图的权限等操作。 一个数据库通常由一个或多个表组成。如表31所示,一个表都有一个名字标识(library),并且由多行组成,每一行包含带有数据的记录。 表31图书借阅信息表library Id FirstName LastName Book_ID Time 1 Ann Bush 1001 202011 2 Jack Gate 1002 202012 3 Peter Hill 1003 202013 对数据库的操作,主要是通过SQL语句来实现。SQL语句主要分为以下几类: 数据定义语言(Data Definition Language,DDL),数据查询语言(Data Query Language,DQL)、数据操作语言(Data Manipulation Language,DML)和数据控制语言(Data Control Language,DCL)。不同于其他编程语言,SQL 不区分字母大小写。此外,SQL通常采用分号来分隔不同的语句。 1. DDL语句 DDL语句主要用于修改、创建和删除数据库对象,常见的DDL语句包括CREATE、ALTER、DROP等。 创建新数据库采用CREATE DATABASE 命令,其语法结构如下。 create database 数据库名 on [primary] ( <数据文件参数> [,…n][<文件组参数>] ) [log on] ( <日志文件参数> [,…n] ) 创建新表采用CREATE TABLE命令,其语法结构如下。 CREATE TABLE 表名称 ( 列名称1 数据类型, 列名称2 数据类型, 列名称3 数据类型, … ) 【例3.1】创建新表CREATE TABLE Persons。 ( PersonID int, LastName varchar(255), FirstName varchar(255), Address varchar(255), City varchar(255) ); 变更(改变)数据库表采用ALTER TABLE 命令,其语法结构如下。 ALTER TABLE table_name 删除表采用DROP TABLE 命令,其语法结构如下。 DROP COLUMN column_name 索引是对数据库表中一个或多个列的值进行排序的结构,相当于一本书前面的文件夹,能加快数据库的查询速度。 创建索引(搜索键)采用CREATE INDEX命令,其语法结构如下。 CREATE INDEX index_name ON table_name(column_name) 【例3.2】在Persons表的LastName列上创建一个名为PIndex的索引。 CREATE INDEX PIndex ON Persons(LastName) 2. DML语句 DML语句主要用来查询和更新数据。常见的DML语句包括SELECT、UPDATE、DELETE和INSERT INTO等。 从数据库表中获取数据采用SELECT 命令,其语法结构如下。 SELECT列名称 FROM 表名称 【例3.3】从表31中选取 LastName 列的数据。 SQL语句: SELECT LastName FROM Persons 输出结果如表32所示。 更新数据库表中的数据采用UPDATE 命令,其语法结构如下。 表32选取LastName列的数据 LastName Bush Gate Hill UPDATE表名称 SET 列名称 = 新值 WHERE 列名称 = 某值 【例3.4】将表31中Lastname 是Gate的人对应的Time列更新为202014。 SQL语句: UPDATE Person SET Time = '2020-1-4' WHERE LastName = ' Gate ' 输出结果如表33所示。 表33更新Time后的表 Id FirstName LastName Book_ID Time 1 Ann Bush 1001 202011 2 Jack Gate 1002 202014 3 Peter Hill 1003 202013 从数据库表中删除数据采用DELETE 命令,其语法结构如下。 DELETE FROM表名称 WHERE 列名称 = 值 【例3.5】删除表31中LastName叫Hill的数据。 SQL语句: DELETE FROM Person WHERE LastName = 'Hill' 输出结果如表34所示。 表34删除Hill行后的表 Id FirstName LastName Book_ID Time 1 Ann Bush 1001 202011 2 Jack Gate 1002 202012 向数据库表中插入数据采用INSERT INTO命令,其语法结构如下。 INSERT INTO 表名称 VALUES(值1, 值2, …) 【例3.6】将表35中的数据插入表31。 表35新一行数据表 Id FirstName LastName Book_ID Time 4 Bill King 1004 202014 SQL语句: INSERT INTO Persons VALUES('Gates', 'Bill', 'Xuanwumen 10', 'Beijing') 输出结果如表36所示。 表36插入新行数据的表 Id FirstName LastName Book_ID Time 1 Ann Bush 1001 202011 2 Jack Gate 1002 202012 3 Peter Hill 1003 202013 4 Bill King 1004 202014 3. DCL语句 DCL语句主要用于控制存取许可、存取权限等,常见的DCL语句包括GRANT、REVOKE语句。 如需将对指定操作对象的指定操作权限授予指定的用户,采用GRANT命令,其语法结构如下。 GRANT<权限> ON<对象类型 >< 对象名> TO <用户> [ WITH GRANT OPTION] 【例3.7】使用 GRANT 语句创建一个新的用户 testUser,密码为 testPwd。用户 testUser 对所有的数据有查询、插入权限,并授予 GRANT 权限。 SQL语句: GRANT SELECT,INSERT ON *.* TO 'testUser'@'localhost' IDENTIFIED BY 'testPwd' 授予用户的权限可以由数据库管理员或其他授权者用REVOKE语句收回。 其语法结构如下。 REVOKE <权限> ON<对象类型><对象名> FROM<用户>[CASCADE|RESTRICT] 【例3.8】使用 REVOKE语句撤销用户 testUser 对所有的数据的查询、插入权限。 SQL语句: REVOKE SELECT,INSERT ON *.* FROM 'testUser'@'localhost' 3.2.2MySQL安装 MySQL是一种关系数据库管理系统,采用标准化的 SQL 语言进行数据库管理,其特点为体积小、速度快,相比较其他数据库管理系统,还具有免费、开源的优点。在 Web 应用方面,MySQL 是目前采用最多的RDBMS(Relational Database Management System,关系数据库管理系统)应用软件。 本节将介绍MySQL的安装及配置步骤。 首先,进入MySQL的官网(https://www.mysql.com/),如图319 所示,进入下载页面。 图319MySQL官网及下载页面 然后,下拉找到MySQL Community (GPL) Downloads超链接,单击,如图320所示。 图320MySQL社区版下载页面 如图321所示,在可选择的下载版本中选择免费的MySQL Community Server版本进行下载。 图321MySQL社区服务器端下载页面 如图322所示,选择“Windows(x86,64bit),ZIP Archive”免安装版进行下载。 图322MySQL的Windows版下载页面 接下来将下载后的压缩包进行解压,解压路径任选,但不能有中文路径。 然后进行MySQL的配置。由于后续部分命令需要root权限,所以用管理员身份打开命令行,如图323所示。 然后通过命令符的方式,跳转到解压后的MySQL的bin文件夹下,如图324所示。 图323用管理员身份打开命令行 图324跳转到MySQL的bin文件夹 图325安装MySQL的服务 通过输入命令: mysqld install,便安装了MySQL的服务,如图325所示。 通过输入命令: mysqld initialize console,进行MySQL的初始化操作,同时会产生一个随机密码,如图326中右下角的矩形框所示,复制保存这个密码,后续修改会用到该密码。 图326MySQL初始化操作 图327开启MySQL服务 通过输入命令: net start mysql,开启MySQL服务,如图327所示。 通过输入命令: mysql u root p,来验证MySQL是否安装成功,密码为之前随机生成的密码。若结果如图328所示,则说明MySQL已经安装成功。 图328登录验证MySQL是否安装成功 如果想把密码修改为“123”,则输入命令“alter user 'root'@'localhost' identified by '123';”,若出现如图329所示结果,则证明修改成功。 图329修改MySQL密码 最后可以采用修改后的密码重新登录验证一次,若出现如图330所示结果,则表明验证成功。 图330验证MySQL新密码是否修改成功 同时为了方便操作,我们也为MySQL设置一个全局变量。如图331所示,依次选择“我的电脑”→“属性”→“高级系统设置”→“环境变量”→“新建”,输入变量名mysql,变量值为MySQL的安装路径。 图331添加MySQL全局变量 然后把新建的MySQL变量添加到Path系统变量中,如图332所示。 图332修改Path变量 3.2.3使用数据库管理工具管理MySQL 前面已经进行了MySQL的安装及配置,但采用命令行方法管理MySQL既不直观也不方便,因此出现了多种数据库管理工具。其中,Navicat作为一个桌面版MySQL数据库管理和开发工具,具备图形化的用户界面,可以让用户使用和管理更为轻松。Navicat支持中文,有免费版本提供。 接下来将介绍使用Navicat来管理MySQL。首先下载安装Navicat,安装完成后再启动Navicat。然后单击软件左上角的“连接”图标,选择MySQL数据库类型,如图333所示。 图333Navicat启动界面 如图334所示,在弹出的新建连接对话框中,选择“常规”标签页,再输入自定义的连接名,作为管理数据库的标记名,主机名填数据库的服务器名称,因为MySQL安装在本地,所以填写localhost,如果是在其他主机或网络服务器则填写对应的IP,接着输入之前配置MySQL时的密码,最后确认连接。 图334Navicat连接设置界面 连接成功后,可以看到左边出现了自定义的连接名,双击打开。可以看到所有的数据库列表,打开其中一个,可以看到表名列表,如图335所示。 图335Navicat中的数据库及表列表 右击“表”选项,选择“新建表”,即可创建新表,创建后在右边会出现编辑列表框,在第一列可输入新表的字段名,还可以修改类型、长度以及主键,如果要添加新的字段,单击“插入字段”即可,如图336所示。 图336在Navicat中新建表的字段 单击“保存”之后,一个新表即被建立,如图337所示。 图337在Navicat中生成的新表 如果想再添加多行信息,单击下方的“+”号,表示增加一行,接着输入信息,可以单击下方的“√”号,或者按Ctrl+S键保存,如图338所示。 图338在Navicat中添加新行 在表名上右击,可以执行设计表、删除表等多项操作。在数据库名上右击,可以执行新建数据库、删除数据库和编辑数据库等多项操作。 此外,也可以使用Navicat书写SQL语句来操作数据库、表和表记录。如图339所示,单击“查询”选项卡,选择“新建查询”,即可弹出输入SQL语句界面。 我们需要实现在person表中再添加一行的功能,如图340所示,输入INSERT into SQL语句,完成后单击“运行”按钮。 图339在Navicat中输入SQL语句 图340在Navicat中输入插入新行SQL语句 如图341所示,信息栏提示代码实现了成功添加一行的操作。 双击person表,单击“刷新”后显示更新后的内容,新的一行被成功添加到表中,如图342所示。 图341SQL语句信息提示栏 图342SQL语句运行后的新表 3.3Spring MVC Spring的Web框架围绕DispatcherServlet设计。DispatcherServlet的作用是将请求分发到不同的处理器。Spring的Web框架包括可配置的处理器(handler)映射、视图(view)解析、本地化(local)解析、主题(theme)解析以及对文件上传的支持。 Spring Web MVC允许使用任何对象作为命令对象(或表单对象)——不必实现某个特定于框架的接口或从某个基类继承。Spring的数据绑定相当灵活,例如,它认为类型不匹配这样的错误应该是应用级的验证错误,而不是系统错误。所以你不需要为了保证表单内容的正确提交,而重复定义一个和业务对象有相同属性的表单对象来处理简单的无类型字符串或者对字符串进行转换。 Spring Web MVC框架具有如下特点。  清晰的角色划分: 控制器(controller)、验证器(validator)、命令对象(command object)、表单对象(form object)、模型对象(model object)、Servlet分发器(DispatcherServlet)、处理器映射(handler mapping)、视图解析器(view resolver)等。每个角色都可以由一个专门的对象来实现。  强大而直接的配置方式: 将框架类和应用程序类都能作为JavaBean配置,支持跨多个context的引用,例如,在Web控制器中对业务对象和验证器的引用。  可适配、非侵入: 可以根据不同的应用场景,选择合适的控制器子类(simple型、command型、form型、wizard型、multiaction型或者自定义),而不是从单一控制器(比如Action/ActionForm)继承。  可重用的业务代码: 可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。  可定制的绑定(binding)和验证(validation): 比如将类型不匹配作为应用级的验证错误,这可以保存错误的值。再比如本地化的日期和数字绑定等。在其他某些框架中,你只能使用字符串表单对象,需要手动解析它并转换到业务对象。  可定制的handler mapping和view resolution: Spring提供从最简单的URL映射,到复杂的、专用的定制策略。与某些Web MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。  灵活的model转换: 在Spring Web框架中,使用基于Map的键/值对来轻松完成与各种视图技术的集成。  可定制的本地化和主题(theme)解析: 支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity(不需要额外的中间层)等。  简单而强大的JSP标签库(Spring Tag Library): 支持包括诸如数据绑定和主题(theme) 之类的许多功能。它提供在标记方面的最大灵活性。  JSP表单标签库: 在Spring 2.0中引入的表单标签库,使得在JSP中编写表单更加容易。  Spring Bean的生命周期可以被限制在当前的HTTP Request或者HTTP Session中。 与其他Web MVC框架一样,Spring的Web MVC框架是一个请求驱动的Web框架,其设计围绕一个中心的servlet进行,它能将请求分发给控制器,并提供其他功能帮助Web应用开发。然而,Spring的DispatcherServlet所做的不仅仅是这些,它和Spring的IoC容器完全集成在一起,从而允许你使用Spring的其他功能。 DispatcherServlet实际上是一个servlet(它继承了HttpServlet)。与其他servlet一样,DispatcherServlet定义在Web应用的web.xml文件中。DispatcherServlet处理的请求必须在同一个web.xml文件里使用urlmapping定义映射。下面的例子演示了如何配置DispatcherServlet。 example org.springframework.web.servlet.DispatcherServlet 1 example *.form 在DispatcherServlet的初始化过程中,框架会在Web应用的WEBINF文件夹下寻找名为[servletname]servlet.xml的配置文件,生成文件中定义的bean。这些bean会覆盖在全局范围(global cope)中定义的同名的bean。下面这个例子展示了在web.xml中DispatcherServlet的配置: ... golfing org.springframework.web.servlet.DispatcherServlet 1 golfing *.do 可以通过两种方式定制Spring的DispatcherServlet: 在web.xml文件中增加添加context参数,或servlet初始化参数。表37是可能用到的参数。 表37DispatcherServlet初始化参数 参数 描述 contextClass 实现WebApplicationContext接口的类,当前的servlet用它来创建上下文。如果没有指定这个参数,那么默认使用XmlWebApplicationContext contextConfigLocation 传给上下文实例(由contextClass指定)的字符串,用来指定上下文的位置。这个字符串可以被分成多个字符串(使用逗号作为分隔符) 来支持多个上下文(在多上下文的情况下,如果同一个bean被定义两次,后面一个优先) namespace WebApplicationContext命名空间。默认值是[servername]servlet 控制器的概念是MVC设计模式的一部分(确切地说,是MVC中的C)。应用程序的行为通常被定义为服务接口,而控制器使得用户可以访问应用所提供的服务。控制器解析用户输入,并将其转换成合理的模型数据,从而可以进一步由视图展示给用户。Spring以一种抽象的方式实现了控制器概念,这样可以支持不同类型的控制器。Spring本身包含表单控制器、命令控制器、向导型控制器等多种多样的控制器。 Spring控制器架构的基础是org.springframework.mvc.Controller接口,其代码如下: public interface Controller { /** * Process the request and return a ModelAndView object which the DispatcherServlet * will render. */ ModelAndView handleRequest( HttpServletRequest request, HttpServletResponse response) throws Exception; } 可以发现,Controller接口仅仅声明了一个方法,它负责处理请求并返回合适的模型和视图。Spring MVC实现的基础就是这3个概念: Model、View以及Controller。虽然Controller接口是完全抽象的,但Spring也提供了许多你可能会用到的控制器。Controller接口仅仅定义了每个控制器都必须提供的基本功能: 处理请求并返回一个模型和一个视图。 为提供一套基础设施,所有的Spring控制器都继承了AbstractController,AbstractController提供了诸如缓存支持和mimetype设置这样的功能。 当从AbstractController继承时,只需要实现handleRequestInternal(HttpServletRequest, HttpServletResponse)抽象方法,该方法将用来实现自定义的逻辑,并返回一个ModelAndView对象。下面这个简单的例子演示了如何从AbstractController继承以及如何在applicationContext.xml中进行配置。 package samples; public class SampleController extends AbstractController { public ModelAndView handleRequestInternal( HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mav = new ModelAndView("hello"); mav.addObject("message", "Hello World!"); return mav; } } 所有Web应用的MVC框架都有其定位视图的方式。Spring提供了视图解析器供你在浏览器显示模型数据,而不必被束缚在特定的视图技术上。Spring内置了对JSP、Velocity模板和XSLT视图的支持。 ViewResolver和View是Spring的视图处理方式中特别重要的两个接口。ViewResolver提供了从视图名称到实际视图的映射。View处理请求的准备工作,并将该请求提交给某种具体的视图技术。 Spring提供了多种视图解析器,如表38所示。 表38视图解析器 ViewResolver 描述 AbstractCachingViewResolver 抽象视图解析器实现了对视图的缓存。在视图被使用之前,通常需要进行一些准备工作。从它继承的视图解析器将对要解析的视图进行缓存 XmlViewResolver XmlViewResolver实现ViewResolver,支持XML格式的配置文件。该配置文件必须采用与Spring XML Bean Factory相同的DTD。默认的配置文件是 /WEBINF/views.xml ResourceBundleViewResolver ResourceBundleViewResolver实现ViewResolver,在一个ResourceBundle中寻找所需bean的定义。这个bundle通常定义在一个位于classpath中的属性文件中。默认的属性文件是views.properties UrlBasedViewResolver UrlBasedViewResolver实现ViewResolver,将视图名直接解析成对应的URL,不需要显式的映射定义。如果视图名和视图资源的名字是一致的,就可使用该解析器,而无须进行映射 InternalResourceViewResolver 作为UrlBasedViewResolver的子类,它支持InternalResourceView(对Servlet和JSP的包装),以及其子类JstlView和TilesView。通过setViewClass方法,可以指定用于该解析器生成视图使用的视图类。更多信息请参考UrlBasedViewResolver的Javadoc 当使用JSP作为视图层技术时,就可以使用UrlBasedViewResolver。这个视图解析器会将视图名解析成URL,并将请求传递给RequestDispatcher来显示视图。 当返回的视图名为test时,这个视图解析器将请求传递给RequestDispatcher,RequestDispatcher再将请求传递给/WEBINF/jsp/test.jsp。 现在对于一些类型的配置数据有一个趋势,就是偏爱注解方式而不是XML文件。为了方便实现,Spring现在(从2.5版本开始)提供了使用注解配置MVC框架下的组件的支持。Spring 2.5为MVC控制器引入了一种基于注解的编程模型,在其中使用诸如@RequestMapping等。通过这种方式实现的控制器不必由特定的基类继承而来,或者实现特定的接口。更进一步地,它们通常并不直接依赖于Servlet或Portlet API,如果需要,它们可以具有访问Servlet或Portlet的功能。 实际开发过程中常利用注解(@RestController、@RequestMapping等)来开发rest接口。接下来将介绍使用Spring 4 @RestController注解实现基于RESTful JSON的 Spring 4 MVC 例子。 首先,通过采用maven来创建项目。然后,修改pom.xml 添加需要的依赖,在其中添加了Jackson library(jacksonmapperasl),用来将响应的数据转换成json 字符串。 4.0.0 com.websystique.springmvc Spring4MVCHelloWorldRestServiceDemo war 1.0.0 Spring4MVCHelloWorldRestServiceDemo Maven Webapp 4.3.0.RELEASE 2.7.5 org.springframework spring-core ${springframework.version} org.springframework spring-web ${springframework.version} org.springframework spring-webmvc ${springframework.version} javax.servlet javax.servlet-api 3.1.0 com.fasterxml.jackson.core jackson-databind ${jackson.library} com.fasterxml.jackson.dataformat jackson-dataformat-xml ${jackson.library} org.apache.maven.plugins maven-compiler-plugin 3.2 1.7 1.7 org.apache.maven.plugins maven-war-plugin 2.4 src/main/webapp Spring4MVCHelloWorldRestServiceDemo false Spring4MVCHelloWorldRestServiceDemo 其次,添加一个Pojo/domain对象,此对象将从控制器返回并被jackson转换为JSON格式。 package com.websystique.springmvc.domain; public class Message { String name; String text; public Message(String name, String text) { this.name = name; this.text = text; } public String getName() { return name; } public String getText() { return text; } } 最后,添加控制器,在其中使用了@RestController 注解,表明本类作为一个控制器,返回的是一个domain/pojo对象而不是视图。这就意味着,不再使用视图解析器,响应中不再发送html数据,而是发送domain对象的特定形式。 package com.websystique.springmvc.controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.websystique.springmvc.domain.Message; @RestController public class HelloWorldRestController { @RequestMapping("/") public String welcome() {//Welcome page, non-rest return "Welcome to RestTemplate Example."; } @RequestMapping("/hello/{player}") public Message message(@PathVariable String player) {//REST Endpoint. Message msg = new Message(player, "Hello " + player); return msg; } } 再添加配置类。 package com.websystique.springmvc.configuration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @Configuration @EnableWebMvc @ComponentScan(basePackages = "com.websystique.springmvc") public class HelloWorldConfiguration { } 再添加初始化类。 package com.websystique.springmvc.configuration; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class HelloWorldInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { return new Class[] { HelloWorldConfiguration.class }; } @Override protected Class[] getServletConfigClasses() { return null; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } } 在完成以上配置之后,就可以创建和发布应用了。 此外,常使用注解@RequestMapping映射请求。下面是一个通过使用@RequestMapping注解来实现表单控制器的例子。 @Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { private final Clinic clinic @Autowired public EditPetForm(Clinic clinic) { this.clinic = clinic; } @ModelAttribute("types") public Collection populatePetTypes() { return this.clinic.getPetTypes(); } @RequestMapping(method = RequestMethod.GET) public String setupForm(@RequestParam("petId") int petId, ModelMap model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; } @RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { new PetValidator().validate(pet, result); if (result.hasErrors()) { return "petForm"; } else { this.clinic.storePet(pet); status.setComplete(); return "redirect:owner.do?ownerId=" + pet.getOwner().getId(); } } } 3.4MyBatis基础 3.4.1初识MyBatis MyBatis本是Apache的一个开源项目iBatis,2010年这个项目由Apache Software Foundation迁移到了Google code,并且改名为MyBatis。2013年11月迁移到Github。MyBatis 是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML或注解来将原始类型、接口和Java POJO(Plain Old Java Objects,普通老式Java对象)配置和映射为数据库中的记录。 Mybatis具有以下特点:  简单易学。  灵活。  SQL和代码的分离,提高了可维护性。  提供映射标签,支持对象与数据库的ORM字段关系映射。  提供对象关系映射标签,支持对象关系组建维护。  提供XML标签,支持编写动态SQL。 MyBatis源码可以通过Maven工具或GitHub下载。 下面介绍通过GitHub下载。单击GitHub网址(https://github.com/ )。在搜索框中搜索 Mybatis,搜索结果如图343所示,同时单击mybatis3链接。 图343mybatis349下载链接 选择mybatis3.5.6版本进行下载,如图344所示。 图344mybatis3.5.6版本 选择下载mybatis3.5.6.zip压缩包,如图345所示,然后将其解压在IntelliJ IDEA中项目所属文件夹中即可,然后在IntelliJ IDEA中打开所解压的文件。 图345mybatis3.5.6.zip压缩包 3.4.2MyBatis配置 MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息。配置文档的顶层结构包括如下几个部分。 1. 属性 这些属性(properties)可以在外部进行配置,并可以进行动态替换。你既可以在典型的Java属性文件中配置这些属性,也可以在properties元素的子元素中设置。例如: <%/*实际应用中,password需要加密后再配置到配置文件中*/%> 设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如: 如果一个属性在不止一个地方进行了配置,那么,MyBatis将按照下面的顺序来加载: 首先读取在properties元素体内指定的属性。 然后根据properties元素中的resource属性读取类路径下属性文件,或根据url属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。 因此,通过方法参数传递的属性具有最高优先级,resource/url属性中指定的配置文件次之,最低优先级的则是properties元素中指定的属性。 从MyBatis 3.4.2开始,可以为占位符指定一个默认值。例如: 2. 设置 这是MyBatis中极为重要的调整设置(settings),它们会改变MyBatis的运行时行为。表39描述了设置中各项设置的含义、默认值等。 表39MyBatis设置中各项设置的含义、默认值 设置名 描述 有效值 默认值 cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存 true | false true lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态 true | false false aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。否则,每个延迟加载属性会按需加载(参考lazyLoadTriggerMethods) true | false false multipleResultSetsEnabled 是否允许单个语句返回多结果集(需要数据库驱动支持) true | false true useColumnLabel 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察 true | false true useGeneratedKeys 允许JDBC支持自动生成主键,需要数据库驱动支持。如果设置为true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby) true | false False续表 设置名 描述 有效值 默认值 autoMappingBehavior 指定MyBatis应如何自动映射列到字段或属性。NONE 表示关闭自动映射; PARTIAL只会自动映射没有定义嵌套结果映射的字段。FULL会自动映射任何复杂的结果集(无论是否嵌套) NONE, PARTIAL, FULL PARTIAL autoMappingUnknownColumnBehavior 指定发现自动映射目标未知列(或未知属性类型)的行为 NONE, WARNING, FAILING NONE defaultExecutorType 配置默认的执行器。SIMPLE就是普通的执行器; REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句,还会执行批量更新 SIMPLE REUSE BATCH SIMPLE defaultStatementTimeout 设置超时时间,它决定数据库驱动等待数据库响应的秒数 任意正整数 未设置 (null) defaultFetchSize 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖 任意正整数 未设置 (null) safeRowBoundsEnabled 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false true | false False safeResultHandlerEnabled 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为false true | false True mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN映射到经典Java属性名aColumn true | false False localCacheScope MyBatis利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。默认值为SESSION,会缓存一个会话中执行的所有查询。若设置值为STATEMENT,本地缓存将仅用于执行语句,对相同SqlSession的不同查询将不会进行缓存 SESSION | STATEMENT SESSION 一个配置完整的settings元素的示例如下: 3. 类型别名 类型别名(typeAliases)可用于为Java类型设置一个缩写名字。它仅用于XML配置,意在减少冗余的全限定类名书写。例如: 4. 对象工厂 每次MyBatis创建结果对象的新实例时,它都会使用一个对象工厂(objectFactory)实例来完成实例化工作。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。如果想覆盖对象工厂的默认行为,那么可以通过创建自己的对象工厂来实现。例如: // ExampleObjectFactory.java public class ExampleObjectFactory extends DefaultObjectFactory { public Object create(Class type) { return super.create(type); } public Object create(Class type, List constructorArgTypes, List constructorArgs) { return super.create(type, constructorArgTypes, constructorArgs); } public void setProperties(Properties properties) { super.setProperties(properties); } public boolean isCollection(Class type) { return Collection.class.isAssignableFrom(type); }} 5. 环境配置 MyBatis可以配置成适应多种环境(environments),这种机制有助于将SQL映射应用于多种数据库之中,现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置; 或者想在具有相同Schema的多个生产数据库中使用相同的SQL映射。还有许多类似的使用场景。 不过要记住: 尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境。 所以,如果想连接两个数据库,就需要创建两个SqlSessionFactory实例,每个数据库对应一个。而如果是3个数据库,就需要3个实例,以此类推,记起来很简单—— 每个数据库对应一个SqlSessionFactory实例。 为了指定创建哪种环境,只要将它作为可选的参数传递给SqlSessionFactoryBuilder即可。可以接受环境配置的两个方法签名是: SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties); environments元素定义了如何配置环境。 还有其他元素的细节见官方文档(http://www.mybatis.org/mybatis3/zh/sqlmapxml.html)。 3.4.3MyBatis关联映射 MyBatis的真正强大之处在于它的语句映射。由于它异常强大,映射器的XML文件就显得相对简单。如果拿它跟具有相同功能的JDBC代码进行对比,你会立即发现省掉了将近95%的代码。MyBatis致力于减少使用成本,让用户能更专注于SQL代码。 查询语句是MyBatis中最常用的元素之一,仅能把数据存到数据库中价值并不大,还要能重新取出来才有用,多数应用也都是查询比修改要频繁。MyBatis的基本原则之一是: 在每个插入、更新或删除操作之间,通常会执行多个查询操作。因此,MyBatis在查询和结果映射方面做了相当多的改进。一个简单查询的select元素是非常简单的。比如: 数据变更语句insert、update和delete的实现非常接近: resultMap元素是MyBatis中最重要最强大的元素。它可以让你从90%的JDBC ResultSets数据提取代码中解放出来,并在一些情形下允许你进行一些JDBC不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份resultMap能够代替实现同等功能的数千行代码。resultMap的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系即可。 以下是简单映射语句的示例,它们没有显式指定resultMap。比如: 上述语句只是简单地将所有的列映射到HashMap的键上,这由resultType属性指定。虽然在大部分情况下都够用,但是HashMap并不是一个很好的领域模型。程序更可能会使用JavaBean或POJO(Plain Old Java Objects,普通老式Java对象)作为领域模型。MyBatis对两者都提供了支持。看看下面这个JavaBean: package com.someapp.model; public class User { private int id; private String username; private String hashedPassword; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getHashedPassword() { return hashedPassword; } public void setHashedPassword(String hashedPassword) { this.hashedPassword = hashedPassword; } } 基于JavaBean的规范,上面这个类有3个属性: id、username和hashedPassword。这些属性会对应到select语句中的列名。 这样的一个JavaBean可以被映射到ResultSet,就像映射到HashMap一样简单。 关联(association)元素处理“有一个”类型的关系。比如,在我们的示例中,一个博客有一个用户。关联结果映射和其他类型的映射工作方式差不多。你需要指定目标属性名以及属性的javaType(很多时候MyBatis可以自己推断出来),在必要的情况下你还可以设置 JDBC类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。 关联的不同之处是,你需要告诉MyBatis如何加载关联。MyBatis有两种不同的方式加载关联:  嵌套select查询——通过执行另外一个SQL映射语句来加载期望的复杂类型。  嵌套结果映射——使用嵌套的结果映射来处理连接结果的重复子集。 关联的嵌套select查询示例如下: 有两个select查询语句: 一个用来加载博客(Blog),另外一个用来加载作者(Author),而且博客的结果映射描述了应该使用selectAuthor语句加载它的author属性。 其他所有的属性将会被自动加载,只要它们的列名和属性名相匹配。 下面的实例用于演示嵌套结果映射如何工作。现在将博客表和作者表连接在一起,而不是执行一个独立的查询语句。 可以看到,博客(Blog)作者(author)的关联元素委托名为authorResult的结果映射来加载作者对象的实例。 3.4.4MyBatis和Spring的整合 MyBatisSpring会帮助你将MyBatis代码无缝地整合到Spring中。它将允许MyBatis参与到Spring的事务管理之中,创建映射器mapper和SqlSession并注入到bean中,以及将 MyBatis的异常转换为Spring的DataAccessException。最终,可以做到应用代码不依赖于 MyBatis、Spring或MyBatisSpring。 在开始使用MyBatisSpring之前,需要先熟悉Spring和MyBatis这两个框架及其有关的配置。 要使用MyBatisSpring模块,只需要在类路径下包含mybatisspring2.0.6.jar文件和相关依赖即可。 如果使用Maven作为构建工具,则仅需要在pom.xml中加入以下代码: org.mybatis mybatis-spring 2.0.6 要和Spring一起使用MyBatis,就需要在Spring应用上下文中定义至少两样东西: 一个 SqlSessionFactory和至少一个数据映射器类。 在MyBatisSpring中,可使用SqlSessionFactoryBea来创建SqlSessionFactory。要配置这个工厂bean,只需要把下面代码放在Spring的XML配置文件中: @Configuration public class MyBatisConfig { @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource()); return factoryBean.getObject(); } } 注意: SqlSessionFactory需要一个DataSource(数据源)。这可以是任意的DataSource,只需要和配置其他Spring数据库连接一样配置它就可以了。 假设定义了一个如下的mapper接口: public interface UserMapper { @Select("SELECT * FROM users WHERE id = #{userId}") User getUser(@Param("userId") String userId); } 那么可以通过MapperFactoryBean将接口加入到Spring中: 需要注意的是,所指定的映射器类必须是一个接口,而不是具体的实现类。在这个示例中,通过注解来指定SQL语句,但是也可以使用MyBatis映射器的XML配置文件。 配置好之后,就可以像Spring中普通的bean注入方法那样,将映射器注入业务或服务对象中。MapperFactoryBean将会负责SqlSession的创建和关闭。如果使用了Spring的事务功能,那么当事务完成时,session将会被提交或回滚。最终任何异常都会被转换成Spring 的DataAccessException异常。 使用Java代码来配置的方式如下: @Configuration public class MyBatisConfig { @Bean public UserMapper userMapper() throws Exception { SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory()); return sqlSessionTemplate.getMapper(UserMapper.class); } } 要调用MyBatis的数据方法,可使用如下代码: public class FooServiceImpl implements FooService { private final UserMapper userMapper; public FooServiceImpl(UserMapper userMapper) { this.userMapper = userMapper; } public User doSomeBusinessStuff(String userId) { return this.userMapper.getUser(userId); } } 3.5本章小结 本章首先介绍了Java Web开发环境的搭建,包括安装Tomcat服务器以及在IntelliJ IDEA集成开发环境中配置Tomcat,并在此基础上发布并运行了一个简单的Web项目; 动态的Web开发需要进行大量数据的处理,因此对数据库技术MySQL也进行了相关介绍,包括SQL基础语法,MySQL的安装及使用数据库管理工具Navicat管理MySQL; 然后针对Java Web开发常用的Spring和MyBatis框架进行了介绍,包括如何使用Spring MVC、MyBatis配置、MyBatis关联映射及MyBatis和Spring的整合等知识。 3.6课后练习 一、 填空题 1. 在Servlet开发中,实现了多个Servlet之间数据共享的对象是。 2. 在Servlet容器启动每一个Web应用时,就会创建一个唯一的ServletContext对象,该对象和Web应用具有相同的。 3. ServletConfig对象是由创建出来的。 4. Tomcat服务器的默认端口号是。 5. 用于监听ServletRequest对象生命周期的接口是。 6. 用于监听HttpSession对象生命周期的接口是。 7. PreparedStatement是Statement的子接口,用于执行的SQL语句。 8. Statement接口的executeUpdate(String sql)方法用于执行SQL中的insert、和delete语句。 二、 判断题 1. ServletConfig对象可以实现多个Servlet之间的数据共享。() 2. 一个元素下配置多个子元素能实现Servlet的多重映射。() 3. 一个Servlet可以映射多个虚拟路径。() 4. 当访问一个Web应用程序时,如果没有指定资源名称,则会访问默认的页面。() 5. 在一个web.xml中只能配置一个监听器。() 6. 采取在servler.xml文件中配置虚拟文件夹,每次修改server.xml文件后,都需要重启服务器,否则修改的配置将不会生效。() 7. 对于相同的SQL语句,Statement对象只会对其编译执行一次。() 8. ResultSet接口表示select查询语句得到的结果集,该结果集封装在一个逻辑表格中。() 三、 选择题 1. 在下列选项中,()方法用于返回映射到某个资源文件的URL对象。 A. getRealPath(String path)B. getResource(String path) C. getResourcePaths(String path)D. getResourceAsStream(String path) 2. 下列选项中, 用于根据虚拟路径得到文件的真实路径的方法是()。 A. String getRealPath(String path) B. URL getResource(String path) C. Set getResourcePaths(String path) D. InputStream getResourceAsStream(String path) 3. 下列选项中, 用于设置ServletContext的域属性的方法是()。 A. setAttribute(String name,String obj) B. setParameter(String name,Object obj) C. setAttribute(String name,Object obj) D. setParameter (String name,Object obj) 4. 下列选项中,()是web.xml中配置初始化参数的标签。 A.< paraminit >B. < initparam >C. < param >D. < init > 5. 下列选项中,可以成功修改Tomcat端口号为80的是()。 A. < Connect port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" / > B. < Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" / > C. < Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" / > D. < Connect port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" / > 6. 下列选项中,表示服务器错误的状态码是()。 A. 100B. 404C. 304D. 500 7. 下面关于executeQuery(String sql)方法,说法正确的是()。 A. 可以执行insert语句B. 可以执行update语句 C. 可以执行select语句D. 可以执行delete语句 8. 下列选项中,用于将参数化的SQL语句发送到数据库的方法是()。 A. prepareCall(Stringsql)B. prepareStatement(Stringsql) C. registerDriver(Driverdriver)D. createStatement() 四、 问答题 1. Tomcate 如何配置Web项目? 2. IntelliJ IDEA中如何发布Web项目? 3. 在Spring框架中如何更有效地使用JDBC? 4. 使用Spring框架的好处是什么? 5. 在Spring AOP 中,关注点和横切关注的区别是什么?