传统代码严重依赖于实现而不是接口。 如:
public class OneAction(){
private UserDao dao = new UserJdbcDao();//严重依赖
privatte UserHibernateDao dao = new UserHibernateDao();
}
非常难以扩展,必须修改原代码。 数据库事务的控制与我们的代码交织在一块,难以维护。 或是数据库事务的代码粒度不够细。不能明确标明何种异常应该回滚事务。 如在这前的OSIV模式中,即使不是SQLException异常也会回滚事务。这就不够细粒度。
Spring 轻量、企业、对象容器、框架。
Spring是一个开源框架,是为了解决企业应用开发的复杂性而创建的。 Spring在软件开发中所占的位置是承上启下。 目的:解决企业应用开发的复杂性。 功能:使用基本的SpringBean代替EJB,并提供了更多的企业应用功能 。 范围:任何Java应用 。 简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
Spring简介:
轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。
非侵入 ——在应用中,一般不需要引用springjar包里的类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,某一接口的具体实现类的选择控制权从调用类中移除,转交给第三方裁决。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务()管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责其它的系统级关注点,例如日志或事务支持。
Spring即是一个容器又是一个框架。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。
框架——Spring提供了很多基础的与业务逻辑无关的功能,比如:事务管理、持久化框架集成等等,使用Spring框架,开发人员可以专注于业务逻辑开发,这个意义上讲它是一个框架。
为何要使用Spring?
至少在我看来,在项目中引入spring立即可以带来下面的好处 降低组件之间的耦合度,实现软件各层之间的解耦。
可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务.也不需处理复杂的事务传播。
容器提供单例模式支持,开发人员不再需要自己编写实现代码。
容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。
容器提供的众多辅作类,使用这些类能够加快应用的开发,如: JdbcTemplate、 HibernateTemplate。
Spring对于主流的应用框架提供了集成支持,如:集成Hibernate、JPA、Struts等,这样更便于应用的开发。
Spring作为容器如何管理你的类
Spring提供了两个类来管理系统中的类, 调用这两个类的某些方法即可以获取到被容器管理的类。 这两个类是:BeanFactory和ApplicationContext. 被Spring管理的不仅是JavaBean,可是任意的类,我们称它们为SpringBean。
Spring的包结构
Spring2.5的包结构。
支持JDK1.4。不支持泛型。必须进行强制类型转换。
Spring3.0的包结构。
Jdk1.5以上。 支持根据BeanID获取Bean实例,支持泛型。
支持根据类名-接口名获取实例。
支持表达式语言。
#{…} 支持新的线程池。
直接支持任务调度。
Spring3.x提供的Jar包说明
spring-context.jar:提供IoC高级功能,JNDI、EJB的支持等
spring-aop.jar:Spring的AOP框架
spring-dao.jar:提供了对数据库的访问抽象,提供了对JDBC和数据库事务的支持
spring-orm.jar:提供了对Hibernate, JDO, iBATIS和OJB的支持
spring-web.jar:提供了对web的支持
spring-webmvc.jar:Spring自己的Web框架
spring-mock.jar:提供了通过Mock方式进行测试的包
org.springframework.beans所有应用都要用到的,它包含访问配置文件、创建和管理bean,以及进行Inversion of Control / Dependency Injection(IoC/DI)操作相关的所有类 org.springframework.core 包含Spring框架基本的核心工具类,Spring其它组件要都要使用到这个包里的类,是其它组件的基本核心
org.springframework.asm Spring独立的asm程序
org.springframework.expression Spring表达式语言 org.springframework.context.support Spring提供在基础IoC功能上的扩展服务,此外还提供许多企业级服务的支持,如邮件服务、任务调度、JNDI定位、EJB集成、远程访问、缓存以及各种视图层框架的封装等
commons-logging Spring日志功能
Spring的两个重要容器类
BeanFactory还是(VS) ApplicationContext.
ApplicationContext是BeanFactory的子接口。
BeanFactory通常称为Bean容器。
ApplicationContext通常称为应用上下文。它的功能要多于BeanFactory,如它可以加载外部的资源文件、可以自动进行AOP切面、可以识别自动代理的的类、可以识别用于监听Bean创建的类等。
-以下是使用ApplicationContext的示例:
通过BeanFactory读取xml文件获取Bean:
理解IoC和DI:
IoC:(Inversion of Control,控制反转)控制反转就是应用程序本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责。这样控制权就由应用程序本身转移到了外部容器,控制权的转移就是所谓的反转。
依赖注入(Dependency Injection):就是在运行期,由外部容器动态的将依赖对象注入到组件中 。 或是在运行期,由外部容器决定具体执行的类代码。
//手工注入方式:
public class PersonServiceBean {
private PersonDao personDao = new PersonDaoBean();
public void save(Person person){
personDao.save(person);
}
}
PersonDaoBean 是在应用内部创建及维护的。如果需要扩展或是修改,就必须要修改应用程序本身的源代码,这不符合OC原则。OC原则是指:OpenClose原则,即开闭原则。是指对修改关闭,对扩展开放。见:<Java与模式>第四章。
理解依赖注入(Dependency Injection)
当我们把依赖对象交给外部容器负责创建,那么PersonServiceBean 类可以改成如下:
public class PersonServiceBean {
private PersonDao personDao ; //通过构造器参数,让容器把创建好的依赖对象注入进PersonServiceBean,当然也可以使用setter方法进行注入。
public PersonServiceBean(PersonDao personDao){
this.personDao=personDao;
}
public void save(Person person){
personDao.save(person);
}
}
所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中。
控制反转-让你类依赖接口而不是实际类, 在运行期间,由外部容器决定具体要执行的类:
配置你的Spring Bean:
由Spring容器管理所有Java类.
声明Bean.
构造器注入和setter注入。
装配Bean,注入List,Map,Set,Array,注入其他Bean.
自动装配。
控制Bean的创建和销毁。
Spring提供了两种容器的实现: (前面已经说过)
1、Bean工厂,由BeanFactory定义。是最简单的容器。
2、应用上下文,由ApplicationContext定义,建立在Bean工厂之上,提供了系统构架服务,如从属性文件中读取文本信息,向有关的事件监听器发布事件等。同时ApplicationContext是BeanFactory的子类。 两个容器都可以调用Bean的私有/公有构造方法实例化一个类。 它们两个的区别: BeanFactory只有当需要获取一个Bean时才会去创建这个Bean的实例。 ApplicationContext在启动时一次初始化所有Bean的实例、可以自动识别代理自动代理类、可以识别自动加载Bean等。
BeanFactory的实现类-XmlBeanFactory:
XmlBeanFactory是BeanFactory的子类。
XmlBeanFactory用于读取beans.xml的配置文件,它接收一个 Resource类的实现: 比较常用的资源定义为:
ClassPathResource - 从classpath中读取
FileSystemResource -从文件系统中读取
ServletContextResource - 必须要在web环境下使用
ApplicationContext
BeanFactory对于简单应用来说,已经非常好用。但为了获取Spring框架强大的功能,你需要使用Spring的高级容器--应用上下文。
ApplicationContext的实现类为:
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
XmlWebApplicationContext – 需要Spring-web包的支持。
工具类:WebApplicationContextUtils用于在Web环境下获取容器。
Bean的装配:
通过构造器注入。 - Inverse of Control (Dependens Injection)
DI 是指在类中声明一个类型的接口,在配置文件中由外部的容器在运行时设置具体的值。
Class person{ private String name = “wj” set…get….. }
<bean id=p class=“…”>
<proprety name=“name=“ value=“ddd/.”>
通过setter方法注入。
给Bean注入集合。
给Bean注入其他Bean.
自动装配。
通过构造器注入: (注入值类型和引用类型)
通过属性注入: (注入值类型和引用类型均可)
注入集合:
注入null值:
Bean的自动装配:
自动装配是指Spring根据指定的自动模式查找相关属性并自动装配。
Spring提供四种Bean的自动装配类型:
byName-试图在容器中查找与属性名称相同的Bean的id,如果没有找到,则此属性装配不成功。
byType-试图在容器中查询与属性类型相同的Bean,如果没有找到,则此属性装配不成功。如果找多个类型相符的Bean则会抛出UnsatisfiedDependencyException异常。
Constructor-查找与自动装配的Bean的构造参数一致的一个或多个Bean。如果存在不确定的Bean或是构造,将会抛出一个异常。
Autodetect -首先使用constructor来装配,然后再尝试用byType来装配。注意,到了spring3.0中,此项已经不再支持。
byName自动装配:
开发步骤:
开发Address类及接口IAddress。
开发Person类并声明Iaddress(接口)成员变量。
在配置文件中通过byName自动装配。
说明:如果没有找到合适的名称匹配,则属性配置不成功,将返回null值。(不会抛出异常)
byType自动装配:
说明:以下例当中,如果出现两个相同类型的属性则会在装配时抛出异常。
以下代码将会抛出一个异常。因为同时存在两个类型相同的Bean。
何时使用自动装配:
尽管自动装配给我们带来了很多方便,但同时也会带来很多问题。
如:按byType装配,不能在容器中多次声明一个Bean.
目前自动装配最大的缺点是缺乏透明。
由于自动装配隐藏了很多细节,所以一般情况下,我们不建议使用自动装配。
控制Bean的创建:
1、Bean的范围。
2、利用工厂方法创建Bean.
3、初始化和销毁Bean.
Bean的范围:
默认情况下,所有Spring的Bean都是单一的。即单例。
Bean的属性scope属性决定生成bean的方式:
Singleton – 单例,默认值 。
Prototype – 每一次请求创建一个新的实例。
Request – 在web容器中,每一个request创建一个。
Session – 在web容器中,第一个会话创建一个实例。 Global – session – Http会话,在portlet上才会有效。
利用工厂方法来创建Bean:
在某些情况下,如果你希望通过工厂方法来获取Bean的实例。则可以提供一个factory-method方法。 在创建之前,你可以在工厂方法中实现一些其他的细节。
一般情况下,工厂方法返回当前类的实例或是子类的实例。
初始化和销毁Bean:
在bean元素中,定义init-method和destory-method属性,可设置初始化方法和销毁方法。需要注意的是:如果scope属性为prototype则不受spring缓存的控制,destory方法也将不会执行。所以,如果希望由spring去调用销毁方法,则不能使用prototype类型的范围。
调用销毁方法:
如果需要Spring显示的去调用销毁方法,则必须销毁整个Spring容器,即关闭或是销毁BeanFactory或ApplicationContext。
上面两个接口中没有定义关闭方法,必须使用它们的子类才可以关闭容器。
容器的关闭,一般情况下不应由程序代码管理,应该交给第三方容器,如tomcat.
高级Bean装配:
父子Bean。
使用Spring的特殊Bean.
引用外部的资源文件。
声明父Bean和子Bean:
创建一个类来扩展另一个类,是面向对像的基础之一。
在Spring中,声明的两个Bean即使在实际类上没有继承关系,也是可以实现继承的。 S
pring的Bean为我们提供了两个属性可以实现继承。
Parent – 用于指明继承的Bean,类似于extends关键字。
Abstract - true|false. - 声明一个Bean为抽象Bean,使用了abstract关键字的类,不会被Spring容器实例化。
父子Bean示例:
1、我们首先声明了一个抽象类。
2、并在配置文件中使用了此类,和使用了 Abstract关键字。
父子Bean需要注意的问题:
在父Bean中定义的属性必须要在子Bean中出现。如果子Bean中没有定义相关属性,将会导致容器初始化失败。
使用Spring的特殊Bean
Spring容器在多大数情况下,会平等的对待所有Bean.
但有些Bean有特殊的任务,通过实现Spring的某些接口,可以将某些Bean当成Spring容器的一部分。
利用这些特殊的Bean我们可以:
对bean进行后处理,介入Bean的创建。
从外部资源文件加载信息。
让Bean了解容器。
配置属性的外在化:-经常用于数据连接
PropertyPlaceholderConfigurer类用于加载外部的资源文件。
<bean>
<property name=“locations”>
<list>
<value></value>
</list>
示例:使用外部的资源文件连接数据库的示例
引入外部的资源文件。
配置简单数据源。
让Bean了解容器:
运行在Spring中的Bean,就像是《黑客帝国》中的人类,无知便是福。
Spring的Bean不知道自己,也不需要知道自己运行在Spring的容器中。这通常是好事,因为一旦让Bean了解容器,那它就与Spring耦合了。从而可能无法在容器之外生存。
但在某些情况下,Bean需要了解自己在Spring中的名称,了解自己运行的环境。
通过实现BeanNameAware,BeanFactoryAware,ApplicationContextAware接口,可以让Bean了解它的名称、BeanFactory、ApplicationContext。
BeanNameAware
通过实现接口BeanNameAware的方法setBeanName可以获知自己的名称
ApplicationContextAware:
在某些情况下,Bean有时也需要访问程序的上下文。