一. Spring IoC容器概述
1. 依赖反转(依赖注入):依赖对象的获得被反转了 ,如果合作对象的引用或依赖关系的管理由具体对象来完成,会导致代码高度耦合和可测试性的降低,这对复杂的面向对象系统的设计是不利的。它是spring框架的核心。
2. 依赖控制反转的实现有多种方式。在Spring中,IoC(Dependency Inversion Principle)容器是实现这个模式的载体,他可以在对象生成或初始化时直接将数据注入到对象中,也可以将对象引用注入到对象数据域中的方式来注入对方法调用的依赖。这种依赖注入是可以递归的,对象被逐层注入。它把对象依赖关系有序地建立起来,简化了对象依赖关系的管理,简化了面向对象系统的复杂性。
3. 应用控制反转后,当对象被创建时,由一个调控系统(IoC容器)内所有对象的外界实体将其所依赖的对象引用传递给它,即依赖被注入到对象中。所以,控制反转是关于一个对象如何获取它所依赖的对象的引用,在这里反转值责任的反转。
因为对象依赖关系的建立和维护并不需要和系统运行状态有很强的关联性,所以这些散落在不同代码中的功能相同的部分就集中成为容器的一部分,也就是成为面向对象系统的基础设施的一部分。
4. 在spring中,spring IOC提供了一个基本的JavaBean容器,通过IoC模式管理依赖关系,并通过依赖注入和AOP切面增强为JavaBean这样的POJO对象赋予事物管理、生命周期管理等基本功能;
注: POJO(Plain Ordinary Java Object)即普通Java类,具有一部分getter/setter方法的那种类就可以称作POJO。实际意义就是普通的JavaBeans(简单的实体类),特点就是支持业务逻辑的协助类。POJO类的作用是方便程序员使用数据库中的数据表,对于程序员来说,可以很方便的将POJO类当作对象来进行使用,也可以方便的调用其get,set方法。但不允许有业务方法,也不能携带有connection之类的方法,即不包含业务逻辑或持久逻辑等。
JavaBean是一个可重复使用的软件组件。实际上JavaBean是一种Java类,通过封装属性和方法成为具有某种功能或者处理某个业务的对象,简称bean。JavaBean 是一种JAVA语言写成的可重用组件。它的方法命名,构造及行为必须符合特定的约定:这个类必须有一个公共的缺省构造函数。这个类的属性使用getter和setter来访问,其他方法遵从标准命名规范。这个类应是可序列化的。 因为这些要求主要是靠约定而不是靠实现接口,所以许多开发者把JavaBean看作遵从特定命名约定的POJO。
6. 具体的注入实现方式包括接口注入(Type 1 IoC),setter注入(Type 2 IoC),构造器注入(Type 3 IoC)。在spring的IoC设计中,setter注入与构造器注入是主要的注入方式。
7. Spring IoC容器的设计中,组要包括两个主要容器系列,一个是BeanFactory接口的简单容器系列,只实现容器的基本功能;另一个是ApplicationContext应用上下文,它作为容器高级形态存在,他在简单容器基础上增加许多面向框架的特性,同时对应用环境做了许多适配。
8. BeanFactory是作为最基本的接口类出现在在Spring的IoC容器体系中的。在此基础上Spring通过定义BeanDefinition来管理基于Spring应用中的各种对象以及他们之间的相互依赖关系,BeanDefinition抽象了对bean的定义,是让容器起作用的主要数据类型,它是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实习依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。
二. IoC容器的设计与实现
1. 接口设计图
1) 从接口BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,是一个主要的BeanFactory的设计路径。BeanFactory定义了基本的IoC容器规范,包括getBean()这样的IoC容器的基本方法。HierarchicalBeanFactory接口在继承了BeanFactory之后,增加了getParentBeanFactory()的接口功能,使BeanFactory具备了双亲IoC容器的管理功能。ConfigurableBeanFactory定义了BeanFactory的配置功能,比如通过setParentBeanFactory设置双亲IoC容器。
双亲IoC容器用途:通过HierarchicalBeanFactory接口,Spring的IoC容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的Bean,但父容器不能访问子容器的Bean。在容器内,Bean的id必须是唯一的,但子容器可以拥有一个和父容器id相同的Bean。父子容器层级体系增强了Spring容器架构的扩展性和灵活性,因为第三方可以通过编程的方式,为一个已经存在的容器添加一个或多个特殊用途的子容器,以提供一些额外的功能。
具体来说,Spring使用父子容器实现了很多功能,比如在Spring MVC中,展现层Bean位于一个子容器中,而业务层和持久层的Bean位于父容器中。这样,展现层Bean就可以引用业务层和持久层的Bean,而业务层和持久层的Bean则看不到展现层的Bean。
2) 第二条接口设计主线是以ApplicationContext应用上下文接口为核心的接口设计,从BeanFactory到ListableBeanFactory,再到ApplicationContext,再到我们常用的WebApplicationContext或者ConfigurableApplicationContext接口。我们常用的上下文基本都是WebApplicationContext或者ConfigurableApplicationContext的实现。在这个接口体系中,ListableBeanFactory和HierarchicalBeanFactory两个接口连接BeanFactory和ApplicationContext应用上下文接口定义。ListableBeanFactory细化了许多BeanFactory的接口定义,比如定义了getBeanDefinitionNames()接口方法。另外ApplicationContext通过继承MessageSource,ResourceLoader,ApplicationEventPublisher接口,在BeanFactory简单的IoC容器的基础上添加了许多高级容器的特性支持。
2. BeanFactory接口定义了IoC容器最基本的形式,并且提供了IoC容器所应遵守的最基本的的服务契约,同时,也是我们使用IoC容器所应遵守的最底层和最基本的编程规范,这些接口定义勾画出了IoC的基本轮廓。
注:要分辨BeanFactory 与 FactoryBean的区别, 两个名字很像,所以容易搞混
BeanFactory: 以Factory结尾,表示它是一个工厂类,是用于管理Bean的一个工厂
FactoryBean:以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,根据该Bean的Id从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身, 如果要获取FactoryBean对象,可以在id前面加一个&符号来获取。
3. spring中的Bean有两种(引用自http://blog.csdn.net/u014604403/article/details/50515588)
一种是普通的bean ,比如下面配置
<bean id="personService" class="com.spring.service.impl.PersonServiceImpl" scope="prototype">
<property name="name" value="is_zhoufeng" />
</bean>
使用BeanFactory根据id personService获取bean的时候,得到的对象就是PersonServiceImpl类型的。
另外一种就是实现了org.springframework.beans.factory.FactoryBean<T>接口的Bean , 那么在从BeanFactory中根据定义的id获取bean的时候,获取的实际上是FactoryBean接口中的getObject()方法返回的对象。以Spring提供的ProxyFactoryBean为例子,配置如下:
<bean id="personServiceByLog" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>com.spring.service.PersonService</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>logInteceptor</value>
<value>ZFMethodAdvice</value>
</list>
</property>
<property name="targetName" value="personService" />
</bean>
那么在代码中根据personServiceByLog来获取的Bean实际上是PersonService类型的。
如果要获取ProxyFactoryBean本身,可以如下
@Test
public void test04() {
ProxyFactoryBean factoryBean = context.getBean("&personServiceByLog", ProxyFactoryBean.class);
PersonService ps = (PersonService) factoryBean.getObject();
String name = ps.getName();
System.out.println(name);
}
4. FactoryBean的使用(引用自http://book.51cto.com/art/201311/419081.htm)
一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean 。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.Springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。
FactoryBean接口对于Spring框架来说占有重要的地位,Spring 自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring 3.0 开始, FactoryBean开始支持泛型,即接口声明改为FactoryBean<T> 的形式:
package org.Springframework.beans.factory;
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
在该接口中还定义了以下3个方法。
T getObject():返回由FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中。
boolean isSingleton():返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。
Class<T> getObjectType():返回FactoryBean创建的bean类型。
当配置文件中<bean>的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。例如:如果使用传统方式配置下面Car的<bean>时,Car的每个属性分别对应一个<property>元素标签。
public class Car {
private int maxSpeed ;
private String brand ;
private double price ;
//get/set方法
}
如果用FactoryBean的方式实现就会灵活一些,下例通过逗号分割符的方式一次性地为Car的所有属性指定配置值:
public class CarFactoryBean implements FactoryBean<Car> {
private String carInfo ;
public Car getObject () throws Exception {
Car car = new Car () ;
String [] infos = carInfo .split ( "," ) ;
car.setBrand ( infos [ 0 ]) ;
car.setMaxSpeed ( Integer. valueOf ( infos [ 1 ])) ;
car.setPrice ( Double. valueOf ( infos [ 2 ])) ;
return car;
}
public Class<Car> getObjectType () {
return Car. class ;
}
public boolean isSingleton () {
return false ;
}
public String getCarInfo () {
return this . carInfo ;
}
// 接受逗号分割符设置属性信息
public void setCarInfo ( String carInfo ) {
this . carInfocarInfo = carInfo;
}
}
有了这个CarFactoryBean后,就可以在配置文件中使用下面这种自定义的配置方式配置Car Bean了:<bean id="car" class="com.test.factorybean.CarFactoryBean" carInfo="超级跑车,400,2000000"/> 当调用getBean("car") 时,Spring通过反射机制发现CarFactoryBean实现了FactoryBean的接口,这时Spring容器就调用接口方法CarFactoryBean#getObject()方法返回。如果希望获取CarFactoryBean的实例,则需要在使用getBean(beanName) 方法时在beanName前显示的加上 "&" 前缀,例如getBean("&car")。
5. 工厂模式和修饰器模式
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。
修饰器模式: http://blog.csdn.net/genffe880915/article/details/38492031
疑问:修饰模式虽然避免了子类实现,但同样要实现相同功能类,但可以使主流程结构清晰。或者还有别的用处;