Spring 快速指南



Spring 框架 - 概述

Spring 是最流行的企业级 Java 应用开发框架。全球数百万开发人员使用 Spring 框架来创建高性能、易于测试和可重用的代码。

Spring 框架是一个开源的 Java 平台。它最初由 Rod Johnson 编写,并于 2003 年 6 月在 Apache 2.0 许可下首次发布。

Spring 在大小和透明度方面都很轻量级。Spring 框架的基本版本大约 2MB。

Spring 框架的核心功能可用于开发任何 Java 应用程序,但也有扩展程序可在 Java EE 平台之上构建 Web 应用程序。Spring 框架旨在简化 J2EE 开发,并通过启用基于 POJO 的编程模型来促进良好的编程实践。

使用 Spring 框架的优势

以下是使用 Spring 框架的一些主要优势:

  • Spring 使开发人员能够使用 POJO 开发企业级应用程序。仅使用 POJO 的好处是您不需要 EJB 容器产品(例如应用程序服务器),而是可以选择仅使用强大的 servlet 容器(例如 Tomcat)或某些商业产品。

  • Spring 以模块化的方式组织。即使包和类的数量很大,您也只需要关注需要的那些,而忽略其余的。

  • Spring 并没有重新发明轮子,而是真正利用了一些现有的技术,例如几种 ORM 框架、日志框架、JEE、Quartz 和 JDK 计时器以及其他视图技术。

  • 使用 Spring 编写的应用程序易于测试,因为与环境相关的代码已移至此框架中。此外,通过使用 JavaBean 风格的 POJO,更容易使用依赖注入来注入测试数据。

  • Spring 的 Web 框架是一个设计良好的 Web MVC 框架,它为诸如 Struts 或其他过度设计或不太流行的 Web 框架提供了很好的替代方案。

  • Spring 提供了一个方便的 API 来将特定于技术的异常(例如由 JDBC、Hibernate 或 JDO 引发的异常)转换为一致的、未经检查的异常。

  • 轻量级 IoC 容器往往很轻量级,尤其是在与 EJB 容器相比时。这对于在内存和 CPU 资源有限的计算机上开发和部署应用程序非常有益。

  • Spring 提供了一个一致的事务管理接口,可以缩小到本地事务(例如,使用单个数据库)并扩展到全局事务(例如,使用 JTA)。

依赖注入 (DI)

Spring 最为著名的技术是控制反转的依赖注入 (DI)控制反转 (IoC)是一个通用概念,可以用多种不同的方式表达。依赖注入只是控制反转的一个具体示例。

在编写复杂的 Java 应用程序时,应用程序类应尽可能独立于其他 Java 类,以增加重用这些类的可能性,并在单元测试时独立于其他类进行测试。依赖注入有助于将这些类粘合在一起,同时保持它们的独立性。

依赖注入究竟是什么?让我们分别看看这两个词。这里的依赖部分转化为两个类之间的关联。例如,类 A 依赖于类 B。现在,让我们看看第二部分,注入。所有这一切都意味着,类 B 将由 IoC 注入到类 A 中。

依赖注入可以通过传递参数给构造函数或通过构造后使用 setter 方法来实现。由于依赖注入是 Spring 框架的核心,我们将在单独的章节中结合相关示例解释此概念。

面向方面编程 (AOP)

Spring 的关键组件之一是面向方面编程 (AOP) 框架。跨越应用程序多个点的函数称为横切关注点,这些横切关注点在概念上与应用程序的业务逻辑是分开的。方面的各种常见示例包括日志记录、声明式事务、安全性、缓存等。

OOP 中模块化的关键单元是类,而在 AOP 中,模块化的单元是方面。DI 帮助您将应用程序对象彼此解耦,而 AOP 帮助您将横切关注点与它们影响的对象解耦。

Spring 框架的 AOP 模块提供了一个面向方面编程的实现,允许您定义方法拦截器和切点,以清晰地解耦实现应分离的功能的代码。我们将在单独的章节中详细讨论 Spring AOP 概念。

Spring 框架 - 架构

Spring 潜在地可以成为您所有企业应用程序的一站式商店。但是,Spring 是模块化的,允许您选择哪些模块适用于您,而无需引入其余模块。以下部分详细介绍了 Spring 框架中所有可用的模块。

Spring 框架提供了大约 20 个模块,可以根据应用程序的需求使用。

Spring Framework Architecture

核心容器

核心容器由核心、Bean、上下文和表达式语言模块组成,其详细信息如下:

  • 核心模块提供框架的基础部分,包括 IoC 和依赖注入功能。

  • Bean模块提供 BeanFactory,它是工厂模式的复杂实现。

  • 上下文模块构建在核心和 Bean 模块提供的坚实基础之上,它是访问已定义和配置的任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。

  • SpEL模块提供了一个强大的表达式语言,用于在运行时查询和操作对象图。

数据访问/集成

数据访问/集成层由 JDBC、ORM、OXM、JMS 和事务模块组成,其详细信息如下:

  • JDBC模块提供了一个 JDBC 抽象层,消除了对繁琐的 JDBC 相关编码的需求。

  • ORM模块为流行的对象关系映射 API 提供集成层,包括 JPA、JDO、Hibernate 和 iBatis。

  • OXM模块提供了一个抽象层,支持 JAXB、Castor、XMLBeans、JiBX 和 XStream 的对象/XML 映射实现。

  • Java 消息服务JMS模块包含用于生成和使用消息的功能。

  • 事务模块支持对实现特殊接口的类以及所有 POJO 的程序化和声明式事务管理。

Web

Web 层由 Web、Web-MVC、Web-Socket 和 Web-Portlet 模块组成,其详细信息如下:

  • Web模块提供基本的 Web 导向集成功能,例如多部分文件上传功能以及使用 servlet 侦听器和面向 Web 的应用程序上下文初始化 IoC 容器。

  • Web-MVC模块包含 Spring 的 Web 应用程序模型-视图-控制器 (MVC) 实现。

  • Web-Socket模块支持 Web 应用程序中客户端和服务器之间的基于 WebSocket 的双向通信。

  • Web-Portlet模块提供要在 portlet 环境中使用的 MVC 实现,并镜像 Web-Servlet 模块的功能。

其他

还有一些其他重要的模块,例如 AOP、Aspects、Instrumentation、Web 和 Test 模块,其详细信息如下:

  • AOP模块提供了一个面向方面编程的实现,允许您定义方法拦截器和切点,以清晰地解耦实现应分离的功能的代码。

  • Aspects模块提供与 AspectJ 的集成,AspectJ 又是一个强大且成熟的 AOP 框架。

  • Instrumentation模块提供类检测支持和类加载器实现,可在某些应用程序服务器中使用。

  • Messaging模块支持 STOMP 作为 Websocket 子协议在应用程序中使用。它还支持用于从 Websocket 客户端路由和处理 STOMP 消息的注解编程模型。

  • Test模块支持使用 JUnit 或 TestNG 框架测试 Spring 组件。

Spring - 环境搭建

本章将指导您如何准备开发环境以开始使用 Spring 框架。它还将教您在设置 Spring 框架之前如何在您的机器上设置 JDK、Tomcat 和 Eclipse:

步骤 1 - 设置 Java 开发工具包 (JDK)

您可以从 Oracle 的 Java 网站下载最新版本的 SDK:Java SE 下载。 您将在下载的文件中找到安装 JDK 的说明,请按照给定的说明安装和配置设置。最后,设置 PATH 和 JAVA_HOME 环境变量以引用包含 java 和 javac 的目录,通常分别为 java_install_dir/bin 和 java_install_dir。

如果您运行的是 Windows 并已将 JDK 安装在 C:\jdk1.6.0_15 中,则必须将以下行添加到 C:\autoexec.bat 文件中。

set PATH=C:\jdk1.6.0_15\bin;%PATH% 
set JAVA_HOME=C:\jdk1.6.0_15 

或者,在 Windows NT/2000/XP 上,您必须右键单击“我的电脑”,选择“属性”→“高级”→“环境变量”。然后,您必须更新 PATH 值并单击“确定”按钮。

在 Unix(Solaris、Linux 等)上,如果 SDK 安装在 /usr/local/jdk1.6.0_15 中并且您使用的是 C shell,则必须将以下内容添加到您的 .cshrc 文件中。

setenv PATH /usr/local/jdk1.6.0_15/bin:$PATH 
setenv JAVA_HOME /usr/local/jdk1.6.0_15 

或者,如果您使用的是集成开发环境 (IDE),例如 Borland JBuilder、Eclipse、IntelliJ IDEA 或 Sun ONE Studio,则必须编译并运行一个简单的程序以确认 IDE 知道您安装了 Java 的位置。否则,您必须按照 IDE 文档中给出的说明进行正确的设置。

步骤 2 - 安装 Apache Common Logging API

您可以从 https://commons.apache.org/logging/ 下载最新版本的 Apache Commons Logging API。下载安装后,将二进制发行版解压缩到方便的位置。例如,在 Windows 上为 C:\commons-logging-1.1.1,在 Linux/Unix 上为 /usr/local/commons-logging-1.1.1。此目录将包含以下 jar 文件和其他支持文档等。

Common Logging API

确保您正确设置了此目录上的 CLASSPATH 变量,否则在运行应用程序时会遇到问题。

步骤 3 - 设置 Eclipse IDE

本教程中的所有示例都是使用 Eclipse IDE 编写的。因此,我们建议您应该在您的机器上安装最新版本的 Eclipse。

要安装 Eclipse IDE,请从https://www.eclipse.org/downloads/下载最新的 Eclipse 二进制文件。下载安装程序后,将二进制分发包解压缩到方便的位置。例如,在 Windows 上为 C:\eclipse,在 Linux/Unix 上为 /usr/local/eclipse,最后适当地设置 PATH 变量。

可以通过在 Windows 机器上执行以下命令启动 Eclipse,或者只需双击 eclipse.exe

%C:\eclipse\eclipse.exe 

可以通过在 Unix(Solaris、Linux 等)机器上执行以下命令启动 Eclipse:

$/usr/local/eclipse/eclipse

成功启动后,如果一切正常,则应显示以下结果:

Eclipse Home page

步骤 4 - 设置 Spring 框架库

现在,如果一切正常,您可以继续设置 Spring 框架。以下是将框架下载并安装到您的机器上的简单步骤。

  • 选择您是想在 Windows 还是 Unix 上安装 Spring,然后继续下一步下载 Windows 的 .zip 文件和 Unix 的 .tz 文件。

  • https://repo.spring.io/release/org/springframework/spring下载最新版本的 Spring 框架二进制文件。

  • 在编写本教程时,在 Windows 机器上下载了spring-framework-4.1.6.RELEASE-dist.zip。解压缩下载的文件后,它会在 E:\spring 中生成以下目录结构。

Spring Directories

您将在E:\spring\libs目录中找到所有 Spring 库。确保正确设置此目录上的 CLASSPATH 变量,否则在运行应用程序时会遇到问题。如果您使用的是 Eclipse,则无需设置 CLASSPATH,因为所有设置都将通过 Eclipse 完成。

完成此最后一步后,您就可以在下一章中继续进行您的第一个 Spring 示例。

Spring - Hello World 示例

让我们开始使用 Spring Framework 进行实际编程。在开始使用 Spring 框架编写您的第一个示例之前,您必须确保已正确设置 Spring 环境,如Spring - 环境设置章节中所述。我们还假设您对 Eclipse IDE 有些了解。

现在让我们继续编写一个简单的 Spring 应用程序,该应用程序将根据 Spring Bean 配置文件中完成的配置打印“Hello World!”或任何其他消息。

步骤 1 - 创建 Java 项目

第一步是使用 Eclipse IDE 创建一个简单的 Java 项目。选择文件 → 新建 → 项目选项,最后从向导列表中选择Java 项目向导。现在使用向导窗口将您的项目命名为HelloSpring,如下所示:

Hello Spring Wizard

成功创建项目后,您的项目资源管理器中将包含以下内容:

Hello Spring Directories

步骤 2 - 添加所需的库

第二步,让我们在项目中添加 Spring 框架和通用日志记录 API 库。为此,右键单击项目名称HelloSpring,然后按照上下文菜单中提供的以下选项操作:构建路径 → 配置构建路径,以显示 Java 构建路径窗口,如下所示:

Java Build Path

现在使用选项卡下可用的添加外部 JAR按钮,从 Spring 框架和通用日志记录安装目录添加以下核心 JAR:

  • commons-logging-1.1.1

  • spring-aop-4.1.6.RELEASE

  • spring-aspects-4.1.6.RELEASE

  • spring-beans-4.1.6.RELEASE

  • spring-context-4.1.6.RELEASE

  • spring-context-support-4.1.6.RELEASE

  • spring-core-4.1.6.RELEASE

  • spring-expression-4.1.6.RELEASE

  • spring-instrument-4.1.6.RELEASE

  • spring-instrument-tomcat-4.1.6.RELEASE

  • spring-jdbc-4.1.6.RELEASE

  • spring-jms-4.1.6.RELEASE

  • spring-messaging-4.1.6.RELEASE

  • spring-orm-4.1.6.RELEASE

  • spring-oxm-4.1.6.RELEASE

  • spring-test-4.1.6.RELEASE

  • spring-tx-4.1.6.RELEASE

  • spring-web-4.1.6.RELEASE

  • spring-webmvc-4.1.6.RELEASE

  • spring-webmvc-portlet-4.1.6.RELEASE

  • spring-websocket-4.1.6.RELEASE

步骤 3 - 创建源文件

现在让我们在HelloSpring项目下创建实际的源文件。首先,我们需要创建一个名为com.tutorialspoint的包。为此,右键单击包资源管理器部分中的src,然后选择新建 → 包选项。

接下来,我们将在 com.tutorialspoint 包下创建HelloWorld.javaMainApp.java文件。

Spring Source Files

以下是HelloWorld.java文件的内容:

package com.tutorialspoint;

public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
}

以下是第二个文件MainApp.java的内容:

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();
   }
}

关于主程序,需要注意以下两点:

  • 第一步是创建一个应用程序上下文,我们使用框架 API ClassPathXmlApplicationContext()。此 API 加载 bean 配置文件,并最终根据提供的 API,负责创建和初始化配置文件中提到的所有对象(即 bean)。

  • 第二步是使用已创建上下文的getBean()方法获取所需的 bean。此方法使用 bean ID 返回一个泛型对象,最终可以将其转换为实际对象。获得对象后,您可以使用此对象调用任何类方法。

步骤 4 - 创建 Bean 配置文件

您需要创建一个 Bean 配置文件,这是一个 XML 文件,充当将 bean(即类)粘合在一起的粘合剂。此文件需要在src目录下创建,如下面的屏幕截图所示:

Beans Configuration File

开发人员通常将此文件命名为Beans.xml,但您可以随意选择任何名称。您必须确保此文件在 CLASSPATH 中可用,并在创建应用程序上下文时在 MainApp.java 文件中使用相同的名称。

Beans.xml 用于为不同的 bean 分配唯一的 ID,并控制创建具有不同值的對象,而不会影响任何 Spring 源文件。例如,使用以下文件,您可以为“message”变量传递任何值,并且可以打印不同的消息值,而不会影响 HelloWorld.java 和 MainApp.java 文件。让我们看看它是如何工作的:

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld">
      <property name = "message" value = "Hello World!"/>
   </bean>

</beans>

当 Spring 应用程序加载到内存中时,框架使用上面的配置文件创建所有已定义的 bean,并为它们分配在<bean>标签中定义的唯一 ID。您可以使用<property>标签传递对象创建时使用的不同变量的值。

步骤 5 - 运行程序

创建源文件和 bean 配置文件后,您就可以进行此步骤了,即编译和运行程序。为此,保持 MainApp.Java 文件选项卡处于活动状态,并使用 Eclipse IDE 中可用的运行选项或使用Ctrl + F11来编译和运行您的MainApp应用程序。如果您的应用程序一切正常,这将在 Eclipse IDE 的控制台中打印以下消息:

Your Message : Hello World!

恭喜,您已成功创建您的第一个 Spring 应用程序。您可以通过更改“message”属性的值并保持两个源文件不变来查看上述 Spring 应用程序的灵活性。

Spring - IoC 容器

Spring 容器是 Spring 框架的核心。容器将创建对象、将它们连接在一起、配置它们并管理它们从创建到销毁的整个生命周期。Spring 容器使用 DI 来管理构成应用程序的组件。这些对象称为 Spring Bean,我们将在下一章中讨论。

容器通过读取提供的配置元数据来获取有关要实例化、配置和组装哪些对象的指令。配置元数据可以通过 XML、Java 注解或 Java 代码来表示。下图显示了 Spring 工作原理的高级视图。Spring IoC 容器使用 Java POJO 类和配置元数据来生成完全配置且可执行的系统或应用程序。

Spring IoC Container

Spring 提供以下两种不同类型的容器。

序号 容器和描述
1 Spring BeanFactory 容器

这是最简单的容器,提供对 DI 的基本支持,并由org.springframework.beans.factory.BeanFactory接口定义。BeanFactory 和相关的接口(例如 BeanFactoryAware、InitializingBean、DisposableBean)仍然存在于 Spring 中,目的是为了向后兼容大量与 Spring 集成的第三方框架。

2 Spring ApplicationContext 容器

此容器添加了更多特定于企业的功能,例如能够从属性文件解析文本消息以及能够将应用程序事件发布给感兴趣的事件侦听器。此容器由org.springframework.context.ApplicationContext接口定义。

ApplicationContext容器包含BeanFactory容器的所有功能,因此通常建议使用ApplicationContext而不是BeanFactory。BeanFactory 仍然可以用于轻量级应用程序,例如移动设备或基于 applet 的应用程序,在这些应用程序中,数据量和速度非常重要。

Spring - Bean 定义

构成应用程序主干并由 Spring IoC 容器管理的对象称为bean。bean 是由 Spring IoC 容器实例化、组装和管理的对象。这些 bean 是使用您提供给容器的配置元数据创建的。例如,您已经在前面的章节中看到的 XML <bean/> 定义的形式。

Bean 定义包含称为配置元数据的信息,容器需要这些信息来了解以下内容:

  • 如何创建 bean
  • bean 的生命周期详细信息
  • bean 的依赖项

所有上述配置元数据都转换为构成每个 bean 定义的一组以下属性。

序号 属性和描述
1

class

此属性是必需的,它指定用于创建 bean 的 bean 类。

2

name

此属性唯一地指定 bean 标识符。在基于 XML 的配置元数据中,您使用 id 和/或 name 属性来指定 bean 标识符。

3

scope

此属性指定从特定 bean 定义创建的对象的范围,我们将在 bean 范围章节中讨论。

4

constructor-arg

这用于注入依赖项,我们将在后续章节中讨论。

5

properties

这用于注入依赖项,我们将在后续章节中讨论。

6

自动装配模式

这用于注入依赖项,我们将在后续章节中讨论。

7

延迟初始化模式

延迟初始化 Bean 会告诉 IoC 容器在第一次请求时创建 Bean 实例,而不是在启动时创建。

8

初始化方法

在容器设置 Bean 的所有必要属性之后立即调用的回调函数。这将在 Bean 生命周期章节中讨论。

9

销毁方法

在包含 Bean 的容器被销毁时使用的回调函数。这将在 Bean 生命周期章节中讨论。

Spring 配置元数据

Spring IoC 容器完全与实际编写此配置元数据的格式解耦。以下是向 Spring 容器提供配置元数据的三个重要方法:

  • 基于 XML 的配置文件。
  • 基于注解的配置
  • 基于 Java 的配置

您已经了解了如何向容器提供基于 XML 的配置元数据,但让我们看看另一个基于 XML 的配置文件示例,其中包含不同的 Bean 定义,包括延迟初始化、初始化方法和销毁方法:

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- A simple bean definition -->
   <bean id = "..." class = "...">
      <!-- collaborators and configuration for this bean go here -->
   </bean>

   <!-- A bean definition with lazy init set on -->
   <bean id = "..." class = "..." lazy-init = "true">
      <!-- collaborators and configuration for this bean go here -->
   </bean>

   <!-- A bean definition with initialization method -->
   <bean id = "..." class = "..." init-method = "...">
      <!-- collaborators and configuration for this bean go here -->
   </bean>

   <!-- A bean definition with destruction method -->
   <bean id = "..." class = "..." destroy-method = "...">
      <!-- collaborators and configuration for this bean go here -->
   </bean>

   <!-- more bean definitions go here -->
   
</beans>

您可以查看Spring Hello World 示例,了解如何定义、配置和创建 Spring Bean。

我们将在单独的章节中讨论基于注解的配置。我们有意在单独的章节中讨论它,因为我们希望您在开始使用注解进行 Spring 依赖注入编程之前,掌握其他一些重要的 Spring 概念。

Spring - Bean 作用域

在定义``时,您可以选择为该 Bean 声明一个作用域。例如,要强制 Spring 在每次需要时都生成一个新的 Bean 实例,您应该将 Bean 的 scope 属性声明为prototype。同样,如果您希望 Spring 每次需要时都返回相同的 Bean 实例,您应该将 Bean 的 scope 属性声明为singleton

Spring 框架支持以下五个作用域,其中三个只有在使用 Web 感知 ApplicationContext 时才可用。

序号 作用域和描述
1

singleton

此作用域将 Bean 定义的作用域限定为每个 Spring IoC 容器一个实例(默认值)。

2

prototype

此作用域将单个 Bean 定义的作用域限定为任意数量的对象实例。

3

request

此作用域将 Bean 定义的作用域限定为 HTTP 请求。仅在 Web 感知 Spring ApplicationContext 的上下文中有效。

4

session

此作用域将 Bean 定义的作用域限定为 HTTP 会话。仅在 Web 感知 Spring ApplicationContext 的上下文中有效。
5

global-session

此作用域将 Bean 定义的作用域限定为全局 HTTP 会话。仅在 Web 感知 Spring ApplicationContext 的上下文中有效。

在本章中,我们将讨论前两个作用域,其余三个将在我们讨论 Web 感知 Spring ApplicationContext 时讨论。

singleton 作用域

如果作用域设置为 singleton,则 Spring IoC 容器将创建由该 Bean 定义确定的对象的单个实例。此单个实例存储在此类 singleton Bean 的缓存中,所有后续对该命名 Bean 的请求和引用都将返回缓存的对象。

默认作用域始终是 singleton。但是,当您只需要一个 Bean 实例时,可以在 Bean 配置文件中将scope属性设置为singleton,如下面的代码片段所示:

<!-- A bean definition with singleton scope -->
<bean id = "..." class = "..." scope = "singleton">
   <!-- collaborators and configuration for this bean go here -->
</bean>

示例

让我们准备好一个可用的 Eclipse IDE,并按照以下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 com.tutorialspoint包下创建 Java 类HelloWorldMainApp
4 src文件夹下创建 Bean 配置文件Beans.xml
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是HelloWorld.java文件的内容:

package com.tutorialspoint;

public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
}

以下是MainApp.java文件的内容:

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      HelloWorld objA = (HelloWorld) context.getBean("helloWorld");

      objA.setMessage("I'm object A");
      objA.getMessage();

      HelloWorld objB = (HelloWorld) context.getBean("helloWorld");
      objB.getMessage();
   }
}

以下是 singleton 作用域所需的配置文件Beans.xml

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld" scope = "singleton">
   </bean>

</beans>

创建源代码和 Bean 配置文件后,让我们运行应用程序。如果应用程序一切正常,它将打印以下消息:

Your Message : I'm object A
Your Message : I'm object A

prototype 作用域

如果作用域设置为 prototype,则 Spring IoC 容器每次请求特定 Bean 时都会创建一个新的 Bean 实例。通常,对于所有有状态 Bean 使用 prototype 作用域,对于无状态 Bean 使用 singleton 作用域。

要定义 prototype 作用域,可以在 Bean 配置文件中将scope属性设置为prototype,如下面的代码片段所示:

<!-- A bean definition with prototype scope -->
<bean id = "..." class = "..." scope = "prototype">
   <!-- collaborators and configuration for this bean go here -->
</bean>

示例

让我们准备好一个可用的 Eclipse IDE,并按照以下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 com.tutorialspoint包下创建 Java 类HelloWorldMainApp
4 src文件夹下创建 Bean 配置文件Beans.xml
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是HelloWorld.java文件的内容

package com.tutorialspoint;

public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
}

以下是MainApp.java文件的内容:

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      HelloWorld objA = (HelloWorld) context.getBean("helloWorld");

      objA.setMessage("I'm object A");
      objA.getMessage();

      HelloWorld objB = (HelloWorld) context.getBean("helloWorld");
      objB.getMessage();
   }
}

以下是 prototype 作用域所需的配置文件Beans.xml

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld" scope = "prototype">
   </bean>

</beans>

创建源代码和 Bean 配置文件后,让我们运行应用程序。如果应用程序一切正常,它将打印以下消息:

Your Message : I'm object A
Your Message : null

Spring - Bean 生命周期

Spring Bean 的生命周期很容易理解。当实例化 Bean 时,可能需要执行一些初始化操作才能使其处于可用状态。同样,当不再需要 Bean 并将其从容器中移除时,可能需要进行一些清理工作。

虽然在 Bean 实例化和销毁之间会发生很多幕后活动,但本章只讨论 Bean 生命周期中的两个重要回调方法,这些方法在 Bean 初始化和销毁时都需要。

要为 Bean 定义设置和拆卸操作,我们只需使用init-method和/或destroy-method参数声明``。init-method 属性指定一个在实例化 Bean 后立即调用的方法。类似地,destroy-method 指定一个在 Bean 从容器中移除之前调用的方法。

初始化回调

org.springframework.beans.factory.InitializingBean 接口指定了一个方法:

void afterPropertiesSet() throws Exception;

因此,您可以简单地实现上述接口,并在afterPropertiesSet()方法中完成初始化工作,如下所示:

public class ExampleBean implements InitializingBean {
   public void afterPropertiesSet() {
      // do some initialization work
   }
}

对于基于 XML 的配置元数据,可以使用init-method属性指定具有 void 无参数签名的方法的名称。例如:

<bean id = "exampleBean" class = "examples.ExampleBean" init-method = "init"/>

以下是类定义:

public class ExampleBean {
   public void init() {
      // do some initialization work
   }
}

销毁回调

org.springframework.beans.factory.DisposableBean接口指定了一个方法:

void destroy() throws Exception;

因此,您可以简单地实现上述接口,并在destroy()方法中完成最终处理工作,如下所示:

public class ExampleBean implements DisposableBean {
   public void destroy() {
      // do some destruction work
   }
}

对于基于 XML 的配置元数据,可以使用destroy-method属性指定具有 void 无参数签名的方法的名称。例如:

<bean id = "exampleBean" class = "examples.ExampleBean" destroy-method = "destroy"/>

以下是类定义:

public class ExampleBean {
   public void destroy() {
      // do some destruction work
   }
}

如果您在非 Web 应用程序环境中使用 Spring 的 IoC 容器;例如,在富客户端桌面环境中,您可以向 JVM 注册一个关闭钩子。这样做可以确保优雅地关闭并调用 singleton Bean 上的相关 destroy 方法,以便释放所有资源。

建议不要使用 InitializingBean 或 DisposableBean 回调,因为 XML 配置在命名方法方面提供了更大的灵活性。

示例

让我们准备好一个可用的 Eclipse IDE,并按照以下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 com.tutorialspoint包下创建 Java 类HelloWorldMainApp
4 src文件夹下创建 Bean 配置文件Beans.xml
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是HelloWorld.java文件的内容:

package com.tutorialspoint;

public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
   public void init(){
      System.out.println("Bean is going through init.");
   }
   public void destroy() {
      System.out.println("Bean will destroy now.");
   }
}

以下是MainApp.java文件的内容。在这里,您需要注册一个关闭钩子registerShutdownHook()方法,该方法在 AbstractApplicationContext 类上声明。这将确保优雅地关闭并调用相关的 destroy 方法。

package com.tutorialspoint;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();
      context.registerShutdownHook();
   }
}

以下是 init 和 destroy 方法所需的配置文件Beans.xml

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld" init-method = "init" 
      destroy-method = "destroy">
      <property name = "message" value = "Hello World!"/>
   </bean>

</beans>

创建源代码和 Bean 配置文件后,让我们运行应用程序。如果应用程序一切正常,它将打印以下消息:

Bean is going through init.
Your Message : Hello World!
Bean will destroy now.

默认初始化和销毁方法

如果您有很多 Bean 具有相同名称的初始化和/或销毁方法,则无需在每个 Bean 上声明init-methoddestroy-method。相反,框架提供了使用``元素上的default-init-methoddefault-destroy-method属性来配置这种情况的灵活性,如下所示:

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
   default-init-method = "init" 
   default-destroy-method = "destroy">

   <bean id = "..." class = "...">
      <!-- collaborators and configuration for this bean go here -->
   </bean>
   
</beans>

Spring - Bean 后处理器

BeanPostProcessor接口定义了您可以实现的回调方法,以提供您自己的实例化逻辑、依赖关系解析逻辑等。您还可以通过插入一个或多个 BeanPostProcessor 实现来实现一些自定义逻辑,这些逻辑在 Spring 容器完成 Bean 的实例化、配置和初始化之后执行。

您可以配置多个 BeanPostProcessor 接口,并且可以通过设置 BeanPostProcessor 实现的order属性来控制这些 BeanPostProcessor 接口的执行顺序Ordered接口。

BeanPostProcessors 对 Bean(或对象)实例进行操作,这意味着 Spring IoC 容器实例化 Bean 实例,然后 BeanPostProcessor 接口执行其工作。

ApplicationContext会自动检测任何定义为实现BeanPostProcessor接口的 Bean,并将这些 Bean 注册为后处理器,然后在容器创建 Bean 时由容器适当地调用。

示例

以下示例显示了如何在 ApplicationContext 的上下文中编写、注册和使用 BeanPostProcessors。

让我们准备好一个可用的 Eclipse IDE,并按照以下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 com.tutorialspoint包下创建 Java 类HelloWorldInitHelloWorldMainApp
4 src文件夹下创建 Bean 配置文件Beans.xml
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是HelloWorld.java文件的内容:

package com.tutorialspoint;

public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
   public void init(){
      System.out.println("Bean is going through init.");
   }
   public void destroy(){
      System.out.println("Bean will destroy now.");
   }
}

这是一个实现 BeanPostProcessor 的非常基本的示例,它会在任何 Bean 初始化之前和之后打印 Bean 名称。您可以在初始化 Bean 之前和之后实现更复杂的逻辑,因为您可以访问后处理器方法中的 Bean 对象。

以下是InitHelloWorld.java文件的内容:

package com.tutorialspoint;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;

public class InitHelloWorld implements BeanPostProcessor {
   public Object postProcessBeforeInitialization(Object bean, String beanName) 
      throws BeansException {
      
      System.out.println("BeforeInitialization : " + beanName);
      return bean;  // you can return any other object as well
   }
   public Object postProcessAfterInitialization(Object bean, String beanName) 
      throws BeansException {
      
      System.out.println("AfterInitialization : " + beanName);
      return bean;  // you can return any other object as well
   }
}

以下是MainApp.java文件的内容。在这里,您需要注册一个关闭钩子registerShutdownHook()方法,该方法在 AbstractApplicationContext 类上声明。这将确保优雅地关闭并调用相关的 destroy 方法。

package com.tutorialspoint;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();
      context.registerShutdownHook();
   }
}

以下是 init 和 destroy 方法所需的配置文件Beans.xml

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld"
      init-method = "init" destroy-method = "destroy">
      <property name = "message" value = "Hello World!"/>
   </bean>

   <bean class = "com.tutorialspoint.InitHelloWorld" />

</beans>

创建源代码和 Bean 配置文件后,让我们运行应用程序。如果应用程序一切正常,它将打印以下消息:

BeforeInitialization : helloWorld
Bean is going through init.
AfterInitialization : helloWorld
Your Message : Hello World!
Bean will destroy now.

Spring - Bean 定义继承

Bean 定义可以包含许多配置信息,包括构造函数参数、属性值和容器特定信息,例如初始化方法、静态工厂方法名称等等。

子 Bean 定义从父定义继承配置数据。子定义可以根据需要覆盖某些值或添加其他值。

Spring Bean 定义继承与 Java 类继承无关,但继承的概念相同。您可以将父 Bean 定义定义为模板,其他子 Bean 可以从父 Bean 继承所需的配置。

当您使用基于 XML 的配置元数据时,您可以使用parent属性指示子 Bean 定义,并将父 Bean 指定为此属性的值。

示例

让我们准备好一个可用的 Eclipse IDE,并按照以下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 com.tutorialspoint包下创建 Java 类HelloWorldHelloIndiaMainApp
4 src文件夹下创建 Bean 配置文件Beans.xml
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是配置文件Beans.xml,我们在其中定义了具有两个属性message1message2的"helloWorld" Bean。接下来,"helloIndia" Bean 通过使用parent属性定义为"helloWorld" Bean 的子 Bean。子 Bean 按原样继承message2属性,并覆盖message1属性,并引入另一个属性message3

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld">
      <property name = "message1" value = "Hello World!"/>
      <property name = "message2" value = "Hello Second World!"/>
   </bean>

   <bean id ="helloIndia" class = "com.tutorialspoint.HelloIndia" parent = "helloWorld">
      <property name = "message1" value = "Hello India!"/>
      <property name = "message3" value = "Namaste India!"/>
   </bean>
</beans>

以下是HelloWorld.java文件的内容:

package com.tutorialspoint;

public class HelloWorld {
   private String message1;
   private String message2;

   public void setMessage1(String message){
      this.message1 = message;
   }
   public void setMessage2(String message){
      this.message2 = message;
   }
   public void getMessage1(){
      System.out.println("World Message1 : " + message1);
   }
   public void getMessage2(){
      System.out.println("World Message2 : " + message2);
   }
}

以下是HelloIndia.java文件的内容:

package com.tutorialspoint;

public class HelloIndia {
   private String message1;
   private String message2;
   private String message3;

   public void setMessage1(String message){
      this.message1 = message;
   }
   public void setMessage2(String message){
      this.message2 = message;
   }
   public void setMessage3(String message){
      this.message3 = message;
   }
   public void getMessage1(){
      System.out.println("India Message1 : " + message1);
   }
   public void getMessage2(){
      System.out.println("India Message2 : " + message2);
   }
   public void getMessage3(){
      System.out.println("India Message3 : " + message3);
   }
}

以下是MainApp.java文件的内容:

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      
      HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
      objA.getMessage1();
      objA.getMessage2();

      HelloIndia objB = (HelloIndia) context.getBean("helloIndia");
      objB.getMessage1();
      objB.getMessage2();
      objB.getMessage3();
   }
}

创建源代码和 Bean 配置文件后,让我们运行应用程序。如果应用程序一切正常,它将打印以下消息:

World Message1 : Hello World!
World Message2 : Hello Second World!
India Message1 : Hello India!
India Message2 : Hello Second World!
India Message3 : Namaste India!

如果您在这里观察到,我们在创建"helloIndia" Bean 时没有传递 message2,但它由于 Bean 定义继承而被传递。

Bean 定义模板

您可以创建一个 Bean 定义模板,其他子 Bean 定义可以使用它,而无需付出太多努力。在定义 Bean 定义模板时,不应指定class属性,而应指定abstract属性,并应将 abstract 属性的值指定为true,如下面的代码片段所示:

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "beanTeamplate" abstract = "true">
      <property name = "message1" value = "Hello World!"/>
      <property name = "message2" value = "Hello Second World!"/>
      <property name = "message3" value = "Namaste India!"/>
   </bean>

   <bean id = "helloIndia" class = "com.tutorialspoint.HelloIndia" parent = "beanTeamplate">
      <property name = "message1" value = "Hello India!"/>
      <property name = "message3" value = "Namaste India!"/>
   </bean>
   
</beans>

父 Bean 无法独立实例化,因为它是不完整的,并且显式标记为abstract(抽象)。当定义像这样是抽象的时,它只能用作纯模板 Bean 定义,作为子定义的父定义。

Spring - 依赖注入

每个基于 Java 的应用程序都有一些对象协同工作,共同呈现最终用户所看到的正在运行的应用程序。在编写复杂的 Java 应用程序时,应用程序类应尽可能独立于其他 Java 类,以增加重用这些类的可能性,并在单元测试期间独立于其他类对其进行测试。依赖注入(有时称为连接)有助于将这些类粘合在一起,同时保持它们的独立性。

假设您有一个包含文本编辑器组件的应用程序,并且您想提供拼写检查功能。

public class TextEditor {
   private SpellChecker spellChecker;
   
   public TextEditor() {
      spellChecker = new SpellChecker();
   }
}

我们在这里所做的是,在 TextEditor 和 SpellChecker 之间创建了依赖关系。在控制反转场景中,我们将改为执行以下操作:

public class TextEditor {
   private SpellChecker spellChecker;
   
   public TextEditor(SpellChecker spellChecker) {
      this.spellChecker = spellChecker;
   }
}

在这里,TextEditor 不必担心 SpellChecker 的实现。SpellChecker 将独立实现,并在 TextEditor 实例化时提供给 TextEditor。整个过程由 Spring 框架控制。

在这里,我们移除了 TextEditor 的全部控制权,并将它放在其他地方(即 XML 配置文件),并且依赖项(即 SpellChecker 类)通过类构造函数注入到 TextEditor 类中。因此,控制流已被依赖注入 (DI)“反转”,因为您已有效地将依赖关系委托给某个外部系统。

注入依赖的第二种方法是通过 TextEditor 类的Setter 方法,我们将在此创建 SpellChecker 实例。此实例将用于调用 setter 方法来初始化 TextEditor 的属性。

因此,DI 存在两种主要变体,接下来的两个小节将分别用示例介绍这两种变体:

序号 依赖注入类型和描述
1 基于构造函数的依赖注入

当容器调用具有多个参数的类构造函数时,每个参数都代表对另一个类的依赖关系,就会实现基于构造函数的 DI。

2 基于 Setter 的依赖注入

基于 Setter 的 DI 是通过容器在调用无参数构造函数或无参数静态工厂方法来实例化 Bean 后,调用 Bean 上的 setter 方法来实现的。

您可以混合使用基于构造函数和基于 Setter 的 DI,但一个好的经验法则是:对于强制依赖项使用构造函数参数,对于可选依赖项使用 Setter。

使用 DI 原则,代码更简洁,当为对象提供其依赖项时,解耦更加有效。对象不查找其依赖项,也不了解依赖项的位置或类,所有这些都由 Spring 框架处理。

Spring - 注入内部 Bean

如您所知,Java 内部类是在其他类的范围内定义的,同样,内部 Bean是在另一个 Bean 的范围内定义的 Bean。因此,`` 元素在 `` 或 `` 元素内被称为内部 Bean,如下所示。

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "outerBean" class = "...">
      <property name = "target">
         <bean id = "innerBean" class = "..."/>
      </property>
   </bean>

</beans>

示例

让我们准备好一个可用的 Eclipse IDE,并按照以下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 在 `com.tutorialspoint` 包下创建 Java 类 `TextEditor`、`SpellChecker` 和 `MainApp`。
4 src文件夹下创建 Bean 配置文件Beans.xml
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是TextEditor.java文件的内容:

package com.tutorialspoint;

public class TextEditor {
   private SpellChecker spellChecker;
   
   // a setter method to inject the dependency.
   public void setSpellChecker(SpellChecker spellChecker) {
      System.out.println("Inside setSpellChecker." );
      this.spellChecker = spellChecker;
   }
   
   // a getter method to return spellChecker
   public SpellChecker getSpellChecker() {
      return spellChecker;
   }
   public void spellCheck() {
      spellChecker.checkSpelling();
   }
}

以下是另一个依赖类文件SpellChecker.java的内容:

package com.tutorialspoint;

public class SpellChecker {
   public SpellChecker(){
      System.out.println("Inside SpellChecker constructor." );
   }
   public void checkSpelling(){
      System.out.println("Inside checkSpelling." );
   }
}

以下是MainApp.java文件的内容:

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      TextEditor te = (TextEditor) context.getBean("textEditor");
      te.spellCheck();
   }
}

以下是配置文件Beans.xml,它使用内部 Bean配置基于 Setter 的注入:

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Definition for textEditor bean using inner bean -->
   <bean id = "textEditor" class = "com.tutorialspoint.TextEditor">
      <property name = "spellChecker">
         <bean id = "spellChecker" class = "com.tutorialspoint.SpellChecker"/>
      </property>
   </bean>

</beans>

创建源代码和 Bean 配置文件后,让我们运行应用程序。如果应用程序一切正常,它将打印以下消息:

Inside SpellChecker constructor.
Inside setSpellChecker.
Inside checkSpelling.

Spring - 注入集合

您已经了解了如何在 Bean 配置文件中使用 `` 标签的 `value` 属性配置基本数据类型,以及使用 `ref` 属性配置对象引用。这两种情况都处理向 Bean 传递单个值。

现在,如果您想传递多个值,例如 Java 集合类型(如 List、Set、Map 和 Properties),该怎么办?为了处理这种情况,Spring 提供了四种类型的集合配置元素,如下所示:

序号 元素和描述
1

<list>

这有助于连接,即注入一系列值,允许重复。

2

<set>

这有助于连接一系列值,但不允许重复。

3

<map>

这可用于注入名称-值对的集合,其中名称和值可以是任何类型。

4

<props>

这可用于注入名称-值对的集合,其中名称和值都是字符串。

您可以使用 `` 或 `` 来连接 `java.util.Collection` 的任何实现或数组

您将遇到两种情况:(a)传递集合的直接值和(b)传递 Bean 的引用作为集合元素之一。

示例

让我们准备好一个可用的 Eclipse IDE,并按照以下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 在 `com.tutorialspoint` 包下创建 Java 类 `JavaCollection` 和 `MainApp`。
4 src文件夹下创建 Bean 配置文件Beans.xml
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是JavaCollection.java文件的内容:

package com.tutorialspoint;
import java.util.*;

public class JavaCollection {
   List addressList;
   Set  addressSet;
   Map  addressMap;
   Properties addressProp;

   // a setter method to set List
   public void setAddressList(List addressList) {
      this.addressList = addressList;
   }
   
   // prints and returns all the elements of the list.
   public List getAddressList() {
      System.out.println("List Elements :"  + addressList);
      return addressList;
   }
   
   // a setter method to set Set
   public void setAddressSet(Set addressSet) {
      this.addressSet = addressSet;
   }
   
   // prints and returns all the elements of the Set.
   public Set getAddressSet() {
      System.out.println("Set Elements :"  + addressSet);
      return addressSet;
   }
   
   // a setter method to set Map
   public void setAddressMap(Map addressMap) {
      this.addressMap = addressMap;
   }
   
   // prints and returns all the elements of the Map.
   public Map getAddressMap() {
      System.out.println("Map Elements :"  + addressMap);
      return addressMap;
   }
   
   // a setter method to set Property
   public void setAddressProp(Properties addressProp) {
      this.addressProp = addressProp;
   }
   
   // prints and returns all the elements of the Property.
   public Properties getAddressProp() {
      System.out.println("Property Elements :"  + addressProp);
      return addressProp;
   }
}

以下是MainApp.java文件的内容:

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      JavaCollection jc=(JavaCollection)context.getBean("javaCollection");

      jc.getAddressList();
      jc.getAddressSet();
      jc.getAddressMap();
      jc.getAddressProp();
   }
}

以下是配置文件Beans.xml,它包含所有类型集合的配置:

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Definition for javaCollection -->
   <bean id = "javaCollection" class = "com.tutorialspoint.JavaCollection">
      
      <!-- results in a setAddressList(java.util.List) call -->
      <property name = "addressList">
         <list>
            <value>INDIA</value>
            <value>Pakistan</value>
            <value>USA</value>
            <value>USA</value>
         </list>
      </property>

      <!-- results in a setAddressSet(java.util.Set) call -->
      <property name = "addressSet">
         <set>
            <value>INDIA</value>
            <value>Pakistan</value>
            <value>USA</value>
            <value>USA</value>
         </set>
      </property>

      <!-- results in a setAddressMap(java.util.Map) call -->
      <property name = "addressMap">
         <map>
            <entry key = "1" value = "INDIA"/>
            <entry key = "2" value = "Pakistan"/>
            <entry key = "3" value = "USA"/>
            <entry key = "4" value = "USA"/>
         </map>
      </property>
      
      <!-- results in a setAddressProp(java.util.Properties) call -->
      <property name = "addressProp">
         <props>
            <prop key = "one">INDIA</prop>
            <prop key = "one">INDIA</prop>
            <prop key = "two">Pakistan</prop>
            <prop key = "three">USA</prop>
            <prop key = "four">USA</prop>
         </props>
      </property>
   </bean>

</beans>

创建源代码和 Bean 配置文件后,让我们运行应用程序。如果应用程序一切正常,它将打印以下消息:

List Elements :[INDIA, Pakistan, USA, USA] 
Set Elements :[INDIA, Pakistan, USA] 
ap Elements :{1 = INDIA, 2 = Pakistan, 3 = USA, 4 = USA} 
Property Elements :{two = Pakistan, one = INDIA, three = USA, four = USA} 

注入 Bean 引用

以下 Bean 定义将帮助您了解如何将 Bean 引用作为集合元素之一注入。您甚至可以将引用和值混合在一起,如下面的代码片段所示:

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Bean Definition to handle references and values -->
   <bean id = "..." class = "...">

      <!-- Passing bean reference  for java.util.List -->
      <property name = "addressList">
         <list>
            <ref bean = "address1"/>
            <ref bean = "address2"/>
            <value>Pakistan</value>
         </list>
      </property>
      
      <!-- Passing bean reference  for java.util.Set -->
      <property name = "addressSet">
         <set>
            <ref bean = "address1"/>
            <ref bean = "address2"/>
            <value>Pakistan</value>
         </set>
      </property>
      
      <!-- Passing bean reference  for java.util.Map -->
      <property name = "addressMap">
         <map>
            <entry key = "one" value = "INDIA"/>
            <entry key = "two" value-ref = "address1"/>
            <entry key = "three" value-ref = "address2"/>
         </map>
      </property>
   </bean>

</beans>

要使用上述 Bean 定义,您需要定义 setter 方法,以便它们能够处理引用。

注入 null 和空字符串值

如果您需要将空字符串作为值传递,则可以按如下方式传递:

<bean id = "..." class = "exampleBean">
   <property name = "email" value = ""/>
</bean>

上述示例等效于 Java 代码:`exampleBean.setEmail("")`

如果您需要传递 NULL 值,则可以按如下方式传递:

<bean id = "..." class = "exampleBean">
   <property name = "email"><null/></property>
</bean>

上述示例等效于 Java 代码:`exampleBean.setEmail(null)`

Spring - Bean 自动装配

您已经学习了如何使用 `` 元素声明 Bean,以及如何在 XML 配置文件中使用 `` 和 `` 元素注入 ``。

Spring 容器可以自动装配协作 Bean 之间的关联,而无需使用 `` 和 `` 元素,这有助于减少为大型基于 Spring 的应用程序编写的 XML 配置量。

自动装配模式

以下是自动装配模式,可用于指示 Spring 容器使用自动装配进行依赖注入。您可以使用 `` 元素的 `autowire` 属性为 Bean 定义指定自动装配模式。

序号 模式和描述
1 no

这是默认设置,这意味着没有自动装配,您应该使用显式 Bean 引用进行连接。您无需为此连接执行任何特殊操作。这就是您已经在依赖注入章节中看到的。

2 byName

按属性名称自动装配。Spring 容器查看在 XML 配置文件中 `autowire` 属性设置为 `byName` 的 Bean 的属性。然后,它尝试将这些属性与其在配置文件中定义的名称相同的 Bean 匹配并连接。

3 byType

按属性数据类型自动装配。Spring 容器查看在 XML 配置文件中 `autowire` 属性设置为 `byType` 的 Bean 的属性。然后,如果其类型与配置文件中 Bean 名称完全匹配,它会尝试匹配并连接属性。如果存在多个此类 Bean,则会抛出致命异常。

4 constructor

类似于 byType,但类型适用于构造函数参数。如果容器中构造函数参数类型的 Bean 不只有一个,则会引发致命错误。

5 autodetect

Spring 首先尝试使用 `constructor` 自动装配,如果失败,Spring 尝试使用 `byType` 自动装配。

您可以使用byTypeconstructor自动装配模式来连接数组和其他类型化集合。

自动装配的局限性

在整个项目中一致地使用自动装配效果最佳。如果通常不使用自动装配,则开发人员可能难以使用它来连接一两个 Bean 定义。尽管自动装配可以显著减少指定属性或构造函数参数的需求,但在使用它们之前,您应该考虑自动装配的局限性和缺点。

序号 局限性和描述
1

覆盖可能性

您仍然可以使用 `` 和 `` 设置来指定依赖项,这些设置将始终覆盖自动装配。

2

基本数据类型

您无法自动装配所谓的简单属性,例如基元类型、字符串和类。

3

容易混淆

自动装配不如显式连接精确,因此,如果可能,请优先使用显式连接。

Spring - 基于注解的配置

从 Spring 2.5 开始,可以使用注解配置依赖注入成为可能。因此,无需使用 XML 来描述 Bean 连接,您可以通过在相关的类、方法或字段声明上使用注解,将 Bean 配置移动到组件类本身。

注解注入在 XML 注入之前执行。因此,对于通过这两种方法连接的属性,后一种配置将覆盖前一种配置。

默认情况下,Spring 容器中不会启用注解连接。因此,在我们使用基于注解的连接之前,我们需要在 Spring 配置文件中启用它。因此,如果您想在 Spring 应用程序中使用任何注解,请考虑以下配置文件:

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context = "http://www.springframework.org/schema/context"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

   <context:annotation-config/>
   <!-- bean definitions go here -->

</beans>

一旦配置了 ``,您就可以开始注释您的代码,以指示 Spring 应该自动将值连接到属性、方法和构造函数。让我们来看一些重要的注解,以了解它们是如何工作的:

序号 注解和描述
1 @Required

@Required 注解应用于 Bean 属性 Setter 方法。

2 @Autowired

@Autowired 注解可以应用于 Bean 属性 Setter 方法、非 Setter 方法、构造函数和属性。

3 @Qualifier

@Qualifier 注解与 @Autowired 一起使用,可以通过指定要连接的确切 Bean 来消除混淆。

4 JSR-250 注解

Spring 支持基于 JSR-250 的注解,包括 @Resource、@PostConstruct 和 @PreDestroy 注解。

Spring - 基于 Java 的配置

到目前为止,您已经了解了如何使用 XML 配置文件配置 Spring Bean。如果您熟悉 XML 配置,那么实际上不需要学习如何进行基于 Java 的配置,因为您将使用任何可用的配置来实现相同的结果。

基于 Java 的配置选项使您可以编写大部分 Spring 配置而无需 XML,而是借助本章介绍的一些基于 Java 的注解。

@Configuration 和 @Bean 注解

使用@Configuration注解一个类表示该类可以被Spring IoC容器用作bean定义的来源。@Bean注解告诉Spring,用@Bean注解的方法将返回一个对象,该对象应该在Spring应用程序上下文中注册为一个bean。最简单的@Configuration类如下所示:

package com.tutorialspoint;
import org.springframework.context.annotation.*;

@Configuration
public class HelloWorldConfig {
   @Bean 
   public HelloWorld helloWorld(){
      return new HelloWorld();
   }
}

以上代码等效于以下XML配置:

<beans>
   <bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld" />
</beans>

这里,用@Bean注解的方法名作为bean ID,它创建并返回实际的bean。你的配置类可以声明多个@Bean。一旦定义了配置类,就可以使用AnnotationConfigApplicationContext加载并将其提供给Spring容器,如下所示:

public static void main(String[] args) {
   ApplicationContext ctx = new AnnotationConfigApplicationContext(HelloWorldConfig.class);
   
   HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
   helloWorld.setMessage("Hello World!");
   helloWorld.getMessage();
}

你可以加载各种配置类,如下所示:

public static void main(String[] args) {
   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

   ctx.register(AppConfig.class, OtherConfig.class);
   ctx.register(AdditionalConfig.class);
   ctx.refresh();

   MyService myService = ctx.getBean(MyService.class);
   myService.doStuff();
}

示例

让我们准备好一个可用的 Eclipse IDE,并按照以下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 因为你使用了基于Java的注解,所以你还需要添加Java安装目录中的CGLIB.jarASM.jar库,后者可以从asm.ow2.org下载。
4 com.tutorialspoint包下创建Java类HelloWorldConfigHelloWorldMainApp
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是HelloWorldConfig.java文件的内容

package com.tutorialspoint;
import org.springframework.context.annotation.*;

@Configuration
public class HelloWorldConfig {
   @Bean 
   public HelloWorld helloWorld(){
      return new HelloWorld();
   }
}

以下是HelloWorld.java文件的内容

package com.tutorialspoint;

public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
}

以下是MainApp.java文件的内容

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext ctx = 
         new AnnotationConfigApplicationContext(HelloWorldConfig.class);
   
      HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
      helloWorld.setMessage("Hello World!");
      helloWorld.getMessage();
   }
}

创建完所有源文件并添加所需的附加库后,让我们运行应用程序。请注意,不需要配置文件。如果你的应用程序一切正常,它将打印以下消息:

Your Message : Hello World!

注入Bean依赖项

当@Beans相互依赖时,表达这种依赖就像让一个bean方法调用另一个方法一样简单,如下所示:

package com.tutorialspoint;
import org.springframework.context.annotation.*;

@Configuration
public class AppConfig {
   @Bean
   public Foo foo() {
      return new Foo(bar());
   }
   @Bean
   public Bar bar() {
      return new Bar();
   }
}

这里,foo bean通过构造函数注入接收对bar的引用。现在让我们来看另一个工作示例。

示例

让我们准备好一个可用的 Eclipse IDE,并按照以下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 因为你使用了基于Java的注解,所以你还需要添加Java安装目录中的CGLIB.jarASM.jar库,后者可以从asm.ow2.org下载。
4 com.tutorialspoint包下创建Java类TextEditorConfigTextEditorSpellCheckerMainApp
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是TextEditorConfig.java文件的内容

package com.tutorialspoint;
import org.springframework.context.annotation.*;

@Configuration
public class TextEditorConfig {
   @Bean 
   public TextEditor textEditor(){
      return new TextEditor( spellChecker() );
   }

   @Bean 
   public SpellChecker spellChecker(){
      return new SpellChecker( );
   }
}

以下是TextEditor.java文件的内容

package com.tutorialspoint;

public class TextEditor {
   private SpellChecker spellChecker;

   public TextEditor(SpellChecker spellChecker){
      System.out.println("Inside TextEditor constructor." );
      this.spellChecker = spellChecker;
   }
   public void spellCheck(){
      spellChecker.checkSpelling();
   }
}

以下是另一个依赖类文件SpellChecker.java的内容

package com.tutorialspoint;

public class SpellChecker {
   public SpellChecker(){
      System.out.println("Inside SpellChecker constructor." );
   }
   public void checkSpelling(){
      System.out.println("Inside checkSpelling." );
   }
}

以下是MainApp.java文件的内容

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext ctx = 
         new AnnotationConfigApplicationContext(TextEditorConfig.class);

      TextEditor te = ctx.getBean(TextEditor.class);
      te.spellCheck();
   }
}

创建完所有源文件并添加所需的附加库后,让我们运行应用程序。请注意,不需要配置文件。如果你的应用程序一切正常,它将打印以下消息:

Inside SpellChecker constructor.
Inside TextEditor constructor.
Inside checkSpelling.

@Import注解

@Import注解允许从另一个配置类加载@Bean定义。考虑如下所示的ConfigA类:

@Configuration
public class ConfigA {
   @Bean
   public A a() {
      return new A(); 
   }
}

你可以在另一个Bean声明中导入上面的Bean声明,如下所示:

@Configuration
@Import(ConfigA.class)
public class ConfigB {
   @Bean
   public B b() {
      return new B(); 
   }
}

现在,不需要在实例化上下文时同时指定ConfigA.class和ConfigB.class,只需要提供ConfigB,如下所示:

public static void main(String[] args) {
   ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
   
   // now both beans A and B will be available...
   A a = ctx.getBean(A.class);
   B b = ctx.getBean(B.class);
}

生命周期回调

@Bean注解支持指定任意初始化和销毁回调方法,就像Spring XML中bean元素上的init-method和destroy-method属性一样:

public class Foo {
   public void init() {
      // initialization logic
   }
   public void cleanup() {
      // destruction logic
   }
}
@Configuration
public class AppConfig {
   @Bean(initMethod = "init", destroyMethod = "cleanup" )
   public Foo foo() {
      return new Foo();
   }
}

指定Bean范围

默认范围是单例,但你可以使用@Scope注解覆盖它,如下所示:

@Configuration
public class AppConfig {
   @Bean
   @Scope("prototype")
   public Foo foo() {
      return new Foo();
   }
}

Spring中的事件处理

在所有章节中,你都看到Spring的核心是ApplicationContext,它管理bean的完整生命周期。ApplicationContext在加载bean时发布某些类型的事件。例如,启动上下文时发布ContextStartedEvent,停止上下文时发布ContextStoppedEvent

ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口提供的。因此,如果一个bean实现了ApplicationListener,那么每次将ApplicationEvent发布到ApplicationContext时,都会通知该bean。

Spring提供以下标准事件:

序号 Spring内置事件和描述
1

ContextRefreshedEvent

ApplicationContext被初始化或刷新时,就会发布此事件。这也可以使用ConfigurableApplicationContext接口上的refresh()方法触发。

2

ContextStartedEvent

当使用ConfigurableApplicationContext接口上的start()方法启动ApplicationContext时,就会发布此事件。在收到此事件后,你可以轮询数据库,或者重新启动任何已停止的应用程序。

3

ContextStoppedEvent

当使用ConfigurableApplicationContext接口上的stop()方法停止ApplicationContext时,就会发布此事件。收到此事件后,你可以进行必要的收尾工作。

4

ContextClosedEvent

当使用ConfigurableApplicationContext接口上的close()方法关闭ApplicationContext时,就会发布此事件。关闭的上下文将结束其生命周期;它无法刷新或重新启动。

5

RequestHandledEvent

这是一个特定于Web的事件,它告诉所有bean已处理HTTP请求。

Spring的事件处理是单线程的,因此如果发布了一个事件,除非所有接收者都收到消息,否则进程将被阻塞,并且流程将不会继续。因此,如果要使用事件处理,在设计应用程序时应注意。

监听上下文事件

要监听上下文事件,bean应该实现ApplicationListener接口,该接口只有一个方法onApplicationEvent()。因此,让我们编写一个示例来查看事件如何传播以及如何根据某些事件编写代码来执行所需的任务。

让我们准备好一个可用的 Eclipse IDE,并按照以下步骤创建一个 Spring 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 com.tutorialspoint包下创建Java类HelloWorldCStartEventHandlerCStopEventHandlerMainApp
4 src文件夹下创建 Bean 配置文件Beans.xml
5 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是HelloWorld.java文件的内容

package com.tutorialspoint;

public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
}

以下是CStartEventHandler.java文件的内容

package com.tutorialspoint;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;

public class CStartEventHandler 
   implements ApplicationListener<ContextStartedEvent>{

   public void onApplicationEvent(ContextStartedEvent event) {
      System.out.println("ContextStartedEvent Received");
   }
}

以下是CStopEventHandler.java文件的内容

package com.tutorialspoint;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStoppedEvent;

public class CStopEventHandler 
   implements ApplicationListener<ContextStoppedEvent>{

   public void onApplicationEvent(ContextStoppedEvent event) {
      System.out.println("ContextStoppedEvent Received");
   }
}

以下是MainApp.java文件的内容

package com.tutorialspoint;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ConfigurableApplicationContext context = 
         new ClassPathXmlApplicationContext("Beans.xml");

      // Let us raise a start event.
      context.start();
	  
      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();

      // Let us raise a stop event.
      context.stop();
   }
}

以下是配置文件Beans.xml

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld">
      <property name = "message" value = "Hello World!"/>
   </bean>

   <bean id = "cStartEventHandler" class = "com.tutorialspoint.CStartEventHandler"/>
   <bean id = "cStopEventHandler" class = "com.tutorialspoint.CStopEventHandler"/>

</beans>

创建源代码和 Bean 配置文件后,让我们运行应用程序。如果应用程序一切正常,它将打印以下消息:

ContextStartedEvent Received
Your Message : Hello World!
ContextStoppedEvent Received

如果你愿意,可以发布你自己的自定义事件,然后可以捕获这些事件来对这些自定义事件采取任何行动。如果你有兴趣编写你自己的自定义事件,可以查看Spring中的自定义事件。

Spring中的自定义事件

编写和发布你自己的自定义事件需要多个步骤。按照本章中给出的说明编写、发布和处理自定义Spring事件。

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中的src文件夹下创建一个包com.tutorialspoint。所有类都将在此包下创建。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 通过扩展ApplicationEvent创建一个事件类CustomEvent。此类必须定义一个默认构造函数,该构造函数应继承自ApplicationEvent类的构造函数。
4 定义事件类后,可以从任何类(例如实现ApplicationEventPublisherAwareEventClassPublisher)发布它。你还需要在XML配置文件中将此类声明为bean,以便容器可以识别该bean为事件发布者,因为它实现了ApplicationEventPublisherAware接口。
5 发布的事件可以在一个类(例如实现ApplicationListener接口并为自定义事件实现onApplicationEvent方法的EventClassHandler)中处理。
6 src文件夹下创建bean配置文件Beans.xml和一个MainApp类,该类将用作Spring应用程序。
7 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是CustomEvent.java文件的内容

package com.tutorialspoint;

import org.springframework.context.ApplicationEvent;

public class CustomEvent extends ApplicationEvent{
   public CustomEvent(Object source) {
      super(source);
   }
   public String toString(){
      return "My Custom Event";
   }
}

以下是CustomEventPublisher.java文件的内容

package com.tutorialspoint;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;

public class CustomEventPublisher implements ApplicationEventPublisherAware {
   private ApplicationEventPublisher publisher;
   
   public void setApplicationEventPublisher (ApplicationEventPublisher publisher) {
      this.publisher = publisher;
   }
   public void publish() {
      CustomEvent ce = new CustomEvent(this);
      publisher.publishEvent(ce);
   }
}

以下是CustomEventHandler.java文件的内容

package com.tutorialspoint;

import org.springframework.context.ApplicationListener;

public class CustomEventHandler implements ApplicationListener<CustomEvent> {
   public void onApplicationEvent(CustomEvent event) {
      System.out.println(event.toString());
   }
}

以下是MainApp.java文件的内容

package com.tutorialspoint;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ConfigurableApplicationContext context = 
         new ClassPathXmlApplicationContext("Beans.xml");
	  
      CustomEventPublisher cvp = 
         (CustomEventPublisher) context.getBean("customEventPublisher");
      
      cvp.publish();  
      cvp.publish();
   }
}

以下是配置文件Beans.xml

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "customEventHandler" class = "com.tutorialspoint.CustomEventHandler"/>
   <bean id = "customEventPublisher" class = "com.tutorialspoint.CustomEventPublisher"/>

</beans>

创建源代码和 Bean 配置文件后,让我们运行应用程序。如果应用程序一切正常,它将打印以下消息:

y Custom Event
y Custom Event

Spring框架的AOP

Spring框架的关键组件之一是面向方面编程(AOP)框架。面向方面编程需要将程序逻辑分解成不同的部分,称为所谓的关注点。跨越应用程序多个点的函数称为横切关注点,这些横切关注点在概念上与应用程序的业务逻辑是分开的。日志记录、审核、声明式事务、安全、缓存等是各种常见的方面示例。

OOP中的模块化关键单元是类,而在AOP中,模块化单元是方面。依赖注入帮助你将应用程序对象彼此解耦,而AOP帮助你将横切关注点与它们影响的对象解耦。AOP就像Perl、.NET、Java等编程语言中的触发器。

Spring AOP模块提供拦截器来拦截应用程序。例如,当执行方法时,可以在方法执行之前或之后添加额外功能。

AOP术语

在我们开始使用AOP之前,让我们熟悉AOP的概念和术语。这些术语并非Spring所特有,而是与AOP相关的。

序号 术语和描述
1

方面

这是一个模块,它具有一组提供横切需求的API。例如,日志记录模块将被称为用于日志记录的AOP方面。应用程序可以根据需要拥有任意数量的方面。

2

连接点

这表示应用程序中你可以插入AOP方面的一个点。你也可以说,这是应用程序中使用Spring AOP框架采取行动的实际位置。

3

建议

这是要在方法执行之前或之后采取的实际操作。这是Spring AOP框架在程序执行期间调用的实际代码段。

4

切点

这是一组一个或多个连接点,建议应该在这些连接点上执行。可以使用表达式或模式指定切点,正如我们将在AOP示例中看到的那样。

5

引入

引入允许你向现有类添加新方法或属性。

6

目标对象

被一个或多个方面建议的对象。此对象始终是一个代理对象,也称为建议对象。

7

织入

织入是将方面与其他应用程序类型或对象链接以创建建议对象的进程。这可以在编译时、加载时或运行时完成。

建议类型

Spring方面可以处理以下五种建议:

序号 建议和描述
1

before

在方法执行之前运行建议。

2

after

在方法执行之后运行建议,无论其结果如何。

3

after-returning

仅当方法成功完成时,才在方法执行后运行建议。

4

after-throwing

仅当方法通过抛出异常退出时,才在方法执行后运行建议。

5

around

在调用建议方法之前和之后运行建议。

自定义方面实现

Spring支持@AspectJ注解样式方法和基于模式的方法来实现自定义方面。以下各节将详细解释这两种方法。

序号 方法和描述
1 基于XML模式

方面是使用常规类以及基于XML的配置来实现的。

2 基于@AspectJ

@AspectJ指的是将方面声明为使用Java 5注解注释的常规Java类的一种样式。

Spring - JDBC框架概述

使用普通的JDBC操作数据库时,编写不必要的异常处理代码、打开和关闭数据库连接等会变得很繁琐。但是,Spring JDBC框架负责所有底层细节,从打开连接、准备和执行SQL语句、处理异常、处理事务到最终关闭连接。

因此,您只需要定义连接参数,指定要执行的SQL语句,并在从数据库获取数据时为每次迭代执行所需的操作。

Spring JDBC提供了几种方法和相应的不同类来与数据库交互。我将采用经典且最流行的方法,该方法使用框架的JdbcTemplate类。这是管理所有数据库通信和异常处理的核心框架类。

JdbcTemplate类

JDBC Template类执行SQL查询、更新语句、存储过程调用,对ResultSet进行迭代,并提取返回的参数值。它还会捕获JDBC异常,并将它们转换为org.springframework.dao包中定义的通用、更具信息量的异常层次结构。

JdbcTemplate类的实例一旦配置后就是线程安全的。因此,您可以配置单个JdbcTemplate实例,然后安全地将此共享引用注入到多个DAO中。

使用JDBC Template类时,一种常见的做法是在Spring配置文件中配置一个DataSource,然后将该共享DataSource bean依赖注入到您的DAO类中,并在DataSource的setter中创建JdbcTemplate。

配置数据源

让我们在我们的TEST数据库中创建一个数据库表Student。我们假设您使用的是MySQL数据库,如果您使用的是其他数据库,则可以相应地更改您的DDL和SQL查询。

CREATE TABLE Student(
   ID   INT NOT NULL AUTO_INCREMENT,
   NAME VARCHAR(20) NOT NULL,
   AGE  INT NOT NULL,
   PRIMARY KEY (ID)
);

现在我们需要向JDBC Template提供一个DataSource,以便它可以自行配置以获取数据库访问权限。您可以在XML文件中使用如下所示的代码片段来配置DataSource:

<bean id = "dataSource" 
   class = "org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name = "driverClassName" value = "com.mysql.jdbc.Driver"/>
   <property name = "url" value = "jdbc:mysql://127.0.0.1:3306/TEST"/>
   <property name = "username" value = "root"/>
   <property name = "password" value = "password"/>
</bean>

数据访问对象 (DAO)

DAO代表数据访问对象,通常用于数据库交互。DAO的存在是为了提供一种读取和写入数据库数据的方法,它们应该通过一个接口公开此功能,应用程序的其余部分将通过该接口访问它们。

Spring中的DAO支持使您可以以一致的方式使用JDBC、Hibernate、JPA或JDO等数据访问技术。

执行SQL语句

让我们看看如何使用SQL和JDBC Template对象对数据库表执行CRUD(创建、读取、更新和删除)操作。

查询整数

String SQL = "select count(*) from Student";
int rowCount = jdbcTemplateObject.queryForInt( SQL );

查询长整数

String SQL = "select count(*) from Student";
long rowCount = jdbcTemplateObject.queryForLong( SQL );

使用绑定变量的简单查询

String SQL = "select age from Student where id = ?";
int age = jdbcTemplateObject.queryForInt(SQL, new Object[]{10});

查询字符串

String SQL = "select name from Student where id = ?";
String name = jdbcTemplateObject.queryForObject(SQL, new Object[]{10}, String.class);

查询并返回一个对象

String SQL = "select * from Student where id = ?";
Student student = jdbcTemplateObject.queryForObject(
   SQL, new Object[]{10}, new StudentMapper());

public class StudentMapper implements RowMapper<Student> {
   public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
      Student student = new Student();
      student.setID(rs.getInt("id"));
      student.setName(rs.getString("name"));
      student.setAge(rs.getInt("age"));
      
      return student;
   }
}

查询并返回多个对象

String SQL = "select * from Student";
List<Student> students = jdbcTemplateObject.query(
   SQL, new StudentMapper());

public class StudentMapper implements RowMapper<Student> {
   public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
      Student student = new Student();
      student.setID(rs.getInt("id"));
      student.setName(rs.getString("name"));
      student.setAge(rs.getInt("age"));
      
      return student;
   }
}

将一行插入表中

String SQL = "insert into Student (name, age) values (?, ?)";
jdbcTemplateObject.update( SQL, new Object[]{"Zara", 11} );

更新表中的一行

String SQL = "update Student set name = ? where id = ?";
jdbcTemplateObject.update( SQL, new Object[]{"Zara", 10} );

从表中删除一行

String SQL = "delete Student where id = ?";
jdbcTemplateObject.update( SQL, new Object[]{20} );

执行DDL语句

您可以使用jdbcTemplate中的execute(..)方法执行任何SQL语句或DDL语句。以下是如何使用CREATE语句创建表的示例:

String SQL = "CREATE TABLE Student( " +
   "ID   INT NOT NULL AUTO_INCREMENT, " +
   "NAME VARCHAR(20) NOT NULL, " +
   "AGE  INT NOT NULL, " +
   "PRIMARY KEY (ID));"

jdbcTemplateObject.execute( SQL );

Spring JDBC框架示例

基于上述概念,让我们检查一些重要的示例,这些示例将帮助您理解在Spring中使用JDBC框架。

序号 示例和描述
1 Spring JDBC示例

此示例将解释如何编写简单的基于JDBC的Spring应用程序。

2 Spring中的SQL存储过程

学习如何在Spring中使用JDBC调用SQL存储过程。

Spring - 事务管理

数据库事务是一系列被视为单个工作单元的操作。这些操作应该要么完全完成,要么完全无效。事务管理是面向RDBMS的企业应用程序的重要组成部分,用于确保数据完整性和一致性。事务的概念可以用以下四个关键属性来描述,称为ACID

  • 原子性 - 事务应被视为单个操作单元,这意味着要么整个操作序列成功,要么不成功。

  • 一致性 - 这表示数据库参照完整性、表中唯一主键等的一致性。

  • 隔离性 - 可能同时存在许多使用相同数据集的事务处理。每个事务都应该与其他事务隔离,以防止数据损坏。

  • 持久性 - 一旦事务完成,此事务的结果必须永久保存,并且由于系统故障而不能从数据库中删除。

真实的RDBMS数据库系统将保证每个事务的全部四个属性。使用SQL向数据库发出事务的简化视图如下:

  • 使用begin transaction命令开始事务。

  • 使用SQL查询执行各种删除、更新或插入操作。

  • 如果所有操作都成功,则执行commit,否则rollback所有操作。

Spring框架在不同的底层事务管理API之上提供了一个抽象层。Spring的事务支持旨在通过向POJO添加事务功能来提供EJB事务的替代方案。Spring支持编程式和声明式事务管理。EJB需要应用程序服务器,但是Spring事务管理无需应用程序服务器即可实现。

本地事务与全局事务

本地事务特定于单个事务资源(如JDBC连接),而全局事务可以跨越多个事务资源,例如分布式系统中的事务。

本地事务管理在应用程序组件和资源位于单个站点且事务管理仅涉及在单个机器上运行的本地数据管理器的情况下非常有用。本地事务更容易实现。

在所有资源分布在多个系统上的分布式计算环境中,需要全局事务管理。在这种情况下,需要在本地和全局级别进行事务管理。分布式事务或全局事务在多个系统上执行,其执行需要全局事务管理系统和所有相关系统的所有本地数据管理器之间的协调。

编程式与声明式

Spring支持两种类型的事务管理:

  • 编程式事务管理 - 这意味着您必须借助编程来管理事务。这提供了极大的灵活性,但难以维护。

  • 声明式事务管理 - 这意味着您将事务管理与业务代码分离。您只需使用注释或基于XML的配置来管理事务。

尽管声明式事务管理不如编程式事务管理灵活,编程式事务管理允许您通过代码控制事务,但声明式事务管理更可取。但作为一种横切关注点,声明式事务管理可以用AOP方法模块化。Spring通过Spring AOP框架支持声明式事务管理。

Spring事务抽象

Spring事务抽象的关键由org.springframework.transaction.PlatformTransactionManager接口定义,如下所示:

public interface PlatformTransactionManager {
   TransactionStatus getTransaction(TransactionDefinition definition);
   throws TransactionException;
   
   void commit(TransactionStatus status) throws TransactionException;
   void rollback(TransactionStatus status) throws TransactionException;
}

序号 方法和描述
1

TransactionStatus getTransaction(TransactionDefinition definition)

此方法根据指定的传播行为返回当前活动事务或创建新事务。

2

void commit(TransactionStatus status)

此方法提交给定事务及其状态。

3

void rollback(TransactionStatus status)

此方法回滚给定事务。

TransactionDefinition是Spring事务支持的核心接口,其定义如下:

public interface TransactionDefinition {
   int getPropagationBehavior();
   int getIsolationLevel();
   String getName();
   int getTimeout();
   boolean isReadOnly();
}

序号 方法和描述
1

int getPropagationBehavior()

此方法返回传播行为。Spring提供了EJB CMT中熟悉的所有事务传播选项。

2

int getIsolationLevel()

此方法返回此事务与其他事务工作的隔离程度。

3

String getName()

此方法返回此事务的名称。

4

int getTimeout()

此方法返回事务必须完成的时间(秒)。

5

boolean isReadOnly()

此方法返回事务是否为只读。

以下是隔离级别的可能值:

序号 隔离和描述
1

TransactionDefinition.ISOLATION_DEFAULT

这是默认隔离级别。

2

TransactionDefinition.ISOLATION_READ_COMMITTED

指示脏读被阻止;可能发生不可重复读和幻读。

3

TransactionDefinition.ISOLATION_READ_UNCOMMITTED

指示可能发生脏读、不可重复读和幻读。

4

TransactionDefinition.ISOLATION_REPEATABLE_READ

指示脏读和不可重复读被阻止;可能发生幻读。

5

TransactionDefinition.ISOLATION_SERIALIZABLE

指示脏读、不可重复读和幻读都被阻止。

以下是传播类型的可能值:

序号 传播和描述
1

TransactionDefinition.PROPAGATION_MANDATORY

支持当前事务;如果没有当前事务,则抛出异常。

2

TransactionDefinition.PROPAGATION_NESTED

如果存在当前事务,则在嵌套事务中执行。

3

TransactionDefinition.PROPAGATION_NEVER

不支持当前事务;如果存在当前事务,则抛出异常。

4

TransactionDefinition.PROPAGATION_NOT_SUPPORTED

不支持当前事务;而是始终非事务性地执行。

5

TransactionDefinition.PROPAGATION_REQUIRED

支持当前事务;如果不存在,则创建一个新事务。

6

TransactionDefinition.PROPAGATION_REQUIRES_NEW

创建一个新事务,如果存在当前事务则挂起当前事务。

7

TransactionDefinition.PROPAGATION_SUPPORTS

支持当前事务;如果不存在则非事务性地执行。

8

TransactionDefinition.TIMEOUT_DEFAULT

使用底层事务系统的默认超时,如果不支持超时则为无。

TransactionStatus接口为事务代码提供了一种简单的方法来控制事务执行和查询事务状态。

public interface TransactionStatus extends SavepointManager {
   boolean isNewTransaction();
   boolean hasSavepoint();
   void setRollbackOnly();
   boolean isRollbackOnly();
   boolean isCompleted();
}

序号 方法和描述
1

boolean hasSavepoint()

此方法返回此事务是否内部携带保存点,即是否基于保存点创建为嵌套事务。

2

boolean isCompleted()

此方法返回此事务是否已完成,即它是否已提交或回滚。

3

boolean isNewTransaction()

如果当前事务是新的,则此方法返回true。

4

boolean isRollbackOnly()

此方法返回事务是否已被标记为只回滚。

5

void setRollbackOnly()

此方法将事务设置为只回滚。

Spring - MVC 框架

Spring Web MVC 框架提供模型-视图-控制器 (MVC) 架构和现成的组件,可用于开发灵活且松散耦合的 Web 应用程序。MVC 模式使应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)分离,同时在这些元素之间提供松散耦合。

  • 模型 (Model) 封装应用程序数据,通常由 POJO 组成。

  • 视图 (View) 负责呈现模型数据,通常生成客户端浏览器可以解释的 HTML 输出。

  • 控制器 (Controller) 负责处理用户请求,构建相应的模型并将其传递给视图进行呈现。

DispatcherServlet

Spring Web 模型-视图-控制器 (MVC) 框架围绕一个DispatcherServlet 设计,该DispatcherServlet 处理所有 HTTP 请求和响应。下图说明了 Spring Web MVC DispatcherServlet 的请求处理工作流程:

Spring DispatcherServlet

以下是对应于传入的 HTTP 请求到DispatcherServlet 的事件序列:

  • 在收到 HTTP 请求后,DispatcherServlet 会咨询HandlerMapping 以调用相应的Controller

  • Controller 获取请求并根据使用的 GET 或 POST 方法调用相应的服务方法。服务方法将根据定义的业务逻辑设置模型数据,并将视图名称返回给DispatcherServlet

  • DispatcherServlet 将借助ViewResolver 来获取请求的定义视图。

  • 一旦视图确定,DispatcherServlet 将模型数据传递给视图,最终在浏览器上呈现。

所有上述组件,即 HandlerMapping、Controller 和 ViewResolver 都是WebApplicationContext 的一部分,WebApplicationContext 是普通ApplicationContext 的扩展,它具有一些 Web 应用程序所需的额外功能。

所需配置

您需要使用web.xml 文件中的 URL 映射来映射您希望DispatcherServlet 处理的请求。以下是一个示例,显示HelloWeb DispatcherServlet 示例的声明和映射:

<web-app id = "WebApp_ID" version = "2.4"
   xmlns = "http://java.sun.com/xml/ns/j2ee" 
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/j2ee 
   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    
   <display-name>Spring MVC Application</display-name>
   
   <servlet>
      <servlet-name>HelloWeb</servlet-name>
      <servlet-class>
         org.springframework.web.servlet.DispatcherServlet
      </servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>

   <servlet-mapping>
      <servlet-name>HelloWeb</servlet-name>
      <url-pattern>*.jsp</url-pattern>
   </servlet-mapping>

</web-app>

web.xml 文件将保存在 Web 应用程序的 WebContent/WEB-INF 目录中。在初始化HelloWeb DispatcherServlet 时,框架将尝试从应用程序 WebContent/WEB-INF 目录中名为[servlet-name]-servlet.xml 的文件中加载应用程序上下文。在本例中,我们的文件将是HelloWebservlet.xml

接下来,<servlet-mapping> 标签指示哪些 URL 将由哪个 DispatcherServlet 处理。这里所有以.jsp 结尾的 HTTP 请求都将由HelloWeb DispatcherServlet 处理。

如果您不想使用默认文件名[servlet-name]-servlet.xml 和默认位置WebContent/WEB-INF,您可以通过在您的 web.xml 文件中添加 servlet 监听器ContextLoaderListener 来自定义此文件名和位置,如下所示:

<web-app...>

   <!-------- DispatcherServlet definition goes here----->
   ....
   <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/HelloWeb-servlet.xml</param-value>
   </context-param>

   <listener>
      <listener-class>
         org.springframework.web.context.ContextLoaderListener
      </listener-class>
   </listener>
   
</web-app>

现在,让我们检查 Web 应用程序WebContent/WEB-INF 目录中HelloWeb-servlet.xml 文件所需的配置:

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:context = "http://www.springframework.org/schema/context"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans     
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/context 
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

   <context:component-scan base-package = "com.tutorialspoint" />

   <bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name = "prefix" value = "/WEB-INF/jsp/" />
      <property name = "suffix" value = ".jsp" />
   </bean>

</beans>

以下是关于HelloWeb-servlet.xml 文件的重要几点:

  • [servlet-name]-servlet.xml 文件将用于创建定义的 bean,覆盖全局范围内定义的任何同名 bean 的定义。

  • <context:component-scan...> 标签将用于激活 Spring MVC 注解扫描功能,允许使用@Controller 和@RequestMapping 等注解。

  • InternalResourceViewResolver 将具有定义的规则来解析视图名称。根据上述定义的规则,名为hello 的逻辑视图将委托给位于/WEB-INF/jsp/hello.jsp 的视图实现。

以下部分将向您展示如何创建实际组件,即控制器、模型和视图。

定义控制器

DispatcherServlet 将请求委托给控制器以执行特定于它的功能。@Controller 注解表示特定类充当控制器的角色。@RequestMapping 注解用于将 URL 映射到整个类或特定处理程序方法。

@Controller
@RequestMapping("/hello")
public class HelloController { 
   @RequestMapping(method = RequestMethod.GET)
   public String printHello(ModelMap model) {
      model.addAttribute("message", "Hello Spring MVC Framework!");
      return "hello";
   }
}

@Controller 注解将类定义为 Spring MVC 控制器。这里,@RequestMapping 的第一次使用表示此控制器上的所有处理方法都相对于/hello 路径。下一个注解@RequestMapping(method = RequestMethod.GET) 用于将 printHello() 方法声明为控制器的默认服务方法,用于处理 HTTP GET 请求。您可以定义另一个方法来处理同一 URL 上的任何 POST 请求。

您可以以另一种形式编写上述控制器,您可以在其中添加其他属性到@RequestMapping 中,如下所示:

@Controller
public class HelloController {
   @RequestMapping(value = "/hello", method = RequestMethod.GET)
   public String printHello(ModelMap model) {
      model.addAttribute("message", "Hello Spring MVC Framework!");
      return "hello";
   }
}

value 属性指示将处理程序方法映射到的 URL,method 属性定义用于处理 HTTP GET 请求的服务方法。关于上面定义的控制器的以下要点需要注意:

  • 您将在服务方法内定义所需的业务逻辑。您可以根据需要在此方法内调用其他方法。

  • 根据定义的业务逻辑,您将在此方法内创建模型。您可以使用 setter 设置不同的模型属性,这些属性将由视图访问以呈现最终结果。此示例创建一个具有其属性“message”的模型。

  • 定义的服务方法可以返回一个字符串,其中包含将用于呈现模型的视图的名称。此示例返回“hello”作为逻辑视图名称。

创建 JSP 视图

Spring MVC 支持许多不同表示技术的视图类型。这些包括 - JSP、HTML、PDF、Excel 工作表、XML、Velocity 模板、XSLT、JSON、Atom 和 RSS 提要、JasperReports 等。但最常见的是我们使用用 JSTL 编写的 JSP 模板。

让我们在 /WEB-INF/hello/hello.jsp 中编写一个简单的hello 视图:

<html>
   <head>
      <title>Hello Spring MVC</title>
   </head>
   
   <body>
      <h2>${message}</h2>
   </body>
</html>

这里${message} 是我们在控制器中设置的属性。您可以有多个属性显示在您的视图中。

Spring Web MVC 框架示例

基于上述概念,让我们检查一些重要的示例,这些示例将帮助您构建您的 Spring Web 应用程序:

序号 示例和描述
1 Spring MVC Hello World 示例

此示例将解释如何编写简单的 Spring Web Hello World 应用程序。

2 Spring MVC 表单处理示例

此示例将解释如何使用 HTML 表单编写 Spring Web 应用程序,以将数据提交到控制器并显示处理后的结果。

3 Spring 页面重定向示例

了解如何在 Spring MVC 框架中使用页面重定向功能。

4 Spring 静态页面示例

了解如何在 Spring MVC 框架中访问静态页面和动态页面。

5 Spring 异常处理示例

了解如何在 Spring MVC 框架中处理异常。

Spring - 使用 Log4J 进行日志记录

这是一个非常易于使用的 Spring 应用程序内的 Log4J 功能。以下示例将引导您完成简单的步骤,以解释 Log4J 和 Spring 之间简单的集成。

我们假设您已经在您的机器上安装了log4J。如果您没有它,您可以从https://logging.apache.org/下载它,然后只需将压缩文件解压缩到任何文件夹中。我们项目中只会使用log4j-x.y.z.jar

接下来,让我们准备一个可用的 Eclipse IDE,并采取以下步骤使用 Spring Web 框架开发基于动态表单的 Web 应用程序:

步骤 描述
1 创建一个名为SpringExample的项目,并在创建的项目中src文件夹下创建一个com.tutorialspoint包。
2 Spring Hello World 示例章节中所述,使用添加外部 JAR选项添加所需的 Spring 库。
3 使用添加外部 JAR 将 log4j 库log4j-x.y.z.jar 也添加到您的项目中。
4 com.tutorialspoint包下创建 Java 类HelloWorldMainApp
5 src文件夹下创建 Bean 配置文件Beans.xml
6 src 文件夹下创建 log4J 配置文件log4j.properties
7 最后一步是创建所有 Java 文件和 Bean 配置文件的内容,然后运行应用程序,如下所述。

以下是HelloWorld.java文件的内容

package com.tutorialspoint;

public class HelloWorld {
   private String message;
   
   public void setMessage(String message){
      this.message  = message;
   }
   public void getMessage() {
      System.out.println("Your Message : " + message);
   }
}

以下是第二个文件MainApp.java的内容

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.apache.log4j.Logger;

public class MainApp {
   static Logger log = Logger.getLogger(MainApp.class.getName());
   
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      log.info("Going to create HelloWord Obj");
      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();
      
      log.info("Exiting the program");
   }
}

您可以以类似于我们生成信息消息的方式生成debugerror 消息。现在让我们看看Beans.xml 文件的内容

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld">
      <property name = "message" value = "Hello World!"/>
   </bean>

</beans>

以下是log4j.properties的内容,它定义了 Log4J 生成日志消息所需的标准规则

# Define the root logger with appender file
log4j.rootLogger = DEBUG, FILE

# Define the file appender
log4j.appender.FILE=org.apache.log4j.FileAppender
# Set the name of the file
log4j.appender.FILE.File=C:\\log.out

# Set the immediate flush to true (default)
log4j.appender.FILE.ImmediateFlush=true

# Set the threshold to debug mode
log4j.appender.FILE.Threshold=debug

# Set the append to false, overwrite
log4j.appender.FILE.Append=false

# Define the layout for file appender
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=%m%n

创建源代码和 bean 配置文件后,让我们运行应用程序。如果您的应用程序一切正常,这将在 Eclipse 控制台中打印以下消息:

Your Message : Hello World!

如果您检查 C:\\ 驱动器,则应该找到您的日志文件log.out,其中包含各种日志消息,如下所示:

<!-- initialization log messages -->

Going to create HelloWord Obj
Returning cached instance of singleton bean 'helloWorld'
Exiting the program

Jakarta Commons Logging (JCL) API

或者,您可以使用Jakarta Commons Logging (JCL) API 在您的 Spring 应用程序中生成日志。JCL 可以从https://jakarta.apache.org/commons/logging/下载。从这个包中,我们技术上只需要commons-logging-x.y.z.jar 文件,它需要像您在上面的示例中放置log4j-x.y.z.jar 一样放置在您的类路径中。

要使用日志记录功能,您需要一个org.apache.commons.logging.Log 对象,然后您可以根据您的需求调用以下方法之一:

  • fatal(Object message)
  • error(Object message)
  • warn(Object message)
  • info(Object message)
  • debug(Object message)
  • trace(Object message)

以下是 MainApp.java 的替换,它使用了 JCL API

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.apache.commons.logging. Log;
import org.apache.commons.logging. LogFactory;

public class MainApp {
   static Log log = LogFactory.getLog(MainApp.class.getName());

   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      log.info("Going to create HelloWord Obj");
      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
      obj.getMessage();

      log.info("Exiting the program");
   }
}

在编译和运行程序之前,您必须确保已将 commons-logging-x.y.z.jar 文件包含在您的项目中。

现在保持上述示例中其余配置和内容不变,如果您编译并运行您的应用程序,您将获得与使用 Log4J API 获得的结果类似的结果。

广告