Spring的基础操作、IOC(控制反转)以及DI(依赖注入)的原理
大名鼎鼎的Spring大家都听过,spring是一个开源框架,Spring为简化企业级应用开发而生。使用Spring可以使简单的JavaBean实现以前只有EJB才能实现的功能。Spring是一个DI(依赖注入)和AOP(面向切面编程)容器框架。
?
Spring具体应用细节,以及与struts和hibernater的整合。我们还没有学习到,今天算是Spring的基础操作和实现IOC(控制反转)以及DI(依赖注入)的原理。
?
?
一、spring框架
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如图 1 所示。
Spring框架概述
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
· 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
· Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
· Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
· Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
· Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
· Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
· Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
Spring 框架的功能可以用在任何 J2EE 服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE 服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web 或 EJB)、独立应用程序、测试环境之间重用。
?
我们今天的重点内容是——核心容器。
?
二、HelloSpring
这个“HelloWorld!”的经典程序,还要引领多长时间?可能会一直下去吧。我们现在就开始编写一个Spring的HelloWorld!程序,来学习一下Spring环境的搭建。
?
1.安装Spring的Eclipse插件。
2.新建立一个Java工程,在工程上右键->Spring Tools->Add Spring Project Nature。
3.导入“spring-framework-2.5.6.SEC01\dist\spring.jar”和“spring-framework-2.5.6.SEC01\lib\jakarta-commons\commons-logging.jar”。我们使用的Spring的版本是2.5,3.0刚刚出来。但3.0与2.5的区别不是很大。
4.在工程中添加“HelloSpring.java”:
package cn.itcast.cc.spring;public class HelloSpring {private String message ;// 构造函数public HelloSpring() {System. out .println( "HelloSpring,Construct!" );}// Spring通过此方法注入属性值public void setMessage(String message) {this . message = message;}// 打印消息public void hello() {System. out .println( "Hello:" + this . message );}}?
5.在“src”目录下,新建一个“applicationContext.xml”文件。(在“src”目录上右键->New->Other->Spring->Spring Bean Definition):
<? 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.xsd" ><!-- 描述一个Bean,name指定Bean的名称,class指定Bean类名 -->< bean name = "helloSpring" class = "cn.itcast.cc.spring.HelloSpring" ><!-- property设置属性值,name指定名称,value指定属性值 -->< property name = "message" value = "**Spring**" /></ bean ></ beans >?
6.添加测试类:
package cn.itcast.cc.spring;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {// 创建应用程序环境ApplicationContext appCon = new ClassPathXmlApplicationContext( "applicationContext.xml" );// 获取名称为helloSpring的BeanHelloSpring hs = (HelloSpring) appCon.getBean( "helloSpring" );// 调用Bean的方法hs.hello();}}?
7.控制台输出:
?
指定Bean的名称,可以使用name,也可以使用id。一般情况下建议使用id,因为id在XML文件中是一个特殊的属性,XML会自动校验ID的唯一性。
?
可能通过属性的setter方法或着Bean的构造函数设置属性的值,但setter是比较常用的。使用构造函数设置属性的值,使用“<constructor-arg?value="**Spring**"/>”替换“property”元素。注意一定要有与之对应的构造方法。
?
从刚开始的Dao接口到工厂模式再到Spring框架,都是为了实现更好的解耦。
三、Spring的配置文件
1.引用Bean
在应用中经常会遇见一个Bean实体,将另一个Bean实体做为自己的属性。那么这个属性值我们在XML文件中应该如何指定呢?Property有一个ref属性,ref属性的值设置为指定Bean元素的名称即可。
?
2.内部Bean
当一个Bean实体只被用作另一个Bean的属性时。我们可以将这个Bean声明为内部Bean。内部Bean无须设置它的id或name值,它被做为子元素嵌入在对应property元素中。
?
3.继承式Bean
bean元素中“depends-on”属性用来指定父类。
Spring 允许将通用的 Bean 配置抽象出来, 组成一个父 Bean。 继承这个父 Bean 的 Bean 称为子 Bean。
子 Bean 从父 Bean 中继承配置, 包括 Bean 的属性和在 <bean> 元素里的属性。
子 Bean 也可以覆盖从父 Bean 继承过来的配置。
父 Bean 可以作为配置模板, 也可以作为 Bean 实例。 若只想把父 Bean 作为模板, 可以设置 <bean> 的abstract 属性为 true, 这样 Spring 将不会实例化这个 Bean。
并不是 <bean> 元素里的所有属性都会被继承。 比如: autowire, dependency-check, abstract 等。
也可以忽略父 Bean 的 class 属性, 让子 Bean 指定自己的类, 而共享相同的属性配置。 但此时 abstract 必须设为 true。
?
4.设置集合属性
经常见到Bean具有集合属性,在hibernate的XML文件中也可以设置集合属性的值。
在 Spring 中可以通过一组内置的 xml 标签(例如: <list>, <set> 或 <map>) 来配置集合属性。
需求: 序列号生成器需要使用多个后缀。 这些后缀使用 “-” 作为分割, 附加在序列号后边。 这些后缀可以是任意的数据类型, 在拼接时将它们转换为字符串。
也可以使用“utility scheme”工具,定义可重用的集合设置,引时xml文件需要引入特定的标签库,参照下面的内容。
?
5.检查属性
设置Bean的“dependency-check="objects"”属性,检查bean中的所有属性是否通过setter方法注入了值。如果是通过构造方法注入,或没有注入都会抛异常。Dependency-check不检查属性值是否合法。
在bean中设置“dependency-check="objects"”属性,它会检查所有属性。如果我们想要检查某一个属性怎么办?首先添加一个:
HelloSpring,Construct!
Hello:**Spring**
或
使用“<context:annotation-config?/>”时,beans的元素属性内容为:
<bean /><context:annotation-config /><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"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-2.5.xsd ">?
然后在Bean的setter方法上添加“@Required”注解,这样Spring将检查被标“@Required”注解的属性。
如果使用的是JRE1.5需要添加“spring-framework-2.5.6.SEC01\lib\j2ee\common-annotations.jar”jar。
?
6.Bean 自动装配
Spring IOC 容器可以帮助自动装配 Bean。需要做的仅仅是在 <bean> 的 autowire 属性里指定自动装配的模式。
byType(根据类型自动装配): 若IOC 容器中有多个与目标Bean类型一致的Bean。在这种情况下, Spring 将无法判定哪个Bean最合适该属性,所以不能执行自动装配。
byName(根据名称自动装配): 必须将目标Bean的名称和属性名设置的完全相同。
constructor(通过构造器自动装配): 当Bean中存在多个构造器时, 此种自动装配方式将会很复杂。 不推荐使用。
?
四、Spring的注解
通过Xml文件配置应用程序是之前流行的方式,但不够直观和便利。现在流行使用注解了。在Spring2.5(包括2.5)之后就开始支持注解了。
?
使用注解时,我们只需要将Spring的配置文件内容设置为:
<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"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-2.5.xsd "> <context:annotation-config /></beans>?
?
?
“@Component”: 基本注解, 标识了一个受 Spring 管理的组件。
“@Respository”: 标识持久层组件。
“@Service”: 标识服务层(业务层)组件。
“@Controller”: 标识表现层组件。
?
“@Autowired”自动装配具有兼容类型的单个 Bean属性。默认情况下, 所有使用 @Authwired 注解的属性都需要被设置。 当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常, 若某一属性允许不被设置, 可以设置 @Authwired 注解的 required 属性为 false。
构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用 @Authwired 注解。
它可以应用在数组类型的属性上, 此时 Spring 将会把所有匹配的 Bean 进行自动装配。
它可以应用在集合属性上, 此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 Bean。
它用在 java、util、Map上时, 若该 Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型兼容的 Bean, 此时 Bean 的名称作为键值。
默认情况下, 当 IOC 容器里存在多个类型兼容的 Bean 时, 通过类型的自动装配将无法工作。 此时可以在 @Qualifier 注解里提供 Bean 的名称。但该注解只能用于修饰字段, 不能用于修饰方法
?
“@Resource”通过名称自动装配 Bean 属性,若没有名称一致的装配类型一致的。
构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用@ Resource 注解。
默认情况下,Spring将试着找出和属性名称相同的Bean。也可以在@Resource注解的name属性里指定 Bean 的名称。
?
五、Spring高级特性
1.Bean的作用域
如果不为指定Bean的作用域,Spring默认(scope="singleton")只维护一个Bean实例,所有调用同一getBean(name)的位置都返回同一实例。设置Bean的作用域为“scope="property"”Spring都返回一个对应Bean的新的实例。
?
2.Bean的初始化和销毁过程
在企业开发中,组件在使用之前往往需要执行一些特定类型的初始化任务,其中包括打开文件,打开网络/数据库连接,分配内存等。同样,当组件结束其生命周期时,也需要执行与之对应的销毁任务。
?
设置Bean的初始化和销毁过程。“@PostConstruct”调用Bean的初始化过程,当Bean创建后并设置属性值后会调标注了此注解的方法。“@PreDestroy”调用Bean的销毁过程,当Bean被销前会调用标注了此注解的方法。
?
注意:当Spring只维护一个Bean实例时,Bean被销前会调用标注了“@PreDestroy”注解的方法,否则不会调用。
?
3.Bean 后置处理器
Bean后置处理器允许在调用初始化方法前后对Bean进行额外的处理。
Bean后置处理器对IOC容器里的所有Bean实例逐一处理,而非单一实例。其典型应用是:检查Bean属性的正确性或根据特定的标准更改Bean的属性。
对Bean后置处理器而言,需要实现接口“BeanPostProcessor”。在初始化方法被调用前后,Spring将把每个Bean实例分别传递给上述接口的以下两个方法:
“postProcessAfterInitialization”和“postProcessBeforeInitialization”。
?
SpringIOC容器对Bean的生命周期进行管理的过程:
1).通过构造器或工厂方法创建Bean实例
2).为Bean的属性设置值和对其他Bean的引用
3).将Bean实例传递给Bean后置处理器的postProcessBeforeInitialization方法
4).调用Bean的初始化方法
5).将Bean实例传递给Bean后置处理器的postProcessAfterInitialization方法
6).Bean可以使用了
7).当容器关闭时,调用Bean的销毁方法
?
注册Bean后置处理器,只需要在配置文件里声明声明一个该处理器的实例,ApplicationContext会探测到实现了BeanPostProcessor接口的Bean并进行注册。
若初始化和销毁方法是通过添加生命周期注解@PostConstructor和@PreDestory实现的,则自定义的Bean后置处理器将无法工作。其原因是CommonAnnotationBeanPostProcessor的优先级比自定义的Bean后置处理器的优先级高。结果在对路径检查之前初始化方法已经被调用。
要定义Bean后置处理器的处理顺序,需要让Bean后置处理器的实例实现Ordered或者PriorityOrdered接口,在getOrder()方法中返回顺序值。该方法返回的数值越低优先级越高。另:PriorityOrdered接口返回的顺序值总优先于Ordered接口返回的顺序值,而CommonAnnotationBeanPostProcessor实现了PriorityOrdered接口。
4.外部化Bean的配置
在配置文件里配置Bean时,有时需要在Bean的配置里混入系统部署的细节信息(例如:文件路径,数据源配置信息等)。而这些部署细节实际上需要和Bean配置相分离。
?
Spring提供了一个PropertyPlaceholderConfigurer的BeanFactory后置处理器,这个处理器允许用户将Bean配置的部分内容外移到属性文件中。可以在Bean配置文件里使用形式为${var}的变量,PropertyPlaceholderConfigurer从属性文件里加载属性,并使用这些属性来替换变量。
?
在Spring2.0中注册PropertyPlaceholderConfigurer:
<bean value="c3p0.properties" /></bean>?
?
在Spring2.5中可通过<context:property-placeholder>,简化注册PropertyPlaceholderConfigurer:
<context:property-placeholder location="classpath:c3p0.properties"/>
此时beans的属性内容为:
<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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsd ">
?