cms自助建站系统,景区网站建设方案 费用,做网站的程序,网站设计主要包括哪些步骤原文#xff1a;Learn Java for Web Development 协议#xff1a;CC BY-NC-SA 4.0 零、简介
这本书是为具有不同经验水平的现代 Java web 开发人员编写的。
学习 Java 编程语言是一项崇高的事业#xff0c;但是在现实世界中#xff0c;仅仅学习 Java 语言是不够的。Java … 原文Learn Java for Web Development 协议CC BY-NC-SA 4.0 零、简介
这本书是为具有不同经验水平的现代 Java web 开发人员编写的。
学习 Java 编程语言是一项崇高的事业但是在现实世界中仅仅学习 Java 语言是不够的。Java 开发人员必须学习 Java EE这是一个相关服务器端技术的集合以便将他们的 Java 技能投入到任何实际应用中。
但是学习 Java EE 也是不够的。Java 语言和 Java EE 可能足以为同一个组织中的项目开发 web 应用作为一种可重用性的手段但是 web 上 Java 的多样化前景渗透着几个 Web 框架如 Spring Web MVC这使得开发更加容易因此Java web 开发人员必须了解这些 web 框架。
但是这还不够。在这篇介绍的第一行我提到这本书是为现代 Java web 开发人员编写的。现代 Java 不仅仅是一种语言它现在是一个完全优化的平台因为其他几种语言如 Groovy 和 Scala称为 JVM 语言现在运行在 Java 虚拟机(JVM)上。所有这样的 JVM 语言尤其是 Groovy都与 Java 有着密切的联系不久你就会遇到 Java 和其他 JVM 语言协同工作的 web 应用。最雄心勃勃的项目将要求您使用这些 JVM 语言构建 web 应用。
这本书满足了现代 Java web 开发人员的所有需求。它是为初学者到中级开发人员设计的并在 Web 上解释了 Java 的细节。例如这本书非常适合那些知道 MVC 这样的技术但还不明白它们是如何以及为什么改变了 web 应用构建方式的开发人员。
这本书也适用于那些想学习 JSF 2(与 Java EE 捆绑在一起)之外的框架的开发人员。这本书涵盖了四种类型的 web 框架:基于请求的、基于组件的、快速的和反应式的。在这四种类型中本书涵盖了五种成熟的 web 框架:Struts 2、Spring Web MVC、JSF 2、Grails 2 和 Play 2。
此外这本书面向那些没有 Java、Groovy 和 Scala 编程语言经验但渴望创建 web 应用的开发人员。本书在附录中提供了这三种语言的要点。
Learn Java for web Development通过一个真实的书店应用展示了最流行的 Web 框架的优势而不是简单地宣布一个 Web 框架是最好的。开发一个完整的真正的应用需要动态功能的无缝协作而构建这样的组件的代码是人为设计的而且过于复杂。这本书没有把重点放在开发这样的移动部件上而是把注意力集中在利用每个 web 框架的优势上。
这本书的结构
这本书由八章组成我将在接下来描述加上前面提到的介绍 Java、Groovy 和 Scala 语言的三个附录。
第一章:Java Web 开发简介
第一章解释了塑造本书的主要目标并强调了后续章节中出现的内容。本章首先讨论了 Java 领域的一个重大变化它的含义以及 Java 在今天到底意味着什么。本章随后讨论了构建现代 Java web 应用的三个主要参与者:JVM 语言、Java EE 和 Java web 框架。
本章介绍了现代 Java web 应用的关键特性如 Ajax 和 REST、用于实时 web 应用的 WebSocket、用于反应式 web 应用的类型安全堆栈以及用于响应式和单页 web 应用的客户端 MVC 框架。最后本章介绍了现代 web 开发的一些重要方面这些方面超出了本书的范围如 Web 上的 Java 信息检索并简要介绍了 Web 3.0 的核心组件它仍然是一个开放的研究主题即语义 Web。
第二章:使用 Servlets 和 JSP 构建 Web 应用
第二章从讨论 web 应用的发展和架构开始。本章接着强调了如何使用标准的 web API。示例应用的第一步仅使用 servlets 和 JSP。然后本章将向您展示如何构建与 Model 2 应用相同的应用。
第三章:Java EE Web 开发的最佳实践
第三章分析了导致需要遵循最佳实践的因果链。本章解释了开发项目的必要性并介绍了表达式语言和 JSTL。本章随后讨论了 Java EE web 层模式。
第四章:使用 Struts 2 构建一个 Web 应用
在第四章中您将了解 Struts 2。Struts 2 已经不像以前那样流行了在本书中Struts 2 被介绍给那些必须维护遗留应用的开发人员。本章首先介绍 Struts 2 的关键架构组件。然后您将学习使用 Struts 2 和 Maven 4 开发您的第一个应用。接下来您将学习开发书店应用并与 Tiles 3 集成。
第五章:用 Spring Web MVC 构建 Java Web 应用
第五章解释了 Spring 框架的三个关键目标:使用依赖注入的松散耦合使用 AOP 处理横切关注点以及使用 Spring 模板移除样板代码。阐明 Spring 3 如何工作本章介绍了 Spring Web MVC 架构。然后您将学习使用 Spring 3 web MVC 构建您的第一个 Web 应用。本章还向您展示了如何构建书店应用。你将学会使用最新版本的 SpringSource 工具套件。
第六章:使用 JSF 的基于组件的 Web 开发
第六章向您介绍一个基于组件的框架名为 JSF 2它与 Java EE 捆绑在一起。在你熟悉了第四章和第五章中提出的基于请求的框架之后理解《JSF 2》将会容易得多。这一章向你展示了 JSF 新协议如何代表了 web 开发中的一个范式转变并向你介绍了 JSF 新协议体系结构的关键组件。在你牢固掌握了体系结构组件之后本章将向你展示如何开发你的第一个 JSF 2 应用同时你将学习 JSF 2 应用的生命周期阶段。然后本章向您展示了如何将 JSF 2 与 Spring 框架集成以便您可以通过 Spring 模板从 JSF 2 web 层访问数据库。最后本章向您展示了如何开发书店应用。
第七章:使用 Grails 进行快速 Web 开发
Grails 是一个快速的应用开发框架可以让您在创纪录的时间内创建 web 应用。第七章向您介绍了用 Grails 生成 web 应用的两种技术:静态和动态搭建。然后本章将带您浏览生成的代码并一步一步地解释代码是如何工作的。给出生成的代码后本章将向您展示如何使用 Grails 2 开发书店应用。本章还介绍了单元测试这是一个在应用开发中经常被忽视的任务。本章向您展示了如何使用 JUnit 测试框架为您的 web 应用构建测试。然后本章将向您展示如何使用内存数据库 H2。在本章中您还将学习使用最新版本的 Groovy-Grails 工具套件。
第八章:玩转 Java 和 Scala
第八章介绍了 Typesafe 堆栈的关键 web 播放器 Play 2 框架并解释了 Typesafe 堆栈如何提供 Java EE 的替代方案来构建基于 Java 和 Scala 的应用。首先您将学习使用 Play 2 开发一个基于 Java 的 web 应用。然后您将学习使用 Play 2 开发一个基于 Scala 的 web 应用。随后本章将展示如何在行动 2 中使用该模型和访问数据库。
一、Java Web 开发简介
一旦被一个新的想法拉伸头脑就再也不会回到它最初的维度。
拉尔夫·瓦尔多·爱默生 Ralph Waldo Emerson
智能机器是一种能扩展其想象力的机器。这方面的一个例子是名为 invokeDynamic 1 的指令它是在 Java 7 中引入的用于优化动态类型语言在 Java 虚拟机(JVM)上的性能。最初为 Java 设计的 JVM 现在可以托管无数的编程语言包括 Groovy 2 和 Scala。 3 这导致了 Java web 开发的复兴。这种交叉授粉的新范例和多样化的、有根据的选项在 Java 生态系统中开辟了许多利基市场导致了比以往任何时候都更加丰富的 web 景观。
开源社区利用运行在 JVM 上的语言所提供的多平台能力通过 web 框架极大地提高了 web 开发的效率。Java EE 4 推进了这一势头由 Spring、 5 等 Java 框架通过标准化和改进 API 和运行时环境开创了这一势头。此外lambdas 形式的函数式编程结构已经被添加到 Java 8 中。因此Java 正在反弹成为一个超级解决方案。
本章通过介绍构建现代 Java web 应用的三个主要参与者为本书做准备:JVM 语言、Java EE 和 Java web 框架。
注意 JVM 语言代表了在 JVM 上运行的一种新的语言类别。有了最新版本 Java 8Java 不再是一种特权 JVM 语言现在只是运行在 JVM 上的许多语言之一。
本章首先介绍 JVM 语言然后介绍 Java EE。Java EE 平台是一组 API 规范充当开发 web 应用的构建块。然后这一章强调了 Java web 框架这将是本书从第四章开始的主题。
JVM 语言
JVM 是一个运行时环境它使您能够使用不同的编程语言来构建 web 应用。JVM 语言可以大致分为两种类型:为 JVM 设计的语言和移植到 JVM 的现有语言。
为 JVM 设计的语言
许多语言是专门为 JVM 设计的表 1-1 描述了其中的几个。除了 Clojure 之外的所有内容都在本书中讨论。
表 1-1 。为 JVM 设计的语言 |
为 JVM 设计的语言
|
描述
| | — | — | | Clojure 6 | Clojure 是一种动态类型的函数式语言。 | | 绝妙的 | Groovy 是一种动态的编译语言语法类似于 Java但更灵活。 | | Java | Java 是一种静态类型的命令式语言。Java 的最新版本 Java 8 支持函数式编程的各个方面。 | | 斯卡拉 | Scala 是一种静态类型的编译语言支持函数式编程的各个方面并执行大量的类型推断很像一种动态语言。 |
以下是一些重要的定义:
动态类型化 :动态类型化通过携带变量中保存的值的类型信息来跟踪关于变量包含何种值的信息。静态类型 :在静态类型中类型信息都是关于变量的而不是变量中的值。命令式语言 :这些语言中的指令可以改变语言的状态。函数式语言 :在函数式语言中函数像在过程式语言中一样对值进行操作但函数不是改变状态而是返回新值的纯数学函数。
图 1-1 显示了 Java 8、Groovy、Scala 和 Clojure 在函数式语言连续体中的位置。Java 8 引入了 lambdas这使得它稍微有点函数性Groovy 从一开始就有函数构造在 Groovy 2.0 中功能性更强Scala 是三种面向对象(OO)语言中功能性最强的。另一方面Clojure 是一种纯函数式的非 OO 语言。 图 1-1 。 JVM 语言的功能分级
注意在图 1-1 中没有提到 Groovy、Scala 和 Clojure 的版本号因为 Java 只支持从 Java 8 开始的函数式编程。
移植到 JVM 的语言
JRuby、Jython 和 Rhino 是现有语言的几个主流 JVM 实现。表 1-2 描述了它们。
表 1-2 。移植到 JVM 的语言 |
移植到 JVM 的语言
|
描述
| | — | — | | JRuby 7 | JRuby 是 Ruby 编程语言的 JVM 重新实现。Ruby 是一种动态类型的 OO 语言具有一些功能特性。 | | jython8 | Jython 是 Python 在 JVM 上的重新实现所以它是一种动态语言。 | | 犀牛 9 | Rhino 在 JVM 上提供了 JavaScript 的实现。JavaScript 是一种动态类型的面向对象语言。 |
这本书基于一些专门为 JVM 设计的主流面向对象 JVM 语言即 Java、Groovy 和 Scala。
Java EE
Java 最初是一种为构建独立应用而设计的编程语言并迅速发展到其他领域。Java 的流行很大程度上可以归功于它在创建 web 应用中的使用。web 应用由静态和动态(交互式)网页组成。静态网页 包含各种类型的标记语言(HTML、XHTML 等)通常用于提供信息动态网页 另一方面能够在附加 web 组件的帮助下生成内容(在第二章中介绍)。因此网络应用是网页的集合并且能够响应请求生成动态内容。与仅用于提供信息的网页不同web 应用允许您执行一些活动并保存结果。然而开发 web 应用与构建独立的应用有着本质的不同需要您理解以下三个关键要素:
Java EE 平台 :这是一组 API 规范是 web 应用的构建块。web 容器:web 容器实现 Java EE 平台的 API 规范。具体来说web 容器提供了用于管理和执行 web 组件的服务比如 servlets、JSP、过滤器、监听器和向客户端呈现响应。web 容器包含在第二章中。
注意有几种类型的容器但是本书将集中讨论主要用于 web 应用的 web 容器。您必须根据您想要开发的应用的类型来选择容器。
Web 组件:这些组件由容器托管。这些 web 组件比如 servlets、JSP、过滤器和监听器将在第二章的中介绍。
Java EE 平台
Java EE 平台由以下两个目标驱动 :
提供作为 web 应用构建块的 API 规范。标准化和降低企业应用开发的复杂性。它通过提供一个应用模型来实现这一点该模型定义了将服务实现为多层应用的架构。
图 1-2 总结了 Java EE 的发展为了简洁起见只显示了每个版本中添加的新规范。 图 1-2 。 Java EE 的演变
注意 修剪(也称为标记为删除)由一个建议的特性列表组成这些特性可能会在下一个 Java EE 版本中删除以减小平台的大小或防止其膨胀。
Web Profile 的目标是允许开发人员使用适当的技术创建 Web 应用。
Java EE 平台旨在通过提供一个应用模型来标准化和降低企业应用开发的复杂性该应用模型定义了将服务实现为多层应用的体系结构。在多层应用中应用的功能被分成不同的功能区域称为层。图 1-3 展示了 Java EE 应用模型中典型的多层架构 。 图 1-3 。Java 中的多层架构
客户端层
客户机层是多层 Java EE 架构中的顶层它由向 Java EE 服务器发出请求的应用客户机组成javaee 服务器通常位于不同的机器上。服务器处理请求并向客户端返回响应。客户端的一个例子是 web 浏览器或独立应用。
Web 层
web 层由处理客户端和业务层之间交互的组件组成。从客户端收到请求后web 层执行以下操作:
收集来自客户端的输入控制客户端上屏幕或页面的流动维护用户会话的数据状态从业务层中的组件获取结果为客户端生成各种格式的动态内容
如图图 1-2 所示Java EE 7 中增加了一个新的 Web Profile 规范。 10 表 1-3 列出了 Web Profile 规范中包含的技术。如前所述Web Profile 的目标是允许开发人员使用适当的技术创建 Web 应用。
表 1-3 。Web Profile 7 规范 |
规格
|
版本
|
统一资源定位器
| | — | — | — | | 联合打击战斗机 | Two point two | JCP . org/en/JSR/detailid344 | | JSP | Two point three | JCP . org/en/JSR/detailid245 | | 标准标记库(JSP Standard Tag Library) | One point two | JCP . org/en/JSR/detailid52 | | 小型应用 | Three point one | JCP . org/en/JSR/detailid340 | | WebSocket | One | JCP . org/en/JSR/detailid356 | | 表达语言 | Three | JCP . org/en/JSR/detailid341 | | 哦我的上帝 | Three point two | JCP . org/en/JSR/detailid345 | | 作业的装配区JobPackArea | Two point one | JCP . org/en/JSR/detailid338 | | JTA | One point two | JCP . org/en/JSR/detailid907 | | Bean 验证 | One point one | JCP . org/en/JSR/detailid349 | | 受管 Beans | One | JCP . org/en/JSR/detailid316 | | 截击机 | One point two | JCP . org/en/JSR/detailid318 | | 上下文和依赖注入 | One point one | JCP . org/en/JSR/detailid346 | | Java 的依赖注入 | One | JCP . org/en/JSR/detailid330 | | 对其他语言的调试支持 | One | JCP . org/en/JSR/detailid45 | | JAX-RS 啊 | Two | JCP . org/en/JSR/detailid339 | | JSON-P | One | JCP . org/en/JSR/detailid353 |
关于表 1-3 中列出的网页简介规格:
在 Java EE 7 中没有对 JSP 和 JSTL 进行任何更改因为这些规范还没有更新。表达式语言已经从 JSP 中移除现在有了自己的 JSR (341)。Servlets 和 JSF 都得到了更新。WebSocket 1.0 是在 Java EE 7 中引入的。
这本书专注于 Java EE 的 web 层我们将在第二章中深入探讨 web 层。
Java EE 的多层架构对 Java 企业应用的开发有着巨大的影响。 Java 企业应用 可以定义为利用 Java EE 提供的企业服务的 Java 应用。事实上如果一个 web 应用以打包在 web 层中的组件的形式利用 Java EE 服务那么它可以被归类为企业应用。如图 1-3 所示Java EE 通过提供一个构建 Java 企业应用的应用模型将这些服务从功能上隔离到不同的层中。因此Java 企业应用反映了 Java EE 的多层架构。图 1-4 展示了一个典型的 web 应用层的一般视图。 图 1-4 。一个企业应用中各层的综合视图
图 1-4 中的每一层都是关注的一个区域用于应用。例如web 层只处理使用 Java EE 的 web 层组件。在一个应用中拥有不同的层会导致所谓的关注点分离。就实现而言这种关注点的分离是使用粗粒度接口实现的。
关注点是应用开发人员需要关注的特性、功能或业务功能。横切这样的关注点在复杂系统中是固有的并导致代码分散这是当一个关注点的代码跨越许多模块时以及代码缠结这是当一个模块中的代码集中处理多个关注点时。代码分散和代码纠缠导致缺乏清晰性、冗余性、刚性和持续重构。图 1-5 说明了日志、事务和安全的系统服务如何横切应用的业务功能。 图 1-5 。涉及系统服务的图书服务
图 1-5 中的 BookService 与系统服务过于相关。每个对象都知道并负责日志记录、安全性和事务。例如在 BookService 中购买一本书的方法应该只关心如何购买这本书而不关心它是安全的还是事务性的。关注点分离是软件工程的主要目标之一它允许您单独处理每个服务从而完成以下任务:
在系统的整个生命周期中促进系统中工件内部和之间的可追溯性控制由变化引起的影响从而提供进化和非侵入性适应的范围促进内聚单元的开发从而促进重用
关注点分离
术语关注点分离 (SoC) 是 Edsger W. Dijkstra 在他的论文《论科学思想的作用》中创造的 11 迪杰斯特拉在以下条款中解释道:
让我试着向你解释对我来说什么是所有智能思维的特征。那就是一个人愿意为了自己的一致性而孤立地深入研究他的主题的一个方面始终知道自己只专注于其中的一个方面。我们知道一个程序必须是正确的我们只能从这个角度来研究它我们也知道它应该是高效的可以说我们可以改天再研究它的效率。在另一种情绪下我们可能会问自己这个项目是否值得如果值得为什么值得。但是什么也得不到——相反—通过同时处理这些不同的方面。这就是我有时称之为“关注点分离”的方法即使不完全可能但据我所知这是有效整理一个人思想的唯一可行的方法。这就是我所说的“将一个人的注意力集中在某个方面”:这并不意味着忽略其他方面这只是公正地对待这样一个事实即从这个方面的观点来看其他方面是不相关的。这是一个和多个轨道的思想同时存在。
网页层
web 应用的 web 层由 Java EE 的 web 层组件组成如 servlets 和 JSP。web 层可以访问服务层但是 web 层和服务层之间不应该有紧密的耦合。也就是说更改服务层不会影响 web 层。
服务层
服务层由 Java EE 的业务层组件组成比如 Enterprise JavaBean s(EJB)。服务层可以访问数据访问层但是服务层和数据访问层之间不应该有紧密的耦合。事实上服务层不应该知道任何关于 web 或数据访问层的事情。服务层为 web 层提供了粗粒度的接口。
数据访问层
数据访问层由 Java EE 的数据层组件组成如 JDBC 和 JPA。这一层不应该包含任何业务逻辑。该层通过向服务层提供粗粒度接口从服务层抽象出实际的持久性机制(换句话说JDBC 或 JPA)。
注意这种架构的调用流程总是从顶层到底层。换句话说服务层应该能够调用数据访问层而不是相反。
在本章中您将构建书店应用的数据访问层并通过独立的 Java 应用对其进行查询。在第二章中您将使用 Java EE 的 web 层组件(特别是 servlets 和 JSP)将这个独立的 Java 应用替换为 web 层。在本书中你将会用到这个数据访问层从第四章开始你将会通过使用不同的 web 框架重新构建 web 层来重复构建 web 应用。
Oracle 和 Java 社区进程(JCP)提供了标准化的企业组件如果可以使用这些组件构建成功的企业应用那么我们为什么还需要 web 框架呢web 框架是用来做什么的下一节将回答这些问题。
Java Web 框架
虽然 Java EE 在标准化企业基础设施、提供应用模型和提供足以开发 web 应用的组件方面做得很好但是有两个主要问题与之相关。
直接与 Java EE 组件交互通常会产生大量样板代码甚至代码冗余。使用 Java EE 基础设施创建企业应用是一项艰巨的任务需要大量的专业知识。通常参与创建企业 Java EE 应用的团队成员扮演着不同的角色他们可能并不都具有满足 Java EE 标准的专业水平。
框架解决了这两个主要问题(以及在第三章中详细讨论的其他几个问题)。表 1-4 描述了你将在本书中学到的 web 框架。
表 1-4 。基于 JVM 的 Web 框架 |
Web 框架
|
语言
|
从下载
| | — | — | — | | 支柱 2 | Java | struts.apache.org/download.cgi#struts2314 | | 框架 | Java | www.springsource.org/spring-community-download | | JSF 2 | Java | www.oracle.com/technetwork/java/javaee/downloads/index.html | | Grails 2 | 绝妙的 | www.grails.org/download | | 游戏 2 | Java 和 Scala | www.playframework.com/download |
既然您已经看到了构建现代 Java web 应用的三个主要参与者(JVM 语言、Java EE 和 Java web 框架)那么是时候深入研究 Java 的一些细节了。
下一节将介绍 Java这样您就可以构建自己的第一个独立 Java 应用。由于这本书是以使用 Java 的 web 开发为中心而不是关于 Java 作为一种编程语言所以对 Java 的介绍是简短的——这足以帮助语言新手理解后面的章节。
Java 入门
一个 Java 应用是一个当你使用 Java 命令启动 JVM 时执行的计算机程序。在 Java 编程语言中所有的源代码首先都是用。java 扩展。javac 编译器将源文件编译成。包含字节码指令的类文件。JVM 读取这些字节码指令并将它们翻译成每台计算机执行的机器语言操作。通过使 JVM 在许多平台上可用Sun 将 Java 转变成了一种跨平台语言。如图 1-6 所示完全相同的字节码可以在任何开发了 JVM 的操作系统上运行。 图 1-6 。跨平台 Java
因为 JVM 可以在许多不同的操作系统上使用所以。类文件能够在 Windows、Unix、Linux 或 Mac OS 上运行。在接下来的部分我将向您展示如何编译和运行您的第一个 Java 应用。但是首先您需要设置开发环境。
设置开发环境
Java 软件有两个发行版。
Java 运行时环境(JRE )Java 开发工具包(JDK )
JRE 包括一个 JVM 和核心库它本质上只是一个运行字节码的环境。JDK 包括 JRE、Java 编译器(javac)和其他工具——编写和编译 Java 程序所需的基本软件。
在开始编译和运行 Java 程序之前您需要下载并安装 JDK并配置一些系统环境变量。
本书大部分代码需要 Java 7但部分代码基于 Java 8所以你应该安装 Java 8。要获得最新版本的 JDK)请按照下列步骤操作:
在网络浏览器中打开www.oracle.com/technetwork/java/javase/downloads/index.html。单击下载 JDK 按钮。按照网站提供的说明进行操作。运行安装程序并接受任何默认值。
要确认您已经正确安装了 JDK请在命令行上从您机器上的任何目录键入 javac。如果您看到如何正确运行 javac 的说明那么您已经成功安装了它。
创建并运行您的第一个 Java 应用
本节演示了如何在 Windows 上创建、编译和执行一个简单的 Java 应用。每个 Java 应用都有一个作为程序起点的类(通常称为入口点)。清单 1-1 展示了一个 HelloWorld 入口点类。
清单 1-1 。一款 HelloWorld Java 应用
1. public class HelloWorld {
2. public static void main(String[] args) {
3. System.out.println(Hello World.);
4. }
5. }第 2 行:第 2 行中的 main 方法使这个类成为入口点类。该方法接受输入并启动程序。
Java 应用的名称应该是入口点类的名称保存 Java 类的文件必须与该类同名。因此清单 1-1 中的 HelloWorld 类必须存储在一个名为 HelloWorld.java 的文件中。
注意每个 Java 应用只有一个 main 方法。
您使用 JDK 安装目录的 bin 目录中的 javac 程序来编译 Java 程序。假设您已经在计算机上编辑了 PATH 环境变量那么您应该能够从任何目录调用 javac。要编译清单 1-1 中的 HelloWorld 类请执行以下操作: 打开命令提示符转到保存 HelloWorld.java 文件的目录。 键入以下命令: javac HelloWorld.java如果一切顺利javac 将在您的工作目录中创建一个名为 HelloWorld.class 的文件。
运行 Java 应用
要运行您的 java 应用您必须使用 Java 程序该程序是带有命令 java 的 JDK 的一部分。同样添加了 PATH 环境变量后您应该能够从任何目录调用 java。从您的工作目录中键入以下内容:
java HelloWorld请注意您不包括。运行 Java 应用时的类扩展。您将在控制台上看到以下内容:
Hello World.用 IDE 开发 Java 应用
在本书中您将使用 Eclipse Kepler 集成开发环境(IDE)。要下载软件请遵循以下步骤:
在网络浏览器中打开www.eclipse.org/downloads/。按照网站提供的说明进行操作。运行安装程序并接受任何默认值。
在 IDE 中创建您的第一个项目
启动 Eclipse 后您可以创建一个新项目如下所示: 从“文件”菜单中选择“新建”然后选择“项目”。将出现“新建项目”窗口。 In the New Project window, double-click Java Project. The New Java Project window appears, as illustrated in Figure 1-7. 图 1-7 。创建 Java 项目 在“项目名称”字段中输入 chapter1 。 单击完成。您可以在这里更改许多其他选项。然而对于我们的目的来说默认设置就可以了。
创建应用
要为您的第一个程序创建一个包含 main 方法的类请按照下列步骤操作: Right-click the chapter1 project in the Eclipse Package Explorer, choose New, and then choose Class. The New Java Class window displays, as shown in Figure 1-8. 图 1-8 。创建 Java 类 一个包将类分组在一起。在 Name 字段中您可以键入类名即 HelloWorld。选中提供 main 方法(public static void main(String args[]))的复选框。当你完成后你应该有一个类似于清单 1-2 中的类。 点击“生成评论”这个很快会解释。
清单 1-2 。简单的 Java 应用
packageapress.helloworld;/*** A Hello World Java application* author Vishal Layka**/
public class HelloWorld {/*** Entry point* paramargs*/public static void main(String[] args){System.out.println(Hello World);}}现在您可以通过单击工具栏中的“运行”按钮或从“运行”菜单中选择“运行”来运行应用。
然后Eclipse 会在代码区域下显示一个控制台面板显示程序的输出。在这种情况下它说“你好世界。”
Javadoc 注释
Javadoc 注释 以/*字符序列开始以/字符序列结束。编译器会忽略这些字符序列之间的所有内容。在 Eclipse 中您可以通过选择类或方法名并按 AltShiftJ 来添加 Javadoc 注释。
要生成 Javadoc在 Eclipse 中选择项目选择项目菜单点击 Generate Javadoc如图图 1-9 所示。 图 1-9 。生成 Javadoc
将会打开一个窗口(图 1-10 )您可以在其中选择需要生成 Javadoc 的 Java 项目或其底层资源。还有其他几种选择您可以选择是否为公共/私有 API 生成 Javadoc等等。现在在“Javadoc 命令”字段中配置 javadoc.exe 文件浏览并选择应该生成 Javadoc 的目标文件夹。 图 1-10 。生成 Javadoc
单击完成。在控制台上您可以看到 Javadoc 生成的进度。图 1-11 显示了生成的 Javadoc。 图 1-11 。HelloWorld 类的 Javadoc
现在您将学习如何创建一个简单但功能强大的独立书店应用版本您将在本书中使用它。
书店应用
这本书不是简单地宣称一个 web 框架是最好的而是打算通过一个真实世界的书店应用来展示最流行的 web 框架的优势。开发一个完整的真正的应用需要动态功能的无缝协作而构建这样的组件的代码是人为设计的而且过于复杂。这本书没有把重点放在开发这样的移动部件上而是把注意力集中在利用每个 web 框架的优势上。在整本书中您将学习如何使用 Java EE 和 Java web 框架来构建书店 web 应用。在本章中您将通过构建一个传统的独立 Java 书店应用迈出第一步。在第二章中你将把单机应用转换成 web 应用。
在本书中我将使用一个 web 应用案例研究来演示如何使用 servlets 和 JSP 以及不同的 web 框架(如 JSF、Struts 2、Spring web MVC)和快速 Web 开发框架(如 Grails 和 Play)来编写 Web 应用。该应用允许用户通过关键字查看和搜索书籍通常是通过作者的名字或姓氏以及书名。
书店应用的数据模型
本节介绍了一个简单的数据模型该模型将用于本书中的书店 web 应用。当需要时我将在每章中逐步扩展这个模型。该模型是一个简单的图书数据库由三个表组成。
类别表存储不同类别的书籍类别包括 Java、Scala 等等。图书表存储图书的详细信息比如书名。作者表存储作者的详细信息。
每个类别可以有零本或多本书。例如书店里可能没有或有更多属于 Java 类别的书籍。换句话说Category 和 Book 表之间是一对多的关系。同样每本书可以有一个或多个作者。换句话说Book 和 Author 表之间是一对多的关系。图 1-12 中的实体关系图说明了这种关系。 图 1-12 。数据模型的实体关系图
这个数据模型还不能用于生产因为您可以在类别和图书之间建立多对多的关系在图书和作者之间建立多对多的关系。我保持数据模型简单这样数据模型的复杂性就不会妨碍学习构建 web 应用的技巧。然而你可以例如在书和作者之间建立一个多对多的关系如图 1-13 所示。 图 1-13 。书籍和作者之间的多对多关系
BookAuthor 表的唯一目的是提供图书和作者之间的多对多关系。
注如图图 1-13 所示图书与图书作者之间是一对多关系作者与图书作者之间是一对多关系。事实上BookAuthor 表的唯一目的是提供图书和作者之间的多对多关系换句话说一个作者可以写很多本书一本书可以有很多作者。
由于跨几个领域的 web 应用大量涌现许多关系和非关系数据库如 NoSQL 12 已经出现。在本书中我将使用 MySQL 13 因为它是使用最广泛的免费数据库管理系统(DBMS)。要安装 MySQL请转到dev.mysql.com/downloads/并点击下载。可以下载 MySQL Server 5.5 或更新版本。你可以在dev.mysql.com/doc/refman/5.5/en/installing.html看到安装 MySQL 的说明。
要创建图书数据库请使用以下命令:
create database books;您需要使用以下命令指示 MySQL 在 books 数据库中创建表:
use books;现在您可以使用清单 1-3 中的语句创建表格。
清单 1-3 。为书店创建桌子
CREATE TABLE CATEGORY (
ID INT NOT NULL AUTO_INCREMENT ,
CATEGORY_DESCRIPTION VARCHAR(20) NOT NULL ,
PRIMARY KEY (ID)
);CREATE TABLE BOOK (
ID INT NOT NULL AUTO_INCREMENT,
CATEGORY_ID INT NOT NULL ,
BOOK_TITLE VARCHAR(60) NOT NULL,
PUBLISHER VARCHAR(60) NOT NULL ,
PRIMARY KEY (ID) ,
CONSTRAINT FK_BOOK_1 FOREIGN KEY (CATEGORY_ID) REFERENCES CATEGORY(ID));CREATE TABLE AUTHOR (
ID INT NOT NULL AUTO_INCREMENT ,
BOOK_ID INT NOT NULL ,
FIRST_NAME VARCHAR(20) NOT NULL ,
LAST_NAME VARCHAR(20) NOT NULL ,
PRIMARY KEY (ID) ,
CONSTRAINT FK_AUTHOR_1 FOREIGN KEY (BOOK_ID) REFERENCES BOOK (ID)
);您可以使用显示表格命令验证创建的表格如图图 1-14 所示。 图 1-14 。数据库中的所有表
您也可以使用命令 describe 或 desc 检查表格的结构如图图 1-15 所示。 图 1-15 。表格的结构
现在使用 insert 语句填充这些表如下所示:
insert into category (category_description) values (Clojure);
insert into category (category_description) values (Groovy);
insert into category (category_description) values (Java);
insert into category (category_description) values (Scala);您可以验证填充的类别表如图图 1-16 所示。 图 1-16 。类别表中的所有类别
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (1, Practical Clojure, Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (2, Beginning Groovy, Grails and Griffon, Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (2, Definitive Guide to Grails 2, Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (2, Groovy and Grails Recipes, Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (3, Modern Java Web Development, Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (3, Java 7 Recipes, Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (3, Java EE 7 Recipes, Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (3, Beginning Java 7 , Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (3, Pro Java 7 NIO.2, Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (3, Java 7 for Absolute Beginners, Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (3, Oracle Certified Java Enterprise Architect Java EE7, Apress);
insert into Book (CATEGORY_ID, BOOK_TITLE, PUBLISHER) values (4, Beginning Scala, Apress);您可以验证如图图 1-17 所示的已填充的图书表。 图 1-17 。图书表中的所有图书
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (1, Luke, VanderHart);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (2, Vishal, Layka);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (3, Jeff, Brown);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (4, Bashar, Jawad);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (5, Vishal, Layka);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (6, Josh, Juneau);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (7, Josh, Juneau);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (8, Jeff, Friesen);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (9, Anghel, Leonard);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (10, Jay, Bryant);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (11, B V, Kumar);
insert into Author (BOOK_ID, FIRST_NAME, LAST_NAME) values (12, David, Pollak);您可以验证填充的作者表如图图 1-18 所示。 图 1-18 。作者表中的所有作者
书店应用的数据访问层
现在数据库已经准备好了您将为应用构建数据访问层。数据访问层将通过 JDBC 从数据库中检索数据并将结果集直接映射到 Java 对象中。这些 Java 对象是应用中的域对象是数据库中表的 Java 表示。数据访问层负责以透明的方式与底层持久性机制进行交互以便从数据库中存储和检索对象。这种透明性意味着数据访问层可以将持久化机制从普通的 JDBC 14 切换到 ORM 15 持久化技术如 Hibernate、 16 JPA、 17 等而不影响数据访问层的客户端。这种透明性是通过数据访问对象(DAO)模式实现的如图 1-19 所示。DAO 对象提供了到数据库或底层持久化机制的接口从而从客户端抽象出底层实现。 图 1-19 。道模式
DAO 将应用调用映射到持久性机制并提供特定的数据操作而不公开数据库的细节。DAO 接口抽象了从客户机(应用对象)访问数据的实现细节并提供了客户机(应用对象)需要的特定于域的对象。
首先您需要为数据库表的 Java 对象表示创建特定于领域的类。清单 1-4 、 1-5 和 1-6 分别显示了图书、作者和类别领域类。
清单 1-4 。型号:类别
package com.apress.books.model;public class Category {private Long id;private String categoryDescription;public Long getId() {return id;}public void setId(Long id) {this.id id;}public String getCategoryDescription() {returncategoryDescription;}public void setCategoryDescription(String categoryDescription) {this.categoryDescription categoryDescription;}public String toString() {return Category - Id: id , Category Description: categoryDescription;}}清单 1-5 。型号:书本
package com.apress.books.model;import java.util.List;
import com.apress.books.model.Author;public class Book {private Long id;private Long categoryId;private String bookTitle;private ListAuthor authors;private String publisherName;public Long getId() {return id;}public void setId(Long id) {this.id id;}public Long getCategoryId() {return categoryId;}public void setCategoryId(Long categoryId) {this.categoryId categoryId;}public String getBookTitle() {return bookTitle;}public void setBookTitle(String bookTitle) {this.bookTitle bookTitle;}public ListAuthor getAuthors() {return authors;}public void setAuthors(List272103_1_En authors) {this.authors authors;}public String getPublisherName() {return publisherName;}public void setPublisherName(String publisherName) {this.publisherName publisherName;}public String toString() {return Book - Id: id , Book Title: bookTitle;}}清单 1-6 。型号:作者
package com.apress.books.model;public class Author {private Long id;private Long bookId;private String firstName;private String lastName;public Long getId() {return id;}public void setId(Long id) {this.id id;}public Long getBookId() {return bookId;}public void setBookId(Long bookId) {this.bookId bookId;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName lastName;}public String toString() {return Author - Id: id , Book id: bookId , First Name: firstName , Last Name: lastName;}}现在让我们从 BookDAO 的一个简单接口开始它封装了 web 应用访问的所有数据。清单 1-7 显示了 BookDAO 接口。
清单 1-7 。书道界面 1. package com.apress.books.dao;2.3. import java.util.List;4.5. import com.apress.books.model.Book;6. import com.apress.books.model.Category;7.8. public interface BookDAO {9. public ListBookfindAllBooks();
10.
11. public ListBooksearchBooksByKeyword(String keyWord);
12.
13. public ListCategoryfindAllCategories();
14.
15. public void insert(Book book);
16.
17. public void update(Book book);
18.
19. public void delete(Long bookId);
20.
21. }第 9 行:这是 findAllBooks()方法用于列出数据库中的所有书籍。第 11 行 : SearchBooksByKeyword(字符串关键字)允许用户通过书名中的关键字或者作者的名和姓来搜索书籍。应用需要 findAllCategories()来提供图书的分类列表。
该接口中的方法对应于应用的 CRUD 术语(换句话说创建、读取、更新和删除)。清单 1-8 展示了 BookDAO 接口的实现。
清单 1-8 。BookDAO 接口的实现 1. package com.apress.books.dao;2.3. import java.sql.Connection;4. import java.sql.DriverManager;5. import java.sql.PreparedStatement;6. import java.sql.ResultSet;7. import java.sql.SQLException;8. import java.sql.Statement;9. import java.util.ArrayList;
10. import java.util.List;
11.
12. import java.apress.books.model.Author;
13. import java.apress.books.model.Book;
14. import java.apress.books.model.Category;
15.
16. public class BookDAOImpl implements BookDAO {
17.
18. static {
19. try {
20. Class.forName(com.mysql.jdbc.Driver);
21. } catch (ClassNotFoundException ex) {
22. }
23. }
24.
25. private Connection getConnection() throws SQLException {
26. return DriverManager.getConnection(jdbc:mysql://localhost:3306/books,
27. root, password);
28. }
29.
30. private void closeConnection(Connection connection) {
31. if (connection null)
32. return;
33. try {
34. connection.close();
35. } catch (SQLException ex) {
36. }
37. }
38.
39. public ListBook findAllBooks() {
40. ListBook result new ArrayList();
41. ListAuthor authorList new ArrayList();
42.
43. String sql select * from book inner join author on book.id author.book_id;
44.
45. Connection connection null;
46. try {
47. connection getConnection();
48. PreparedStatement statement connection.prepareStatement(sql);
49. ResultSet resultSet statement.executeQuery();
50. while (resultSet.next()) {
51. Book book new Book();
52. Author author new Author();
53. book.setId(resultSet.getLong(id));
54. book.setBookTitle(resultSet.getString(book_title));
55. book.setCategoryId(resultSet.getLong(category_id));
56. author.setBookId(resultSet.getLong(book_Id));
57. author.setFirstName(resultSet.getString(first_name));
58. author.setLastName(resultSet.getString(last_name));
59. authorList.add(author);
60. book.setAuthors(authorList);
61. book.setPublisherName(resultSet.getString(publisher));
62. result.add(book);
63. }
64. } catch (SQLException ex) {
65. ex.printStackTrace();
66. } finally {
67. closeConnection(connection);
68. }
69. return result;
70. }
71.
72.
73. public ListBook searchBooksByKeyword(String keyWord) {
74. ListBook result new ArrayList();
75. ListAuthor authorList new ArrayList();
76.
77. String sql select * from book inner join author on book.id author.book_id
78. where book_title like %
79. keyWord.trim()
80. %
81. or first_name like %
82. keyWord.trim()
83. %
84. or last_name like % keyWord.trim() %;
85.
86. Connection connection null;
87. try {
88.
89. connection getConnection();
90. PreparedStatement statement connection.prepareStatement(sql);
91. ResultSet resultSet statement.executeQuery();
92. while (resultSet.next()) {
93. Book book new Book();
94. Author author new Author();
95. book.setId(resultSet.getLong(id));
96. book.setBookTitle(resultSet.getString(book_title));
97. book.setPublisherName(resultSet.getString(publisher));
98. author.setFirstName(resultSet.getString(first_name));
99. author.setLastName(resultSet.getString(last_name));
100. author.setBookId(resultSet.getLong(book_id));
101. authorList.add(author);
102. book.setAuthors(authorList);
103. result.add(book);
104. }
105. } catch (SQLException ex) {
106. ex.printStackTrace();
107. } finally {
108. closeConnection(connection);
109. }
110.
111. return result;
112. }
113.
114. public ListCategory findAllCategories() {
115. ListCategory result new ArrayList();
116. String sql select * from category;
117.
118. Connection connection null;
119. try {
120. connection getConnection();
121. PreparedStatement statement connection.prepareStatement(sql);
122. ResultSet resultSet statement.executeQuery();
123. while (resultSet.next()) {
124. Category category new Category();
125. category.setId(resultSet.getLong(id));
126. category.setCategoryDescription(resultSet
127. .getString(category_description));
128. result.add(category);
129. }
130. } catch (SQLException ex) {
131. ex.printStackTrace();
132. } finally {
133. closeConnection(connection);
134. }
135. return result;
136. }
137.
138. public void insert(Book book) {
139. }
140.
141. public void update(Book book) {
142. }
143.
144. public void delete(Long bookId) {
145.
146. }
147. }清单 1-8 是 BookDao 接口的一个实现用于与这种交互包括连接到数据库并通过纯 JDBC 选择、删除和更新数据。JDBC 提供了特定于每个数据库的驱动程序并允许 Java 对数据库进行编码。
第 18 到 37 行:这几行显示了管理 JDBC 连接所需的代码。第 26 行:getConnection()方法返回一个驱动实现的 java.sql.Connection 接口。这个接口允许您对数据库运行 SQL 语句。为此您需要提供一个 MySQL 连接器/J JAR 文件。MySQL Connector/J 是一个本地 Java 驱动程序它将 JDBC 调用转换成 MySQL 数据库可以理解的网络协议。DriverManager 管理驱动程序并提供建立数据库连接的静态方法。
注意你可以从 http://dev.mysql.com/downloads/connector/j/下载 MySQL 连接器/J。将这个连接器 JAR 放在项目的类路径中。
第 30 行到第 37 行:需要关闭连接因为就应用的性能而言连接是很昂贵的。第 39 到 144 行:这几行是 BookDAO 接口中 CRUD 服务的实现。第 67、108 和 133 行:您为 CRUD 服务中的每个语句创建了一个连接。您需要关闭这些连接让它们保持打开状态会导致应用的性能下降。
数据访问层的客户端
现在您的数据访问层已经准备好了您将使用独立的 Java 应用查询它。在第二章中你将用一个网络应用替换这个 Java 应用。清单 1-9 展示了 Java 应用。
清单 1-9 。单机书店 Java App 1. package com.apress.books.client;2. import java.util.List;3.4. import com.apress.books.dao.BookDAO;5. import com.apress.books.dao.BookDAOImpl;6. import com.apress.books.model.Book;7.8. public class BookApp {9. private static BookDAO bookDao new BookDAOImpl();
10.
11. public static void main(String[] args) {
12. // List all books
13. System.err.println(Listing all Books:);
14. findAllBooks();
15. System.out.println();
16. // search book by keyword
17. System.err.println(Search book by keyword in book title : Groovy:);
18.
19. searchBooks(Groovy);
20. System.out.println();
21.
22. System.err.println(Search book by keyword in authors name : Josh:);
23.
24. searchBooks(Josh);
25.
26.
27. }
28.
29. private static void findAllBooks() {
30. ListBook books bookDao.findAllBooks();
31. for (Book book : books) {
32. System.out.println(book);
33. }
34. }
35. private static void searchBooks(String keyWord) {
36. ListBook books bookDao.searchBooksByKeyword(keyWord);
37. for (Book book : books) {
38. System.out.println(book);
39. }
40. }
41. }图 1-20 说明了独立应用的目录结构。 图 1-20 。独立书店应用的目录结构
运行此应用会产生以下输出: 在下一章中您将开发 web 层来代替这个客户端并调用书店 web 应用中的数据访问层。
Java Web 领域的趋势和技术
现在是时候深入研究当今 Java web 领域的趋势和技术了。对于 Java 新手来说这可能令人望而生畏但目标是让您熟悉 Java web 应用开发中的工具、技术和趋势以便让您对现代 Java 前景有一个初步了解。当您学习使用 Grails 2 和 Play 2 等快速 web 框架开发 web 应用时您会发现这些工具和技术中的大多数都是现成的。
正如本章所提到的JVM 最初是为 Java 设计的现在可以支持无数的编程语言包括 Groovy 和 Scala。作为这种新兴的多道程序设计范例的结果现代 web 应用通常具有以下一种或多种特征:
响应式 web 应用单页 web 应用实时网络应用反应式 web 应用混搭和 web 服务
响应式网络应用
网络最大的优势之一就是它的灵活性。然而这种灵活性也是其最大的弱点。当在一个浏览器上测试的 web 应用在另一个浏览器上被查看并且不能正常运行时这个弱点就显现出来了。随着智能手机的出现这种跨浏览器兼容性问题越来越严重。如图 1-21 所示截至 2013 年底全球有 68 亿移动用户到 2016 年这一数字将增长到 80 亿(【www.itu.int/ITU-D/ict/facts/index.html】??)。 图 1-21 。移动订阅
Web 应用既可以在桌面上运行也可以在智能手机上运行但是为桌面和智能手机创建单独的 web 应用会带来巨大的开发和维护开销。2010 年 5 月Ethan Marcotte 为 List Apart 写了一篇名为“响应式网页设计”的文章定义了一种突破性的方法。他使用现有的工具(这将在本节稍后解释)创建了一个在不同设备上显示精美的网站如图图 1-22 和图 1-23 所示。 图 1-22 。Ethan Marcotte 的响应网站 图 1-23 。智能手机上的同一个响应网站
web 应用是与设备无关的因为它们可以适应运行它们的设备。这项技术甚至重新思考了页面布局的设计方式。您可以更进一步在设计 web 应用时考虑最小的屏幕(智能手机),然后逐步增强应用以在桌面屏幕上运行。这种技术被称为移动优先设计。移动优先设计的理念是如果你设计的界面和网络组件能够在智能手机上以可接受的性能运行那么在桌面屏幕上的性能将会非常快。此外明智地使用智能手机也将适用于桌面屏幕这将使应用的可用性更好。
开发响应式 web 应用所采用的核心技术有 CSS3、18jQuery19和 jQuery 插件、 20 LESS、21coffee script、 22 以及 Bootstrap、 23 Polyfills
以下是响应式世界中的一些重要定义:
不引人注目的 JavaScript24:不引人注目的 JavaScript 是一种分离关注点的手段即把外观和感觉从行为关注点中分离出来。这就产生了一个纯标记JavaScript(行为)在不同的浏览器和设备上不引人注目地工作。jQuery 是一个流行的库有助于编写不引人注目的 JavaScript。CoffeeScript 编译成 JavaScript。它被用作 JavaScript 的替代品并大大减少了代码。CSS3 媒体查询 :媒体查询是让 web 应用做出响应的主要手段。媒体查询使用 CSS 文件中的媒体特征(如设备宽度、设备高度、方向和设备纵横比)来开发响应性 web 应用。LESS : LESS 是 CSS3 样式表变得不可管理时使用的 CSS 预处理器。LESS 扩展了 CSS 的动态行为比如混合和函数。Polyfills : Polyfills 是 JavaScript用于制作支持 HTML5 的浏览器。聚合填充提供了浏览器中缺少的功能并提供了一个后备。Modernizr : Modernizr 是一个 JavaScript 库可以检测浏览器中的 HTML5 和 CSS3 特性并有条件地加载 polyfills。
单页 Web 应用(SPA)
web 应用开发的另一个趋势是单页面 web 应用的出现。
客户端代码——如 HTML、JavaScript 和 CSS——通过单个页面加载进行检索在该过程中的任何时候都不会重新加载页面并且控件也不会转移到另一个页面。资源(如图像)会动态加载并添加到页面以响应事件。
spa 是使用 Node.js 25 作为 web 服务器构建的。AngularJS 是一个功能齐全的 SPA 框架。
实时 Web 应用
一个实时 web 应用根据事件的性质通过客户端和服务器之间的异步双向通信在可测量和可接受的时间段内对事件做出响应。WebSocket 是开发实时 web 应用的核心技术。WebSocket 通过单一 TCP 连接提供全双工和双向通信协议。也就是说客户端和服务器可以相互发送消息并且相互独立。
需要实时功能的应用的几个例子是聊天应用、多人在线游戏、股票事务应用等等。
注意【WebSocket 的 Java API 定义为 JSR 356参见表 1-3 。
反应式网络应用
反应式应用是一类新的应用与传统的基于网络的应用有着本质的不同由类型安全 26 反应式平台驱动。Typesafe 反应式平台是一套集成产品包括 Play 2 框架、Akka、 27 和 Scala以及用于命令和控制的 Typesafe 控制台。由于多核处理器以及它提倡异步和基于事件的编程反应式编程变得至关重要。Play 框架是企业 Java 堆栈的替代方案。Play 是为现代 web 和移动应用的需求而构建的利用了 REST、JSON 和 WebSocket 等技术。这些技术允许创建通过任何现代浏览器呈现的丰富、高度交互的用户界面同时使并行呈现页面的各个部分以及进行部分页面更新或渐进式增强变得更加容易。
混搭和网络服务
mashup 是一个 web 应用它使用来自多个来源的内容来创建一个显示在单一图形界面中的新服务。使用 mashups您可以通过组合 web 服务来开发强大的应用。你可以在 www.programmableweb.com/apis/directory/1?sortmashups 的找到流行的混搭 API。这一节将关注 web 服务然后触及一个迷人的趋势这仍然是一个研究领域:语义 Web。
一个 web 服务 是一个存储在一台机器上的软件组件可以被另一台机器上的应用(或其他软件组件)通过网络访问。web 服务所在的机器被称为 web 服务主机。客户端应用通过网络向 web 服务主机发送请求web 服务主机处理请求并返回响应。使 web 服务可用于接收客户端请求被称为发布web 服务从客户端应用使用 web 服务被称为消费web 服务。
Web 服务使用 XML 和 JSON 28 等技术进行数据交换。
JavaScript 对象符号
JavaScript 对象符号(JSON) 用于表示数据作为 XML 的替代。JSON 减少了 web 请求的负载提高了 web 应用的整体性能。JSON 是一种基于文本的数据交换格式用于将 JavaScript 中的对象表示为由字符串表示的名称-值对的集合。
注 JSON 处理在 JSR 353 中定义为 JSON 处理的 Java API 参见表 1-3 。
两个 Java APIs 促进了 web 服务:
JAX-WS :这是基于简单对象访问协议(SOAP) 29 这是一种基于 XML 的协议允许 web 服务和客户端进行通信即使客户端和 web 服务是用不同的语言编写的。JAX-RS :这使用了表述性状态转移(REST)这是一种网络架构使用 Web 的传统请求-响应机制如 GET 和 POST 请求。
简单对象访问协议
简单对象访问协议(SOAP)是一个独立于平台的协议它使用 XML 与 web 服务进行交互通常是通过 HTTP。每个请求和响应都封装在 SOAP 消息中SOAP 消息是包含 web 服务处理消息所需信息的 XML 标记。SOAP web 服务的工作方式如下:
当调用 SOAP web 服务的方法时请求被封装在 SOAP 信封中的 SOAP 消息中并发送到 web 服务所在的服务器。当 SOAP )web 服务收到此消息时它会解析表示消息的 XML然后处理消息的内容。然后web 服务在处理完请求后在另一个 SOAP 消息中将响应发送给客户端。客户端解析响应。
代表性状态转移
罗伊·菲尔丁于 2000 年在加州大学欧文分校的博士论文 30 中引入并定义了术语表征状态转移 (REST) 。REST 指的是一种实现 web 服务的架构风格称为 RESTful web 服务。RESTful web 服务中的每个方法都由唯一的 URL 标识。
注意 RESTful web 服务被定义为 JSR 339如表 1-3 所示。
与 SOAP 不同REST 执行以下操作:
将资源标识为 URI使用一组定义良好的 HTTP 方法来访问资源使用资源的多种表示格式
语义网(Web 3.0)
Web 2.0 在 2004 年开始成形。它由谷歌开创随后是视频分享、社交网络、微博、照片分享、维基百科等社交应用以及第二人生等虚拟世界。混搭在社交应用和 Web 2.0 的发展中扮演了重要角色。
术语语义网指的是 W3C 对链接数据网的设想。语义网可以被看作是一套标准允许机器理解网上信息的意思。今天的网络由本身没有意义的数据组成而这些意义必须在从网络上收集数据后手工构建。语义 Web 技术使您能够在 Web 上创建数据存储构建词汇表并编写处理数据的规则。关联数据是通过 RDF、 31 SPARQL、 32 和 OWL 等技术实现的。语义网是网络的未来也是一个正在研究的课题。我推荐艾伦·施瓦茨的一个可编程的 Web:一个未完成的工作 34 作为语义 Web 上的优秀资源。你也可以关注语义网上的最新消息。
摘要
本章介绍了 Java 语言然后带您进行了一次旋风式的 Java 之旅。Java 世界的多样化景观由几个 web 框架(如 Struts 2、Spring Web MVC、JSF 2、Grails 2 和 Play 2)组成这些框架使开发变得容易得多因此作为 Java web 开发人员您需要熟悉这些 web 框架。现代 Java 不仅仅是一种语言现在它是一个针对其他几种行业优势语言(如 Groovy、Clojure 和 Scala)的完全优化的平台。所有这些语言尤其是 Groovy都与 Java 有着密切的联系不久您将会遇到 web 应用其中 Java 和这些替代的 JVM 语言将协同工作。
随后的章节将解决现代 Java web 开发人员的所有这些需求。具体来说在下一章中您将创建一个 Hello World web 应用它利用了 web 应用的基本构件即 servlets 和 Java 服务器页面。然后您将把这个独立的应用转换成您的第一个成熟的 web 应用:一个使用 servlets 和 JSP 的书店应用。
Cr . open JDK . Java . net/√jrose/pres/2009 910-vml . pdf
2【http://groovy.codehaus.org/】??
3【www.scala-lang.org/】??
4【www.oracle.com/technetwork/java/javaee/overview/index.html】??
5【http://spring.io/】??
6【http://clojure.org/】??
7【http://jruby.org/】??
8【www.jython.org/】??
9T3developer . Mozilla . org/en-US/docs/Rhino _ documentation
10【www.oracle.com/technetwork/java/javaee/tech/index.html】??
11www . cs . ute xas . edu/users/EWD/transcriptions/ewd 04 xx/ewd 447 . html
12【http://nosql-database.org/】??
13【www.mysql.com/】??
14【www.oracle.com/technetwork/java/overview-141217.html】??
15【http://en.wikipedia.org/wiki/Object-relational_mapping】??
16【www.hibernate.org/】??
17www . Oracle . com/tech network/Java/javaee/tech/persistence-JSP-140049 . html
18【www.w3.org/Style/CSS/current-work.en.html】??
19【http://jquery.com/】??
【20】【http://plugins . jquery . com/
21【http://lesscss.org/】??
22【http://coffeescript.org/】??
23【http://getbootstrap.com/】??
24【www.w3.org/wiki/The_principles_of_unobtrusive_JavaScript】??
25【http://nodejs.org/】??
26【http://typesafe.com/】??
27【http://akka.io/】??
28【www.json.org/】??
29【www.w3.org/TR/soap/】??
30www . ics . UCI . edu/∞fielding/pubs/dissertation/rest _ arch _ style . htm
31【www.w3.org/RDF/】??
32【www.w3.org/TR/rdf-sparql-query/】??
33【www.w3.org/TR/owl-features/】??
34www . morganclaypool . com/doi/pdf/10.2200/s 00481 ed 1v 01y 201302 wbe 005
二、使用 Servlets 和 JSP 构建 Web 应用
协议就是一切。
弗朗索瓦·朱利亚尼
核心互联网协议充实并支撑着 web因此理解这些协议是理解 Web 应用如何开发的基础。
互联网是一个巨大的网络网络一般来说互联网上的所有机器都可以分为两类:服务器和客户端。客户端是请求一些信息的机器而服务器是提供这些信息的机器。从信息提供者(即服务器)流向信息请求者(即客户端)的信息数据受一个明确的规则的约束该规则管理服务器传输的信息的编组和客户端翻译或读取的信息的解组。这个规则被称为 协议。web 浏览器(即客户端)、web 服务器(即服务器)和 web 应用都通过超文本传输协议(HTTP)相互通信。 客户端向 web 服务器发送 HTTP 请求web 服务器以 HTTP 响应的形式返回请求的数据。HTTP 客户端和 HTTP 服务器是万维网的基石HTTP 是万维网的通用语言。
HTTP 是一种请求-响应无状态协议其必然结果是从 web 服务器的角度来看任何请求都是来自 web 浏览器的第一个请求。当客户端请求资源时该请求还以统一资源定位符(URL) 的形式包含所请求资源的标识。在 RFC 3986 1 中URL 被描述为唯一标识资源的统一方式。URL 被设计成通过描述资源在网络上的“位置”来隐含地提供定位资源的方法。
注URL 是统一资源标识符(URI)的一种具体形式是一种区分实体的机制。但是 URIs 本身是抽象的。URI 有两种具体形式:URL 和统一资源名(URN)。骨灰盒仍然是实验性的没有被广泛采用。
通用 URL 是由组件组成的分层序列其结构为 scheme://hostName:port number/path/resource查询字符串。
要识别 URL 的各个部分请考虑一个列出书店网站上某本书的详细信息的 URL如下所示:
http://www.yourbookstore.com/bookstore/bookServlet?actionbookDetails图 2-1 展示了这个 URL 的各个部分。 图 2-1 。URL 的剖析
主机名和端口号一起被称为机构。默认情况下像 Tomcat 这样的 web 服务器会监听端口 8080 上的传入请求。在图 2-1 中显示的 URL 的某些部分是可选的包括端口号(默认为众所周知的端口 80 和 443分别用于 HTTP 和 HTTPS 方案)和查询字符串。
注意 HTTPS 是安全套接字层(SSL)上的 HTTP它允许安全、加密的通信。
如果存在查询字符串是一系列名称-值对前面有一个问号()并用一个符号分隔这些对。
注意只有 GET 方法支持查询字符串。还有其他 HTTP 协议方法如 POST、DELETE 和 PUT。
web 应用是协同工作以在 web 上提供特定功能的 Web 组件的集合。在 Java EE 规范中web 组件被定义为一个 Servlet 或一个 Java 服务器页面(JSP )页面。
注意除了 servlets 和 JSP 页面web 应用还可以包括静态资源例如 HTML 文档、图像和定义 web 应用属性的元数据或配置文件但是这些不被认为是 web 组件。
web 应用及其组成组件在 web 容器中管理和执行也称为 servlet 容器 它为 web 应用提供了额外的功能如安全性。当 web 服务器收到对特定 web 组件(如 servlet 或 JSP 页面)可以提供的特定功能的请求时web 服务器会将请求转发给 web 组件所在的 servlet 容器。所有对动态内容的请求(也就是对负责生成动态内容的 web 组件的所有请求)都由 servlet 容器来协调如图 2-2 中的所示。 图 2-2 。动态内容请求
Java EE servlet 和 JSP 规范描述了 Servlet 容器必须提供的服务契约并指定了 Servlet 应该如何使用这些服务。就实现而言 servlet 是一个 Java 类充当动态 web 资源。
小型应用
Servlets 是 Java web 应用的中央处理单元负责 web 应用所需的大部分处理。具体来说servlet 是一个实现 javax.servlet.Servlet 接口的 Java 类。Servlet 接口定义了所有 Servlet 必须实现的方法。“一枚戒指统治他们所有人”这个接口和其他方法一起定义了关键的生命周期方法比如 init()、service()和 destroy()分别用于初始化 servlet、服务请求和从服务器中删除 servlet。表 2-1 描述了 javax.servlet.Servlet 接口的所有方法。
表 2-1 。Servlet 接口的生命周期和非生命周期方法
|
修饰符和类型
|
方法
| | — | — | | 空的 | initServletConfig config | | 空的 | 服务(ServletRequest reqServletResponse res) | | 空的 | 销毁() | | 如何获取 | getServletConfig() | | 线 | getServletInfo() |
在 servlet 的生命周期中容器在适当的时刻按以下顺序调用生命周期方法:
servlet 被构造然后用 init 方法初始化。客户端对服务方法的任何调用都会得到处理。然后用 destroy 方法销毁 servlet回收垃圾并最终完成。
这里解释一下表 2-1 中说明的 Servlet 接口方法:
init(ServletConfig):在实例化 servlet 后由 servlet 容器调用一次。在 servlet 成为接收任何请求的候选者之前该方法必须成功完成。service():在 servlet 的 init()方法成功完成之后由 servlet 容器调用以允许 servlet 响应请求。destroy():由容器调用以销毁 servlet并作为一种方法在 servlet 被销毁之前必须释放获取的资源。getServletConfig():允许 servlet 以该方法返回的 ServletConfig 对象的形式获取启动信息。ServletConfig 对象包含 servlet 的初始化和启动参数。getServletInfo():允许 servlet 返回自己的信息比如 servlet 的作者和版本。
使用 Servlet 的第一个 Web 应用
在第一章中你安装了 Eclipse 开普勒 IDE。在本节中您将在 Eclipse 中开发您的第一个 web 应用。具体来说您将使用 Tomcat 7 作为 HTTP 服务器和 servlet 容器。你可以从tomcat.apache.org/download-70.cgi下载 Tomcat 的源代码发行版作为 ZIP 文件来安装 Tomcat 7。
启动 Eclipse选择窗口首选项菜单选项显示首选项对话框如图 2-3 所示。 图 2-3 。首选项对话框验证安装的 JRE
在这个对话框的左窗格中深入到 Java Installed JREs并验证您以前安装的 JRE8/JDK 8 版本是否出现。如果没有请单击添加按钮添加对您的 JDK 的引用。选择文件新建动态 Web 项目创建一个动态 Web 项目如图图 2-4 所示。将项目命名为 helloworld 如图图 2-5 所示。 图 2-4 。创建新项目 图 2-5 。创建 helloworld 项目
点击下一步勾选“生成 web.xml 部署描述符”如图图 2-6 所示。稍后我们将看到如何在没有 web.xml 的情况下配置 web 模块。 图 2-6 。配置网络模块设置
点击完成创建一个新的 Java 类如图图 2-7 所示。 图 2-7 。创建一个 Java 类:servlet
用清单 2-1 中的代码修改生成的 HelloWorld 类。
清单 2-1 。HelloWorld Servlet
1.package apress.helloworld;
2.
3.import java.io.IOException;
4.import java.io.PrintWriter;
5.
6.import javax.servlet.http.HttpServlet;
7.import javax.servlet.http.HttpServletRequest;
8.import javax.servlet.http.HttpServletResponse;
9.
10.public class HelloWorld extends HttpServlet{
11.
12.protected void doGet(HttpServletRequest request,
13.HttpServletResponse response)
14.{
15.try
16.{
17.response.setContentType(text/html);
18.PrintWriter printWriter response.getWriter();
19.printWriter.println(h2);
20.printWriter.println(Hello World);
21.printWriter.println(/h2);
22.}
23.catch (IOException ioException)
24.{
25.ioException.printStackTrace();
26.}
27.}
28.
29.}用清单 2-2 中的代码修改 web.xml 文件。
清单 2-2 。Web.xml:部署描述符
1.?xml version1.0 encodingUTF-8?
2.web-app xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance
3.fontnamehttp://java.sun.com/xml/ns/javaee xmlns:webhttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd
4.xsi:schemaLocationhttp://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd
5.idWebApp_ID version3.0
6.display-namehelloworld/display-name
7.servlet
8.servlet-nameHelloWorld/servlet-name
9.servlet-classapress.helloworld.HelloWorld/servlet-class
10./servlet
11.servlet-mapping
12.servlet-nameHelloWorld/servlet-name
13.url-pattern/hello/url-pattern
14./servlet-mapping
15.welcome-file-list
16.welcome-fileindex.html/welcome-file
17.welcome-fileindex.htm/welcome-file
18.welcome-fileindex.jsp/welcome-file
19.welcome-filedefault.html/welcome-file
20.welcome-filedefault.htm/welcome-file
21.welcome-filedefault.jsp/welcome-file
22./welcome-file-list
23./web-app现在我们需要在 Tomcat 中将 HelloWorld servlet 配置为 web 模块。在 Eclipse 的菜单栏中选择 Window Show View Servers如图图 2-8 所示。 图 2-8 。添加服务器
在 Servers 选项卡上右键单击并添加 Tomcat 7 作为新的服务器如图图 2-9 所示。 图 2-9 。定义服务器
接下来你必须定义一个新的服务器如图 2-10 所示。 图 2-10 。定义 Tomcat 服务器
现在通过将资源移动到 configured 部分的右边来配置 helloworld 项目如图 2-11 所示。 图 2-11 。在服务器上配置资源
单击“添加”,将在服务器上配置该资源。然后单击完成。启动服务器使用图 2-12 中所示的 URL 访问应用。 图 2-12 。启动服务器
你现在可以通过 URLlocalhost:8080/hello world/hello访问你的第一个 web 应用如图图 2-13 所示。 图 2-13 。运行 helloworld web 应用
在下一节中您将了解请求如何流经应用以及容器如何在您开发的 helloworld 应用中找到 servlet。然后您将使用 HelloWorld servlet 来理解 servlet 的生命周期方法。
HelloWorld Servlet 的请求流
在 HelloWorld servlet 可以生成响应之前来自 web 浏览器的请求会流经 web 服务器和 servlet 容器如下面几节所述。
HTTP 请求消息
用于访问 helloworld web 应用的 URL 是localhost:8080/hello world/hello。当用户通过这个 URL 访问 web 应用时web 浏览器创建 HTTP 请求如清单 2-3 所示。
清单 2-3 。HttpRequest 消息
1.GET /helloworld/hello HTTP/1.1
2.Host: localhost:8080
3.User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko)
Chrome/26.0.1410.43 Safari/537.31第 3 行:这一行描述了特定的用户代理(web 浏览器)它通过使用 HTTP 请求特定的资源来发起通信如第 1 行所述。 Line 1: The request made by the user agent is of the form shown in Figure 2-14. 图 2-14 。HTTP 请求消息剖析 Line 2 :运行 web 服务器的这台机器(你的机器)是服务器机器。指定 Localhost否则您将使用计算机的主机名。例如将安装在运行 HTTP 服务器的系统上的浏览器指向 localhost 将显示安装在该系统上的网站的主页如图图 2-15 所示。 图 2-15 。网络服务器的主页
图 2-15 显示的是 Tomcat 的主页当你在你的机器上运行 Tomcat 的 bin 目录下的 startup.bat你就会看到这个主页。服务器使用带编号的端口使其服务对互联网可用服务器上可用的每个服务一个端口。例如如果一台服务器正在运行一个 web 服务器那么这个 web 服务器通常位于端口 80 上对于 Tomcat 来说是 8080。
审查请求
当客户端(web 浏览器)发出请求(在本例中是 GET 请求)时web 服务器(Tomcat)在第 1 行的请求中看到资源路径/helloworld/hello并确定用户请求的资源不是静态页面(例如. html 文件),因此将请求转发给 web 容器(Tomcat)。敏锐的读者会注意到 Tomcat 充当了 web 服务器和 web 容器的角色。
定位 Servlet
请求中的资源路径(清单 2-3 中的第 1 行)通过清单 2-2 中的 web.xml 文件映射到 HelloWorld servlet 。这个 web.xml 文件被称为部署描述符因为它向 web 容器描述了部署的 servlet。通过部署描述符web 容器确定需要调用哪个 servlet 来服务 web 浏览器发起的原始 HTTP 请求。为了方便读者web.xml 再次显示在清单 2-4 中。
清单 2-4 。web.xml
1.?xml version1.0 encodingUTF-8?
2.web-app fontnamehttp://java.sun.com/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd version3.0
3.servlet
4.servlet-nameHelloWorld /servlet-name
5.servlet-class
6.apress.helloworld.HelloWorld
7./servlet-class
8./servlet
9.servlet-mapping
10.servlet-nameHelloWorld/servlet-name
11.url-pattern/hello /url-pattern
12./servlet-mapping
13./web-app第 1 行到第 2 行:这些行包含样板 XML说明 XML 文件使用的版本、编码和模式。第 3 行到第 8 行:标签用于配置我们的 servlet。它包含两个嵌套标签: servlet-name 定义 servlet 的逻辑名称而 servlet-class 表示定义 servlet 的 Java 类。第 9 行到第 12 行:XML 标签用于配置我们的 servlet。它包含两个嵌套标签: servlet-name 匹配在 servlet 标签中设置的值而 url-pattern 设置 servlet 将执行的 url 模式。
Java EE web 应用从上下文根中运行。上下文根是 URL 中服务器名称和端口之后的第一个字符串。比如在 URLlocalhost:8080/helloworld/hello 中字符串 hello world 就是上下文根。 url-pattern 的值相对于应用的上下文根。Java EE web.xml 文件可以包含许多附加的 xml 标记。除了将 URL 映射到实际的 servlets您还可以使用部署描述符定制 web 应用的其他方面比如安全角色、错误页面、标记库和初始配置信息。但是这个 helloworld 应用不需要这些额外的标记。web 容器加载 HelloWorld servlet 类并实例化它。只创建 HelloWorld servlet 的一个实例对 HelloWorld servlet 的并发请求在同一个实例上执行。每个客户端请求都会生成一对新的请求和响应对象。容器运行多个线程来处理对 HelloWorld servlet 的单个实例的多个请求。
注意在分布式 web 应用中每个 JVM 都有一个特定 servlet 的实例但是每个 JVM 仍然只有那个 servlet 的一个实例。
生命周期法
对象的生命周期描述了对象在其存在期间必须经历的一系列步骤。servlet 的生命不同于普通的 Java 类因为 servlet 必须在 web 容器内部执行。图 2-16 显示了 HelloWorld servlet 的层次结构。 图 2-16 。HelloWorld servlet 的层次结构
通用服务器
大多数 servlet 通过 Servlet API 提供的抽象 javax.servlet.GenericServlet 类提供类似的基本功能。GenericServlet 类的实现是独立于协议的因此它是否必须响应 HTTP 或 FTP 请求并不重要。GenericServlet 抽象类定义了一个 init()方法默认的 init(ServletConfig)方法调用该方法来执行任何特定于应用的 Servlet 初始化。
http servlet〔??〕
在 web 应用中GenericServlet 类中缺少任何依赖于协议的处理意味着开发人员必须在自己创建的任何子类中编写处理代码。由于 HTTP 是 Web 上最著名和最广泛使用的协议Servlet API 还包括 GenericServlet 的一个更抽象的子类:javax.servlet.http.HttpServlet。
注意 HTTP/1.1 定义了七种请求方法。HttpServlet 类为这些方法中的每一个提供了默认实现您可以在 Servlet 中覆盖这些方法。然而大多数 web 应用包含只覆盖 doGet()和 doPost()方法的 servlets。
对 HttpServlet 子类的 HTTP 请求要经过许多步骤:
容器对公共服务(ServletRequestServletResponse)方法的调用。将此调用委托给 HttpServlet 的受保护服务(HttpServletRequestHttpServletResponse)方法。受保护的服务(HttpServletRequestHttpServletResponse)方法然后委托给适当的 doXxx 方法这取决于用于请求的 HTTP 方法。
HelloWorld Servlet
如前一节所述servlet 的超类包括 init()的两个版本一个采用 ServletConfig另一个没有参数。init(ServletConfig)方法调用无参数 init()因此您只需要覆盖无参数版本。
init( )
容器在 servlet 实例上调用 init()。您可以覆盖它以便获得向其他对象注册的数据库连接。否则运行 Genericservlet 中的 init()方法。
创建 servlet 实例时会调用它的 init()方法。init()方法允许 servlet 在处理第一个请求之前初始化自己。您可以在 web.xml 文件中或通过注释为 servlet 指定 init()参数。web 容器调用 servlet 的 init()方法(在 servlet 的生命周期中只调用一次)init()方法必须在容器可以调用 service()方法之前完成。
服务()
容器调用 servlet 的 service()方法。这个方法查看请求确定 HTTP 方法的类型并在 servlet 上调用匹配的 doGet()或 doPost()。你永远不能超越它。您的工作是覆盖 doGet()或 doPost()让来自 HTTPServlet 的服务实现担心调用正确的那个。
当在 servlet 上调用 service()方法时它将被传递对实现 HttpServletRequest 和 HttpServletResponse 接口的 HttpServletRequest 和 HttpServletResponse 对象的引用。容器实现了这些接口。对于 servlet 收到的每个请求都会调用 servlet 的 service()方法。对于 HttpServlet 子类通常调用 doGet()、doPost()等方法之一。容器创建两个对象:HTTPServletRequest 和 HttpServletResponse。只要 servlet 在 servlet 容器中是活动的就可以多次调用 service()方法。service()方法调用 doGet()/doPost()。您总是在 servlet 中覆盖至少其中一个。
destroy( )
servlet 容器调用 servlet 上的 destroy()方法这个方法只被调用一次。这个方法为 servlet 提供了一个机会在它被破坏之前释放获得的资源。
ServletContext 和 ServletConfig
调用 init()方法后servlet 为每个 servlet 获取一个 ServletConfig 对象为每个 web 应用获取一个 ServletContext。在分布式环境中每个 JVM 都有一个 ServletContext。ServletContext 是一种方法通过它 servlet 可以连接容器和 web 应用的其他部分。在 servlet 中只有当您的 servlet 不是 HttpServlet 或 GenericServlet 时才需要通过 ServletConfig 来获取 ServletContext。
servlet config 对象可用于执行以下操作:
将您不希望硬编码到 servlet 中的部署时信息(如数据库或企业 bean 查找名称)传递给 servlet。这个部署时信息被称为 servlet 初始化参数。servlet init 参数将在下一节讨论。访问 ServletContext。
ServletContext 对象可用于执行以下操作:
访问 web 应用参数设置应用的所有组件都可以访问的属性获取服务器信息包括容器的名称和版本以及支持的 API 版本
servlet 可以有三种类型的参数:
请求参数初始化(init)参数上下文初始化(context-init)参数
初始化参数
初始化参数在 web.xml 文件中定义如清单 2-5 所示。
清单 2-5 。定义初始化参数
servlet
init-param
param-nameemail /param-name
param-valuevishalway.gmail.com/param-value
/init-param
/servlet在 servlet 初始化之前不能使用 init-parameters。您的 servlet 继承了 getServletConfig()因此您可以从 servlet 中的任何方法调用它来获得对 ServletConfig 的引用。一旦有了 ServletConfig 引用就可以调用 getInitParam()。
当容器初始化 servlet 时会发生以下情况:
容器为 servlet 创建一个惟一的 ServletConfig。容器从部署描述符中读取 init 参数并在 ServletConfig 对象中设置它们。然后容器将 ServletConfig 传递给 servlet 的 init (servletConfig)方法。
注意一旦容器在 ServletConfig 中设置了 init 参数容器就不再从部署描述符中读取 init 参数除非 servlet 被重新部署。
上下文初始化参数
上下文初始化参数类似于初始化参数。context-init 参数和 init 参数之间的主要区别在于context 参数适用于整个 web 应用而 init 参数仅适用于 servlet。清单 2-6 展示了 web.xml 文件中的上下文初始化参数。
清单 2-6 。定义上下文初始化参数
context-param
param-nameemail /param-name
param-valuevishalwaygmail.com/param-value
/context-param元素没有嵌套在元素中。
清单 2-7 展示了如何从 servlet 中获取 context-init 参数。
清单 2-7 。获取上下文初始化参数
out.println(getServletContext().getInitParameter(email);每个 servlet 都继承了一个 getServletContext()方法。getServletContext()方法返回一个 ServletContext 对象。
请求调度员
在 web 应用中有两种方法可以改变请求流。
重定向请求:请求被重定向到一个完全不同的 URL。重定向可以通过在响应对象上调用 sendRedirect() 来完成。重定向是由浏览器完成的。分派请求:请求被分派给 web 应用中的另一个组件通常是 JSP 页面。请求分派不同于重定向因为它在服务器端完成工作。对请求调用 RequestDispatcher对响应调用 redirect。
图 2-17 显示了 RequestDispatcher 接口中的方法。 图 2-17 。请求调度程序接口
您可以通过两种方式获得 RequestDispatcher。
从 ServletRequest 获取 RequestDispatcher从 ServletContext 获取 RequestDispatcher
从 ServletRequest 获取 RequestDispatcher】
RequestDispatcher view request.getRequestDispatcher(bookDetails.jsp);ServletRequest 中的 getRequestDispatcher()方法获取请求转发到的资源的路径。如果路径以正斜杠(/)开头则容器认为它是从 web 应用的根目录开始的。如果路径不是以正斜杠开头容器认为它是相对于原始请求的。
从 ServletContext 获取 RequestDispatcher】
RequestDispatcher view getServletContext().getRequestDispatcher(/bookDetails.jsp);getRequestDispatcher()方法接受一个资源的字符串路径请求将被转发到该资源。从上下文或请求中获得的 RequestDispatcher 可用于转发到资源因为 RequestDispatcher 知道您要转发到的资源换句话说就是作为参数传递给 getRequestDispatcher()的资源。清单 2-8 展示了在 RequestDispatcher 上调用 forward。
清单 2-8 。在 RequestDispatcher 上呼叫转发
RequestDispatcher view request.getRequestDispatcher(bookDetails.jsp);
view.forward(request, response);过滤器
过滤器是一个可重用的 Java 组件可以转换 HTTP 请求、响应和头信息的内容。过滤器用于以下用途:
在调用请求之前访问静态或动态内容或修改请求头在调用后拦截 web 组件的调用通过以特定顺序使用过滤器链来提供对 web 组件的操作在呈现响应头和响应数据之前修改它们
过滤器是通过实现 javax.servlet.Filter 接口并提供一个无参数构造函数来创建的。过滤器是在 web 应用中配置的要么在部署描述符中使用元素要么在WebFilterannotation 中配置(在下一节中介绍)。在元素中必须声明以下内容:
:用于将过滤器映射到 servlet 或 URL:容器用来标识过滤器类型
注意你也可以声明一个过滤器的初始化参数。
清单 2-9 展示了过滤器的声明。
清单 2-9 。声明过滤器
filter
filter-nameResponseFilter/filter-name
filter-classcom.apress.ResponseServlet/filter-class
/filter可以使用元素将过滤器与 servlet 相关联。清单 2-10 将响应过滤器 Filter 映射到 ResponseServlet servlet。
清单 2-10 。将过滤器映射到 Servlet
filter-mapping
filter-nameResponse Filter/filter-name
servlet-nameResponseServlet/servlet-name
/filter-mapping过滤器可以使用 urlpattern 与 servlets 组相关联如清单 2-11 所示。
清单 2-11 。将过滤器与一组 Servlets 相关联
filter-mapping
filter-nameResponse Filter/filter-name
url-pattern/*/url-pattern
/filter-mapping在清单 2-11 中响应过滤器应用于 web 应用中的所有 servlets。
web 应用通常包含以下过滤组件:
认证过滤器缓存过滤器数据压缩过滤器加密过滤器图像转换过滤器日志和审计过滤器
通过注释配置 Servlet
从 Servlet 3.0 开始可以通过 web.xml 或使用注释或两者来配置 Servlet。表 2-2 描述了符合 Servlet 3.0 的 web 容器所支持的注释。
表 2-2 。配置 Servlet 的注释
|
注释
|
描述
| | — | — | | web 过滤器 | 在 web 应用中定义筛选器 | | WebInitParam | 指定要传递给 servlet 或过滤器的初始化参数 | | WebListener | 注释一个侦听器以获取事件 | | webservlet | 定义 web 应用中的组件 | | MultipartConfig | 指示请求属于 mime/multipart 类型 |
在接下来的小节中您将开发一个 helloworld 项目在这个项目中您将通过注释来配置 servlet。右键单击项目 helloworld创建一个新的 servlet 类并将类名命名为 HelloWorld如图图 2-18 所示。单击下一步。 图 2-18 。创建 servlet
在下一个屏幕上您可以填写与部署描述符相关的信息例如初始化参数和 URL 映射如图 2-19 中的所示。对于 HelloWorld 应用您不必填写初始化参数的值。URL 映射的默认值在本例中是/HelloWorld就可以了。“URL mappings”字段中的值是 URL 的 servlet 路径如前一节所述。单击下一步。 图 2-19 。网址映射
在下一个屏幕上指定修饰符、要实现的接口和要生成的方法存根如图 2-20 所示。如果尚未检查 doGet 和 doPost请检查它们。然后单击完成。 图 2-20 。指定方法
IDE 生成清单 2-12 中的 HelloWorld servlet。
清单 2-12 。使用注释的 HelloWorld Servlet
1.package apress.helloworld;
2.
3.import java.io.IOException;
4.import javax.servlet.ServletException;
5.import javax.servlet.annotation.WebServlet;
6.import javax.servlet.http.HttpServlet;
7.import javax.servlet.http.HttpServletRequest;
8.import javax.servlet.http.HttpServletResponse;
9.
10./**
11\. * Servlet implementation class HelloWorld
12\. */
13.WebServlet(urlPatterns { /HelloWorld }, description A hello world servlet)
14.public class HelloWorld extends HttpServlet {
15.private static final long serialVersionUID 1L;
16.
17. /**
18. * see HttpServlet#HttpServlet()
19. */
20. public HelloWorld() {
21. super();
22. // TODO Auto-generated constructor stub
23. }
24.
25./**
26\. * see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
27\. */
28.protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
29.// TODO Auto-generated method stub
30.}
31.
32./**
33\. * see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
34\. */
35.protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
36.// TODO Auto-generated method stub
37.}
38.
39.}第 13 行:这一行显示了WebServlet 注释的用法。HelloWorld servlet 使用WebServlet 注释来指定名称、URL 模式、初始化参数和通常在 web.xml 部署描述符中指定的其他配置项。第 14 行:这一行显示 HelloWorld servlet 扩展了 HTTPServlet。第 28 到 37 行:这几行显示了 IDE 生成的 doGet 和 doPost 方法。
将清单 2-13 中的代码添加到 HelloWorld servlet 的 doGet 方法中。
清单 2-13 。印刷“Hello World”
PrintWriter out response.getWriter();
out.println(h2Hello World !/h2);您必须导入 java.io.Printwriter。您可以在 Eclipse 中通过选择 Source Add Import 或按 CtrlShiftM现在您可以在服务器上运行应用了。在 Eclipse 的 helloworld 项目中右键单击 HelloWorld.java然后选择 Run As Run on Server。在这种情况下您使用的服务器是 Tomcat 7.0。然后使用以下 URL 访问该应用:
http://localhost:8080/helloworld/HelloWorld图 2-21 显示了输出。 图 2-21 。访问应用
有关 Java Servlet 技术的更多信息请参考 Java Servlet 3.1 规范 2 和 Java Servlet 网站。 3
Java 服务器页面
Servlets 使 web 服务器能够生成动态内容。然而servlets 有一个主要的缺点即 HTML 代码需要硬连接到 Java 代码中。为了消除这种横切关注点Java Server Pages (JSP) 技术应运而生。JSP 使用静态 HTML 内容和动态内容的组合来生成 web 页面从而分离了在 Java 代码中嵌入 HTML 内容的顾虑。
您的第一个使用 JSP 的 Web 应用
现在您将使用 JSP 创建一个“Hello World”应用。因为您之前已经创建了一个同名的项目所以请确保先删除或重命名旧项目。然后在项目浏览器中点击右键选择动态 Web 项目如图图 2-22 所示。将项目命名为 helloworld 。 图 2-22 。创建 helloworld 项目
右键单击项目 helloworld创建一个新的 JSP 文件命名为helloworld.jsp。单击下一步。在下一个屏幕上单击完成。 图 2-23。创建 JSP 文件
修改 helloworld.jsp 的代码如清单 2-14 所示。
清单 2-14 。helloworld.jsp
!DOCTYPE html
html
head
meta http-equivContent-Type contenttext/html; charsetISO-8859-1
titleHello World/title
/head
body
Hello World!
/body
/html在服务器上部署应用如图图 2-24 所示。 图 2-24 。在服务器上运行应用
使用localhost:8080/hello world/hello . JSP启动应用。
图 2-25 显示了输出。 图 2-25 。访问 JSP
JSP 基础
这一节将介绍基本的 JSP 构造。对经典方法的正确理解对于理解其局限性和欣赏更高级技术的力量是必不可少的比如表达式语言这是下一章的主题。在表达式语言被添加到 JSP 规范之前十亿个 JSP 页面是使用传统方法编写的您可能仍然需要维护它们或重构它们。对向后兼容性的需求是 JSP 规范仍然涵盖经典组件的原因之一。但是当您必须在应用中编写新的 JSP 时您不应该编写向后兼容的代码相反你应该使用最佳实践方法这将在下一章中解释。当 JSP 1.0 在 1999 年被添加到 JSP 规范中时它意味着通过在模板数据中嵌入业务逻辑代码来生成动态的、基于 web 的内容。为此提供了以下 JSP 元素用于在 JSP 页面中操纵 Java 对象并对其执行操作从而支持动态内容的生成:
指令声明公式脚本片断隐式对象标准动作
JSP 指令
JSP 指令是 JSP 容器的指令在页面翻译过程中进行处理。指令提供了一种机制使 JSP 引擎可以使用页面级信息。指令在指令分隔符之间声明并采用以下形式:
% directive {attributevalue}* %页指令
page 指令用于提供容器生成底层 servlet 所使用的特定 JSP 页面的相关说明。以下是页面指令的基本语法:
% page attributevalue %表 2-3 描述了与页面指令相关的属性。
表 2-3 。页面指令的属性
|
属性
|
目的
| | — | — | | 自动冲洗 | 控制 servlet 输出缓冲区的行为。它指示缓冲区满时是否应该自动写入。 | | 缓冲器 | 指定 servlet 输出流的缓冲模型。它表示缓冲区的大小。 | | 内容类型 | 指定响应的 MIME 类型和字符编码方案。 | | 错误页 | 指定处理错误条件并报告运行时异常的 JSP 的 URL。 | | 延伸 | 指示生成的 servlet 必须扩展的超类。 | | 进口 | 指定在 JSP 页面中使用的类类似于 Java 中的 import 语句。 | | 信息 | 为 servlet 的 getServletInfo()方法指定一个字符串。 | | isELIgnoredisELEnabled | 指定 JSP 页面中是否允许 EL 表达式。 | | isErrorPage | 指定此 JSP 页面是否用于处理错误情况和报告运行时异常。 | | isScriptingEnabled | 指定 JSP 页面中是否允许脚本元素。 | | 是线程安全的 | 指示 JSP 页是否可以处理并发请求。 | | 语言 | 指示 JSP 页面中使用的脚本语言。 | | 会议 | 指定 JSP 页面是否参与 HTTP 会话。 |
包含指令
include 指令用于指定应该包含在当前 JSP 页面翻译单元中的静态资源。include 指令有一个名为 file 的属性它指定应该包含的资源的 URL。该指令的一般形式如下:
% include filerelative url 以下示例演示了如何使用 include 指令在当前翻译单元中包含标准的 JSP 页眉和页脚(清单 2-15 )。你可以创建一个像你的第一个 web 应用 helloworld 一样的项目用 main.jsp 替换 helloworld.jsp。【清单 2-15 展示了 main.jsp 的 ??。
清单 2-15 。main.jsp
1.% include fileheader.jsp %
2.
3.pcontent/p
4.
5.% include filefooter.jsp %第 1 行:这一行包括翻译时 main.jsp 文件中的 header.jsp。第 5 行:这一行包括翻译时 main.jsp 文件中的 footer.jsp。
清单 2-16 展示了 header.jsp。
清单 2-16 。header.jsp
1.html
2.head/head
3.body
4.%out.print(header); %第 4 行:这一行使用了一个隐式对象 out。隐式对象将在本章后面介绍。隐式 out 对象表示 JspWriter 类的一个实例用于将字符数据写入响应流。
清单 2-17 展示了 footer.jsp。
清单 2-17 。footer.jsp
1.%out.print(footer); %
2./body
3./html图 2-26 显示了输出。 图 2-26 。使用 include 指令
Taglib 指令
Java Server Pages API 提供了封装功能的标准动作下一节将对此进行介绍。JSP API 还允许您定义实现自定义行为的自定义操作。几个这样的定制动作也称为定制标签被组装在一个名为标签库的库中。taglib 指令用于定义当前 JSP 页面中标记库的前缀和位置。taglib 指令使用以下语法:
% taglib uriuri prefixtagPrefix uri 属性值是指定标记库位置的绝对或相对路径prefix 属性指定要在当前 JSP 中使用的自定义操作。清单 2-18 展示了 taglib 指令在一个名为 helloTagLib 的示例标签库中的用法该标签库包含一个名为 hello 的标签用于打印“Hello World”消息。
清单 2-18 。Taglib 指令的用法
1.% taglib uri/ helloTagLib prefixhelloTag %
2.html
3.body
4.helloTag:hello/
5./body
6./htmlLine1 :库 helloTagLib 的 URI 和前缀 helloTag第 4 行:通过前缀使用 hello 标签
声明
使用声明JSP 允许您在 JSP 页面中声明方法和变量。一旦它们出现在 JSP 页面中就可以在整个页面中被 scriptlets 和表达式使用。JSP 声明放在声明分隔符之间。由于声明与表达式和 script let 一起使用所以我将在下面的小节中介绍表达式和 script let然后我将向您展示如何在 JSP 页面中使用声明、script let 和表达式。
表情
表达式类似于 scriptlets但是它们计算一个常规的 Java 表达式并作为响应的一部分向客户机返回一个结果这个结果是一个字符串或者可以转换成字符串的东西。一般语法如下:
% expression %小脚本
Scriptlets 是包含在分隔符中的 Java 代码块用于创建动态内容。清单 2-19 用一个脚本和表达式说明了声明的用法。
清单 2-19 。声明、脚本和表达式的用法
1.%!
2.public String hello() {
3.String msg Hello World;
4.return msg;
5.}
6.%
7.Message from bScriptlet/b: %hello();%br/
8.Message from bExpression/b: %hello() %第 1 行到第 6 行 : 这些行包含一个 JSP 声明声明了一个 hello()方法。第 1 行是开始标记第 6 行是声明的结束标记。第 7 行:第 1 行到第 6 行声明的 hello()方法用在了第 7 行的一个表达式中。
图 2-27 举例说明了声明、脚本和表达式的用法。 图 2-27 。使用声明、scriptlet 和表达式
隐式对象
在 web 应用中多个 web 组件通过作为四个范围对象的属性维护的对象来相互协作和共享信息。您可以通过使用代表作用域的类的 getAttribute 和 setAttribute 方法来访问这些属性。表 2-4 列出了范围对象。
表 2-4 。范围对象 |
范围对象
|
中文版
|
可从访问。。。
| | — | — | — | | 应用/web 上下文 | javax.servlet.ServletContext | 应用中的 Web 组件 | | 请求 | javax.servlet.ServletRequest 的子类型 | 处理请求的 Web 组件 | | 会议 | javax.servlet.http.HttpSession | 会话中的 Web 组件 | | 页 | javax . servlet . JSP . JSP 上下文 | 创建对象的 JSP 页面 |
注意除了标准的 servlet 请求、会话和应用作用域JSP 添加了第四个作用域称为页面作用域。
JSP 页面可以通过脚本变量访问一些特定的对象。这些对象由 JSP 容器提供被称为隐式对象。这些隐式对象可以在 scriptlets、表达式或作为 EL 表达式的一部分来访问。(EL 表达式在第三章的中介绍。)表 2-5 列出了对应 API 的九个隐式对象。
表 2-5 。隐式对象
|
隐含对象
|
使用
|
应用接口
| | — | — | — | | 应用 | 访问应用级对象 | ServletContext | | 配置 | 提供配置信息 | 如何获取 | | 例外 | 访问错误状态 | JSP 异常 | | 在外 | 访问 JSP 输出流 | 对象 | | 页面张版 | 提供对当前 JSP 的引用 | 目标 | | 对象 | 访问 JSP 容器 | 对象 | | 请求 | 提供对客户端请求的访问 | ServletRequest | | 反应 | 提供对 JSP 响应的访问 | servlet 响应 | | 会议 | 跨客户端请求共享信息 | 会话 |
这些隐式对象将在下面的章节中详细描述。
应用
隐式应用对象提供了对 javax.servlet.ServletContext 接口的引用。ServletContext 接口用于提供对任何上下文初始化参数的访问这些参数是通过 web 应用的部署描述符为 JSP 页面配置的。web 容器存储在其中的 ServletContext 对象和参数可供整个 web 应用使用。application 对象为 JSP 页面的开发人员提供了对 ServletContext 对象的访问。
配置
与 application 对象类似config 对象提供了对 web 应用的 ServletConfig 接口的引用。ServletConfig 接口用于通过 web 应用的部署描述符提供对已经为 JSP 页面配置的任何初始化参数的访问。config 对象为 JSP 开发人员提供了对 ServletConfig 对象的访问。
例外〔??〕
JSP 可以使用隐式异常对象来处理错误条件并使用 errorPage 页面指令报告运行时异常。
出局
隐式 out 对象表示用于将字符数据写入响应流的 JspWriter 类的实例。
页〔??〕
JSP 隐式页面对象是 object 类的一个实例表示当前 JSP 页面。
page context〔??〕
pageContext 通过提供对与 JSP 页面相关联的所有名称空间和几个页面属性的访问来提供上下文信息。此外它还包含对隐式对象的引用。
请求
请求对象是 javax . servlet . http . http servlet request 接口的实例。它代表客户端请求。请求隐式对象通常用于获取请求参数、请求属性、头信息和查询字符串值。
响应
隐式响应对象是 javax . servlet . http . http servlet response 接口的一个实例表示要给客户机的响应。隐式响应对象通常用于设置响应内容类型、添加 cookies 和重定向响应。
会话
JSP 隐式会话对象是实现 javax.servlet.http.HttpSession 接口的 Java 类的实例。它用于存储客户端的会话状态。
清单 2-20 展示了常用隐式对象的用法。首先它展示了在 servlet 的请求、会话和应用范围中设置 book 属性的常见任务。然后显示它们的 JSP 等价物。
清单 2-20 。常见隐式对象的用法
1.getServletContext().setAttribute(book, book);
2.request.setAttribute(book, book);
3.request.getSession().setAttribute(book, book);
4.application.setAttribute(book book);
5.request.setAttribute(book book);
6.session.setAttribute(book book);
7.pageContext.setAttribute(book book);Line 1 :在 ServletContext 中设置 book 属性不使用隐式对象。第 2 行:设置请求对象中的 book 属性。请求对象也是 JSP 中的隐式对象。因此在 servlet 中设置属性类似于在 JSP 页面中设置属性。第 3 行:在 session 中设置 book 属性不使用隐式对象。第 4 行:使用 application 隐式对象设置 ServletContext 中的 book 属性。第 5 行:设置请求对象中的 book 属性。request 也是 JSP 中的隐式对象。因此在 JSP 中设置属性类似于在 servlet 中设置属性。第 6 行:使用 session 隐式对象设置 session 中的 book 属性。第 7 行:使用 pageContext 隐式对象设置 PageContext 中的 book 属性。servlet 中没有 pageContext 的等价物。PageContext 实例提供对与 JSP 页面相关联的所有名称空间的访问提供对几个页面属性的访问并提供实现细节之上的一层。隐式对象会自动添加到 pageContext。
标准动作
JSP 标准操作提供了一种完成以下任务的方法:
操纵 JavaBeans动态包含文件执行 URL 转发
行动
动作提供了一种在运行时包含指令的方法该指令用于在声明 JSP 页面中包含单独 web 组件的内容。使用标准包含动作的语法如下:
jsp:include pagerelativeURL flushtrue/我们将创建两个 jsp如清单 2-21 和清单 2-22 所示来说明 jsp:include 动作的使用。
清单 2-21 。main.jsp
1.html
2.head
3./head
4.body
5.%out.print(Inside main.jsp); %br/
6.jsp:include pagesub.jsp/
7./body
8./html第 6 行:使用 jsp:include 来包含目标 jsp 页面(sub.jsp)
清单 2-22 。sub.jsp
1.html
2.head
3./head
4.body
5.%out.print(Inside sub.jsp); %br/
6./body
7./html图 2-28 显示了访问 main.jsp 时的输出。 图 2-28 。使用 jsp:include 动作
动作
动作用于将当前请求转发给另一个资源比如静态页面、JSP 页面或 servlet。该操作的语法如下:
jsp:forward pagerelativeURL /我们将使用前一节中创建的两个 JSP 来说明动作的使用如清单 2-23 和清单 2-24 中所示。
清单 2-23 。利用 main.jsp 的前进行动
1.html
2.head
3./head
4.body
5.%out.print(Inside main.jsp); %br/
6.jsp:forward pagesub.jsp/
7./body
8./html第 6 行:使用 jsp:forward 转发到目标 jsp 页面(sub.jsp)。
清单 2-24 。sub.jsp
1.html
2.head
3./head
4.body
5.%out.print(Inside sub.jsp); %br/
6./body
7./html图 2-29 显示了访问 main.jsp 时的输出。 图 2-29 。前进动作的用法
为了理解 include 动作和 forward 动作之间的区别比较清单 2-21 和清单 2-23 以及图 2-28 和图 2-29 。在清单 2-23 中我们在 main.jsp 中使用了转发动作而不是包含动作。转发操作将控制权转移给 sub.jsp就像包含操作一样。但是当 sub.jsp 完成时与 include 操作不同控制权不会回到 main.jsp。
、和动作
这三个标准动作可以消除大量的脚本代码包括声明、scriptlets 和表达式。
useBean 动作用于声明和初始化 Bean 对象。一旦 bean 被初始化就可以使用 jsp:setProperty 和 jsp:getProperty 操作来设置和获取 bean 属性
动作的语法如下:
jsp:useBean idsomeId classSomeClass /动作设置 bean 的属性。
动作具有以下语法其中 someId 是 useBean 的 Id:
jsp:setProperty namesomeId propertysomeProperty .../jsp: getproperty“”动作顾名思义获取给定属性的值。如果属性不是字符串它会将其转换为字符串。/jsp:
jsp: getproperty“”动作具有以下语法其中 someId 是 useBean 的 Id:/jsp:
jsp:getProperty namesomeId propertysomeProperty .../清单 2-25 展示了如何创建用户 bean而清单 2-26 展示了这三个动作在 JSP 页面中的用法。
清单 2-25 。用户 Bean
1.package com.apress.jspactions;
2.
3.public class User {
4.
5.private String name;
6.
7.public String getName() {
8.return name;
9.}
10.
11.public void setName(String name) {
12.this.name name;
13.}
14.
15.}清单 2-25 中的用户 bean 将在 user.jsp 使用如清单 2-26 中的所示。
清单 2-26 。user.jsp
1.html
2.head
3./head
4.body
5.jsp:useBean iduser classcom.apress.jspactions.User /
6.jsp:setProperty nameuser propertyname valuevishal /
7.Hellonbsp;jsp:getProperty nameuser propertyname /
8./body
9./html图 2-30 显示了访问 user.jsp 时的输出。 图 2-30 。useBean、getProperty 和 setProperty 操作的用法
MVC 模式
自从有了面向对象编程的概念模型-视图-控制器(MVC) 模式的动机就一直存在。在 MVC 之前浏览器直接访问 JSP 页面。换句话说JSP 页面直接处理用户请求。这被称为模型 1 架构如图 2-31 所示。模型 1 架构展示了分散的应用控制这导致了紧密耦合和脆弱的表示层。 图 2-31 。模型 1 架构
设计 JSP 页面的 Model-2 架构实际上是应用于 web 应用的 MVC 模式。MVC 起源于 Smalltalk后来发展到了 Java 社区。图 2-32 显示了模型 2(换句话说MVC)架构。在 Model-2 中控制器处理用户请求而不是另一个 JSP 页面。控制器被实现为一个 servlet。当用户提交请求时执行以下步骤:
控制器 servlet 处理用户的请求。控制器 servlet 根据请求实例化适当的 JavaBeans。控制器 servlet 与中间层通信或直接与数据库通信以检索所需的数据。控制器在以下上下文之一中设置 JavaBeans:请求、会话或应用。控制器基于请求 URL 将请求分派到下一个视图。视图使用步骤 4 中的 JavaBeans 来显示数据。 图 2-32 。模型 2 架构
书店应用
在前一章中我们为书店应用开发了数据访问层并使用独立的 Java 应用对其进行了查询。在这一章中我们将用表示层取代独立的 Java 层。底部的数据访问层保持不变如图图 2-33 所示。 图 2-33 。用表示层替换独立的 Java 客户端
在生产就绪的应用中您还应该添加一个服务层来处理数据库异常。随着应用的增长一个分区的应用保持关注点的清晰分离。图 2-34 显示了书店应用的目录结构。 图 2-34 。书店应用的目录结构
主页
图 2-35 显示了应用的主页。在输入 URL(localhost:8080/book web/books)时主页会显示菜单其中包含书店数据库中可用书籍的类别。 图 2-35 。书店应用的主页
图 2-36 显示了书店应用的 MVC 架构。为了简洁和理解类别在主页上的显示方式图中只显示了与主页和类别相关的组件。 图 2-36 。MVC 在书店中的应用
在图 2-36 所示的 MVC 架构中 M 代表类别 V 代表 home.jsp C 代表 BookController。应用流程包括六个步骤如以下部分所述。
步骤 1:从请求中定位 Servlet
URL(localhost:8080/book Web/books)用于动态内容因此 web 服务器将请求转发给 servlet 容器(Tomcat)。清单 2-27 展示了部署描述符。
清单 2-27 。BookstoreWeb 应用的部署描述符
1.?xml version1.0 encodingUTF-8?
2.web-app xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance
3.fontnamehttp://java.sun.com/xml/ns/javaee xmlns:webhttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd
4.xsi:schemaLocationhttp://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd
5.idWebApp_ID version3.0
6.display-namebookWeb/display-name
7.servlet
8.servlet-nameBookServlet/servlet-name
9.servlet-classcom.apress.bookweb.controller.BookController/servlet-class
10.init-param
11.param-namebase/param-name
12.param-value/bookWeb/books/param-value
13./init-param
14.init-param
15.param-nameimageURL/param-name
16.param-value/bookWeb/images/param-value
17./init-param
18.load-on-startup1/load-on-startup
19./servlet
20.context-param
21.param-nameparam1/param-name
22.param-value/bookWeb/books/param-value
23./context-param
24.context-param
25.param-nameimageURL/param-name
26.param-value/bookWeb/images/param-value
27./context-param
28.servlet-mapping
29.servlet-nameBookServlet/servlet-name
30.url-pattern/books /url-pattern
31./servlet-mapping
32.welcome-file-list
33.welcome-fileindex.html/welcome-file
34.welcome-fileindex.htm/welcome-file
35.welcome-fileindex.jsp/welcome-file
36.welcome-filedefault.html/welcome-file
37.welcome-filedefault.htm/welcome-file
38.welcome-filedefault.jsp/welcome-file
39./welcome-file-list
40./web-app第 30 行 : url-pattern/books 被映射到 Servlet-mapping 元素中的 BookServlet该元素被映射到第 9 行的 servlet 类 BookController。第 20 到 27 行:我们在 web.xml 文件中为 servlet 指定上下文参数因为上下文参数可用于整个 web 应用。当一个 servlet 实例被创建时它的 init()方法被 servlet 容器调用。init()方法允许 servlet 在处理第一个请求之前初始化自己。我们在 BookController 中重写 init(ServletConfig config)方法以便从书店数据库中获取类别。这些类别将对整个应用可用。BookController 中被覆盖的 init(ServletConfig config)如清单 2-28 中的所示。
步骤 2 和步骤 3:通过 DAO 访问数据库从数据库中获取类别并在模型中设置类别
清单 2-28 显示了图书控制器。
清单 2-28 。BookController 的 init()方法
1.public void init(ServletConfig config) throws ServletException {
2.super.init(config);
3.BookDAO bookDao new BookDAOImpl();
4.// calling DAO method to retrieve bookList from Database
5.ListCategory categoryList bookDao.findAllCategories();
6.ServletContext context config.getServletContext();
7.context.setAttribute(categoryList, categoryList);
8.}第 5 行:这个类别列表是通过调用 bookDao 对象上的 findallcocategories()从数据库中获得的。Line7 :类别列表是在 ServletContext 中设置的这样整个 webapp 都可以使用这个列表。
步骤 4:分派到视图
随着 init()方法在前一步中完成容器调用 servlet 的 service()方法(在 servlet 的生命周期方法中讨论)。这个方法查看请求确定 HTTP 方法并在 servlet 上调用匹配的 doget()或 dopost()。清单 2-29 展示了 servlet 的 doGet()和 doPost()方法。
清单 2-29 。BookController 中的 doGet()和 doPost()
1.protected void doGet(HttpServletRequest request,
2.HttpServletResponse response) throws ServletException, IOException {
3.doPost(request, response);
4.}
5.
6.protected void doPost(HttpServletRequest request,
7.HttpServletResponse response) throws ServletException, IOException {
8.String base /jsp/;
9.String url base home.jsp;
10.String action request.getParameter(action);
11.String category request.getParameter(category);
12.String keyWord request.getParameter(keyWord);
13.if (action ! null) {
14.switch (action) {
15.case allBooks:
16.findAllBooks(request, response);
17.url base listOfBooks.jsp;
18.break;
19.case category:
20.findAllBooks(request, response);
21.url base category.jsp?category category;
22.break;
23.case search:
24.searchBooks(request, response, keyWord);
25.url base searchResult.jsp;
26.break;
27.
28.}
29.}
30.RequestDispatcher requestDispatcher getServletContext()
31..getRequestDispatcher(url);
32.requestDispatcher.forward(request, response);
33.}第 3 行:doPost()方法是从 doGet()方法调用的。第 9 行:这一行构造了指向主页视图(home.jsp)的 URL。第 10 行:该行从请求中获取动作参数。但是由于这是一个主页没有关联的 action 参数所以变量 action 为 null。第 13 行到第 29 行:跳过第 13 行到第 29 行的代码块因为动作为空。如果 action 不为 nullURL 将被重新构造为指向不同的视图这取决于 action 值是 allBooks 还是 category 或 search。第 32 行:request dispatcher 转发到第 31 行 URL 中的视图名。
步骤 5:从视图中访问模型
在前面的步骤中控制器使用 RequestDispatcher 将 home.jsp 转发到视图。
清单 2-30 展示了 home.jsp 的一个片段其中包括 leftColumn.jsp。leftColumn.jsp 文件使用模型类别在主页的左侧菜单上显示类别。
清单 2-30 。包括 home.jsp 的 leftColumn.jsp
1.body
2.div idcentered
3.
4.jsp:include pageheader.jsp flushtrue /
5.br /
6.jsp:include pageleftColumn.jsp flushtrue /
7.span classlabelFeatured Books/span
8...........
9./div
10./body第 6 行:JSP:include标签用于包含 leftColumn.jsp。这样做是因为应用的左侧栏(菜单)对应用中的所有屏幕都是通用的但是我们没有在所有屏幕中编写左侧栏而是将它编写在一个 JSP 页面中并在需要的任何地方包含它作为一种可重用性的方法。(在接下来关于 web 框架的几章中我们将看到更多重用 JSP 的高级技术。)
清单 2-31 展示了与 leftColumn.jsp 类别相关的代码片段其中类别被访问。
清单 2-31 。在 leftColumn.jsp 访问类别模型
1.lidivspan classlabel stylemargin-left: 15px;Categories/span/div
2.ul
3.%
4.ListCategory categoryList1 (ListCategory) application.getAttribute(categoryList);
5.IteratorCategory iterator1 categoryList1.iterator();
6.while (iterator1.hasNext()) {
7.Category category1 (Category) iterator1.next();%
8.lia classlabelhref%param1%?actioncategorycategoryId%category1.getId()%category%category1.getCategoryDescription()%spanclasslabel stylemargin-left: 30px;%category1.getCategoryDescription()%/span/a
9./li
10.%}%
11./ul/li第 4 行:这一行从 ServletContext 中获取类别列表。我们已经将类别列表保存在步骤 2 中从数据库获得的 ServletContext 中。第 6 行到第 10 行:类别细节显示在标记中比如你在主页上看到的类别描述。
注意清单 2-31 中的 JSP 页面使用 scriptlets 和表达式来获取类别并显示它们。使用 scriptlets 和表达式是不好的做法应该尽可能避免。这是下一章的主题它将向您展示如何用 JSTL 和 EL 替换 scriptlets 和表达式。
步骤 6:发送响应
上一步中构建的视图被交付给浏览器。
列出所有的书
当用户点击菜单上的所有书籍时显示所有书籍的列表如图图 2-37 所示。 图 2-37 。列出所有书籍
“所有书籍”链接在 leftColumn.jsp 文件中。清单 2-32 展示了菜单上所有书籍链接的代码片段。
清单 2-32 。leftColumn.jsp 所有图书链接
1.lidiv
2.a classlink1 href%param1%?actionallBooksspan
3.stylemargin-left: 15px; classlabelAll Books/span/a
4./div/li第 2 行:这一行是菜单中显示的所有书籍链接。当点击这个链接时action-allBooks 的值作为参数添加到 URL 中如 URL: http:localhost:8080/bookWeb/books?actionallBooks所示
执行步骤 2从请求中定位 servlet但是这一次动作不为 null值为 allBooks。因此执行 BookController 中 doPost()方法的代码块如清单 2-33 所示。
清单 2-33 。BookController 中 doPost()中的所有书籍
1.protected void doPost(HttpServletRequest request,
2.HttpServletResponse response) throws ServletException, IOException {
3.String base /jsp/;
4.String url base home.jsp;
5.String action request.getParameter(action);
6.String category request.getParameter(category);
7.String keyWord request.getParameter(keyWord);
8.if (action ! null) {
9.switch (action) {
10.case allBooks:
11.findAllBooks(request, response);
12.url base listOfBooks.jsp;
13.break;
14.case category:
15.findAllBooks(request, response);
16.url base category.jsp?category category;
17.break;
18.case search:
19.searchBooks(request, response, keyWord);
20.url base searchResult.jsp;
21.break;
22.
23.}
24.}
25.RequestDispatcher requestDispatcher getServletContext()
26..getRequestDispatcher(url);
27.requestDispatcher.forward(request, response);
28.}第 8 行:动作不为空动作的值为 allBooks。第 10 行到第 12 行:调用帮助器方法 findAllBooks(requestresponse)重新构造 URL 指向 listOfBooks.jspRequestDispatcher 转发到以 URL 形式提供给 RequestDispatcher 的视图。
清单 2-34 显示了 BookController 中的帮助器方法 findAllBooks(请求响应)。
清单 2-34 。BookController 中的 findAllBooks()
1.private void findAllBooks(HttpServletRequest request,
2.HttpServletResponse response) throws ServletException, IOException {
3.try {
4.BookDAO bookDao new BookDAOImpl();
5.ListBook bookList bookDao.findAllBooks();
6.request.setAttribute(bookList, bookList);
7.
8.} catch (Exception e) {
9.System.out.println(e);
10.}
11.}第 5 行到第 6 行:使用 DAO 上的 findAllBooks()方法从数据库中获得所有书籍的列表并在请求中设置为一个属性。
按类别搜索书籍
当用户点击菜单上的特定类别时显示该类别的图书列表如图图 2-38 所示。 图 2-38 。按类别搜索图书
我们在上一节中看到这些类别位于 leftColumn.jsp。清单 2-35 展示了类别的代码片段。
清单 2-35 。菜单上的类别链接(leftColumn.jsp)
1.li
2.a classlabel href%param1%?actioncategorycategoryId%category1.getId()%category%category1.getCategoryDescription()%span classlabel stylemargin-left: 30px;%category1.getCategoryDescription()%/span/a
3./li第二行:这是菜单中显示的类别的链接。当点击这个链接时类别的 ID 和描述以及动作类别的名称作为参数被添加到 URL 中如下面的 URL 所示: http:localhost:8080/bookWeb/books?actioncategorycategoryId1categoryclojure再次执行步骤 2从请求中定位 servlet这一次动作不为 null并且有一个值类别。因此执行 BookController 中 doPost()方法的代码块如清单 2-36 所示。
清单 2-36 。BookController 中的 doPost()
1.protected void doPost(HttpServletRequest request,
2.HttpServletResponse response) throws ServletException, IOException {
3.String base /jsp/;
4.String url base home.jsp;
5.String action request.getParameter(action);
6.String category request.getParameter(category);
7.String keyWord request.getParameter(keyWord);
8.if (action ! null) {
9.switch (action) {
10.case allBooks:
11.findAllBooks(request, response);
12.url base listOfBooks.jsp;
13.break;
14.case category:
15.findAllBooks(request, response);
16.url base category.jsp?category category;
17.break;
18.case search:
19.searchBooks(request, response, keyWord);
20.url base searchResult.jsp;
21.break;
22.
23.}
24.}
25.RequestDispatcher requestDispatcher getServletContext()
26..getRequestDispatcher(url);
27.requestDispatcher.forward(request, response);
28.}第 8 行:动作不为空动作的值为类别。第 15 行到第 16 行:调用帮助器方法 findAllBooks(requestresponse)重新构造 URL 指向 listOfBooks.jspRequestDispatcher 转发给以 URL 形式提供给 RequestDispatcher 的视图。
清单 2-37 显示了 BookController 中的帮助器方法 findAllBooks(请求响应)。
清单 2-37 。BookController 中的 findAllBooks()
1.private void findAllBooks(HttpServletRequest request,
2.HttpServletResponse response) throws ServletException, IOException {
3.try {
4.BookDAO bookDao new BookDAOImpl();
5.ListBook bookList bookDao.findAllBooks();
6.request.setAttribute(bookList, bookList);
7.
8.} catch (Exception e) {
9.System.out.println(e);
10.}
11.}第 5 行到第 6 行:使用 DAO 上的 findAllBooks()方法从数据库中获得所有书籍的列表并在请求中设置为属性。
通过关键字搜索书籍
您可以通过作者姓名或书名中的关键词来搜索图书如图图 2-39 所示。 图 2-39 。通过关键字搜索图书
在我们讨论关键字搜索如何工作之前让我们先来看看搜索的一个可用性方面。搜索栏旁边有一个问号用于帮助用户。换句话说鼠标悬停时会显示一个工具提示指示要使用的搜索参数如图 2-40 所示。 图 2-40 。搜索参数的工具提示
为了理解工具提示是如何工作的请看一下搜索字段的标记它在 leftColumn.jsp如清单 2-38 所示。
清单 2-38 。搜索字段标记
1.form classsearch
2.input typehidden nameaction valuesearch /
3.input idtexttypetext namekeyWord size12 /
4.spanclasstooltip_message?/span
5.p /
6.input idsubmit typesubmit valueSearch /
7./form第 4 行:这有一个类 tooltip_message。工具提示在这个类上使用 jQuery 和 CSS。
Listing 2-39 illustrates the jQuery code.清单 2-39 。工具提示的 jQuery
1.$(document).ready(function () {
2.$(span.tooltip_message).hover(function () {
3.$(this).append(div classmessagepSearch by Keyword in:ulliAuthor First Name /liliAuthor Last Name liTitle of the book /li/ul/p/div);
4.},function () {
5.$(div.message).remove();
6.});
7.});第 2 行:类 tooltip_message 和 span 标签被用作调用悬停功能的选择器。第 3 行:将在工具提示中显示的消息附加到第 2 行选择器返回的对象上。
以类似的方式工具提示可以添加到主屏幕上的图像如图图 2-41 所示。清单 2-40 说明了所使用的 jQuery 函数。 图 2-41 。图像的工具提示
清单 2-40 。图像提示的 jQuery 函数
$(span.tooltip_img1).hover(function(){$(this).append(div classmessageulliTitle - Beginning Groovy, Grails and Griffon/liliAuthor: Vishal Layka/liliPublisher: Apress/li/ul/div);
}, function(){$(div.message).remove();});Listing 2-41 illustrates the CSS code for the tooltip.清单 2-41 。工具提示的 CSS
1.span.tooltip_message,span.tooltip_img1 {
2.cursor: pointer;
3.display: inline-block;
4.background-color: #F20B26;
5.width: 16px;
6.height: 18px;
7.color: #ffffff;
8.font-size: 12px;
9.font-weight: bold;
10.text-align: center;
11.position: relative;
12.}
13.
14.span.tooltip_message:hover {
15.background-color: #04FF97;
16.}
17.
18.div.message {
19.background-color: #04FF97;
20.color: #000000;
21.position: absolute;
22.left: 18px;
23.top: -18px;
24.z-index: 1000000;
25.text-align: left;
26.width: 280px;
27.}This CSS and jQuery code in Listing 2-40 and Listing 2-41 are included in leftColumn.jsp, as illustrated in Listing 2-42.清单 2-42 。在 leftColumn.jsp 访问 CSS 和 jQuery 文件
1.link relstylesheet hrefcss/bookstore.css typetext/css /
2.script srcjs/bookstore.js typetext/javascript/script
3.script typetext/javascript srcjs/jquery-1.9.1.js/script第 1 行:这是清单 2-41 所示规则的外部化 CSS 文件第 2 行:这是清单 2-40 中 jQuery 函数的外部化 JavaScript 文件第 3 行:第 3 行指定了我们正在使用的 jQuery 库。
现在我们将从 web 应用中的关键字搜索功能开始。清单 2-43 展示了搜索字段的标记。
清单 2-43 。搜索字段标记
1.form classsearch
2.input typehidden nameaction valuesearch /input idtext
3.typetext namekeyWord size12 /span
4.classtooltip_message?/span
5.p /
6.input idsubmit typesubmit valueSearch /
7./form第 2 行:该行指定动作值搜索。第 6 行:该行提交请求。
当用户提交搜索请求时(再次从请求中定位 servlet)执行第 2 步这一次操作是值搜索。因此执行 BookController 中 doPost()方法中的搜索用例如清单 2-44 中的所示。
清单 2-44 。BookController 中的 doPost()
1.protected void doPost(HttpServletRequest request,
2.HttpServletResponse response) throws ServletException, IOException {
3.String base /jsp/;
4.String url base home.jsp;
5.String action request.getParameter(action);
6.String category request.getParameter(category);
7.String keyWord request.getParameter(keyWord);
8.if (action ! null) {
9.switch (action) {
10.case allBooks:
11.findAllBooks(request, response);
12.url base listOfBooks.jsp;
13.break;
14.case category:
15.findAllBooks(request, response);
16.url base category.jsp?category category;
17.break;
18.case search:
19.searchBooks(request, response, keyWord);
20.url base searchResult.jsp;
21.break;
22.
23.}
24.}
25.RequestDispatcher requestDispatcher getServletContext()
26..getRequestDispatcher(url);
27.requestDispatcher.forward(request, response);
28.}第 18 行:执行案例搜索动作值为 search第 19 行:调用 search books()方法。searchBooks()在列表 2-45 中有说明。*第 20 行:*第 20 行构造了视图的 URL。第 26 行:视图的 URL 被提供给 RequestDispatcher。
清单 2-45 展示了控制器用来调用 DAO 的 searchBooks()助手方法。
清单 2-45 。BookController 中的 searchBooks()
1.private void searchBooks(HttpServletRequest request,
2.HttpServletResponse response, String keyWord)
3.throws ServletException, IOException {
4.try {
5.BookDAO bookDao new BookDAOImpl();
6.ListBook bookList bookDao.searchBooksByKeyword(keyWord);
7.
8.request.setAttribute(bookList, bookList);
9.
10.} catch (Exception e) {
11.System.out.println(e);
12.}
13.}
14.第 6 行:调用 BookDAO 中定义的 searchBooksByKeyword()方法根据搜索关键字获取图书列表。我们在第一章中为 web 应用构建数据访问层时使用了这种方法。
摘要
本章介绍了 servlets 和 JSP并向您展示了如何使用这些 web 组件创建您的第一个 web 应用。然后本章实现了现实世界中基于 MVC 的 Java web 应用一个使用 servlets 和 JSP 的书店。在下一章中我们将扩充这个应用以使用 JSTL 和表达式语言将业务逻辑关注点从表示中分离出来的最佳实践。
www . IETF . org/RFC/RFC 3986 . txt
JCP . org/en/JSR/detailid340
3【www.oracle.com/technetwork/java/index-jsp-135475.html】??