第 1章 Spring 学习目的与要求 本章重点讲解 Spring的基础知识。通过本章的学习,了解 Spring的体系结构,理解 Spring IoC与 AOP的基本原理,了解 Spring Bean的生命周期、实例化以及作用域,掌握 Spring的事务管理。 主要内容 . Spring开发环境的构建 . Spring IoC . Spring AOP . Spring Bean . Spring的数据库编程 Spring是当前主流的 Java开发框架,为企业级应用开发提供了丰富的功能。掌握 Spring框架的使用,已是 Java开发者必备的技能之一。本章将学习如何使用 Eclipse开发 Spring程序,不过在此之前需要构建 Spring的开发环境。 1.1 Spring概述 视频讲解 1.1.1 Spring的由来 Spring是一个轻量级 Java 开发框架,昀早由 Rod Johnson创建,目的是解决企业级应用 开发的业务逻辑层和其他各层的耦合问题。它是一个分层的 JavaSE/EEfull-stack(一站式)轻 量级开源框架,为开发 Java应用程序提供全面的基础架构支持。 Spring负责基础架构,因此 Java开发者可以专注于应用程序的开发。 1.1.2 Spring的体系结构 Spring的功能模块被有组织地分散到约 20个模块中,这些模块分布在核心容器( Core Container)层、数据访问 /集成( Data Access/Integration)层、 Web层、面向切面的编程( Aspect Oriented Programming,AOP)模块、植入(Instrumentation)模块、消息传输( Messaging) 和测试(Test)模块中,如图 1.1所示。 1 Core Container Spring的 Core Container是其他模块建立的基础,由 Beans(spring-beans)、Core(spring-core)、Context(spring-context)和 Expression(spring-expression,Spring表达式语言)等模块组成。 图 1.1 Spring的体系结构 spring-beans模块:提供了 BeanFactory,是工厂模式的一个经典实现, Spring将管理对象称为 Bean。 spring-core模块:提供了框架的基本组成部分,包括控制反转( Inversion of Control,IoC)和依赖注入( Dependency Injection,DI)功能。 spring-context模块:建立在 spring-beans和 spring-core模块基础上,提供一个框架式的对象访问方式,是访问定义和配置的任何对象媒介。 spring-expression模块:提供了强大的表达式语言支持运行时查询和操作对象图。这是对 JSP 2.1规范中规定的统一表达式语言( Unified EL)的扩展。该语言支持设置和获取属性值、属性分配、方法调用、访问数组、集合和索引器的内容、逻辑和算术运算、变量命名以及从 Spring的 IoC容器中以名称检索对象;它还支持列表投影、选择以及常见的列表聚合。 2 AOP和 Instrumentation Spring框架中与 AOP和 Instrumentation相关的模块有 AOP(spring-aop)模块、 Aspects(spring-aspects)模块以及 Instrumentation(spring-instrument)模块。 spring-aop模块:提供了一个符合 AOP要求的面向切面的编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以便干净地解耦。 spring-aspects模块:提供了与 AspectJ的集成功能, AspectJ是一个功能强大且成熟的 AOP框架。 spring-instrument模块:提供了类植入( Instrumentation)支持和类加载器的实现,可以在特定的应用服务器中使用。 Instrumentation提供了一种虚拟机级别支持的 AOP实现方式,使得开发者无须对 JDK做任何升级和改动,就可以实现某些 AOP的功能。 3 Messaging Spring 4.0以后新增了 Messaging(spring-messaging)模块,该模块提供了对消息传递体系结构和协议的支持。 4 Data Access/Integration 数据访问/集成层由 JDBC(spring-jdbc)、ORM(spring-orm)、OXM(spring-oxm)、JMS(spring-jms)和 Transactions(spring-tx)模块组成。 spring-jdbc模块:提供了一个 JDBC的抽象层,消除了烦琐的 JDBC编码和数据库厂商特有的错误代码解析。 spring-orm模块:为流行的对象关系映射( Object-Relational Mapping)API提供集成层, 包括 JPA和 Hibernate。使用 spring-orm模块,可以将这些 O/R映射框架与 Spring提供的所 有其他功能结合使用,例如声明式事务管理功能。 spring-oxm模块:提供了一个支持对象 /XML映射的抽象层实现,如 JAXB、Castor、JiBX和 XStream。 spring-jms模块(Java Messaging Service):指 Java消息传递服务,包含用于生产和使用消息的功能。自 Spring 4.1后,提供了与 spring-messaging模块的集成。 spring-tx模块(事务模块):支持用于实现特殊接口和所有 POJO(普通 Java对象)类的编程和声明式事务管理。 5 Web Web层由 Web(spring-web)、WebMVC(spring-webmvc)、WebSocket(spring-websocket) 和 WebFlux(spring-webflux)模块组成。 spring-web模块:提供了基本的 Web开发集成功能,例如多文件上传功能、使用 Servlet 监听器初始化一个 IoC容器以及 Web应用上下文。 spring-webmvc模块:也称为 Web-Servlet模块,包含用于 Web应用程序的 Spring MVC和 REST Web Services实现。 Spring MVC框架提供了领域模型代码和 Web表单之间的清晰分离,并与 Spring Framework的所有其他功能集成。本书后续章节将会详细讲解 Spring MVC框架。 spring-websocket模块:Spring 4.0后新增的模块,它提供了 WebSocket和 SockJS的实现,主要是与 Web前端的全双工通信的协议。 Web Flux模块: spring-webflux是一个新的非阻塞函数式 Reactive Web框架,可以用来建立异步的、非阻塞、事件驱动的服务,并且扩展性非常好。(该模块是 Spring 5的新增模块。) 6 Test Test(spring-test)模块:支持使用 JUnit或 TestNG对 Spring组件进行单元测试和集成测试。 1.2 Spring开发环境的构建 使用 Spring框架开发应用前,应先搭建其开发环境。本书前 4章使用的开发环境是基于 Eclipse平台的 Java Web应用的开发环境。 视频讲解 1.2.1 使用 Eclipse开发 Java Web应用 为了提高开发效率,通常需要安装 IDE(集成开发环境)工具。 Eclipse是一个可用于开发 Web应用的 IDE工具。 登录https://www.eclipse.org/,打开如图 1.2所示的 Eclipse官方主页。 图 1.2 Eclipse官方主页 单击图 1.2中的 Download按钮,打开如图 1.3所示的 Eclipse IDE 2020-12下载页面。 图 1.3 Eclipse IDE 2020-12下载页面 单击图 1.3中的 Download Packages超链接,打开如图 1.4所示的 Eclipse IDE 2020-12版本选择页面。 图 1.4 Eclipse IDE 2020-12版本选择页面 根据操作系统的位数,下载相应的 Eclipse。本书采用的 Eclipse是 Windows 64-bit(eclipse-jee-2020-12-R-win32-x86_64.zip)。 eclipse-jee-2020-12-R-win32-x86_64自带 Java SE 15,因此,使用此版本的 Eclipse开发 Java Web应用,仅需对 Web服务器和 Eclipse进行一些必要的设置。 1 Web服务器 目前,比较常用的 Web服务器包括 Tomcat、JRun、Resin、WebSphere、WebLogic等,本书采用的是 Tomcat 9.0。 登录 Apache软件基金会的官方网站 http://jakarta.Apache.org/tomcat,下载 Tomcat 9.0的免安装版(本书采用的 Tomcat是 apache-tomcat-9.0.30-windows-x64.zip)。登录网站后,首先在 Download中选择 Tomcat 9,然后在 Binary Distributions的 Core中选择相应版本即可。 将下载的 apache-tomcat-9.0.30-windows-x64.zip解压到某个目录下,例如解压到 E:\Java soft,解压缩后将出现如图 1.5所示的目录结构。 图 1.5 Tomcat目录结构 2 安装 Eclipse Eclipse下载完成后,解压到自己设置的路径下,即可完成安装。安装 Eclipse后,双击 Eclipse安装目录下的 eclipse.exe文件,启动 Eclipse。 3 集成 Tomcat 启动 Eclipse,选择 Window/Preferences菜单项,在弹出的对话框中选择 Server/Runtime Environments命令。在弹出的对话框中,单击 Add按钮,弹出如图 1.6所示的 New Server Runtime Environment对话框,在此可以配置各种版本的 Web服务器。 图 1.6 Tomcat配置对话框 在图 1.6中选择 Apache Tomcat v9.0服务器版本,单击 Next按钮,进入如图 1.7所示对话框。 图 1.7 选择 Tomcat目录 在图 1.7中单击 Browse按钮,选择 Tomcat的安装目录,单击 Finish按钮即可完成 Tomcat配置。 至此,可以使用 Eclipse创建 Dynamic Web Project(Java Web应用),并在 Tomcat下运行。 1.2.2 Spring的下载及目录结构 使用 Spring框架开发应用程序时,除了引用 Spring自身的 JAR包外,还需要引用 commons.logging的 JAR包。 1 Spring的 JAR包 Spring官方网站升级后,建议通过 Maven和 Gradle下载。对于不使用 Maven和 Gradle下载的开发者,本书给出一个 Spring Framework JAR官方直接下载路径: https://repo.springsource.org/ libs-release-local/org/springframework/spring/。本书采用的是 spring-5.3.2-dist.zip。将下载到的 ZIP文件解压缩,解压缩后的目录结构如图 1.8所示。 图 1.8 spring-framework-5.3.2的目录结构 图 1.8中,docs目录包含 Spring的 API文档和开发规范。 图 1.8中,libs目录包含开发 Spring应用所需要的 JAR包和源代码。该目录下有三类 JAR文件,其中,以 -5.3.2.jar结尾的文件是 Spring框架 class的 JAR包,即开发 Spring应用所需要的 JAR包;以-5.3.2-javadoc.jar结尾的文件是 Spring框架 API文档的压缩包;以 -5.3.2-sources.jar结尾的文件是 Spring框架源文件的压缩包。在 libs目录中,有 4个基础包: spring-core-5.3.2.jar、spring-beans-5.3.2.jar、spring-context-5.3.2.jar和 spring-expression-5.3.2.jar,分别对应 Spring核心容器的 4个模块: spring-core模块、spring-beans模块、 spring-context模块和 spring-expression模块。 图 1.8中,schema目录包含开发 Spring应用所需要的 schema文件,这些 schema文件定义了 Spring相关配置文件的约束。 2 commons.logging的 JAR包 Spring框架依赖于 Apache Commons Logging组件,该组件的 JAR包可以通过官方网站 http://commons.apache.org/proper/commons-logging/download_logging.cgi下载,本书下载的是 commons-logging-1.2-bin.zip,解压缩后,即可找到 commons-logging-1.2.jar。 对于 Spring框架的初学者,开发 Spring应用时,只需要将 Spring的 4个基础包和 commons-logging-1.2.jar复制到 Web应用的 WEB-INF/lib目录下。如果不明白需要哪些 JAR包,可以将 Spring的 libs目录中的 spring-XXX-5.3.2.jar全部复制到 WEB-INF/lib目录下。 1.2.3 第一个 Spring入门程序 本节通过一个简单的入门程序向读者演示 Spring框架的使用过程。 【例 1-1】Spring框架的使用过程。 具体实现步骤如下。 1 使用 Eclipse创建 Web应用并导入相关 JAR包 使用 Eclipse创建一个名为 ch1_1的 Dynamic Web Project,并将 Spring的 4个基础包和第三方依赖包 commons-logging-1.2.jar复制到 ch1_1的 WEB-INF/lib目录中,如图 1.9所示。 图 1.9 Web应用 ch1_1导入的 JAR包 2 创建接口 TestDao Spring解决的是业务逻辑层和其他各层的耦合问题,因此它将面向接口的编程思想贯穿整个应用系统。 在 src目录下,创建一个 dao包,并在 dao包中创建接口 TestDao,接口中定义一个 sayHello()方法,代码如下: package dao; public interface TestDao { public void sayHello(); } 3 创建接口 TestDao的实现类 TestDaoImpl 在 dao包下创建接口 TestDao的实现类 TestDaoImpl,代码如下: package dao; public class TestDaoImpl implements TestDao{ @Override public void sayHello() { System.out.println("Hello, Study hard!"); } } 4 创建配置文件 applicationContext.xml 在 src目录下,创建名为 config的包,并在该包中创建 Spring的配置文件 applicationContext.xml,在配置文件中使用实现类 TestDaoImpl创建一个 id为 test的 Bean,代码如下: 5 创建测试类 在 src目录下,创建一个 test包,并在 test包中创建 Test类,代码如下: package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import dao.TestDao; public class Test { public static void main(String[] args) { //初始化 Spring容器 ApplicationContext,加载配置文件 //@SuppressWarnings抑制警告的关键字,有泛型未指定类型 @SuppressWarnings("resource") ApplicationContext appCon = ClassPathXmlApplicationContext("config/applicationContext.xml"); //通 new 过容器获取 test实例 TestDao tt = (TestDao)appCon.getBean("test");//test为配置文件中的 id tt.sayHello(); }} 执行上述 main()方法后,将在控制台输出“ Hello, Study hard!”。上述 main()方法中并没有使用 new运算符创建 TestDaoImpl类的对象,而是通过 Spring容器获取实现类 TestDaoImpl的对象,这就是 Spring IoC的工作机制。 1.3 Spring IoC 1.3.1 Spring IoC的基本概念 视频讲解 IoC(控制反转)是一个比较抽象的概念,是 Spring框架的核心,用来消减计算机程序的耦合问题。依赖注入( DI)是 IoC的另外一种说法,只是从不同的角度,描述相同的概念。下面通过实际生活中的一个例子解释 IoC和 DI。 人们需要一件东西时,第一反应就是找东西,例如想吃面包。在没有面包店和有面包店两种情况下,你会怎么做?在没有面包店时,昀直观的做法可能是你按照自己的口味制作面包,也就是一个面包需要主动制作。然而,时至今日,各种面包店的盛行,你不想制作面包时,可以把自己的口味告诉店家,一会儿就可以吃到可口的面包了。注意,你并没有制作面包,而是由店家制作,但是完全符合你的口味。 上面只是列举了一个非常简单的例子,但包含了控制反转的思想,即把制作面包的主动权交给店家。下面通过面向对象编程思想,继续探讨这两个概念。 当某个 Java对象(调用者,比如你)需要调用另一个 Java对象(被调用者,即被依赖对象,比如面包)时,在传统编程模式下,调用者通常会采用“ new被调用者”的代码方式创建对象(比如你自己制作面包)。这种方式会增加调用者与被调用者之间的耦合性,不利于后期代码的升级与维护。 当 Spring框架出现后,对象的实例不再由调用者创建,而是由 Spring容器(比如面包店)创建。Spring容器会负责控制程序之间的关系(比如面包店负责控制你与面包的关系),而不是由调用者的程序代码直接控制。这样,控制权由调用者转移到 Spring容器,控制权发生了反转,这就是 Spring的控制反转。 从 Spring容器角度来看, Spring容器负责将被依赖对象赋值给调用者的成员变量,相当于为调用者注入它所依赖的实例,这就是 Spring的依赖注入,主要目的是解耦,体现一种“组合”的理念。 综上所述,控制反转是一种通过描述(在 Spring中可以是 XML或注解)并通过第三方去产生或获取特定对象的方式。在 Spring中实现控制反转的是 IoC容器,其实现方法是依赖注入。 1.3.2 Spring的常用注解 在 Spring框架中,尽管使用 XML配置文件可以很简单地装配 Bean(如例 1-1),但如果 应用中有大量的 Bean需要装配时,会导致 XML配置文件过于庞大,不方便以后的升级维护。 因此,更多时候推荐开发者使用注解( annotation)的方式去装配 Bean。 Spring框架基于 AOP编程( 1.4节)实现注解解析,因此,在使用注解编程时,需要导 入 spring-aop-5.3.2.jar包。 在 Spring框架中定义了一系列的注解,常用注解如下。 1 声明 Bean的注解 1)@Component 该注解是一个泛化的概念,仅仅表示一个组件对象( Bean),可以作用在任何层次上,没有明确的角色。 2)@Repository 该注解用于将数据访问层( DAO)的类标识为 Bean,即注解数据访问层 Bean,其功能与@Component()相同。 3)@Service该注解用于标注一个业务逻辑组件类( Service层),其功能与 @Component()相同。 4)@Controller 该注解用于标注一个控制器组件类( Spring MVC的 Controller),其功能与 @Component()相同。 2 注入 Bean的注解 1)@Autowired 该注解可以对类的成员变量、方法及构造方法进行标注,完成自动装配的工作。通过 @Autowired的使用消除 setter和 getter方法。默认按照 Bean的类型进行装配。 2)@Resource 该注解与@Autowired功能一样。区别在于,该注解默认按照名称装配注入,只有当找不到与名称匹配的 Bean时才会按照类型装配注入;而 @Autowired默认按照 Bean的类型进行装配,如果想按照名称装配注入,则需要结合 @Qualifier注解一起使用。 @Resource注解有两个属性: name和 type。name属性指定 Bean的实例名称,即按照名称来装配注入; type属性指定 Bean类型,即按照 Bean的类型进行装配。 3)@Qualifier 该注解与@Autowired注解配合使用。当 @Autowired注解需要按照名称装配注入时,则需要结合该注解一起使用, Bean的实例名称由 @Qualifier注解的参数指定。 1.3.3 基于注解的依赖注入 Spring IoC容器(ApplicationContext)负责创建和注入 Bean。Spring提供使用 XML配置、注解、Java配置以及 groovy配置实现 Bean的创建和注入。本书前 4章尽量使用注解(@Component、@Repository、@Service以及@Controller等业务 Bean的配置)和 XML配置 方式。 下面通过一个简单实例向读者演示基于注解的依赖注入的使用过程。