SpringIOC(很好的文章转载)

课程说明

一、 技术概览

轻量级的企业应用开发越来越受到广大Java应用开发者的追捧,而Spring框架又是轻量级容器的杰出代表。由于Spring的使用日渐广泛,因此已有许多应用服务器WAS)的应用采用了Spring框架。本书先介绍Spring的依赖注入、面向切面编程、Spring抽象JDBC框架,然后介绍三大框架的整合。

Spring框架的核心思想我们可以用两个字来描述,那就是“解耦”。应用程序的各个部分之间(包括代码内部和代码与平台之间)尽量形成一种松耦合的结构,使得应用程序有更多的灵活性。应用内部的解耦主要通过一种称为控制反转(IOC)的技术来实现。控制反转的基本思想就是本来由应用程序本身来主动控制的调用等逻辑转变成由外部配置文件来被动控制。由于控制反转的概念相对比较广泛,很多应用服务器实际上也实现了不同程度的控制反转技术,只是这些应用服务器对应用程序的侵入性太强。因此Martin Fowler专门写了一篇文章讨论控制反转这个概念,并提出一个描述更为准确的概念,叫依赖注入(Dependency Injection)。

Spring框架中的各个部分都充分使用了这种依赖注入的技术实现,从而给应用以最大的灵活度。实际上,这种依赖注入的参数化应用控制并不是Spring的首创,比如IBM的多渠道应用整合平台(Branch Transformation Toolkit,BTT)很早就采用了这种外部参数化控制的技术。BTT中的“对象工厂”Spring框架中的BeanFactory也有着异曲同工之妙。

Spring框架另外一个比较重要的技术是它对于面向切面的编程(AOP)的支持。随着应用复杂度的逐渐上升和对应用灵活性要求的提高,IT逻辑和业务逻辑尽量分离的呼声也越来越高。AOP技术作为实现这种分离的一种比较好的途径而越来越受到大家的重视。Spring提供的是一种动态AOP实现,也即通过代理模式动态地在目标对象的方法前后插入相应的处理代码。应用程序与底层应用服务器平台的解耦也可以借助AOP技术来实现。Spring内置的AOP支持是一种锦上添花的功能。它使得一些本来必须由容器支持的功能,比如事务控制可以脱离开容器运行,从而达到“瘦身”的目的。这也是为什么Spring框架常被人成为轻量级容器的一个原因。

JDBC基于SQL,不要求我们掌握其他框架的查询语言,简单易学,因此学习成本低。另外,在使用 JDBC 时,可以更细致地调整数据访问的性能。JDBC 还允许我们利用数据库的特有功能,而其他框架可能不鼓励甚至禁止使用它们。 但是JDBC并不完美,无论是执行查询,更新,插入还是删除操作, JDBC都要求我们正确地管理连接和语句,还要处理可能抛出的SQLException,及时的释放资源。这显然造成了大量的代码重复。这似乎印证了 Pareto 法则:只有 20% 的代码是查询操作所必需的,而80%代码是样板代码。Spring抽象JDBC框架基于模板设计模式,将上述必须都又和核心业务无关的样板代码封装到模板方法中,以简化开发,让编程人员可以将精力集中在核心业务之上。

Spring框架可以与许多已有的框架技术结合使用。J2EE技术应用的一个重要特点是相关的开源社区非常活跃。Web应用的不同层次都有非常多优秀的开源框架存在。比如Web层的Struts,ORM映射层的Hibernate等。Spring框架并不重新发明轮子,它的出现不是为了替代这些已有的框架。相反,Spring框架在设计上可以独立构建应用或者结合已有的框架一起构建应用。另外一个值得指出的地方是Spring框架的几大模块之间相互耦合度很小,因此Spring框架的使用可以根据实际需要选其部分模块循序渐进的使用,而非必须统统照搬。

本章简介

Spring是用于简化企业应用程序开发过程的开源框架,属于轻量级的控制反向 (IOC, Inversion of control)和面向切面编程 (AOP,即 Aspect Oriented  Programming)的容器框架。本章以 Spring的起源及背景为起点,介绍Spring的工作原理。然后以组装计算机为贯穿案例介绍Spring Bean的封装机制、Spring对Bean的管理,分别使用设值注入、构造注入、自动注入等方式组装Bean。最后通过一个示例介绍集合属性的使用。

 

1.1 Spring简介

1.1.1 Spring 的历史

Spring的基础架构起源于2000年早期,创始人为毕业于悉尼大学的音乐学博士Rod Johnson。2002 年后期,Rod Johnson 发布了《Expert One-on-One J2EE 设计与开发》一书,在书中,他对传统的J2EE技术(以EJB为核心)日益臃肿和低效提出了质疑,他觉得应该有更简洁的做法,于是提出了Interface21,也就是Spring框架的雏形。他还随书提供了Interface21 开发包以实现初步框架的开发,Interface21 即书中思想的具体实现。Rod Johnson 以 Interface21 开发包为基础,通过改造与扩充将其升级为更加开放、清晰、全面、高效的开发框架——Spring。2003年2月,Spring 框架正式成为开源项目,并发布于SourceForge中。后期随着数百甚至上千开发者贡献各自的经验,Spring 在改进与加强中变得日益强大,开发者的热心与投入使 Spring 社区十分活跃。

 

Spring框架的发展与成熟,离不开日复一日为 Spring 社区默默地做出伟大贡献的会员们。

1.1.1 Spring工作原理

Spring是一种通过JavaBean配置应用程序的方法。我们不需要通过new关键词创建对象,而是在配置文件中配置JavaBean。当对象与对象之间有依赖关系的时候,我们也只需要在配置文件中把依赖关系体现出来,这些被配置的Bean将会纳入Spring管理,放置于Spring容器中。我们只需要写很少量的代码便可得到Spring容器,并且从Spring容器中得到配置的JavaBean。这种解决依赖性的方法即控制反转 (IOC,即Inversion of Control)或者依赖注入(Dependency Injection),从技术上来说,即使用某种容器组织相互依赖的对象。除了IOC之外,Spring还可以将分散在系统的公共代码统一组织起来,在运行的时候加入到系统中,这就是AOP(面向切面编程)。

1.1.2 Spring框架简介

Spring 是用于简化企业应用程序开发过程的开源框架,属于轻量级的控制反转 (IOC)和面向切面编程 (AOP,即 Aspect Oriented Programming)的容器框架,解决了J2EE开发中许多常见的问题。我们需要了解一下Spring中的一些名词:

(1)轻量级:以所占大小及系统开销分析,Spring属于轻量级。整个Spring框架可以打包为 1M左右的JAR包,且系统开销较小。同时,Spring为非侵入式,若系统基于Spring开发,其所含的对象一般不依赖于Spring的类。

(2)IOC: IOC使对象被动接受依赖类,而并非主动获取。也就是说,告诉 Spring“你”是什么,“你”需要什么对象,然后Spring会在系统运行到适当的时候,把“你”要的对象主动给“你”,同时也把“你”交给其他需要“你”的对象。所有的类的创建、销毁都由Spring来控制,控制对象生存周期的不再是引用它的对象,而是 Spring。对于某个具体的对象而言,使用 Spring 之前是它控制其他对象,现在是所有对象都被 Spring 控制,所以叫控制反转。在系统运行中,动态的向某个对象提供它所需要的其他对象,这一点是通过DI(依赖注入)实现的。

(3)AOP:面向切面编程(也叫面向方面编程),关注系统的横向切面。通俗点说就是把代码“切开”,然后在需要的位置上动态加入公共代码。比如日志或者事务支持。

(4)容器:Spring 是一个包含且管理系统对象生命周期和配置的容器,在使用 Spring 应用开发的时候,几乎所有的 JavaBean 对象都需要 Spring 来“盛放”。Spring 容器的作用是管理对象。

(5)Spring框架:Spring能够通过简单的组件组合为复杂的系统。Spring框架为分层架构,由7个定义良好的模块组成,各模块构建于核心容器之上,核心容器定义了创建、配置及管理Bean的方式,如图 1.1.2 所示。



 1.1.2 中,各模块 (或组件)可以单独存在,也可以与其他一个或多个模块联合实现。各模块的功能如下:

(1)Spring Core:核心容器,用于提供 Spring框架的基本功能,其主要组件为 BeanFactory,是工厂模式的实现。BeanFactory使用反向控制(IOC)模式将应用程序的配置及依赖性规范与实际应用程序代码分开。

(2)Spring Context:核心模块的BeanFactory使Spring成为容器,上下文 (Context)模块使其成为框架。此模块扩展了BeanFactory的概念,增加了对国际化(I18N,即Internationalization)消息、事件的传播以及验证的支持;同时,此模块提供诸多企业服务,如电子邮件、JNDI访问、EJB集成、远程以及时序调度(Scheduling)服务,支持对模版框架 (如Velocity、FreeMarker)的集成。

(3)Spring AOP:通过配置管理特性,Spring AOP模块将面向切面编程功能集成至框架中,使Spring框架管理的任何对象均支持AOP。Spring AOP模块向基于 Spring的应用程序中的对象提供事务管理服务。此模块无需依赖于EJB组件,可以使用 Spring AOP将声明式事务管理集成至应用程序中。

(4)Spring DAO:JDBC DAO抽象层提供了意义重大的异常层次结构,简化了错误处理过程并极大地减少了需要编写的异常代码 (如打开或关闭连接),运用此结构可以管理异常、处理不同数据库供应商抛出的错误消息。Spring DAO的面向JDBC异常遵从通用的 DAO异常层次结构。

(5)Spring ORM:Spring框架中插入了若干ORM框架,提供ORM的对象关系工具,包括JDO、Hibernate以及iBatis SQL Map,都遵从Spring的通用事务及DAO异常层次结构。

(6)Spring Web:Web上下文模块建立在应用程序上下文模块之上,向基于Web 的应用程序提

供上下文,因而Spring框架支持与Struts集成。同时,Web模块简化了请求的处理过程以及将请求参数绑定至域对象的工作。

(7)Spring MVC:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC框架变为高度可配置,它容纳了大量视图技术,包括JSP、Velocity、Tiles、iText及POI等。

创建Spring项目可以使用MyEclipse开发工具,先创建项目,然后增加Spring支持。操作过程如图1.1.3和1.1.4所示。




1.1.4中类库的添加只包含核心类库,使用Spring控制反转的时候一般不需要添加其他类库。

1.1  Spring Bean封装机制

1.1.1 Spring Bean 

Spring以Bean的方式管理所有的组件,J2EE 的全部组件都使用Bean管理。在Spring中,

除了标准的JavaBean,其他任何对象和组件都可以作为Bean。

应用中各层的对象均由Spring管理,对象以Bean方式存在。Spring负责创建Bean的实例并管理其生命周期,Bean运行于Spring的容器。Spring上下文是生产Bean的工厂,Bean是Spring工厂生产的实例。Spring产生工厂时,需要确定每个Bean的实现类;Bean实例的使用者面向接口,因此无须关心Bean实例的实现类。Spring工厂负责维护Bean实例的实例化,使用者则无须关心。

Bean的定义通常使用XML配置文件,正确定义的Bean 由Spring提供实例化以及依赖关系的注入等。最简单的Spring配置文件代码如示例1.1所示。

示例1.1


[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans  
  3.     xmlns="http://www.springframework.org/schema/beans"  
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.     xmlns:p="http://www.springframework.org/schema/p"  
  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">  
  7. </beans>  

Spring的schemaLocation详细规定了Spring配置文件的合法元素、各元素出现的先后顺序、各元素的合法子元素以及合法属性等。

增加对实体对象管理的Bean,配置文件代码如示例1.2所示。

示例1.2

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">  
  5.     <bean id="user" class="com.hr.entity.UserVo" abstract="false"  
  6. lazy-init="default" autowire="default" />  
  7. </beans>  

上述代码中定义了UserVo对象。XML中Bean节点的部分属性如下:

[html]  view plain  copy
  1. <bean  
  2. id="beanId"  
  3. name ="beanName"  
  4. class="beanClass"  
  5. parent="parentBean"  
  6. abstract="true | false"  
  7. scope="prototype|singleton"  
  8. lazy-init="true | false | default"  
  9. autowire="no | byName | by Type | constructor | auto detect |default">  
  10. </ bean>  

以上属性的含义如表 1-1-l所示。

1-1-1 Bean属性

属性

含义

id

Bean的唯一标识名,必须为合法的XML ID,在整个XML文档中唯一,如果没有特殊需要,我们在标识一个Bean的时候,一般推荐使用id。

name

用于为id创建一个或多个别名,可以是任意字母或者符号,多个别名之间以逗号或空格分隔。

class

用于定义类的全限定名(包名加类名),Spring在创建对象的时候需要用到此属性,因此该属性不能指定接口。

parent

子类Bean定义其所引用的父类Bean,继承父类的所有属性。值得一提的是,在写代码的时候,即便是两个类之间没有继承关系,我们同样可以使用该属性。

abstract

用于定义Bean是否为抽象Bean,默认为false,表示此Bean将不会被实例化。一般用于父类Bean,可以在子类继承的时候使用。

scope

用于定义Bean的作用域,singleton表示在每个Spring IoC容器中一个bean定义对应一个对象实例,即Spring使用单例模式获取实例。prototype表示一个bean定义对应多个对象实例,即非单例模式

lazy-init

用于定义Bean是否实现初始化,默认为default。若为true,将在BeanFactory启动时初始化所有Singleton Bean;若为false,则在Bean请求时创建Singleton Bean

autowire

用于定义Bean的自动装配方式,默认为default,包括不使用自动装配功能、通过Bean的属性名实现自动装配、通过Bean的类型实现自动装配、通过Bean类的反省 (Introspection)机制决定选择使用constructor或者byType。

1.1.1 Application Context

Spring包括两种不同的容器:BeanFactory 和 ApplicationContext。BeanFactory提供基本的IOC支持;ApplicationContext则基于BeanFactory,提供应用程序框架服务。Spring提供了BeanFactory与 ApplicationContext 的多个实现。

ApplicationContext包括 BeanFactory 的全部功能,除非应用程序对性能要求很高时才考虑BeanFactory,其他情况下建议优先使用ApplicationContext。

应用中出现多个配置文件时,应采用BeanFactory的子接口ApplicationContext创建 BeanFactory的实例。ApplicationContext通常使用ClassPathXmlApplicationContext实现类,该类以classpath路径下的XML配置文件创建ApplicationContext。使用该类创建Spring容器的代码如示例1.3所示。

示例1.3

// 搜索classpath路径,以classpath路径下的applicationContext.xml创建对象ApplicationContext applicationContext = new ClassPathXmlApplicationContext(

"applicationContext.xml");

在实际应用中,会将Spring配置分别放在不同的配置文件中,如果一个应用中有两个配置文件application.xml和bean.xml,则创建容器实例的方法如示例1.4所示。

示例1.4

// 搜索classpath路径,以classpath路径下的

//applicationContext.xml和bean.xml创建ApplicationContext。 

ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml","bean.xml"});

ApplicationContext接口包括以下 3 个重要方法:

(1)containsBean(String name)方法。

//判断Spring容器是否包含ID为 user的Bean

boolean flag=applicationContext.containsBean("user");

(2)getBean(String name)方法。

//返回ID为 user的Bean

UserVo userBean= (UserVo)applicationContext.getBean("user");

该方法的功能是从Spring容器中获取一个对象,该方法的参数可以是Spring配置文件中Bean的id或者name,如果name指定了多个标识,只需传入一个标识。

(3)getType(String name)方法。

//返回:ID为 user 的类型

Class beanType= applicationContext.getType("user");

以下示例将从Spring容器中获取一个user对象,并输出user的信息。

示例1.5

[java]  view plain  copy
  1. // 以classpath路径下的applicationContext.xml创建Spring容器  
  2. ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "applicationContext.xml");  
  3. // 从Spring容器中获取ID为user的Bean  
  4. UserVo user = (UserVo) applicationContext.getBean("user");  
  5. // 封装姓名和年龄  
  6. user.setName("张三丰");  
  7. user.setAge(100);  
  8. // 输出该Bean的信息  
  9. user.printInfo();  

从示例1.5我们发现Bean的创建将交给Spring管理,我们要做的就是在Spring配置文件中对Bean配置即可。

1.1 Spring DI/IOC

1.1.1 什么是Spring的依赖注入

我们先看看什么叫依赖。在生活中,依靠别人或者别的事物而不能独立或者自立,我们称为依赖。那么在应用中,什么叫依赖呢?

依赖指的是两个实例之间的关系。其中一个实例是独立的,另一个实例是非独立的(依赖的),它依靠另外一个实例。比如计算机对象,它包含主机对象和显示器对象。如果没有主机对象或者显示器对象,则计算机对象就是不完整的,不能正常使用,我们就说计算机对象依赖于主机对象和显示器对象。

那么什么是注入呢?

计算机对象离不开主机对象和显示器对象,程序在运行过程中,我们必须给计算机对象提供它所需要的主机对象和显示器对象,把主机对象和显示器对象像“打针”一样提供给计算机对象,这个过程就叫做注入。

也就是说,如果一个对象需要另外一个对象才能正常使用,我们在程序运行的时候,给该对象提供它所需要的对象,这就是“依赖注入”。我们知道,Spring将会管理几乎所有的Bean对象,而对象与对象之间可能存在依赖关系,在程序运行过程中,Spring把我们所需要的对象都拼装好,这就是Spring的依赖注入。

在传统的 Java设计中,当 Java实例的调用者创建被调用的 Java实例时,要求被调用的 Java类出现在调用者的代码中,二者之间无法实现松耦合。工厂模式则对此进行了改进,使调用者无须关心被调用者的具体实现过程,只要获得符合某种标准 (接口)的实例即可使用。其调用的代码面向接口编程,支持调用者与被调用者解耦,因此工厂模式得以大范围地使用。但在工厂模式中,调用者需要自行定位工厂,与特定工厂耦合,所以仅在一定程度上实现了调用者与被调用者的解耦。Spring 的出现使调用者无须自行定位工厂,当程序运行至需要被调用者时,系统将自动提供被调用者实例。事实上,调用者与被调用者均由 Spring管理,二者之间的依赖关系由Spring提供。

下面以计算机的组装为例来演示此过程。计算机由主机和显示器组成,我们需要输出一台计算机的信息,比如该计算机由什么型号的主机、什么型号的显示器组成。下面我们一起来分析并设计该系统。

主机设计成一个类,该类(MainFrame)中有一个方法,用来输出主机的型号,如示例1.6所示。

示例1.6

[java]  view plain  copy
  1. private String modelType;//型号  
  2. // 输出主机信息  
  3. public void printMainFrameInfo() {  
  4.     System.out.println("主机型号:" + modelType);  
  5. }  
  6. //setter & getter  

显示器设计成一个接口,该接口(Display)声明一个输出显示器信息的方法,如示例1.7所示。

示例1.7

public void printDisplayInfo();

显示器接口有两个实现,分别是三星显示器和LG显示器,如示例1.8所示。

示例1.8

[java]  view plain  copy
  1. //三星显示器  
  2. public class SamSungDisplay implements Display {  
  3.     public void printDisplayInfo() {  
  4.         System.out.println("显示器:三星显示器");  
  5.     }  
  6. }  
  7. //LG显示器  
  8. public class LgDisplay implements Display {  
  9.         public void printDisplayInfo() {  
  10.         System.out.println("显示器:LG显示器 ");  
  11.     }  
  12. }  

计算机由主机和显示器组成,因此计算机类(Computer)有两个属性,一个是主机,一个是显示器,如示例1.9所示。

示例1.9

[java]  view plain  copy
  1. private MainFrame mainFrame;// 主机  
  2. private Display display;// 显示器接口  
  3.     // 输出计算机配置信息  
  4.     public void printComputerInfo() {  
  5.         System.out.println("计算机配置如下:");  
  6.         mainFrame.printMainFrameInfo();// 输出主机信息  
  7.         display.printDisplayInfo();// 输出显示器信息  
  8.     }  
  9. //setter & getter  

根据需求,计算机依赖于主机和显示器,因此可以使用Spring的依赖注入来实现。我们需要在配置文件中配置三星显示器、LG显示器和计算机。

三星显示器的配置如示例1.10所示。

示例1.10

<bean id="samsung" class="s3spring.ch01.computer.mainframe.SamSungDisplay" />

LG显示器的配置如示例1.11所示。

示例1.11

<bean id="lg" class="s3spring.ch01.computer.mainframe.LgDisplay" />

主机的modelType属性是直接量,直接量指的是基本数据类型和字符串,对直接量的注入,我们可以使用<value/>元素来指定。主机的配置如示例1.12所示。

示例1.12

<bean id="mainFrame" class="s3spring.ch01.computer.mainframe.MainFrame">

<property name="modelType" value="三星高配主机"/>

</bean>

目前为止,我们已经把主机对象和显示器对象交给了Spring,接下来,我们需要让Spring管理计算机对象,计算机对象对主机对象和显示器对象有依赖,我们需要用Spring的依赖注入来实现。

Spring的依赖注入对调用者与被调用者几乎无任何要求。依赖注入通常分为设值注入、构造注入、自动注入等。

1.1.1 设值注入方式

设值注入是指通过setter方法传入被调用者的实例,所以Bean的属性要求有对应的setter和getter方法,此方式因简单直观而得以广泛使用。

计算机的属性为主机和显示器,我们可以使用<ref/>元素来指定,该元素用来将bean中指定属性的值设置为对容器中的另外一个bean的引用,采用设值注入方法的配置如示例1.13所示。

示例1.13

[java]  view plain  copy
  1. <!-- 计算机的配置 -->  
  2. <bean id="computer"  class="s3spring.ch01.computer.Computer">  
  3.     <!-- 设值注入主机,name对应computer的属性,ref对应所依赖的bean-->  
  4.     <property name="mainFrame" ref="mainFrame"/>  
  5.     <!-- 设值注入三星显示器 -->  
  6.     <property name="display" ref="samsung"/>  
  7. </bean>  

如果mainFrame、samsung和computer这三个Bean的配置在同一个配置文件,我们也可以把<property name=“mainframe” ref=“mainframe”/>更改为

<property name=“mainframe” ><ref local=“mainframe”/></property>。如果mainFrame、samsung和computer不在同一个配置文件中,则需要更改为<property name=“mainframe”><ref bean=“mainframe”/></property>,无论是local还是bean都可以指定为一个所依赖的Bean的id或者name。

由配置文件可知,Spring在管理Bean时非常灵活。Bean与 Bean 间的依赖关系在配置文件中组织,并非在代码中编写。Spring通过配置文件精确地为每个Bean注入实例化的属性。因此,配置文件Bean的class元素不能为接口,只能为实现类。

Spring 自动管理Bean定义property元素,在执行无参的构造方法和创建默认的Bean 的实例后,Spring会调用对应的setter方法注入属性值。property定义的属性值不再由 Bean创建或管理,而是被动接受Spring的注入。Bean的 ID 属性为其唯一标识,程序通过 ID 属性对Bean进行访问,Bean与 Bean间的依赖关系也由 ID属性实现。设值注入的测试代码如示例1.14所示。

示例1.14

[java]  view plain  copy
  1. ApplicationContext applicationContextContext = new ClassPathXmlApplicationContext("applicationContext.xml");  
  2. Computer computer=(Computer)applicationContextContext.getBean("computer");  
  3.     //输出计算机的配置信息  
  4. computer.printComputerInfo();  

目前为止,我们成功的从Spring容器中获取了计算机对象。Spring的依赖注入使得我们在维护项目的时候变的非常简单,如果把显示器更改LG显示器,我们并不需要修改任何代码,修改配置文件即可实现,配置文件的修改如下。

<property name="display" ref="lg"/>

1.1.1 构造注入方式

构造注入是指通过构造方法完成依赖关系的注入,并非 setter方法。将以上设值注入按构造注入重新实现,则需要在Computer中增加带参构造方法,如示例1.15所示。

示例1.15

[java]  view plain  copy
  1. public Computer(MainFrame mainFrame, Display display) {  
  2.     this.mainFrame = mainFrame;  
  3.     this.display = display;  
  4. }  

Spring配置文件需要修改,修改后如示例1.16所示。

示例1.16

[html]  view plain  copy
  1. <bean id="computer" class="s3spring.ch01.computer.Computer">  
  2. //构造注入主机和显示器  
  3.     <constructor-arg index="0" ref="mainFrame" />  
  4.     <constructor-arg index="1" ref="samsung" />  
  5. </bean>  

采用构造注入方式时,Spring容器将会在程序运行时调用Computer的带参构造方法来创建实例,该带参构造有两个参数,index指的是第几个参数,ref指传入该参数的Bean。其执行效果与设值注入完全相同,区别在于属性的创建时机不同。设值注入创建默认的Bean实例后,调用对应的 setter方法注入依赖关系。构造注入则在创建 Bean实例时完成依赖关系的注入。

1.1.1 自动注入

Spring容器可以自动注入(autowire)相互协作bean之间的关联关系。因此,如果可能的话,可以自动让Spring通过检查BeanFactory中的内容,来替我们指定bean的协作者(其他被依赖的bean)。由于autowire可以针对单个bean进行设置,因此可以让有些bean使用autowire,有些bean不采用。autowire的方便之处在减少或者消除属性或构造器参数的设置,这样可以简化配置文件,如果直接使用property和constructor-arg注入依赖的话,那么将总是覆盖自动装配,在xml配置文件中,可以在<bean/>元素中使用autowire属性指定,如表1-1-2所示。

1-1-2 autowire属性

模式

 说明

no

不使用自动装配。必须通过ref元素指定依赖,这是默认设置。由于显式指定协作者可以使配置更灵活、更清晰,因此对于较大的部署配置,推荐采用该设置。

byName

根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。

byType

如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个该类型的bean,那么将会抛出异常,并指出不能使用byType方式进行自动装配。若没有找到相匹配的bean,则属性不会被设置。

如果把配置文件修改为示例1.17所示,则采用自动装配来组装计算机。

示例1.17

[html]  view plain  copy
  1. <bean id="computer" class="s3spring.ch01.computer.Computer" autowire="byName">  
  2.     <property name="display" ref="samsung"></property>  
  3. </bean>  

由于使用byName自动装配,容器会检查ID和Computer的两个属性mainFrame以及display名称匹配的Bean并注入,由于存在ID为mainFrame的Bean,所以Computer的mainFrame属性被自动注入,而display是显式注入,所以覆盖了display属性的自动注入。

1.1.1 集合属性

通过<list/>、<set/>、<map/>及<props/>元素可以定义和设置与Java Collection类型对应List、Set、Map及Properties的值。示例1.18中定义TestBean类,该类有四个属性,分别是List、Map、Properties、Set类型。

[java]  view plain  copy
  1. private List list;  
  2. private Map map;  
  3. private Properties prop;  
  4. private Set set;  

TestBean类的配置如示例1.19所示。

示例1.19

[html]  view plain  copy
  1. <bean id="testBean" class="com.bean.TestBean">  
  2. <!--list属性的配置 -->  
  3.     <property name="list">  
  4.         <list>  
  5.             <value>v1</value>  
  6.             <value>v2</value>  
  7.         </list>  
  8.     </property>  
  9.     <!-- map属性的配置 -->  
  10. <property name="map">  
  11.         <map>  
  12.             <entry key="k1">  
  13.                 <value>v1</value>  
  14.             </entry>  
  15.             <entry key="k2">  
  16.                 <value>v2</value>  
  17.             </entry>  
  18.         </map>  
  19.     </property>  
  20. <!-- prop属性的配置 -->  
  21.     <property name="prop">  
  22.         <props>  
  23.             <prop key="k1">str1</prop>  
  24.             <prop key="k2">str2</prop>  
  25.         </props>  
  26.     </property>  
  27.     <!-- set属性的配置 -->  
  28.     <property name="set">  
  29.         <set>  
  30.             <value>v1</value>  
  31.             <value>v2</value>  
  32.         </set>  
  33.     </property>  
  34. </bean>  

程序运行的时候,我们从Spring容器中获取testBean实例,该实例的四个集合属性中将会包含指定的对象。

1.1.1 Spring 注入方式的比较

1. 设值注入的特点

对于习惯了传统JavaBean开发的程序员而言,通过setter方法设定依赖关系更加直观自然。当依赖关系(或继承关系)较复杂时,构造注入方式的构造函数相当庞大,且需要在构造函数中设定所有依赖关系,此时使用设值注入方式则简单快捷。除此之外,某些第三方类库要求组件必须提供默认的构造函数 (如 Struts 中的 Action),此时构造注入方式的依赖注入机制会突显局限性,难以完成预期的功能,必须通过设值注入实现。

2. 构造注入的优点

在构造期即创建完整、合法的对象,构造注入无疑是此Java设计原则的最佳响应者。

而且构造注入还避免了编写繁琐的setter方法,所有的依赖关系都在构造函数中设定,使依赖关系集中呈现,可读性增加。由于不存在 setter方法,而是在构造时由容器一次性设定依赖关系。因此,组件在创建之后即处于相对稳定状态,无须担心上层代码在调用过程中执行setter方法时破坏组件之间的依赖关系。对于 Singleton模式组件,这种破坏将对整个系统产生重大的影响。通过构造注入,可以在构造函数中决定依赖关系的注入顺序。对于大量依赖外部服务的组件而言,依赖关系的获取顺序至关重要。

1.1 基于注解的容器配置

基于注解(Annotation)的配置有越来越流行的趋势,Spring顺应这种趋势,从2.5版起提供了完全基于注解配置 Bean、装配 Bean 的功能,您可以使用基于注解的 Spring IoC 替换原来基于 XML 的配置。

我们仍然使用计算机组装的例子来讲解基于注解的IOC容器配置语法。

1.1.1 使用 @Component注解配置bean

只需要在类的定义语句上方加上一个 @Component 注解,就将该类定义为一个bean了,请看示例1.20,将Computer、MainFrame、SamSungDisplay、LgDisplay配置成bean:

示例1.20 

[java]  view plain  copy
  1. @Component  
  2. public class Computer {   
  3. private MainFrame mainFrame;// 主机  
  4.     private Display display;// 显示器接口  
  5.     // 输出计算机配置信息  
  6.     public void printComputerInfo() {  
  7.         System.out.println("计算机配置如下:");  
  8.         mainFrame.printMainFrameInfo();// 输出主机信息  
  9.         display.printDisplayInfo();// 输出显示器信息  
  10.     }  
  11. }  
  12.   
  13. @Component  
  14. public class MainFrame {  
  15.     private String modelType;// 型号  
  16.     // 输出主机信息  
  17.     public void printMainFrameInfo() {  
  18.         System.out.println("主机型号:" + modelType);  
  19.     }  
  20. }   
  21.   
  22. @Component  
  23. public class SamSungDisplay implements Display {  
  24.     public void printDisplayInfo() {  
  25.         System.out.println("显示器:三星显示器");  
  26.     }  
  27. }   
  28.   
  29. @Component  
  30. public class LgDisplay implements Display {  
  31.     public void printDisplayInfo() {  
  32.         System.out.println("显示器:LG显示器 ");  
  33.     }  
  34. }  

如上代码所示,我们通过@Component注解分别将Computer、MainFrame、SamSungDisplay、LgDisplay配置成了bean(注意,在后面的示例中,我们会默认已经配置了上述4个bean)。bean名称默认为将第一个字母转换为小写的类名。如Computer类的bean默认名称是computer。上述代码的等价xml配置是:

[html]  view plain  copy
  1. <bean id="samSungDisplay" class="s3spring.ch01.computer.annotation.SamSungDisplay" />  
  2. <bean id="lgDisplay" class="s3spring.ch01.computer.annotation.LgDisplay" />  
  3. <bean id="mainFrame" class="s3spring.ch01.annotation.computer.MainFrame"/>  
  4. <bean id="computer"  class="s3spring.ch01.annotation.computer.Computer"/>  

@Component注解唯一的一个可选参数是value,用于指定bean的名称(即id值,所以必须是唯一的),如示例1.21所示,声明Computer类为一个bean,id为computer:

示例1.21

@Component(value=”computer”)

public class Computer { …… }

可以省略参数名称,简写为:

@Component(”computer”)

public class Computer { …… }

Spring还提供了更加细化的用于定义bean的注解形式:@Repository、@Service、@Controller,它们分别对应数据访问层bean,业务层bean,和视图层bean。目前版本中,这些注解与@Component的语义是一样的,完全通用,在Spring以后的版本中可能会给它们追加更多的语义。所以,我们推荐使用@Repository、@Service、@Controller来替代@Component。

1.1.1 扫描 @Component标注的类

在使用 @Component 注解后,Spring 容器必须启用类扫描机制,以找到所有使用@Component标注的类,将它们作为bean来管理。从Spring 2.5开始, 对 context 命名空间进行了扩展,提供了这一功能,请看下面的配置,引入context命名空间,并扫描bean:

示例1.22 

[html]  view plain  copy
  1. <beans  
  2.     xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xmlns:p="http://www.springframework.org/schema/p"  
  5.     xmlns:context="http://www.springframework.org/schema/context"  
  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  7. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  8. http://www.springframework.org/schema/context  
  9.      http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
  10.   
  11. <context:component-scan base-package="s3spring.ch1.annotation.computer"/>  
  12.   
  13. </beans>  

Spring 容器在初始化时会扫描 <context:component-scan/> 标签的 base-package 属性指定的类包及其递归子包中所有的类,找出 @Component标注的类,将它们配置成bean。然后,我们就可以通过Spring 容器的工厂方法获取并使用bean了。请看示例1.23,通过Spring容器获取Computer bean:

示例1.23 

[java]  view plain  copy
  1. ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");  
  2. Computer c = ac.getBean(Computer.class);  

执行示例1.23,如果没有抛出任何异常,即表示能够正常获得bean。

1.1.1 使用 @Scope注解配置bean 的作用域

@Scope用于定义bean的作用域,singleton表示在每个Spring IoC容器中一个bean定义对应一个对象实例,即Spring使用单例模式获取实例。prototype表示一个bean定义对应多个对象实例,即非单例模式,我们常称作多例。默认情况下,Spring bean 的作用域为 singleton。示例1.24 将Computer类配置成一个“多例”的bean:

示例1.24

@Component

@Scope(value="prototype")

public class Computer { …… }

可以省略参数名称,简写为:

@Component

@Scope("prototype")

public class Computer { …… }

1.1.2 使用 @Autowired注解

Spring 2.5版本起, Spring引入了 @Autowired 注解,它可以对类成员变量、方法及构造函数进行标注,以完成自动装配依赖的工作。

来看一下示例1.25,使用 @Autowired 为成员变量自动注入依赖:

示例1.25 

[java]  view plain  copy
  1. @Component  
  2. @Scope("prototype")  
  3. public class Computer {  
  4.     @Autowired  
  5.     private MainFrame mainFrame;// 主机  
  6. @Autowired  
  7.     private Display display;// 显示器接口  
  8.   
  9.     // 输出计算机配置信息  
  10.     public void printComputerInfo() {  
  11.         System.out.println("计算机配置如下:");  
  12.         mainFrame.printMainFrameInfo();// 输出主机信息  
  13.         display.printDisplayInfo();// 输出显示器信息  
  14.     }  
  15. }  

将 @Autowired 标注于实例属性mainFrame和display上面Spring将直接采用 Java 反射机制获取 mainFrame 和 display 属性的类型,再根据类型查找“唯一”匹配的bean来进行依赖自动注入.(如果bean的类型与属性的类型相同,或者bean的类型是属性类型的子类型或接口实现,则类型匹配)。一般推荐将 @Autowired放在setter方法之上。

由于和mainFrame属性类型匹配的bean只有MainFrame 一个,所以自动装配依赖可以顺利进行。但是和display 属性的类型匹配的bean有两个(SamSungDisplay和LgDisplay都实现了Display接口,而display属性类型正是 Display),此时Spring 应用容器不知道该用哪一个bean为Computer的display属性注入依赖值,从而导致创建Computer bean失败,抛出BeanCreationException。异常信息如下:

Error creating bean with name 'computer': Injection of autowired dependencies failed;

……

org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [s3spring.ch1.annotation.computer.Display] is defined: expected single matching bean but found 2: [lgDisplay, samSungDisplay]

那么如何解决根据类型返回多个bean导致的创建bean失败的问题呢?答案是指定需要注入的bean 的名称。要为Computer bean的display属性注入三星显示器,就需要告诉Spring我们需要的是符合Display类型,且id为“samSungDisplay”的bean。

指定依赖bean名称的方法有两种:

把属性名称做为依赖bean名称

l 使用 @Qualifier 注解明确指定依赖bean的名称,我们将在1.4.5小节讲解

将display属性改名为“samSungDisplay”,当Spring 应用容器根据属性类型返回多个bean时,会继续以反射的方式获得属性的名称 “samSungDisplay”,从而找到同名的bean。请看示例1.26:

示例1.26

[java]  view plain  copy
  1. @Component  
  2. @Scope("prototype")  
  3. public class Computer {  
  4.     @Autowired  
  5.     private MainFrame mainFrame;// 主机  
  6. @Autowired  
  7.     private Display samSungDisplay;// 显示器接口  
  8.   
  9.     // 输出计算机配置信息  
  10.     public void printComputerInfo() {  
  11.         System.out.println("计算机配置如下:");  
  12.         mainFrame.printMainFrameInfo();// 输出主机信息  
  13.         samSungDisplay.printDisplayInfo();// 输出显示器信息  
  14.     }  
  15. }  

我们也可以使用setter方法,构造方法和其它的方法,根据参数类型和名称来注入依赖bean,请看示例1.27:

示例1.27

[java]  view plain  copy
  1. @Component  
  2. @Scope("prototype")  
  3. public class Computer {  
  4.     private MainFrame mainFrame;// 主机  
  5.     private Display display;// 显示器接口  
  6. @Autowired   
  7. //构造函数的变量名与容器中的对象名保持一致,否则需要@Qualifier指定注入 Bean 的名称  
  8.     public Computer(MainFrame mainFrame, Display samSungDisplay) {  
  9.         super();  
  10.         this.mainFrame = mainFrame;  
  11.         this.display = samSungDisplay;  
  12.     }  
  13.      ……  
  14. }  

1.1.1 使用 @Qualifier注解为自动注入指定依赖bean的名称

Spring 允许我们通过 @Qualifier 注解指定注入 Bean 的名称。

示例1.28

[java]  view plain  copy
  1. @Component  
  2. @Scope("prototype")  
  3. public class Computer {  
  4.     @Autowired  
  5.     private MainFrame mainFrame;// 主机  
  6. @Autowired  
  7. @Qualifier("samSungDisplay")  
  8.     private Display display;// 显示器接口  
  9.     ……  
  10. }  

使用@Qualifier注解为方法指定要注入的依赖bean的名称。

示例1.29 

[java]  view plain  copy
  1. @Component  
  2. @Scope("prototype")  
  3. public class Computer {  
  4.     private MainFrame mainFrame;// 主机  
  5.     private Display display;// 显示器接口  
  6. @Autowired  
  7. public Computer( MainFrame mainFrame,   
  8. @Qualifier("samSungDisplay") Display display ) {  
  9.         super();  
  10.         this.mainFrame = mainFrame;  
  11.         this.display = display;  
  12.     }  
  13.      ……  
  14. }  

总结,使用 @Autowired  注解自动装配依赖的过程如下:

1) 首先根据属性的类型(或方法、构造方法参数的类型)在Spring 应用容器中查找类型匹配的bean

2) 如果没有类型匹配的bean,抛出BeanCreationException;如果只有一个,则注入依赖,完成自动装配;如果不只一个,则继续执行步骤3;

3) 如果通过 @Qualifier指定了bean 名称,则从所有符合类型的bean中返回指定的bean,完成自动装配;如果没有通过 @Qualifier制定bean 名称,则通过反射技术获取当前属性的名称作为bean 名称返回指定的bean,完成自动装配;

1.1.1 使用 @Resource 注解注入依赖

Spring 不但支持自己定义的@Component、@Scope、 @Autowired、 @Qualifier注解,还支持几个由 JSR-250 规范定义的注解,它们分别是 @Resource、@PostConstruct 以及 @PreDestroy。

@Resource 的作用相当于 @Autowired,只不过 @Autowired 默认按 byType 自动注入,面 @Resource 默认按 byName 自动注入罢了。@Resource 有两个属性是比较重要的,分别是 name 和 type,Spring 将@Resource 注解的 name 属性解析为 Bean 的名字,而 type 属性则解析为 Bean 的类型。所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性,这时将通过反射机制使用 byName 自动注入策略。

@Resource执行结果,即使name不一致也可以注入,就是按照类型了。

请看示例1.30:

示例1.30

[java]  view plain  copy
  1. @Component  
  2. @Scope("prototype")  
  3. public class Computer {  
  4. @Resource  
  5.     private MainFrame mainFrame;//采用属性名称作为依赖bean的名称  
  6.   
  7. @Resource(name=”samSungDisplay”)  
  8.     private Display display;// 采用@Resource注解name参数值作为依赖bean的名称  
  9.   
  10.      ……  
  11. }  

1.1.1 @PostConstruct 和 @PreDestroy

Spring 容器中的 Bean 是有生命周期的Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作。JSR-250 为初始化之后/销毁之前指定执行方法定义了两个注解类,分别是 @PostConstruct 和 @PreDestroy,这两个注解只能应用于方法上。标注了 @PostConstruct 注解的方法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。请看示例1.31:

示例1.31

[java]  view plain  copy
  1. @Component  
  2. @Scope("prototype")  
  3. public class Computer {  
  4.      ……  
  5. @PostConstruct  
  6. public void postConstruct1(){  
  7.          System.out.println("执行postConstruct1");  
  8.       }  
  9.   
  10.  @PreDestroy  
  11. public void preDestroy1(){  
  12.          System.out.println("执行preDestroy1");   
  13.       }  
  14.   
  15. }  

执行示例1.31,在bean被初始化时,会输出“执行postConstruct1”。在bean被销毁时,会输出“执行preDestroy1”。我们通常可以在标注了 @PostConstruct的方法中完成一些资源初始化的工作,在 标注了 @PreDestroy 的方法中完成一些释放资源的操作。注意的是@PreDestroy要在容器关闭之前,所以要使用以下代码初始化容器:

[java]  view plain  copy
  1. ConfigurableApplicationContext context =   
  2. new ClassPathXmlApplicationContext(“applicationContext”);  
  3. //释放容器资源,会调用@PreDestroy注解的方法  
  4. context.close();[新添加]  
  5. 或者  
  6. ClassPathXmlApplicationContext context =   
  7. new ClassPathXmlApplicationContext(“applicationContext”);  
  8. context.destory();  

执行示例1.31,在bean被初始化时,会输出“执行postConstruct1”。在bean被销毁时,会输出“执行preDestroy1”。我们通常可以在标注了 @PostConstruct的方法中完成一些资源初始化的工作,在 标注了 @PreDestroy 的方法中完成一些释放资源的操作。注意的是@PreDestroy要在容器关闭之前,所以要使用以下代码初始化容器:

 

ConfigurableApplicationContext context =

new ClassPathXmlApplicationContext(“applicationContext”);

//释放容器资源,会调用@PreDestroy注解的方法

context.close();

或者

ClassPathXmlApplicationContext context =

new ClassPathXmlApplicationContext(“applicationContext”);

context.destory();

注意:如果Bean类配置了@Scope("prototype")则不会调用@PreDestory注解的方法.


本章总结

 

 Spring框架

 Spring 是用于简化企业应用程序开发过程的开源框架

 Spring 的核心是一个控制反转容器

 Spring 提供了AOP实现

 Spring 提供了常用WEB框架整合的支持

 Spring 提供了对ORM技术整合的支持

 Spring Bean 封装机制

 Spring以Bean的方式管理所有的组件

 Spring包括两种不同的容器:BeanFactory 和 ApplicationContext

 依赖注入

 设置注入

 构造器注入

 自动注入

 集合注入

 基于注解配置应用容器

 @Component

 @scope

 @Autowired

 @Qualifier

  @Resource

 @PostConstruct 和 @PreDestroy。


任务实训部分

 

1: 使用Spring获取Bean对象

训练技能点

 Spring Bean的管理

 Bean的配置

 Spring容器的创建

需求说明

我们在配置电脑的时候,不同型号对应不同的硬盘,先要求编写Spring程序,能够根据不同的硬盘型号,得到不同的硬盘实例。

实现思路

(1) 定义硬盘接口。

(2) 定义不同型号的硬盘实现类。

(3) Spring配置文件中配置硬盘实现类。

(4) 创建ClassPathXmlApplicationContext并获取硬盘对象。

关键代码

(1) 定义硬盘接口。

public interface HardDisk {

public void printHardDiskInfo();//输出硬盘信息

}

(2) 定义两个硬盘实现类。

//希捷硬盘

public class SeaGateHardDisk implements HardDisk {

public void printHardDiskInfo() {

System.out.println("希捷160G硬盘");

}

}

//三星硬盘

public class SamSungHardDisk implements HardDisk {

public void printHardDiskInfo() {

System.out.println("三星250G硬盘");

}

}

(3) 使用<bean>标签对三星硬盘、希捷硬盘进行配置,三星硬盘的标识为samSungHardDisk,希捷硬盘的标识为seaGateHardDisk,配置文件中的的关键代码如下所示。

//三星硬盘的配置

<bean id="samSungHardDisk" class="bean.SamSungHardDisk"/>

//希捷硬盘的配置

<bean id="seaGateHardDisk" class="bean.SeaGateHardDisk"/>

(4) 编写测试类测试。

ApplicationContext context =

       new ClassPathXmlApplicationContext("applicationContext.xml");

// 获取三星硬盘

HardDisk hardDisk = (HardDisk) context.getBean("samSungHardDisk");

hardDisk.printHardDiskInfo();

// 获取希捷硬盘

hardDisk = (HardDisk) context.getBean("seaGateHardDisk");

hardDisk.printHardDiskInfo();

2: 设值注入实现打印机组装

训练技能点

 Spring设值注入

需求说明

打印机由墨盒和纸张组成,墨盒分彩色墨盒和黑白墨盒,纸张有A3纸和A4纸。我们在打印的时候,经常切换墨盒和纸张,现要求编写一个Spring程序组装一台打印机并输出组成该打印机的纸张和墨盒信息。

实现思路

(1) 定义墨盒接口(Ink)和纸张接口(Paper),Ink接口中声明方法 printInkInfo()输出墨盒信息,Paper接口中声明方法printPaperInfo()输出纸张信息。

(2) 定义打印机类(Printer)。打印机类中包含方法printerInfo(),输出打印机的配置。

(3) 定义墨盒实现类(ColorInk和GrayInk)和纸张实现类(A3Paper和A4Paper)。

(4) Spring配置文件,先配置彩色墨盒和黑白墨盒,然后配置A3纸和A4纸,最后配置打印机,使用设值方式在打印机中注入墨盒和纸张。

(5) 编写测试类输出打印机信息。

 

3: 使用构造注入重新组装打印机

训练技能点

 Spring构造注入

需求说明

任务实训1的基础上,使用构造注入重新组装打印机。

实现思路

(1) 在Printer类中增加带参构造方法。

(2) 修改配置文件,改为构造注入。

4: 使用ByName自动注入重新组装打印机

训练技能点

 Spring自动注入

需求说明

升级实训任务3,打印机包括硬盘、显示器、电源,要求使用Spring程序组装打印机,并输出打印机的配置信息。

实现思路

(1) 添加电源类Power,该类有一个方法printPowerInfo(),用于输出电源信息。

(2) 修改配置文件为ByName自动注入。

(3) 编写测试类进行测试。

关键代码

(1) 编写电源类:

public class Power {

private String type;// 电源型号

public void printPowerInfo() {

System.out.println("电源:" + type);

}

public String getType() {

return type;

}

public void setType(String type) {

this.type = type;

}

}

(2) 在打印机类中增加属性:

private Power power;// 电源

public Power getPower() {

return power;

}

public void setPower(Power power) {

this.power = power;

}

(3) 修改Spring配置文件,添加以下配置:

<bean id="power" class="bean.Power">

  <property name="type">

<value>长城</value>

  </property>

</bean>

<bean id="printer" class="bean.Printer" autowire="byName">

5: 使用Spring管理部门对象

训练技能点

 list元素的使用

需求说明

某公司销售部有三名员工,请编写Spring程序输出销售部的部门名称、该部门所有员工的员工姓名和员工地址。要求使用集合元素实现该需求。

实现思路

(1) 创建EmpVo类和DeptVo类。

(2) DeptVo类中定义List集合属性emps。

(3) 配置Spring文件。

(4) 编写测试类,输出给部门详细信息。

关键代码

(1) 员工信息包括员工姓名和员工地址,我们可以在员工类中增加员工姓名属性和员工地址属性。

public class EmpVo {

private String name;//员工名称

private String adress;//员工地址

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getAdress() {

return adress;

}

public void setAdress(String adress) {

this.adress = adress;

}

}

部门中可以包含多个员工,我们可以在部门类中增加两个属性,分别是部门名称和部门员工集合,关键代码如下所示。

private String deptName;// 部门名称

private List emps;// 部门员工集合

(2) 编写Spring配置文件,对部门的配置如下所示。

<bean id="dept" class="bean.DeptVo">

<property name="deptName">

<value>销售部</value>

</property>

<property name="emps">

<list>

<ref bean="emp0" />

<ref bean="emp1" />

</list>

        </property>

</bean>


巩固练习

一.选择题

1. 有关Spring,以下说法错误的是()。

   A. Spring是一个轻量级框架。

   B. Spring提供依赖注入容器、AOP实现、DAO/ORM支持、Web集成等功能。

   C. Spring的目标是使现有的技术变的更加好用。

   D. Spring不支持自动注入。

2. 以下关于依赖注入的选项中,说法正确的是()。

   A. 依赖注入的目标是在代码之外管理程序组件间的依赖关系

   B. 依赖注入就是“面向切面编程”

   C. 依赖注入是面向对象技术的替代品

   D. 依赖注入会增大程序的规模

3. 关于IOC的理解,以下说法正确的是()。

   A. 控制反转

   B. 对象被动接受依赖类

   C. 对象主动寻找依赖类

   D. 必须使用接口

4. 下列选项中,属于Spring依赖注入方式的是()。

   A. set方法注入

   B. 构造方法注入

   C. get方法注入

   D. 接口注入

5. 以下关于在Spring中配置Bean的id属性的说法中,正确的是()。

   A. id属性值可以重复

   B. id属性值不可以重复

   C. id属性是必需的,没有id属性时会报锗

D. id属性不是必需的

6. 下列哪一个注解用于声明一个bean()。

   A. @Resource

   B. @Component

   C. @Scope

D. @Qualifier

7. 下列关于使用注解来进行依赖注入的描述正确的是()。

   A.  @Autowired 默认按 byType 自动注入依赖

B. @Autowired 默认按 byName 自动注入依赖

   C. @Resource 默认按 byType 自动注入依赖

D. @Resource 默认按 byName 自动注入依赖

 

二.操作题

1.某程序系统中有如下几个类。

public class Equip {//装备

private String name;//装备名称

private String type;//装备类型,头盔、铠甲等

private Long speedPlus;//速度增效

private Long attackPlus;//攻击增效

private Long defencePlus;//防御增效

//Getters & Setters

. . .

}

public class Player {// 玩家

private Equip armet;// 头盔

private Equip loricae;// 铠甲

private Equip boot;// 靴子

private Equip ring;// 指环

// Getters & Setters

public void updateEquip(Equip equip) {

if ("头盔".equals(equip.getType())) {

System.out.println(armet.getName() + "升级为" + equip.getName());

this.armet = equip;

}

   . . .

}

}

根据以上信息,使用Spring DI配置一个拥有如下装备的玩家,如表1-3-1所示。

1-3-1 装备信息

装备

战神头盔

连环锁子甲

波斯追风靴

蓝魔指环

速度增效

2

6

8

8

攻击增效

4

4

2

12

防御增效

6

15

3

2

 

2.编写一个程序,实现对员工信息的CURD,要求DAO类中编写5个方法:

get(int id)、save(EmpVo emp)、delete(int id)、update(EmpVo emp)、findAll()方法,编写BIZ类,BIZ类中一个方法:doUpdate(EmpVo emp),方法功能为指定编号的员工如果存在,把薪水在原有基础上增加500;如果不存在,则提示该员工不存在,更改失败。使用Spring管理DAO和BIZ,并从Spring容器中获取BIZ实例进行测试。

提示:创建EmpBiz接口和实现类,在实现类中增加DAO属性。

public class EmpBizImpl implements EmpBiz {

private EmpDAO empDAO;

public void doUpdate(EmpVo emp)

{

//调用DAO类方法操作

}

//setter & getter

}

3. 自从世界上出现飞机以来,飞机的结构形式虽然在不断改进,飞机类型不断增多,但到目前为止,除了极少数特殊形式的飞机之外,大多数飞机都是由下面六个主要部分组成,即:两支机翼、一个机身、两支尾翼、一个起落装置、一个操纵系统和四个动力装置。请使用Spring的依赖注入组装一架飞机。

提示:

编写六个接口,分别对应机翼、机身、尾翼、起落装置、操纵系统、动力装置。在飞机类中增加两个机翼属性,分别表示左机翼和右机翼,尾翼和动力装置采用相同的处理。

 

4.移动公司某营业点需要一个会员账户管理系统,该系统需要实现以下功能

1)显示所有会员账户信息。

2)为指定的会员账户充值。

3)删除指定的账户。

4)禁用或者启用指定的账户。

5)根据状态查询会员账户。

系统首页参考界面如图1.3.1所示:

 

1.3.1 账户管理系统首页

当点击“充值”时,如果该会员状态为禁用状态,则给出“禁用状态的会员不能充值”提示,点击“启用”可以将会员状态更改为“启用状态”。参考界面如图1.3.2。

 

1.3.2 充值失败提示

会员充值界面如图1.3.3所示。

 

1.3.3会员充值界面

当会员充值成功之后,系统跳转至首页,显示当前最新会员信息。

点击“删除”将删除会员信息,删除成功之后,系统首页将显示最新的会员信息。

请根据以上需求完成以下内容:

该系统的数据库设计,DAO层的编写,业务逻辑层的实现,并使用Spring管理业务逻辑层对象。

提示:

在业务逻辑层增加DAO类属性,使用Spring在Biz中注入DAO,编写测试代码,对Biz中方法进行测试,不需要WEB层的开发。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值