Spring-秘籍-全-

Spring 秘籍(全)

原文:zh.annas-archive.org/md5/c6fde735267f01a54525b252e09d43f8

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

与 Ruby on Rails、Django 和现代 PHP 框架相比,使用 Java 进行 Web 开发的学习曲线较高。Spring 是 Web 开发中最常用的 Java 框架,它使得配置尽可能简单,尤其是在最近将配置类从 XML 文件转换为纯 Java 类之后。本书专注于以最有效的方式让您快速上手 Spring 4。

本书涵盖内容

第一章, 创建 Spring 应用程序,涵盖了在 Mac OS、Ubuntu 和 Windows 上安装 Java 和其他软件,以及如何构建 Spring 应用程序和 Spring Web 应用程序。

第二章, 定义 Bean 和使用依赖注入,介绍了 Spring Bean,并演示了如何定义和使用它们。

第三章, 使用控制器和视图,描述了如何创建控制器方法,使用 JSP 视图,以及构建页面模板和多语言页面。

第四章, 查询数据库,解释了如何使用 JDBC 与数据库交互,以及如何将 Hibernate 集成到 Spring 应用程序中。

第五章, 使用表单,描述了如何初始化表单,在表单提交时检索表单数据,并详细说明了表单小部件(文本字段、选择字段等)的使用。

第六章, 管理安全,介绍了 Spring Security,并演示了如何执行用户认证和用户授权,以及如何配置 HTTPS。

第七章, 单元测试,介绍了使用 JUnit 和 TestNG 进行单元测试,并解释了如何测试 Spring 应用程序。

第八章, 运行批处理作业,详细说明了批处理作业如何与 Spring 一起工作,并解释了如何构建批处理作业以及如何从 Web 应用程序或从命令行执行它们。

第九章, 处理移动设备和平板电脑,解释了如何使 Spring Web 应用程序根据访问它的设备类型显示不同的内容。

第十章, 连接到 Facebook 和 Twitter,解释了如何访问 Facebook 或 Twitter 账户以获取一些现有数据或创建新数据(推文、帖子等)。

第十一章, 使用 Java RMI、HTTP Invoker、Hessian 和 REST,涵盖了 Spring 应用程序如何使用各种技术通过网络与其他软件组件进行交互。

第十二章,使用面向方面编程,解释了 AOP(面向方面编程)是什么,如何与 Spring 一起使用,并涵盖了其一些常见用途。

为本书所需

您需要一个装有 Mac OS、Ubuntu 和 Windows 的计算机。要构建 Spring 应用程序,您至少需要 Java。第一章,创建 Spring 应用程序,涵盖了在每种操作系统上安装 Java、Maven、Tomcat 和 Eclipse。

本书面向的对象

如果您有一些 Java 和 Web 开发(不一定是 Java)的经验,并希望快速精通 Spring,本书适合您。

部分

在本书中,您将找到一些经常出现的标题(准备就绪、如何操作、工作原理、更多内容以及相关内容)。

为了清楚地说明如何完成食谱,我们使用以下部分:

准备就绪

本节描述了如何为食谱设置项目。

如何操作…

本节包含遵循食谱所需的步骤。

工作原理…

本节通常包含对上一节发生情况的详细说明。

更多内容…

本节包含有关食谱的附加信息,以便您对食谱有更深入的了解。

相关内容

本节提供了有关食谱的其他有用信息的链接。

规范

在本书中,您将找到许多文本样式,用于区分不同类型的信息。以下是一些这些样式的示例及其含义的解释。

文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名显示如下:“我们可以通过使用include指令来包含其他上下文。”

代码块设置如下:

public String userList() {
  return "userList";
}

当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:

public String userList() {
  return "userList";
}

新术语重要词汇以粗体显示。屏幕上看到的单词,例如在菜单或对话框中,在文本中显示如下:“在Maven下,选择Maven 项目并点击下一步 >。”

注意

警告或重要注意事项以这样的框显示。

小贴士

小技巧和窍门看起来像这样。

读者反馈

我们欢迎读者的反馈。告诉我们您对本书的看法——您喜欢或不喜欢的地方。读者反馈对我们来说很重要,因为它帮助我们开发出您真正能从中获得最大收益的标题。

要向我们发送一般反馈,只需发送电子邮件至<feedback@packtpub.com>,并在邮件主题中提及本书的标题。

如果您在某个主题上具有专业知识,并且您有兴趣撰写或为本书做出贡献,请参阅我们的作者指南www.packtpub.com/authors

客户支持

现在您已成为 Packt 书籍的骄傲拥有者,我们有一些事情可以帮助您从您的购买中获得最大收益。

下载示例代码

您可以从 www.packtpub.com 下载您购买的所有 Packt 出版物的示例代码文件。如果您在其他地方购买了这本书,您可以访问 www.packtpub.com/support 并注册,以便将文件直接通过电子邮件发送给您。

勘误

尽管我们已经尽一切努力确保我们内容的准确性,但错误仍然会发生。如果您在我们的某本书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以节省其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问 www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入您的勘误详情来报告它们。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站或添加到该标题的勘误部分下的现有勘误列表中。

要查看之前提交的勘误表,请访问 www.packtpub.com/books/content/support,并在搜索字段中输入书籍名称。所需信息将出现在勘误部分。

盗版

互联网上版权材料的盗版是一个持续存在的问题,涉及所有媒体。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上发现我们作品的任何非法副本,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。

请通过 <copyright@packtpub.com> 联系我们,并提供涉嫌盗版材料的链接。

我们感谢您在保护我们作者以及为我们提供有价值内容的能力方面提供的帮助。

问题

如果您在这本书的任何方面遇到问题,您可以通过 <questions@packtpub.com> 联系我们,我们将尽力解决问题。

第一章:创建 Spring 应用程序

在本章中,我们将介绍以下食谱:

  • 在 Mac OS 上安装 Java、Maven、Tomcat 和 Eclipse

  • 在 Ubuntu 上安装 Java、Maven、Tomcat 和 Eclipse

  • 在 Windows 上安装 Java、Maven、Tomcat 和 Eclipse

  • 创建一个 Spring Web 应用程序

  • 运行 Spring Web 应用程序

  • 在标准 Java 应用程序中使用 Spring

简介

在本章中,我们首先将介绍一些 Spring 开发工具的安装:

  • Java:Spring 是一个 Java 框架。

  • Maven:这是一个类似于 Ant 的构建工具。它使得将 Spring 库添加到项目中变得容易。Gradle 作为另一个构建工具选项。

  • Tomcat:这是一个 Java Web 应用程序的 Web 服务器。您也可以使用 JBoss、Jetty、GlassFish 或 WebSphere。

  • Eclipse:这是一个 IDE。您也可以使用 NetBeans、IntelliJ IDEA 等。

然后,我们将构建一个 Spring Web 应用程序,并使用 Tomcat 运行它。

最后,我们将看到 Spring 也可以在标准 Java 应用程序中使用(不是 Web 应用程序)。

在 Mac OS 上安装 Java、Maven、Tomcat 和 Eclipse

我们首先安装 Java 8,因为它默认情况下并未安装在 Mac OS 10.9 或更高版本上。然后,我们将安装 Maven 3,这是一个类似于 Ant 的构建工具,用于管理我们将使用的(Spring、Hibernate 等)外部 Java 库。Maven 3 还可以编译源文件并生成 JAR 和 WAR 文件。我们还将安装 Tomcat 8,这是一个流行的 Java Web 应用程序的 Web 服务器,我们将在这本书的整个过程中使用它。也可以使用 JBoss、Jetty、GlassFish 或 WebSphere。最后,我们将安装 Eclipse IDE,但您也可以使用 NetBeans、IntelliJ IDEA 等。

如何操作...

首先安装 Java,然后是 Maven、Tomcat 和 Eclipse。

安装 Java

  1. 从 Oracle 网站oracle.com下载 Java。在 Java SE 下载部分,选择 Java SE 8 SDK。选择接受许可协议并下载Mac OS X x64包。到该页面的直接链接是www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html安装 Java

  2. 打开下载的文件,启动它,并完成安装。

  3. 在您的~/.bash_profile文件中,设置JAVA_HOME环境变量。将jdk1.8.0_40.jdk更改为您系统上的实际文件夹名称(这取决于您使用的 Java 版本,它定期更新):

    export JAVA_HOME="/Library/Java/JavaVirtualMachines/ jdk1.8.0_40.jdk/Contents/Home"
    
  4. 打开一个新的终端并测试它是否工作:

    $ java -version
    java version "1.8.0_40"
    Java(TM) SE Runtime Environment (build 1.8.0_40-b26)
    Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)
    
    

安装 Maven

  1. 从 Apache 网站maven.apache.org/download.cgi下载 Maven。选择当前稳定版本的二进制 zip 文件安装 Maven

  2. 解压缩下载的文件,并将提取的文件夹移动到方便的位置(例如,~/bin)。

  3. 在您的 ~/.bash_profile 文件中,添加一个指向该文件夹的 MAVEN_HOME 环境变量。例如:

    export MAVEN_HOME=~/bin/apache-maven-3.3.1
    
  4. bin 子目录添加到您的 PATH 环境变量中:

    export PATH=$PATH:$MAVEN_HOME/bin
    
  5. 打开一个新的终端并测试其是否工作:

    $ mvn –v
    Apache Maven 3.3.1 (12a6b3...
    Maven home: /Users/jerome/bin/apache-maven-3.3.1
    Java version: 1.8.0_40, vendor: Oracle Corporation
    Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_...
    Default locale: en_US, platform encoding: UTF-8
    OS name: "mac os x", version: "10.9.5", arch... …
    
    

安装 Tomcat

  1. 从 Apache 网站下载 Tomcat tomcat.apache.org/download-80.cgi 并选择 核心 二进制发行版。安装 Tomcat

  2. 解压下载的文件并将提取的文件夹移动到方便的位置(例如,~/bin)。

  3. 使 bin 子目录中的脚本可执行:

    chmod +x bin/*.sh
    
  4. 使用 catalina.sh 脚本启动 Tomcat:

    $ bin/catalina.sh run
    Using CATALINA_BASE:   /Users/jerome/bin/apache-tomcat-7.0.54
    ...
    INFO: Server startup in 852 ms
    
    
  5. Tomcat 默认在 8080 端口运行。在网页浏览器中,访问 http://localhost:8080/ 以检查其是否工作。

安装 Eclipse

  1. www.eclipse.org/downloads/ 下载 Eclipse。选择 Java EE 开发者 Eclipse IDEMac OS X 64 位 版本。安装 Eclipse

  2. 解压下载的文件并将提取的文件夹移动到方便的位置(例如,~/bin)。

  3. 通过执行 eclipse 二进制文件启动 Eclipse:

    ./eclipse
    

更多内容...

可以使用这两个脚本以后台进程运行 Tomcat:

bin/startup.sh
bin/shutdown.sh

在开发机器上,将 Tomcat 的文件夹放在家目录中的某个位置(例如,~/bin)很方便,这样其内容可以在不使用 root 权限的情况下更新。

在 Ubuntu 上安装 Java、Maven、Tomcat 和 Eclipse

我们首先安装 Java 8。然后,我们将安装 Maven 3,一个类似于 Ant 的构建工具,用于管理我们将使用的(Spring、Hibernate 等等)外部 Java 库。Maven 3 还编译源文件并生成 JAR 和 WAR 文件。我们还将安装 Tomcat 8,这是一个流行的 Java 网络应用服务器,我们将在这本书中使用它。也可以使用 JBoss、Jetty、GlassFish 或 WebSphere。最后,我们将安装 Eclipse IDE,但您也可以使用 NetBeans、IntelliJ IDEA 等等。

如何操作...

首先安装 Java,然后安装 Maven、Tomcat 和 Eclipse。

安装 Java

  1. 添加此 PPA个人软件包存档):

    sudo add-apt-repository -y ppa:webupd8team/java
    
    
  2. 刷新可用的软件包列表:

    sudo apt-get update
    
    
  3. 下载并安装 Java 8:

    sudo apt-get install –y oracle-java8-installer
    
    
  4. 测试其是否工作:

    $ java -version
    java version "1.8.0_40"
    Java(TM) SE Runtime Environment (build 1.8.0_40-b25)
    Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25…
    
    

安装 Maven

  1. 从 Apache 网站下载 Maven maven.apache.org/download.cgi。选择当前稳定版本的 二进制 zip 文件安装 Maven

  2. 解压下载的文件并将生成的文件夹移动到方便的位置(例如,~/bin)。

  3. 在您的 ~/.bash_profile 文件中,添加一个指向该文件夹的 MAVEN_HOME 环境变量。例如:

    export MAVEN_HOME=~/bin/apache-maven-3.3.1
    
  4. bin 子目录添加到您的 PATH 环境变量中:

    export PATH=$PATH:$MAVEN_HOME/bin
    
  5. 打开一个新的终端并测试其是否工作:

    $ mvn –v
    Apache Maven 3.3.1 (12a6b3...
    Maven home: /home/jerome/bin/apache-maven-3.3.1
    Java version: 1.8.0_40, vendor: Oracle Corporation
    …
    
    

安装 Tomcat

  1. 从 Apache 网站tomcat.apache.org/download-80.cgi下载 Tomcat,并选择核心二进制发行版。安装 Tomcat

  2. 解压缩下载的文件,并将提取的文件夹移动到方便的位置(例如,~/bin)。

  3. 使bin子文件夹中的脚本可执行:

    chmod +x bin/*.sh
    
  4. 使用catalina.sh脚本启动 Tomcat:

    $ bin/catalina.sh run
    Using CATALINA_BASE:   /Users/jerome/bin/apache-tomcat-7.0.54
    ...
    INFO: Server startup in 852 ms
    
    
  5. Tomcat 默认运行在 8080 端口。转到http://localhost:8080/以检查它是否工作。

安装 Eclipse

  1. www.eclipse.org/downloads/下载 Eclipse。选择Linux 64 Bit版本的Eclipse IDE for Java EE Developers安装 Eclipse

  2. 解压缩下载的文件,并将提取的文件夹移动到方便的位置(例如,~/bin)。

  3. 通过执行eclipse二进制文件来启动 Eclipse:

    ./eclipse
    

更多内容…

可以使用这两个脚本将 Tomcat 作为后台进程运行:

bin/startup.sh
bin/shutdown.sh

在开发机器上,将 Tomcat 的文件夹放在家目录的某个位置(例如,~/bin)是很方便的,这样其内容就可以在不使用 root 权限的情况下更新。

在 Windows 上安装 Java、Maven、Tomcat 和 Eclipse

我们首先安装 Java 8。然后,我们将安装 Maven 3,这是一个类似于 Ant 的构建工具,用于管理我们将使用的(Spring、Hibernate 等)外部 Java 库。Maven 3 还可以编译源文件并生成 JAR 和 WAR 文件。我们还将安装 Tomcat 8,这是一个流行的 Java 网络应用程序的 Web 服务器,我们将在这本书的整个过程中使用它。也可以使用 JBoss、Jetty、GlassFish 或 WebSphere。最后,我们将安装 Eclipse IDE,但您也可以使用 NetBeans、IntelliJ IDEA 等。

如何操作…

首先安装 Java,然后安装 Maven、Tomcat 和 Eclipse。

安装 Java

  1. 从 Oracle 网站oracle.com下载 Java。在 Java SE 下载部分,选择 Java SE 8 SDK。选择接受许可协议并下载Windows x64包。到页面的直接链接是www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html安装 Java

  2. 打开下载的文件,启动它,并完成安装。

  3. 导航到控制面板 | 系统和安全 | 系统 | 高级系统设置 | 环境变量…

  4. 添加一个值为C:\Program Files\Java\jdk1.8.0_40JAVA_HOME系统变量。将jdk1.8.0_40更改为您系统上的实际文件夹名称(这取决于 Java 的版本,它定期更新)。安装 Java

  5. 通过在命令提示符中输入java –version来测试它是否工作。安装 Java

安装 Maven

  1. 从 Apache 网站maven.apache.org/download.cgi下载 Maven。选择当前稳定版本的二进制 zip 文件。安装 Maven

  2. 解压下载的文件。

  3. 在您的用户文件夹中创建一个Programs文件夹。

  4. 将提取的文件夹移动到其中。

  5. 导航到控制面板 | 系统和安全 | 系统 | 高级系统设置 | 环境变量…

  6. 添加一个MAVEN_HOME系统变量,其路径为 Maven 文件夹。例如,C:\Users\jerome\Programs\apache-maven-3.2.1

  7. 打开Path系统变量。

  8. 在其后面追加;%MAVEN_HOME%\bin。安装 Maven

  9. 通过打开命令提示符并输入mvn –v来测试它是否正在运行。安装 Maven

安装 Tomcat

  1. 从 Apache 网站tomcat.apache.org/download-80.cgi下载 Tomcat,并选择32 位/64 位 Windows 服务安装程序的二进制发行版。安装 Tomcat

  2. 启动并完成安装。

  3. Tomcat 默认运行在 8080 端口。访问http://localhost:8080/以检查它是否正在运行。

安装 Eclipse

  1. www.eclipse.org/downloads/下载 Eclipse。选择Java EE 开发者 Eclipse IDE 的 Windows 64 位版本。安装 Eclipse

  2. 解压下载的文件。

  3. 启动eclipse程序。

创建一个 Spring Web 应用程序

在本食谱中,我们将使用 Eclipse 构建一个简单的 Spring Web 应用程序。我们将:

  • 创建一个新的 Maven 项目

  • 将 Spring 添加到其中

  • 添加两个 Java 类来配置 Spring

  • 创建一个“Hello World”网页

在下一个食谱中,我们将编译并运行这个 Web 应用程序。

如何做到这一点…

在本节中,我们将使用 Eclipse 创建一个 Spring Web 应用程序。

在 Eclipse 中创建一个新的 Maven 项目

  1. 在 Eclipse 中,在文件菜单中选择新建 | 项目…

  2. Maven下,选择Maven 项目并点击下一步 >

  3. 选择创建一个简单项目(跳过存档选择)复选框,然后点击下一步 >

  4. 组 ID字段中输入com.springcookbook。在工件 ID字段中输入springwebapp。对于打包,选择war并点击完成

使用 Maven 将 Spring 添加到项目中

打开项目根目录下的 Maven 的pom.xml配置文件。选择pom.xml选项卡以直接编辑 XML 源代码。在project XML 节点下,定义 Java 和 Spring 的版本。还要添加 Servlet API、Spring Core 和 Spring MVC 依赖项:

<properties>
  <java.version>1.8</java.version>
  <spring.version>4.1.5.RELEASE</spring.version>
</properties>

<dependencies>
  <!-- Servlet API -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
  </dependency>

  <!-- Spring Core -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <!-- Spring MVC -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
  </dependency>
</dependencies>

为 Spring 创建配置类

  1. 在左侧窗格包资源管理器中,右键单击项目文件夹并选择新建 | 包…

  2. com.springcookbook.config 包中,创建 AppConfig 类。在 Source 菜单中,选择 Organize Imports 以添加所需的导入声明:

    package com.springcookbook.config;
    @Configuration
    @EnableWebMvc
    @ComponentScan (basePackages = {"com.springcookbook.controller"})
    public class AppConfig {  
    }
    
  3. 仍然在 com.springcookbook.config 包中,创建 ServletInitializer 类。添加所需的导入声明如下:

    package com.springcookbook.config;
    
    public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class<?>[0];
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class<?>[]{AppConfig.class};
        }
    
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    }
    

创建一个 "Hello World" 网页

com.springcookbook.controller 包中,创建 HelloController 类及其 hi() 方法:

@Controller
public class HelloController {
  @RequestMapping("hi")
  @ResponseBody
  public String hi() {
      return "Hello, world.";
  }
}

它是如何工作的…

本节将提供更多关于每个步骤发生情况的详细信息。

在 Eclipse 中创建一个新的 Maven 项目

生成的 Maven 项目是一个 pom.xml 配置文件,以及一系列空目录:

pom.xml
src
 |- main
    |- java
    |- resources
    |- webapp
 |- test
    |- java
    |- resources

使用 Maven 将 Spring 添加到项目中

声明的 Maven 库及其依赖项将由 Eclipse 在后台自动下载。它们列在左侧窗格的 Package Explorer 下的 Maven Dependencies 中。

Tomcat 提供了 Servlet API 依赖,但我们仍然声明了它,因为我们的代码需要它来编译。由于 <scope>provided</scope> 声明,Maven 不会将其包含在生成的 .war 文件中。

为 Spring 创建配置类

AppConfig 是一个 Spring 配置类。它是一个标准的 Java 类,带有以下注解:

  • @Configuration:这声明它为一个 Spring 配置类

  • @EnableWebMvc:这使 Spring 能够接收和处理网络请求

  • @ComponentScan(basePackages = {"com.springcookbook.controller"}):这扫描 com.springcookbook.controller 包中的 Spring 组件

ServletInitializer 是 Spring servlet 的配置类;它取代了标准的 web.xml 文件。它将由 SpringServletContainerInitializer 自动检测,该初始化器由任何 Servlet 3 自动调用。ServletInitializer 扩展了 AbstractAnnotationConfigDispatcherServletInitializer 抽象类并实现了所需的方法:

  • getServletMappings():这声明了 servlet 根 URI。

  • getServletConfigClasses():这声明了 Spring 配置类。在这里,我们声明了之前定义的 AppConfig 类。

创建一个 "Hello World" 网页

我们在 com.springcookbook.controller 包中创建了一个控制器类,我们在 AppConfig 中声明了它。当导航到 http://localhost:8080/hi 时,hi() 方法将被调用,浏览器中将显示 Hello, world。这将在 第三章 使用控制器和视图 中进一步解释。

运行 Spring 网络应用程序

在这个菜谱中,我们将使用之前菜谱中的 Spring 网络应用程序。我们将使用 Maven 编译它,并用 Tomcat 运行它。

如何做到这一点…

这里是编译和运行 Spring 网络应用程序的步骤:

  1. pom.xml 中,在 project XML 节点下添加以下样板代码。它将允许 Maven 生成 .war 文件,而无需 web.xml 文件:

    <build>
        <finalName>springwebapp</finalName>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-war-plugin</artifactId>
          <version>2.5</version>
          <configuration>
            <failOnMissingWebXml>false</failOnMissingWebXml>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  2. 在 Eclipse 中,在左侧窗格 包资源管理器 中选择 springwebapp 项目文件夹。在 运行 菜单中选择 运行 并选择 Maven 安装,或者你可以在项目文件夹根目录的终端中执行 mvn clean install。在这两种情况下,都会生成一个包含 springwebapp.war 文件的 target 文件夹。

  3. target/springwebapp.war 文件复制到 Tomcat 的 webapps 文件夹。

  4. 启动 Tomcat。

  5. 在浏览器中,访问 http://localhost:8080/springwebapp/hi 检查它是否工作。如何操作…

它是如何工作的…

pom.xml 中,样板代码防止 Maven 抛出错误,因为没有 web.xml 文件。Java 网络应用程序需要 web.xml 文件;然而,由于 Servlet 规范 3.0(在 Tomcat 7 及更高版本中实现),它不再需要。

还有更多…

在 Mac OS 和 Linux 上,你可以在 Tomcat 的 webapps 文件夹中创建一个指向你项目文件夹中 .war 文件的符号链接。例如:

ln -s ~/eclipse_workspace/spring_webapp/target/springwebapp.war ~/bin/apache-tomcat/webapps/springwebapp.war

因此,当你的项目文件夹中的 .war 文件被更新时,Tomcat 会检测到它已被修改,并将自动重新加载应用程序。

在标准 Java 应用程序中使用 Spring

在本食谱中,我们将使用 Spring 构建一个标准的 Java 应用程序(不是网络应用程序)。我们将:

  • 创建一个新的 Maven 项目

  • 将 Spring 添加到其中

  • 添加一个配置 Spring 的类

  • 添加一个 User

  • 在 Spring 配置类中定义一个 User 单例

  • main() 方法中使用 User 单例

如何操作…

在本节中,我们将介绍在标准(非网络)Java 应用程序中使用 Spring 的步骤。

在 Eclipse 中创建一个新的 Maven 项目

  1. 在 Eclipse 中,在 文件 菜单中选择 新建 | 项目...

  2. Maven 下,选择 Maven 项目 并点击 下一步 >

  3. 选择 创建一个简单项目(跳过存档选择) 复选框并点击 下一步 >

  4. 对于 组 ID 字段,输入 com.springcookbook。对于 工件 ID 字段,输入 springapp。点击 完成

使用 Maven 将 Spring 添加到项目中

打开 Maven 的 pom.xml 配置文件,位于项目根目录。选择 pom.xml 选项卡以直接编辑 XML 源代码。在 project XML 节点下,定义 Java 和 Spring 版本并添加 Spring Core 依赖项:

<properties>
  <java.version>1.8</java.version>
  <spring.version>4.1.5.RELEASE</spring.version>
</properties>

<dependencies>
  <!-- Spring Core -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
  </dependency>
</dependencies>

为 Spring 创建一个配置类

  1. 创建 com.springcookbook.config Java 包;在左侧窗格 包资源管理器 中,右键单击项目并选择 新建 | 包…

  2. com.springcookbook.config 包中创建 AppConfig 类。在 菜单中选择 整理导入 以添加所需的导入声明:

    @Configuration
    public class AppConfig {
    }
    

创建 User 类

创建一个包含两个 String 字段的 User Java 类:

public class User {
  private String name;
  private String skill;

  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getSkill() {
    return skill;
  }
  public void setSkill(String skill) {
    this.skill = skill;
  }
}

在 Spring 配置类中定义一个 User 单例

AppConfig 类中,定义一个 User bean:

  @Bean
  public User admin(){
    User u = new User();
    u.setName("Merlin");
    u.setSkill("Magic");
    return u;
  }

在 main() 方法中使用 User 单例

  1. 创建包含main()方法的com.springcookbook.main包:

    package com.springcookbook.main;
    public class Main {
      public static void main(String[] args) {
    }
    }
    
  2. main()方法中,检索 User 单例并打印其属性:

    AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext(AppConfig.class);
    
    User admin = (User) springContext.getBean("admin");
    
    System.out.println("admin name: " + admin.getName());
    System.out.println("admin skill: " + admin.getSkill());
    
    springContext.close();
    
  3. 测试其是否工作;在运行菜单中,选择运行在 main()方法中使用 User 单例

它是如何工作的...

我们创建了一个 Java 项目,并向其中添加了 Spring。我们定义了一个名为adminUserbean(bean 名称默认为 bean 方法名称)。Spring beans 将在下一章中解释。

Main类中,我们从AppConfig类创建了一个 Spring 上下文对象,并从中检索了adminbean。我们使用了该 bean,最后关闭了 Spring 上下文。

第二章。定义 Bean 和使用依赖注入

在本章中,我们将介绍以下菜谱:

  • 定义一个 Bean 显式使用 @Bean

  • 使用 @Component 隐式定义一个 Bean

  • 通过 @Autowired 注入依赖使用 Bean

  • 直接使用 Bean

  • 列出所有 Bean

  • 使用多个配置类

简介

Bean 是 Spring 的核心。它们是由 Spring 实例化和管理的标准 Java 对象。

Bean 主要用于:

  • 以某种方式配置 Spring(数据库连接参数、安全等)

  • 避免使用 依赖注入 硬编码依赖,这样我们的类才能保持自包含和可单元测试

在本章中,你将学习如何定义 Bean 并使用它们。

使用 @Bean 显式定义一个 Bean

定义 Bean 的最简单方法是在 Spring 配置类中创建一个带有 @Bean 注解的方法,返回一个对象(实际的 Bean)。这类 Bean 通常用于以某种方式配置 Spring(数据库、安全、视图解析等)。在这个菜谱中,我们将定义一个包含数据库连接详情的 Bean。

如何做…

在 Spring 配置类中添加一个带有 @Bean 注解的 dataSource() 方法,并返回一个 Datasource 对象。在这个方法中,创建一个初始化了数据库连接详情的 DriverManagerDataSource 对象:

@Bean
public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();

        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/db1");
        dataSource.setUsername("root");
        dataSource.setPassword("123");

        return dataSource;
}

它是如何工作的…

在启动时,由于 @BeandataSource() 方法会自动执行并返回一个 Datasource 对象,该对象被 Spring 存储在一个称为 ApplicationContext 的 Spring 对象中。Bean 名称是 dataSource,与它的方法名称相同。从这一点开始,任何对 dataSource() 的调用都将返回相同的缓存 DataSource 对象;dataSource() 不会再次实际执行。这是通过面向切面编程实现的;任何对 dataSource() 的调用都会被 Spring 拦截,直接返回对象而不是执行方法。

还有更多…

要自定义 Bean 名称,请使用名称参数:

@Bean(name="theSource")
public DataSource dataSource() {
...

要强制 dataSource() 在每次调用时执行(并返回不同的对象),请使用带有 prototype 范围的 @Scope 注解:

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public DataSource dataSource() {
...

使用我们自己的类定义 Bean 是可能的。例如,如果我们有一个 UserService 类,我们可以在 Spring 配置类中定义一个 UserService Bean:

@Bean
public UserService userService() {
        return new UserService();
}

然而,通常更简单的是让 Spring 通过在 UserService 类上使用 @Component 注解自动生成这类 Bean,正如在 使用 @Component 隐式定义一个 Bean 菜谱中解释的那样。

使用 @Component 隐式定义一个 Bean

Bean 不必定义在 Spring 配置类中。Spring 会自动从任何带有 @Component 注解的类生成 Bean。

准备工作

我们将在 第一章 中创建的基本 Web 应用程序 创建 Spring Web 应用程序 菜单中,使用 创建 Spring 应用程序

创建 com.springcookbook.service 包以及其中的以下服务类:

public class UserService {
  public int findNumberOfUsers() {
    return 10;
  }
}

如何做到这一点...

定义 Bean 的步骤如下,通过向现有类添加 @Component

  1. 在 Spring 配置文件中,在 @ComponentScan 类注解中添加 com.springcookbook.service 基础包:

    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackages = {"com.springcookbook.controller", "com.springcookbook.service"})
    public class AppConfig {  
    }
    
  2. UserService 类中添加 @Component

    @Component
    public class UserService {
      public int findNumberOfUsers() {
        return 10;
      }
    }
    

它是如何工作的...

在启动时,Spring 会扫描 com.springcookbook.service 包。UserService 类被注解为 @Component,因此会自动从它实例化一个 Bean。默认情况下,Bean 的名称将是 userService,基于类名。

要指定一个自定义名称,请使用以下代码:

@Component('anAmazingUserService')
public class UserService {

更多内容...

如果 UserService Bean 需要进行一些自定义初始化,例如基于当前环境,可以像在之前的配方中解释的那样显式定义和初始化 Bean,即 使用 @Bean 显式定义 Bean

@Controller@Service@Repository 也是一种组件注解;Spring 在启动时会自动从带有这些注解的类中实例化一个 Bean。使用这些组件注解并不是严格必要的,但它们可以使组件类的角色更加清晰;@Controller 用于控制器类,@Service 用于服务类(因此我们会为我们的 UserService 类使用它),而 @Repository 用于持久化类。它们还为组件类添加了一些额外的功能。请参阅 docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/beans.html#beans-stereotype-annotations

通过 @Autowired 进行依赖注入使用 Bean

Spring 配置 Bean,如 使用 @Bean 显式定义 Bean 配方中的 Bean,会被 Spring 自动发现和使用。要在你的类中使用一个 Bean(任何类型的 Bean),请将 Bean 添加为字段并注解它为 @Autowired。Spring 会自动初始化这个字段为 Bean。在这个配方中,我们将在控制器类中使用一个现有 Bean。

准备工作

我们将使用 使用 @Component 隐式定义 Bean 配方中的代码,其中我们定义了一个 UserService Bean。

如何做到这一点...

这里是使用现有 Bean 的步骤:

  1. 在控制器类中,添加一个被 @Autowired 注解的 UserService 字段:

    @Autowired
    UserService userService;
    
  2. 在控制器方法中,使用 UserService 字段:

    @RequestMapping("hi")
    @ResponseBody
    public String hi() {
      return "nb of users: " + userService.findNumberOfUsers();
    }
    
  3. 在浏览器中,访问 http://localhost:8080/hi 以检查它是否正常工作。

它是如何工作的...

当控制器类被实例化时,Spring 会自动将现有的 UserService Bean 初始化到 @Autowired 字段中。这被称为依赖注入;控制器类只需声明其依赖项,即一个 UserService 字段。是 Spring 通过向其中注入一个 UserService 对象来初始化这个字段的。

如果 Spring 无法找到该依赖项的现有 bean,则会抛出异常。

更多...

可以设置要使用的 bean 的名称:

@Autowired("myUserService")
UserService userService;

当使用接口时,依赖注入非常有用。例如,我们可以用UserService接口及其实现UserServiceImpl来替换我们的UserService类。一切都会按原样工作,除了现在可以简单地用另一个类(例如,为了单元测试目的)替换UserServiceImpl

直接使用 bean

可以通过将包含所有 bean 的 Spring 的ApplicationContext作为你的类的依赖项来直接从 Spring 获取 bean,而不是使用依赖注入。在这个配方中,我们将向控制器类注入一个现有的 bean。

准备工作

我们将使用使用@Component 注解隐式定义 bean配方中的代码,其中我们定义了一个UserServicebean。

如何操作...

这里是获取和使用一个豆类直接的方法:

  1. 在控制器类中,添加一个被@Autowired注解的ApplicationContext字段:

    @Autowired
    private ApplicationContext applicationContext;
    
  2. 在控制器方法中,使用ApplicationContext对象及其getBean()方法来检索UserServicebean:

    UserService userService = (UserService)applicationContext.getBean("userService");        
    

它是如何工作的...

当控制器类被实例化时,Spring 会自动用它的ApplicationContext对象初始化@Autowired字段。ApplicationContext对象引用了所有的 Spring beans,因此我们可以直接通过名称获取一个 bean。

更多...

可以通过类来获取一个 bean,而不需要知道它的名字。

applicationContext.getBean(UserService.class);  

列出所有 bean

这在调试目的上可能很有用。

准备工作

我们将使用使用@Component 注解隐式定义 bean配方中的代码,其中我们定义了一个UserServicebean。

如何操作...

这里是检索当前 Spring 的ApplicationContext对象中 bean 名称的步骤:

  1. 在你的类中,添加一个被@Autowired注解的ApplicationContext字段:

    @Autowired
    private ApplicationContext applicationContext;
    
  2. 在该类的某个方法中,使用ApplicationContext及其getBeanDefinitionNames()方法来获取 bean 名称列表:

    String[] beans = applicationContext.getBeanDefinitionNames();
    for (String bean : beans) {
      System.out.println(bean);
    }  
    

它是如何工作的...

当控制器类被实例化时,Spring 会自动用它的ApplicationContext对象初始化@Autowired字段。ApplicationContext对象引用了所有的 Spring beans,因此我们可以获取使用它的所有 bean 的列表。

更多...

要从其名称获取 bean 本身,请使用getBean()方法:

applicationContext.getBean("aBeanName");

使用多个配置类

当配置类中有许多 bean 定义时,Spring 配置类可能会变得相当长。在这种情况下,将其拆分为多个类可能很方便。

准备工作

我们将使用使用@Bean 注解显式定义 bean配方中的代码。

如何操作...

这是添加第二个配置类的方法:

  1. 创建一个新的配置类,例如,在com.springcookbook.config包中的DatabaseConfig

    @Configuration
    public class DatabaseConfig {
    …
    
  2. ServletInitializer类中,在getServletConfigClasses()方法中添加DatabaseConfig类:

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{AppConfig.class, DatabaseConfig.class};
    }
    
  3. Datasource实体从AppConfig类移动到DatabaseConfig类。

还有更多...

如果你正在使用没有ServletInitializer类的 Spring 应用程序,你可以从你的主要配置类中包含其他配置类:

@Configuration
@Import({ DatabaseConfig.class, SecurityConfig.class })
public class AppConfig {
…
}

第三章。使用控制器和视图

在本章中,我们将介绍以下食谱:

  • 将路由关联到控制器方法

  • 使用 JSP 视图

  • 从控制器传递属性到 JSP 视图

  • 在控制器方法中使用动态路由参数

  • 使用控制器路由的公共前缀

  • 使用 Tiles 页面模板

  • 使用拦截器在控制器前后执行一些代码

  • 构建多语言页面

简介

一个 Spring Web 应用程序使用MVC模型-视图-控制器)架构来处理 HTTP 请求,如下面的图像所示:

简介

一个 HTTP 请求,由一个路由(例如,/user/list)标识,执行一个控制器方法。之后渲染一个视图,通常是一个 JSP 文件,并将生成的 HTML 作为响应发送回去。

在本章中,我们将从创建控制器和视图开始。然后,你将学习如何从控制器方法中检索 URL 参数。我们将介绍两种使用页面模板和 URL 前缀来减少代码重复的标准方法。最后,我们将探讨与控制器和视图相关的高级主题:拦截器和国际化。

本章中的食谱将与第一章中“创建 Spring Web 应用程序”食谱类似的项目一起工作,第一章“创建 Spring 应用程序”,其中包含一个用@EnableWebMvc注解的 Spring 配置类,并扫描一个专门用于控制器类的 Java 包:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.springcookbook.controller"})
public class AppConfig {  
}

这是项目结构:

简介

将路由关联到控制器方法

在这个食谱中,你将学习如何定义一个控制器方法,用于执行给定路由。

如何做...

创建给定路由的控制器方法的步骤如下:

  1. 在你的控制器包(例如,com.springcookbook.controller)中创建一个控制器类。这是一个用@Controller注解的正常 Java 类:

    @Controller
    public class UserController {
    ...
    }
    
  2. 添加一个控制器方法。这是一个标准的 Java 方法,用@RequestMapping注解,它将路由作为参数:

    @RequestMapping("/user/list")
    public void userList() {
    ...
    }
    

工作原理…

一个具有/user/list路由的请求将执行userList()方法。

使用 JSP 视图

在这个食谱中,你将学习如何在控制器方法执行后渲染并返回一个 JSP 视图。

如何做...

创建 JSP 视图的步骤如下:

  1. pom.xml中添加 JSTL 的 Maven 依赖项:

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    
  2. 将 JSP 视图解析器添加到 Spring 配置类中:

    @Bean
    public ViewResolver jspViewResolver(){
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setViewClass(JstlView.class);
        resolver.setPrefix("/WEB-INF/jsp/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
    
  3. 创建一个控制器方法:

    @RequestMapping("/user/list")
    public void userList() {
      ...
    }
    
  4. 创建/WEB-INF/jsp/user/list.jsp JSP:

    <html>
    <body>
      There are many users.
    </body>
    </html>
    

工作原理…

控制器方法路径是/user/list。使用 JSP 视图解析器,Spring 将找到并渲染相应的/WEB-INF/jsp/user/list.jsp JSP。

如果路径是/user_list,相应的 JSP 将是/WEB-INF/jsp/user_list.jsp

这是当前的项目结构:

工作原理…

还有更多...

从控制器方法显式返回一个String对象是可能的,Spring 将使用它来查找 JSP。在这个例子中,将使用/WEB-INF/jsp/my_friends.jsp

@RequestMapping("/user/list")
public String userList() {
  return "my_friends";
}

有关在 JSP 文件中可以做什么的更多信息,请参阅www.tutorialspoint.com/jsp/jsp_standard_tag_library.htm

Thymeleaf、FreeMarker 和 Velocity 是流行的视图框架,它们提供了对 JSP 的替代方案。FreeMarker 和 Velocity 默认由 Spring 支持。Thymeleaf 提供自己的视图解析器。

从控制器传递属性到 JSP 视图

在这个菜谱中,你将学习如何在控制器方法中设置属性并在 JSP 视图中使用它们。

如何做到这一点...

从控制器传递数据到视图的步骤如下:

  1. Model参数添加到控制器方法中:

      @RequestMapping("/user/list")
      public void userList(Model model) {
      ...
    
  2. 在控制器方法中,向Model对象添加属性:

    model.addAttribute("nbUsers", 13);
    
  3. 在 JSP 文件中使用属性:

    <p>There are ${nbUsers} users</p>
    

它是如何工作的...

在控制器中,nbUsers变量被设置为13。在 JSP 文件中,${nbUsers} EL表达式语言)元素将被渲染为13,因此将返回以下 HTML:

<p>There are 13 users</p>

在控制器方法中使用动态路由参数

现在我们将定义路由的动态段并在相关的控制器方法中使用它们。例如,我们希望/user/5/name/user/6/email路由执行相同的控制器方法,但使用不同的参数:showUserField(5, "name")showUserField(6, "email")

如何做到这一点...

使用{}括起来包含动态路由段,并使用@PathVariable注解相应的控制器方法参数:

@RequestMapping("/user/{id}/{field}")
public void showUserField(@PathVariable("id") Long userId, @PathVariable("field") String field) {
...
}

它是如何工作的...

对于/user/5/email路由的请求将执行showUserField(5,"email")方法。@PathVariable("id") Long userId将路由参数id转换为userId方法参数。同样,field路由参数作为String传递给showUserField()

一个不正确的路由,如/user/test/email(这是不正确的,因为test子串不能转换为Long对象),将触发一个带有消息客户端发送的请求在语法上不正确的 400 服务器错误。

使用控制器路由的公共前缀

在这个菜谱中,我们将定义一个控制器所有路由共享的路由前缀。我们将以/user开始UserController控制器的路由。

如何做到这一点...

设置路由前缀的步骤如下:

  1. 将带有公共路由前缀的@RequestMapping添加到控制器类中:

    @Controller
    @RequestMapping("/user")
    public class UserController {
    ...
    }
    
  2. 将带有剩余路由的@RequestMapping添加到控制器方法中:

    @RequestMapping("/list")
    public void userList() {
      ...
    }
    
    @RequestMapping("/add")
    public void addUser() {
      ...
    }
    

它是如何工作的...

对于/user/add路由的请求将执行addUser()方法。对于/user/list路由的请求将执行userList()方法。

使用 Tiles 页面模板

使用页面模板,避免在每一个 JSP 中重复页面的公共元素(HTML 头部、页眉、页脚、导航等)。

如何做到这一点...

这里是使用 Tiles 的步骤:

  1. pom.xml中添加 Tiles Maven 依赖项:

    <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-servlet</artifactId>
      <version>3.0.5</version>
    </dependency>
    
    <dependency>
      <groupId>org.apache.tiles</groupId>
      <artifactId>tiles-jsp</artifactId>
      <version>3.0.5</version>
    </dependency> 
    
  2. 在 Spring 配置类中,删除 JSP 视图解析器(如果存在)。

  3. 在 Spring 配置类中声明 Tiles:

    // declare Tiles configuration file
    @Bean
    public TilesConfigurer tilesConfigurer() {
      TilesConfigurer tilesConfigurer = new TilesConfigurer();
      final String[] definitions = { "/WEB-INF/tiles.xml" };
        tilesConfigurer.setDefinitions(definitions);
      return tilesConfigurer;
    }
    
    // declare Tiles as a view resolver
    @Bean
    public ViewResolver tilesViewResolver() {
      TilesViewResolver resolver = new TilesViewResolver();
      return resolver;
    }
    
  4. 创建/WEB-INF/tiles.xml Tiles 配置文件:

    <tiles-definitions>  
    
        <definition name="template" template="/WEB-INF/jsp/templates/template.jsp" />
    
        <definition name="*" extends="template">
            <put-attribute name="body" value="/WEB-INF/jsp/{1}.jsp" />
        </definition>
    
    </tiles-definitions>
    
  5. 创建/WEB-INF/jsp/templates/template.jsp页面模板:

    <!DOCTYPE HTML>
    <%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
    
    <html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <h1>Spring Cookbook</h1>
    
        <tiles:insertAttribute name="body" />    
    </body>
    </html>
    
  6. 在控制器方法中,返回标准 JSP 文件的基本名称。例如,对于/jsp/home.jsp

    ...
    return "home";
    

如何工作…

当 Spring 配置被加载时,使用声明的tiles.xml配置文件初始化 Tiles。

如何工作…

当请求到达时,控制器方法被执行并返回"home"字符串,该字符串与tiles.xml中定义的名为"*"相匹配。此定义将使用template定义,并将body变量传递给它,值为/WEB-INF/jsp/home.jsp。在template.jsp中,tiles:insertAttribute标签将被home.jsp的内容替换。

总结来说,home.jsp文件与template.jsp集成,生成的 HTML 作为响应发送。

template.php中,确保包含以下内容以便能够使用 Tiles 标签,如<tiles:insertAttribute>

<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>

还有更多…

Tiles 可以在 JSP 文件位于子文件夹并支持多个页面模板时使用。还可以在一个地方定义重复的文本。

使用子文件夹组织 JSP

随着 JSP 文件数量的增加,您可以通过根据章节分组并使用子文件夹来维护它们:

/jsp
 |- /user
 |   |- list.jsp
 |   |- add.jsp
 |- /article
 |   |- list.jsp
 |   |- add.jsp
 |- home.jsp

在控制器方法中,返回带有jsp基本名称的文件夹,例如,user/list

tiles.xml文件中添加以下定义:

<definition name="*/*" extends="template">
    <put-attribute name="body" value="/WEB-INF/jsp/{1}/{2}.jsp" />
</definition>    

使用多个页面模板

要处理多个模板,在tiles.xml中为每个模板定义一个前缀。例如,我们以下定义了一个带有main_前缀的主模板,使用template1.jsp JSP,以及一个带有secondary_前缀的辅助模板,使用template2.jsp JSP:

    <definition name="template1" template="/WEB-INF/templates/template1.jsp" />
    <definition name="template2" template="/WEB-INF/templates/template2.jsp" />

    <definition name="main_*" extends="template1">
        <put-attribute name="body" value="/WEB-INF/jsp/{1}.jsp" />
    </definition>

    <definition name="secondary_*" extends="template2">
        <put-attribute name="body" value="/WEB-INF/jsp/{1}.jsp" />
    </definition>

在控制器方法中,对于home.jsp,返回"main_home"以使用template1或返回"secondary_home"以使用template2

使用文本属性仅定义一次页面标题

标题通常需要在 HTML 页面中显示两次:一次在页面<head>部分的<title>标签中,一次在页面<body>部分的<h1>标签中。使用 Tiles,您可以在外部.properties文件中只定义一次:

  1. tiles.xml中,为模板定义添加一个 title 属性:

    <definition name="*" extends="template">
      <put-attribute name="title" value="{1}.title" />
    ...
    
  2. template.jsp中,获取标题并使用它:

    <!DOCTYPE HTML>
    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
    <%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
    
    <c:set var="titleKey">        
      <tiles:getAsString name="title" />
    </c:set>
    
    <html>
    <head>
       <title><spring:message code="${titleKey}" />/title>
    </head>
    <body>
      <h1><spring:message code="${titleKey}" /></h1>
      ... 
    
  3. 创建src/main/resources/messages.properties文件:

    home.title=Home
    

要了解更多关于 Tiles 的信息,请访问tiles.apache.org/framework/tutorial/

使用拦截器在控制器前后执行一些代码

在本菜谱中,您将学习如何使用拦截器,在请求工作流程的不同时刻通过preHandle()postHandle()afterCompletion()钩子执行一些代码:

使用拦截器在控制器前后执行一些代码

拦截器用于身份验证、日志记录和性能分析(等等)。

如何做到这一点…

这里是创建和注册拦截器的步骤:

  1. 创建一个扩展HandlerInterceptorAdapter的类:

    public class PerformanceInterceptor extends HandlerInterceptorAdapter {
    
  2. 覆盖你想要使用的方法:

    @Override
    public boolean preHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler) throws Exception {
        …
      return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler,
        ModelAndView modelAndView) throws Exception {
      ...
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request,
        HttpServletResponse response, Object handler, Exception ex)
        throws Exception {
      ...
    }
    

    注意

    注意,如果preHandle()返回false,则请求工作流程将停止,并且不会调用控制器方法。

  3. 使 Spring 配置类扩展WebMvcConfigurerAdapter并使用@EnableWebMvc注解:

    @Configuration
    @EnableWebMvc
    public class AppConfig extends WebMvcConfigurerAdapter{
    ...
    
  4. 在 Spring 配置类中,将拦截器声明为一个 bean,并使用addInterceptors()方法注册它:

    @Bean
    public HandlerInterceptor performanceInterceptor() {
      PerformanceInterceptor interceptor;
      interceptor = new PerformanceInterceptor();
      return interceptor;
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
      registry.addInterceptor(performanceInterceptor());
    }
    

它是如何工作的…

拦截器方法在请求工作流程的相应时刻执行。

更多…

要将拦截器限制为特定的 URL,请向拦截器注册中添加路径模式:

@Override
public void addInterceptors(InterceptorRegistry registry) {
  registry.addInterceptor(performanceInterceptor())
.addPathPatterns("/home", "/user/*");
}

在此示例中,拦截器方法将为/home/user/list/user/add执行,但不会为/contact执行。

构建多语言页面

接下来,我们将学习如何使用单个 JSP 创建多语言页面(英语和法语),默认显示英语,并提供一个链接切换到法语。然后,我们将文本存储在 JSP 之外,在两种语言中,分别存储在单独的.properties文件中。

如何做到这一点…

这里是构建双语文本 JSP 视图的步骤:

  1. 创建 JSP:

    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
    
    <html>
    <body>
      <h1><spring:message code="home.title" /></h1>
      <p><spring:message code="home.intro" /></p>
    
      <p>
        <a href="?lang=en">English</a> |
        <a href="?lang=fr">French</a>
      </p>
    </body>
    </html>
    
  2. 创建英文.properties文件src/main/resources/messages.properties

    home.title=Home
    home.intro=This is a magnificent home page, isn't it?
    
  3. 创建法语.properties文件src/main/resources/messages_fr.properties

    home.title=Accueil
    home.intro=Splendide page d'accueil, non ?
    
  4. 在 Spring 配置中,声明.properties文件:

    @Bean
    public MessageSource messageSource() {
      ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
      messageSource.setBasename("classpath:/messages");
      messageSource.setUseCodeAsDefaultMessage(true);
      return messageSource;
    }
    
  5. 确保 Spring 配置类扩展WebMvcConfigurerAdapter并使用@EnableWebMvc注解:

    @Configuration
    @EnableWebMvc
    public class AppConfig extends WebMvcConfigurerAdapter{
    ... 
    
  6. 定义一个允许使用lang URL 参数更改当前语言的LocaleChangeInterceptor拦截器。注册拦截器:

    @Bean
    public HandlerInterceptor localeChangeInterceptor() {
      LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
      interceptor.setParamName("lang");
      return interceptor;
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
      registry.addInterceptor(localeChangeInterceptor());
}
    
  7. 在 cookie 中存储用户语言选择并声明默认语言:

    @Bean
    public LocaleResolver localeResolver() {
      CookieLocaleResolver localeResolver = new CookieLocaleResolver();
      localeResolver.setDefaultLocale(new Locale("en"));
      return localeResolver;
    }
    

它是如何工作的…

以下步骤描述了前面代码的工作原理:

  1. 当请求到来时,Spring 首先检查它是否有一个包含语言值的 cookie。如果答案是肯定的,它就使用它作为当前语言;否则,它使用默认语言。这种行为来自将CookieLocaleResolver声明为区域解析器的声明。

  2. 然后LocaleChangeInterceptor检查 URL 中是否存在lang参数。如果答案是肯定的,它就使用它作为当前语言(而不是默认或 cookie 语言)。

  3. home.jsp被渲染时,其文本是从对应当前语言的.properties文件中获取的。如果找不到给定消息键的文本,则显示键本身。这种行为来自messageSource.setUseCodeAsDefaultMessage(true)

更多…

你可能需要从控制器方法中检索当前语言名称。你也可能需要在 URL 中而不是在 cookie 中设置页面语言。

获取当前语言

要从控制器或拦截器中检索当前语言,请使用以下代码:

Locale locale = LocaleContextHolder.getLocale();
String lang = locale.getLanguage(); // fr
String language = locale.getDisplayLanguage(); // French
String language2 = locale.getDisplayLanguage(locale); // français

在 URL 中使用语言

Spring 没有提供方便的方式来处理 URL 中的语言(例如,/en/user/list)。相反,必须手动完成:

  1. 使用拦截器从 URL 中检索语言并覆盖当前语言。

  2. 将控制器方法映射前缀为支持的语言(以便 Spring 可以从带有语言的路由中检索它):

    @Controller
    @RequestMapping("{en|fr}/user/*")
    public class UserController {
      @RequestMapping("list")
      public String userList(Model model) {
        ...
      }
    }.
    
  3. 当生成内部链接时,假设$lang包含当前语言,请在其前加上当前语言:

    <spring:url value="/${lang}/home" var="home" />
    <a href="${home}">Home</a>
    

第四章:查询数据库

在本章中,我们将介绍以下食谱:

  • 连接到数据库

  • 创建 DAO 类

  • 从控制器类中调用 DAO 方法

  • 保存一个对象

  • 检索一个对象

  • 检索对象列表

  • 检索具有依赖关系的对象列表

  • 更新一个对象

  • 删除一个对象

  • 查找 SQL 查询的结果数量

  • 一次性保存对象列表

  • 使用事务回滚不完整的数据库修改

  • 使用 Hibernate 进行强大的对象持久化和查询

简介

JDBCJava 数据库连接)和 Hibernate 是从 Spring 应用程序查询数据库最常用的两种技术。

对于小型项目和简单的数据模型,JDBC 很简单;你自己编写 SQL 查询,Spring 提供了将查询结果转换为对象的辅助工具。

对于复杂的数据模型,类之间有多个关系时,Hibernate 更容易使用;你处理的是一个标准的 Java 框架(仍然在幕后使用 JDBC),它会为你生成 SQL 查询。

本章重点介绍 JDBC,因为 Spring 并没有改变使用 Hibernate 的常规方式。然而,Hibernate 与 Spring 的集成在 使用 Hibernate 进行强大的对象持久化和查询 食谱中有所涉及。

连接到数据库

在这个食谱中,我们将从 Spring 应用程序连接到 MySQL 或 PostgreSQL 数据库。要连接到其他数据库系统,请访问 www.oxygenxml.com/database_drivers.html 以找到相关的依赖项、驱动类和 URL 类型。

准备工作

你需要一个正在运行的 MySQL 或 PostgreSQL 数据库。

如何做到这一点…

这里是从 Spring 应用程序连接到现有数据库的步骤:

  1. pom.xml 中添加 Spring JDBC 的 Maven 依赖项:

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>4.1.6.RELEASE</version>
    </dependency>
    
  2. 如果你使用 MySQL,请在 pom.xml 中添加其 Maven 依赖项:

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.35</version>
    </dependency>
    
  3. 如果你使用 PostgreSQL,请在 pom.xml 中添加其 Maven 依赖项:

    <dependency>
      <groupId>postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>9.1-901-1.jdbc4</version>
    </dependency>
    

在 Spring 配置中,添加一个包含数据库连接详情的 DataSource bean。

  1. 如果你使用 MySQL:

    @Bean
    public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
    
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/db1");
            dataSource.setUsername("user1");
            dataSource.setPassword("pass1");
    
            return dataSource;
    }
    
  2. 如果你使用 PostgreSQL:

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
    
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl("jdbc:postgresql://localhost:5432/db1");
        dataSource.setUsername("user1");
        dataSource.setPassword("pass1");
    
        return dataSource;
    }
    
  3. 在 Spring 配置中,添加一个 JdbcTemplate bean,将 DataSource 作为参数:

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
      return new JdbcTemplate(dataSource);
    }
    

它是如何工作的…

创建了一个连接(Datasource 对象),连接到名为 db1 的数据库,端口为 3306(MySQL)或 5432(PostgreSQL),使用用户 user1

JdbcTemplate bean 是一个 Spring 对象,它提供了使用 JDBC 查询数据库的便捷方法。它使用之前定义的 DataSource bean。我们将从我们的 DAO 类中使用 JdbcTemplate bean。

创建 DAO 类

在这个食谱中,我们将创建一个 DAO数据访问对象)类。DAO 类提供了保存和从数据库检索对象的方法。它可以从控制器中使用,例如:

创建 DAO 类

控制器从UserDAO调用findUsers()方法,它负责从数据库获取结果(使用在先前的配方中定义的JdbcTemplatebean)。

如何操作…

这里是创建 DAO 类的步骤:

  1. 创建一个带有@Repository注解的类:

    @Repository
    public class UserDAO {
    
  2. 向其中添加一个自动注入的JdbcTemplate字段:

    @Autowired
    private JdbcTemplate jdbcTemplate;
    

它是如何工作的…

@Repository允许UserDAO类自动被发现和实例化为一个 bean。

JdbcTemplate字段将通过 Spring 通过依赖注入自动初始化,使用先前的配方中定义的JdbcTemplatebean。

从控制器类中调用 DAO 方法

在这个配方中,我们将看到如何从控制器类中调用 DAO 方法。

准备工作

我们将使用先前的配方中定义的 DAO 类,并假设它有一个add(User)方法。在接下来的配方中,我们将编写实际的 DAO 方法。

如何操作…

这里是使用控制器类中的 DAO 方法步骤:

  1. 在您的控制器类中,添加 DAO 作为@Autowired字段:

    @Controller
    public class UserController {
      @Autowired
     private UserDAO userDAO;
    
    
  2. 在任何控制器方法中使用 DAO:

    userDAO.add(user);
    

它是如何工作的…

由于@AutowireduserDAO字段将通过 Spring 使用依赖注入自动初始化。

保存对象

在这个配方中,我们将创建一个 DAO 方法来将对象保存到数据库中;将在相应的数据库表中添加一行,例如:

保存对象

准备工作

您需要一个模型类,例如:

public class User {
  private Long id;
  private String firstName;
  private Integer age;

您需要有一个匹配的数据库表,例如:

CREATE TABLE `user` (
  `id` int(11) AUTO_INCREMENT,
  `first_name` text,
  `age` int(11),
  PRIMARY KEY (`id`)
)

您需要一个具有JdbcTemplate属性的 DAO 类(参考创建 DAO 类配方)

如何操作…

定义一个带有问号作为实际行值占位符的 SQL 插入查询。使用update()方法执行查询,使用对象中的实际值:

public void add(User user) {
  String sql = "insert into user (first_name, age) values (?, ?)";
  jdbcTemplate.update(sql, user.getFirstName(), user.getAge());
}

它是如何工作的…

jdbcTemplate对象负责处理 JDBC 样板代码;打开和关闭数据库连接以及处理异常。update()方法接受 SQL 查询和将替换 SQL 查询中问号的实际值。

获取对象

在这个配方中,我们创建一个 DAO 方法来检索数据库行,我们将使用它来创建一个对象。

如何操作…

使用 SQL 选择查询并使用RowMapper从结果创建一个对象:

  1. 在 DAO 类中,添加一个实现RowMapper的内联类。这个类定义了如何从数据库行生成一个User对象:

    private class UserMapper implements RowMapper<User> {
      public User mapRow(ResultSet row, int rowNum) throws SQLException {
        User user = new User();
    
        user.setId(row.getLong("id"));
        user.setFirstName(row.getString("first_name"));
        user.setAge(row.getInt("age"));
    
        return user;
      }
    }
    
  2. 添加一个 DAO 方法,它将执行一个 SQL select查询并使用一个UserMapper对象来生成一个User对象:

    public User findById(Long id) {
      String sql = "select * from user where id=?";
      User user = jdbcTemplate.queryForObject(sql, new Object[]{id}, new UserMapper());
      return user;
    }
    

它是如何工作的…

queryForObject()方法使用the UserMapper对象从结果数据库行生成一个User对象。

在这个例子中,我们通过其 ID 检索用户,这是queryForObject()的第二个参数,作为一个数组元素。

更多内容…

如果数据库列名与对象属性名匹配,则无需定义自定义的 RowMapper 接口,只需使用 ParameterizedBeanPropertyRowMapper 类:

public User findById(Long id) {
  String sql = "select * from user where id=?";
  User user = jdbcTemplate.queryForObject(sql, new Object[]{id}, ParameterizedBeanPropertyRowMapper.newInstance(User.class));
  return user;
}

获取对象列表

在这个菜谱中,我们将添加一个 DAO 方法来从数据库行中检索并创建一个对象列表。

如何做到这一点...

执行一个 SQL select 查询并使用 RowMapper 从结果中生成一个对象列表:

public List<User> findAll() {
  String sql = "select * from user";
  List<User> userList = jdbcTemplate.query(sql, ParameterizedBeanPropertyRowMapper.newInstance(User.class));
  return userList;
}

它是如何工作的...

query() 方法使用 RowMapper 从返回的数据库行生成对象。

我们假设数据库表列与对象属性匹配,使用了 ParameterizedBeanPropertyRowMapper 类;然而,正如前一个菜谱中所述,可以使用自定义的 RowMapper 接口。

获取带有其依赖关系的对象列表

在这个菜谱中,我们将添加一个 DAO 方法,从一个连接多个表的 SQL 查询中生成一个带有其依赖关系的对象列表。我们将检索一个包含 User 对象及其 Post 对象(这些用户撰写的博客文章)的对象列表。

准备工作

您需要具有相互关联的模型类。在这个例子中,一个用户有许多帖子:

public class User {
  private Long id;
  private String firstName;
  private Integer age;
  private LinkedList<Post> posts = new LinkedList<Post>();

public class Post {
  private long id;
  private String title;
  private Date date;
  private User user;

您需要具有相应的数据库表,例如:

CREATE TABLE `user` (
  `id` int(11) AUTO_INCREMENT,
  `first_name` text,
  `age` int(11),
  PRIMARY KEY (`id`)
)

CREATE TABLE `post` (
  `id` int(11) AUTO_INCREMENT,
  `title` text,
  `date` datetime,
  `user_id` int(11),
  PRIMARY KEY (`id`),
  CONSTRAINT `user_id` FOREIGN KEY (`id`) REFERENCES `user` (`id`)
)

如何做到这一点...

使用 SQL select 查询并使用实现 ResultSetExtractor 的类从结果中生成一个对象列表,该类在返回对象列表之前遍历整个行列表:

  1. 添加一个 DAO 方法,执行一个带有 left join 的 SQL select 语句并使用 ResultSetExtractor 生成一个对象列表:

    public List<User> findAll() {
      String sql = "select u.id, u.first_name, u.age, p.id as p_id, p.title as p_title, p.date as p_date from user u left join post p on p.user_id = u.id order by u.id asc, p.date desc";
      return jdbcTemplate.query(sql, new UserWithPosts());
    }
    
  2. 添加一个实现 ResultSetExtractor 的内联类:

    private class UserWithPosts implements ResultSetExtractor<List<User>> {
    
      public List<User> extractData(ResultSet rs) throws SQLException,
          DataAccessException {
    
        Map<Long, User> userMap = new ConcurrentHashMap<Long, User>();
        User u = null;
        while (rs.next()) {
          // user already in map?
          Long id = rs.getLong("id");
          u = userMap.get(id);
    
          // if not, add it
          if(u == null) {
            u = new User();
            u.setId(id);
            u.setFirstName(rs.getString("first_name"));
            u.setAge(rs.getInt("age"));
            userMap.put(id, u);
          }
    
          // create post if there's one
          Long postId = rs.getLong("p_id");
          if (postId > 0) {
            System.out.println("add post id=" + postId);
            Post p = new Post();
            p.setId(postId);
            p.setTitle(rs.getString("p_title"));
            p.setDate(rs.getDate("p_date"));
            p.setUser(u);
            u.getPosts().add(p);
          }
        }
    
        return new LinkedList<User>(userMap.values());
      }
    }
    

它是如何工作的...

由于 left join,我们有时从数据库中获得具有相同用户的行列表,但代表不同的帖子。每一行不能独立处理,否则我们最终会创建多个相同的用户。因此,我们使用 ResultSetExtractor,它允许我们遍历行列表。

我们使用一个 User 对象的映射来跟踪当前行的 User 是否已经被创建。

在 SQL 查询中,我们明确列出了列名,以确保它们在结果行中将具有不同的名称。否则,例如,“帖子 id”可能会与“用户 id”混淆。

更新一个对象

在这个菜谱中,我们将添加一个 DAO 方法来使用对象的字段更新数据库中的一个现有行。

如何做到这一点...

使用 SQL update 查询并使用 update() 方法执行它:

public void update(User user) {
  String sql = "update user set first_name=?, age=? where id=?";
  jdbcTemplate.update(sql, user.getFirstName(), user.getAge(), user.getId());
}

更多...

同时拥有一个 save() 方法也很方便,如果数据库行不存在,它将创建该行:

public void save(User user) {
  if (user.getId() == null) {
    add(user);
  }
  else {
    update(user);
  }
}

删除一个对象

在这个菜谱中,我们将添加一个 DAO 方法来从数据库中删除一个现有的行。

如何做到这一点...

使用 SQL delete 查询并使用 update() 方法执行它:

public void delete(User user) {
  String sql = "delete from user where id=?";
  getJdbcTemplate().update(sql, user.getId());
}

查找 SQL 查询的结果数量

在这个菜谱中,我们将添加一个 DAO 方法,快速获取 SQL 查询的结果数量,而不实际将行加载到内存中。

如何做到这一点...

使用 SQL count(*)函数,并通过queryForObject()方法获取值,第二个参数指定返回类型为Long

public long countMinorUsers() {
  String sql = "select count(*) from age < 18";
  return jdbcTemplate.queryForObject(sql, Long.class);
}

一次性保存对象列表

在这个菜谱中,我们将添加一个 DAO 方法,以高效地将对象列表保存到数据库中。

如何操作...

使用接受 SQL insert查询和值列表作为参数的batchUpdate()方法:

public void add(List<User> userList) {
  String sql = "insert into user (first_name, age) values (?, ?)";

  List<Object[]> userRows = new ArrayList<Object[]>();
  for (User user : userList) {
        userRows.add(new Object[] {user.getFirstName(), user.getAge()});
  }

   jdbcTemplate.batchUpdate(sql, userRows);
}

工作原理...

将从 SQL insert查询字符串和值列表生成一系列 SQL insert查询。它们将被发送到数据库并一次性提交。

使用事务回滚不完整的数据库修改

一些数据库修改涉及多个 SQL 查询,例如,插入具有跨多个表属性的对象。如果其中一个查询失败,我们希望撤销之前成功的任何修改。

如何操作...

这里是使 DAO 方法事务化的步骤:

  1. 在 Spring 配置类中添加@EnableTransactionManagement

    @Configuration
    @EnableWebMvc
    @EnableTransactionManagement
    @ComponentScan(basePackages = {"com.spring_cookbook.controllers", "com.spring_cookbook.dao"})
    public class AppConfig {
    …
    
  2. 在 Spring 配置中添加一个DataSourceTransactionManager豆:

    @Bean
    public DataSourceTransactionManager transactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
      transactionManager.setDataSource(dataSource());
      return transactionManager;
    }
    
  3. 使用@Transactional注解 DAO 类:

    @Repository
    @Transactional
    public class UserDAO {
    …
    

工作原理...

@Transactional将每个 DAO 方法包围在一个BEGIN…COMMIT SQL 块中。所以如果有错误(运行时异常),DAO 方法对数据库所做的任何修改都将回滚。

使用 Hibernate 进行强大的对象持久化和查询

在这个菜谱中,你将学习如何使用 Hibernate 与 Spring 结合。我们将使用 MySQL 数据库。

准备工作

在这个菜谱中,我们将使用带有user表的 MySQL 数据库:

CREATE TABLE user (
  id int NOT NULL AUTO_INCREMENT,
  first_name text,
  age int DEFAULT NULL,
  PRIMARY KEY (id)
);

我们将使用这个相应的 JPA 注解的领域类:

@Entity
@Table(name = "user")
public class User {

  @Id
  @GeneratedValue
  private Long id;

  @Column(name = "first_name")
  private String firstName;

  private Integer age;

  // getters and setters.. 

更多关于Java 持久化 APIJPA)的信息,请访问:docs.oracle.com/javaee/6/tutorial/doc/bnbpz.html

如何操作...

这里是集成 Hibernate 与 Spring 的步骤:

  1. pom.xml中添加 Spring ORM、Hibernate 和 MySQL JDBC 驱动的 Maven 依赖项:

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>4.1.6.RELEASE</version>
    </dependency>
    
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.35</version>
    </dependency>  
    
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.3.8.Final</version>
    </dependency>
    
  2. 在 Spring 配置类中添加@EnableTransactionManagement

    @Configuration
    @EnableWebMvc
    @EnableTransactionManagement
    @ComponentScan(basePackages = {"com.spring_cookbook.controllers", "com.spring_cookbook.dao"})
    public class AppConfig {
    …
    
  3. 在 Spring 配置中,添加一个包含数据库连接详情的dataSource豆:

    @Bean
    public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
    
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/db1");
            dataSource.setUsername("user1");
            dataSource.setPassword("pass1");
    
            return dataSource;
    }
    
  4. 在 Spring 配置类中,添加一个接受Datasource对象作为参数的sessionFactory豆方法。在这个豆方法中,我们告诉 Hibernate 生成针对 MySQL 的特定 SQL 代码,并声明我们的User类:

    @Bean
    public SessionFactory sessionFactory(DataSource dataSource) {
      LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);
      Properties props = new Properties();
      props.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
      props.put("hibernate.show_sql", "true");
      sessionBuilder.addProperties(props);
    
      sessionBuilder.addAnnotatedClass(User.class);
    
      return sessionBuilder.buildSessionFactory();
    }
    
  5. 在 Spring 配置类中,添加一个HibernateTransactionManager豆:

    @Bean
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
      return new HibernateTransactionManager(sessionFactory);
    }
    
  6. 使用依赖注入将SessionFactory豆添加到你的 DAO 类中:

    @Autowired
    SessionFactory sessionFactory;
    
  7. 使用这个SessionFactory豆来像往常一样控制 Hibernate,例如,这是一个将User对象添加到数据库的 DAO 方法:

    @Transactional
    public void add(User user) {
      sessionFactory.getCurrentSession().saveOrUpdate(user);
    }
    

第五章。使用表单

在本章中,我们将介绍以下菜谱:

  • 显示和处理表单

  • 使用控制器方法参数获取提交的表单值

  • 使用模型对象设置表单的默认值

  • 自动将表单数据保存到对象中

  • 使用文本、文本区域、密码和隐藏字段

  • 使用选择字段

  • 使用复选框

  • 使用复选框列表

  • 使用单选按钮列表

  • 使用注解验证表单

  • 上传文件

简介

显示和处理表单是繁琐的。Spring 通过初始化表单、生成表单小部件(文本字段、复选框等)以及在表单提交时检索数据来帮助处理。通过模型类中的注解,表单验证变得简单。

显示和处理表单

要显示表单并检索用户提交的数据,请使用第一个控制器方法显示表单。使用第二个控制器方法处理表单提交时的表单数据。

如何做...

显示和处理表单的步骤如下:

  1. 创建一个控制器方法来显示表单:

    @RequestMapping("/addUser")
    public String addUser() {
      return "addUser";
    }
    
  2. 创建一个包含 HTML 表单的 JSP:

    <form method="POST">
      <input type="text" name="firstName" />
      <input type="submit" />
    </form>
    
  3. 创建另一个控制器方法来处理表单提交:

    @RequestMapping(value="/addUser", method=RequestMethod.POST)
    public String addUserSubmit(HttpServletRequest request) {
      String firstName = request.getParameter("firstName");
      ...
      return "redirect:/home";
    }
    

它是如何工作的...

第一个控制器方法显示包含 HTML 表单的 JSP。有关更多详细信息,请参阅第三章 使用 JSP 视图 的菜谱,使用控制器和视图

HTML 表单包含一个文本字段。它通过 POST 提交。表单的 action 属性不存在,因此表单将提交到当前页面 URL (/addUser)。

当表单提交时,调用第二个控制器方法。使用 HttpServletRequest 获取 firstName 表单字段的值。最后,我们将重定向到 /home

两种控制器方法映射到相同的 /addUser URL。第一种方法用于 HTTP GET 请求。第二种方法用于 HTTP POST 请求(因为 method=RequestMethod.POST)。

还有更多...

两种控制器方法可能有不同的 URL。例如,/addUser/addUserSubmit。在这种情况下,在 JSP 中,我们会使用 action 属性:

<form method="POST" action="/addUserSubmit">
  …
</form>

参见

在第二个控制器方法中,为了避免为每个表单字段使用繁琐的 request.getParameter() 方法,请参阅 使用控制器方法参数获取提交的表单值将表单值保存到对象中自动 的菜谱。

使用控制器方法参数获取提交的表单值

在这个菜谱中,你将学习如何使用控制器方法参数获取提交的表单数据。这对于与领域对象无关的简单表单来说很方便。

如何做...

向控制器方法添加一个带有 @RequestParam 注解的参数:

@RequestMapping("processForm")
public void processForm(@RequestParam("name") String userName) {
...

它是如何工作的...

userName 参数由 Spring 初始化为表单字段 name 的提交值。

@RequestParam也可以检索 URL 参数,例如,http://localhost:8080/springwebapp/processForm?name=Merlin

更多内容…

还可以将标准的HttpServletRequest对象作为控制器方法的参数,并直接从其中获取name的提交值:

@RequestMapping("processForm")
public void processForm(HttpServletRequest request) {
  String name = request.getParameter("name");

参见

参考有关将表单值自动保存到对象的食谱以获取更多详细信息。

使用模型对象设置表单的默认值

在本食谱中,你将学习如何显示一个用户可以更改的初始值的表单。

如何操作…

在控制器中创建一个包含默认值的对象。在视图中,使用 Spring 表单标签使用该对象生成表单:

  1. 在控制器中,添加一个带有@ModelAttribute注解的方法,该方法返回一个具有默认值的对象:

    @ModelAttribute("defaultUser")
    public User defaultUser() {
      User user = new User();
      user.setFirstName("Joe");
      user.setAge(18);
      return user;
    }
    
  2. 在控制器中,添加一个显示表单的方法:

    @RequestMapping("addUser")
    public String addUser() {
      return "addUser";
    }
    
  3. 在 JSP 中,使用 Spring 表单标签生成表单:

    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    
    <form:form method="POST" modelAttribute="defaultUser">
      <form:input path="firstName" />
      <form:input path="age" />
      <input type="submit" value="Submit" />
    </form:form>
    

它是如何工作的…

在控制器中,由于@ModelAttributedefaultUser()方法会自动为每个控制器请求调用。返回的User对象存储在内存中作为defaultUser。在 JSP 中,使用defaultUser初始化表单:

  • 它被设置为form:form元素的modelAttribute

    <form:form method="POST" modelAttribute="defaultUser">
    
  • 表单字段从defaultUser的相应属性中获取其值。例如,firstName字段将使用defaultUser.getFirstName()返回的值进行初始化。

自动保存表单数据到对象中

对于直接与模型对象相关的表单,例如,用于添加User的表单,提交的表单数据可以自动保存到该对象的实例中。

如何操作…

在处理表单提交的控制器方法中,将对象作为参数添加,并确保 JSP 中的字段名称与它的属性匹配:

  1. 将带有@ModelAttribute注解的User参数添加到处理表单提交的控制器方法中:

    @RequestMapping(value="addUser", method=RequestMethod.POST)
    public void addUser(@ModelAttribute User user) {
    ...
    
  2. 在 JSP 中,确保表单字段与对象的现有属性相对应:

    <form:input path="firstName" />
    <form:input path="age" />
    

它是如何工作的…

当表单提交时,幕后发生的情况如下:

  • 创建一个新的User对象

  • 通过将表单字段名称与对象属性名称匹配,将表单值注入到对象中,例如:

    user.setFirstName(request.getParameter("firstName"));
    
  • 结果对象通过其@ModelAttribute参数传递给控制器方法

更多内容…

而不是让 Spring 创建一个新的对象,你可以提供一个默认对象的名称:

public void addUser(@ModelAttribute("defaultUser") User user) {
...

在这种情况下,Spring 将使用控制器类中相应的@ModelAttribute方法返回的对象来存储提交的表单数据:

@ModelAttribute("defaultUser")
public User user() {
  User user = new User();
  user.setFirstName("Joe");
  user.setAge(18);
  return user;
}

使用文本、textarea、密码和隐藏字段

在本食谱中,你将学习如何使用 Spring 表单标签显示文本字段、textarea字段、password字段和hidden字段。当表单提交时,我们将在控制器方法中检索字段值。

如何操作…

这里是显示和处理文本字段的步骤:

  1. 如果需要默认值,请使用默认对象的 String 属性(参考 使用模型对象设置表单的默认值 菜谱):

    user.setFirstName("Joe");
    
  2. 在 JSP 中,使用这些 Spring 表单标签之一:

    <form:input path="firstName" />
    <form:textarea path="firstName" />
    <form:password path="firstName" />
    <form:hidden path="firstName" />
    
  3. 在处理表单提交的控制器方法中,确保 @ModelAttribute 对象具有相应的 String 属性:

    public class User {
      private String firstName;
    ...
    

它是如何工作的…

Spring 表单标签生成 HTML 表单字段,并用默认对象中定义的默认值填充它。path 属性对应于默认对象的属性。当表单提交时,表单字段值将保存在 @ModelAttribute 对象的相应属性中。

作为参考,这是生成的 HTML 代码:

<input id="firstName" name="firstName" type="text" value="Joe"/>
<textarea id="firstName" name="firstName">Joe</textarea>
<input id="firstName" name="firstName" type="password" value=""/>
<input id="firstName" name="firstName" type="hidden" value="Joe"/>

注意

注意,默认值实际上并不用于 password 字段。

使用选择字段

在本菜谱中,您将学习如何显示 select 字段。当表单提交时,在控制器方法中检索选中的值。

如何操作…

  1. 在控制器中,添加一个返回包含 select 字段选项的 Map 对象的 @ModelAttribute 方法:

    @ModelAttribute("countries")
    public Map<String, String>countries() {
      Map<String, String> m = new HashMap<String, String>();
      m.put("us", "United States");
      m.put("ca", "Canada");
      m.put("fr", "France");
      m.put("de", "Germany");
      return m;
    }
    
  2. 如果需要默认值,请使用默认对象的 String 属性(参考 使用模型对象设置表单的默认值 菜谱)并使用 Map 中的一个键进行初始化:

    user.setCountry("ca");
    
  3. 在 JSP 中,使用初始化为 @ModelAttribute Map 的 form:select 元素:

    <form:select path="country" items="${countries}" />
    
  4. 在处理表单提交的控制器中,确保 @ModelAttribute 对象(用于保存表单值的对象)具有相应的 String 属性:

    public class User {
      private String country;
    ...
    

它是如何工作的…

form:select 标签生成一个 HTML select 字段,并使用 @ModelAttribute Map 和默认对象中定义的默认值进行初始化。path 属性对应于默认对象的属性。当表单提交时,选中的值将保存在 @ModelAttribute User 对象的相应属性中。

作为参考,这是生成的 HTML 代码:

<select id="country" name="country">
  <option value="de">Germany</option>
  <option value="fr">France</option>
  <option value="us">United States</option>
  <option value="ca" selected="selected">Canada</option>
</select>

更多内容…

对于 @ModelAttribute 对象,不需要使用 Map 类。可以使用 List<String> 对象或直接使用现有类的字段,这可能更方便。

使用 List 对象

除了 Map,您还可以使用 List<String> 对象:

@ModelAttribute("countries")
public List<String>countries() {
  List<String> l = new LinkedList<String>();
  l.add("com");
  l.add("ca");
  l.add("fr");
  l.add("de");    
  return l;
}

JSP 代码保持不变:

<form:select path="country" items="${countries}" />

在生成的 HTML 代码中,显示的文本将与 value 属性相同:

<select id="country" name="country">
  <option value="com">com</option>
  <option value="ca" selected="selected">ca</option>
  <option value="fr">fr</option>
  <option value="de">de</option>
</select>

使用 List

posted @ 2025-09-12 13:55  绝不原创的飞龙  阅读(8)  评论(0)    收藏  举报