Spring的IOC(控制反转)和 DI(依赖注入)机制
2014-12-01 17:02
751 查看
控制反转:就是将生成实现类放入IOC容器中进行统一管理,这样,我们可以对生成对象进行集中控制,实现高内聚低耦合。
IOC容器控制方式:
一、准备配置文件:就像前边HelloWorld配置文件一样,在配置文件中声明Bean定义也就是为Bean配置元数据。
二、由IoC容器进行解析元数据: IoC容器的BeanReader读取并解析配置文件,根据定义生成BeanDefinition配置元数据对象,IoC容器根据BeanDefinition进行实例化、配置及组装Bean。
三、实例化IoC容器:由客户端实例化容器,获取需要的Bean。
小结:
除了测试程序的代码外,也就是程序入口,所有代码都没有出现Spring任何组件,而且所有我们写的代码没有实现框架拥有的接口,因而能非常容易的替换掉Spring,是不是非入侵。
客户端代码完全面向接口编程,无需知道实现类,可以通过修改配置文件来更换接口实现,客户端代码不需要任何修改。是不是低耦合。
如果在开发初期没有真正的实现,我们可以模拟一个实现来测试,不耦合代码,是不是很方便测试。
Bean之间几乎没有依赖关系,是不是很容易重用。
为什么要应用依赖注入,应用依赖注入能给我们带来哪些好处呢?
l 动态替换Bean依赖对象,程序更灵活:替换Bean依赖对象,无需修改源文件:应用依赖注入后,由于可以采用配置文件方式实现,从而能随时动态的替换Bean的依赖对象,无需修改java源文件;
l 更好实践面向接口编程,代码更清晰:在Bean中只需指定依赖对象的接口,接口定义依赖对象完成的功能,通过容器注入依赖实现;
l 更好实践优先使用对象组合,而不是类继承:因为IoC容器采用注入依赖,也就是组合对象,从而更好的实践对象组合。
Ø 采用对象组合,Bean的功能可能由几个依赖Bean的功能组合而成,其Bean本身可能只提供少许功能或根本无任何功能,全部委托给依赖Bean,对象组合具有动态性,能更方便的替换掉依赖Bean,从而改变Bean功能;
Ø 而如果采用类继承,Bean没有依赖Bean,而是采用继承方式添加新功能,,而且功能是在编译时就确定了,不具有动态性,而且采用类继承导致Bean与子Bean之间高度耦合,难以复用。
l 增加Bean可复用性:依赖于对象组合,Bean更可复用且复用更简单;
l 降低Bean之间耦合:由于我们完全采用面向接口编程,在代码中没有直接引用Bean依赖实现,全部引用接口,而且不会出现显示的创建依赖对象代码,而且这些依赖是由容器来注入,很容易替换依赖实现类,从而降低Bean与依赖之间耦合;
l 代码结构更清晰:要应用依赖注入,代码结构要按照规约方式进行书写,从而更好的应用一些最佳实践,因此代码结构更清晰。
注入方式:
注入工厂类中 如果 是静态注入,则无需注册该类
否则先注册该类,之后才能进行注入
静态工厂类Bean定义配置文件(chapter3/staticFactoryDependencyInject.xml)
实例工厂类Bean定义配置文件(chapter3/instanceFactoryDependencyInject.xml)
实例工厂类Bean定义配置文件(chapter3/instanceFactoryDependencyInject.xml)
延迟初始化Bean
延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始化Bean。
配置方式很简单只需在<bean>标签上指定 “lazy-init” 属性值为“true”即可延迟初始化Bean。
Spring容器会在创建容器时提前初始化“singleton”作用域的Bean,“singleton”就是单例的意思即整个容器每个Bean只有一个实例,后边会详细介绍。Spring容器预先初始化Bean通常能帮助我们提前发现配置错误,所以如果没有什么情况建议开启,除非有某个Bean可能需要加载很大资源,而且很可能在整个应用程序生命周期中很可能使用不到,可以设置为延迟初始化。
延迟初始化的Bean通常会在第一次使用时被初始化;或者在被非延迟初始化Bean作为依赖对象注入时在会随着初始化该Bean时被初始化,因为在这时使用了延迟初始化Bean。
容器管理初始化Bean消除了编程实现延迟初始化,完全由容器控制,只需在需要延迟初始化的Bean定义上配置即可,比编程方式更简单,而且是无侵入代码的。
使用depends-on
depends-on是指指定Bean初始化及销毁时的顺序,使用depends-on属性指定的Bean要先初始化完毕后才初始化当前Bean,由于只有“singleton”Bean能被Spring管理销毁,所以当指定的Bean都是“singleton”时,使用depends-on属性指定的Bean要在指定的Bean之后销毁。
二、prototype:即原型,指每次向Spring容器请求获取Bean都返回一个全新的Bean,相对于“singleton”来说就是不缓存Bean,每次都是一个根据Bean定义创建的全新Bean。
单例存在于缓存之中,原型每次都会重新建立。
IOC容器控制方式:
一、准备配置文件:就像前边HelloWorld配置文件一样,在配置文件中声明Bean定义也就是为Bean配置元数据。
二、由IoC容器进行解析元数据: IoC容器的BeanReader读取并解析配置文件,根据定义生成BeanDefinition配置元数据对象,IoC容器根据BeanDefinition进行实例化、配置及组装Bean。
三、实例化IoC容器:由客户端实例化容器,获取需要的Bean。
小结:
除了测试程序的代码外,也就是程序入口,所有代码都没有出现Spring任何组件,而且所有我们写的代码没有实现框架拥有的接口,因而能非常容易的替换掉Spring,是不是非入侵。
客户端代码完全面向接口编程,无需知道实现类,可以通过修改配置文件来更换接口实现,客户端代码不需要任何修改。是不是低耦合。
如果在开发初期没有真正的实现,我们可以模拟一个实现来测试,不耦合代码,是不是很方便测试。
Bean之间几乎没有依赖关系,是不是很容易重用。
为什么要应用依赖注入,应用依赖注入能给我们带来哪些好处呢?
l 动态替换Bean依赖对象,程序更灵活:替换Bean依赖对象,无需修改源文件:应用依赖注入后,由于可以采用配置文件方式实现,从而能随时动态的替换Bean的依赖对象,无需修改java源文件;
l 更好实践面向接口编程,代码更清晰:在Bean中只需指定依赖对象的接口,接口定义依赖对象完成的功能,通过容器注入依赖实现;
l 更好实践优先使用对象组合,而不是类继承:因为IoC容器采用注入依赖,也就是组合对象,从而更好的实践对象组合。
Ø 采用对象组合,Bean的功能可能由几个依赖Bean的功能组合而成,其Bean本身可能只提供少许功能或根本无任何功能,全部委托给依赖Bean,对象组合具有动态性,能更方便的替换掉依赖Bean,从而改变Bean功能;
Ø 而如果采用类继承,Bean没有依赖Bean,而是采用继承方式添加新功能,,而且功能是在编译时就确定了,不具有动态性,而且采用类继承导致Bean与子Bean之间高度耦合,难以复用。
l 增加Bean可复用性:依赖于对象组合,Bean更可复用且复用更简单;
l 降低Bean之间耦合:由于我们完全采用面向接口编程,在代码中没有直接引用Bean依赖实现,全部引用接口,而且不会出现显示的创建依赖对象代码,而且这些依赖是由容器来注入,很容易替换依赖实现类,从而降低Bean与依赖之间耦合;
l 代码结构更清晰:要应用依赖注入,代码结构要按照规约方式进行书写,从而更好的应用一些最佳实践,因此代码结构更清晰。
注入方式:
注入工厂类中 如果 是静态注入,则无需注册该类
否则先注册该类,之后才能进行注入
//静态工厂类 package cn.javass.spring.chapter3; import cn.javass.spring.chapter2.helloworld.HelloApi; public class DependencyInjectByStaticFactory { public static HelloApi newInstance(String message, int index) { return new HelloImpl3(message, index); } }
静态工厂类Bean定义配置文件(chapter3/staticFactoryDependencyInject.xml)
<bean id="byIndex" class="cn.javass.spring.chapter3.DependencyInjectByStaticFactory" factory-method="newInstance"> <constructor-arg index="0" value="Hello World!"/> <constructor-arg index="1" value="1"/> </bean> <bean id="byType" class="cn.javass.spring.chapter3.DependencyInjectByStaticFactory" factory-method="newInstance"> <constructor-arg type="java.lang.String" value="Hello World!"/> <constructor-arg type="int" value="2"/> </bean> <bean id="byName" class="cn.javass.spring.chapter3.DependencyInjectByStaticFactory" factory-method="newInstance"> <constructor-arg name="message" value="Hello World!"/> <constructor-arg name="index" value="3"/> </bean>
//实例工厂类 package cn.javass.spring.chapter3; import cn.javass.spring.chapter2.helloworld.HelloApi; public class DependencyInjectByInstanceFactory { public HelloApi newInstance(String message, int index) { return new HelloImpl3(message, index); } }
实例工厂类Bean定义配置文件(chapter3/instanceFactoryDependencyInject.xml)
<bean id="instanceFactory" class="cn.javass.spring.chapter3.DependencyInjectByInstanceFactory"/> <bean id="byIndex" factory-bean="instanceFactory" factory-method="newInstance"> <constructor-arg index="0" value="Hello World!"/> <constructor-arg index="1" value="1"/> </bean> <bean id="byType" factory-bean="instanceFactory" factory-method="newInstance"> <constructor-arg type="java.lang.String" value="Hello World!"/> <constructor-arg type="int" value="2"/> </bean> <bean id="byName" factory-bean="instanceFactory" factory-method="newInstance"> <constructor-arg name="message" value="Hello World!"/> <constructor-arg name="index" value="3"/> </bean>
实例工厂类Bean定义配置文件(chapter3/instanceFactoryDependencyInject.xml)
延迟初始化Bean
延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始化Bean。
配置方式很简单只需在<bean>标签上指定 “lazy-init” 属性值为“true”即可延迟初始化Bean。
Spring容器会在创建容器时提前初始化“singleton”作用域的Bean,“singleton”就是单例的意思即整个容器每个Bean只有一个实例,后边会详细介绍。Spring容器预先初始化Bean通常能帮助我们提前发现配置错误,所以如果没有什么情况建议开启,除非有某个Bean可能需要加载很大资源,而且很可能在整个应用程序生命周期中很可能使用不到,可以设置为延迟初始化。
延迟初始化的Bean通常会在第一次使用时被初始化;或者在被非延迟初始化Bean作为依赖对象注入时在会随着初始化该Bean时被初始化,因为在这时使用了延迟初始化Bean。
容器管理初始化Bean消除了编程实现延迟初始化,完全由容器控制,只需在需要延迟初始化的Bean定义上配置即可,比编程方式更简单,而且是无侵入代码的。
使用depends-on
depends-on是指指定Bean初始化及销毁时的顺序,使用depends-on属性指定的Bean要先初始化完毕后才初始化当前Bean,由于只有“singleton”Bean能被Spring管理销毁,所以当指定的Bean都是“singleton”时,使用depends-on属性指定的Bean要在指定的Bean之后销毁。
package cn.javass.spring.chapter3.bean; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class ResourceBean { private FileOutputStream fos; private File file; //初始化方法 public void init() { System.out.println("ResourceBean:========初始化"); //加载资源,在此只是演示 System.out.println("ResourceBean:========加载资源,执行一些预操作"); try { this.fos = new FileOutputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } } //销毁资源方法 public void destroy() { System.out.println("ResourceBean:========销毁"); //释放资源 System.out.println("ResourceBean:========释放资源,执行一些清理操作"); try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } public FileOutputStream getFos() { return fos; } public void setFile(File file) { this.file = file; } }
package cn.javass.spring.chapter3.bean; import java.io.IOException; public class DependentBean { ResourceBean resourceBean; public void write(String ss) throws IOException { System.out.println("DependentBean:=======写资源"); resourceBean.getFos().write(ss.getBytes()); } //初始化方法 public void init() throws IOException { System.out.println("DependentBean:=======初始化"); resourceBean.getFos().write("DependentBean:=======初始化=====".getBytes()); } //销毁方法 public void destroy() throws IOException { System.out.println("DependentBean:=======销毁"); //在销毁之前需要往文件中写销毁内容 resourceBean.getFos().write("DependentBean:=======销毁=====".getBytes()); } public void setResourceBean(ResourceBean r e7b7 esourceBean) { this.resourceBean = resourceBean; } }
<bean id="resourceBean" class="cn.javass.spring.chapter3.bean.ResourceBean" init-method="init" destroy-method="destroy"> <property name="file" value="D:/test.txt"/> </bean> <bean id="dependentBean" class="cn.javass.spring.chapter3.bean.DependentBean" init-method="init" destroy-method="destroy" depends-on="resourceBean"> <property name="resourceBean" ref="resourceBean"/> </bean>
package cn.javass.spring.chapter3; import java.io.IOException; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.javass.spring.chapter3.bean.DependentBean; public class MoreDependencyInjectTest { @Test public void testDependOn() throws IOException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("chapter3/depends-on.xml"); //一点要注册销毁回调,否则我们定义的销毁方法不执行 context.registerShutdownHook(); DependentBean dependentBean = context.getBean("dependentBean", DependentBean.class); dependentBean.write("aaa"); } }
Bean的作用域
一、singleton:指“singleton”作用域的Bean只会在每个Spring IoC容器中存在一个实例,而且其完整生命周期完全由Spring容器管理。对于所有获取该Bean的操作Spring容器将只返回同一个Bean。package cn.javass.spring.chapter3; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.config.SingletonBeanRegistry; public class SingletonBeanRegister implements SingletonBeanRegistry { //单例Bean缓存池,此处不考虑并发 private final Map<String, Object> BEANS = new HashMap<String, Object>(); public boolean containsSingleton(String beanName) { return BEANS.containsKey(beanName); } public Object getSingleton(String beanName) { return BEANS.get(beanName); } @Override public int getSingletonCount() { return BEANS.size(); } @Override public String[] getSingletonNames() { return BEANS.keySet().toArray(new String[0]); } @Override public void registerSingleton(String beanName, Object bean) { if(BEANS.containsKey(beanName)) { throw new RuntimeException("[" + beanName + "] 已存在"); } BEANS.put(beanName, bean); } }
二、prototype:即原型,指每次向Spring容器请求获取Bean都返回一个全新的Bean,相对于“singleton”来说就是不缓存Bean,每次都是一个根据Bean定义创建的全新Bean。
package cn.javass.spring.chapter3; public class BeanDefinition { //单例 public static final int SCOPE_SINGLETON = 0; //原型 public static final int SCOPE_PROTOTYPE = 1; //唯一标识 private String id; //class全限定名 private String clazz; //作用域 private int scope = SCOPE_SINGLETON; //鉴于篇幅,省略setter和getter方法; }
package cn.javass.spring.chapter3; import java.util.HashMap; import java.util.Map; public class BeanDifinitionRegister { //bean定义缓存,此处不考虑并发问题 private final Map<String, BeanDefinition> DEFINITIONS = new HashMap<String, BeanDefinition>(); public void registerBeanDefinition(String beanName, BeanDefinition bd) { //1.本实现不允许覆盖Bean定义 if(DEFINITIONS.containsKey(bd.getId())) { throw new RuntimeException("已存在Bean定义,此实现不允许覆盖"); } //2.将Bean定义放入Bean定义缓存池 DEFINITIONS.put(bd.getId(), bd); } public BeanDefinition getBeanDefinition(String beanName) { return DEFINITIONS.get(beanName); } public boolean containsBeanDefinition(String beanName) { return DEFINITIONS.containsKey(beanName); } }
package cn.javass.spring.chapter3; import org.springframework.beans.factory.config.SingletonBeanRegistry; public class DefaultBeanFactory { //Bean定义注册表 private BeanDifinitionRegister DEFINITIONS = new BeanDifinitionRegister(); //单例注册表 private final SingletonBeanRegistry SINGLETONS = new SingletonBeanRegister(); public Object getBean(String beanName) { //1.验证Bean定义是否存在 if(!DEFINITIONS.containsBeanDefinition(beanName)) { throw new RuntimeException("不存在[" + beanName + "]Bean定义"); } //2.获取Bean定义 BeanDefinition bd = DEFINITIONS.getBeanDefinition(beanName); //3.是否该Bean定义是单例作用域 if(bd.getScope() == BeanDefinition.SCOPE_SINGLETON) { //3.1 如果单例注册表包含Bean,则直接返回该Bean if(SINGLETONS.containsSingleton(beanName)) { return SINGLETONS.getSingleton(beanName); } //3.2单例注册表不包含该Bean, //则创建并注册到单例注册表,从而缓存 SINGLETONS.registerSingleton(beanName, createBean(bd)); return SINGLETONS.getSingleton(beanName); } //4.如果是原型Bean定义,则直接返回根据Bean定义创建的新Bean, //每次都是新的,无缓存 if(bd.getScope() == BeanDefinition.SCOPE_PROTOTYPE) { return createBean(bd); } //5.其他情况错误的Bean定义 throw new RuntimeException("错误的Bean定义"); } public void registerBeanDefinition(BeanDefinition bd) { DEFINITIONS.registerBeanDefinition(bd.getId(), bd); } private Object createBean(BeanDefinition bd) { //根据Bean定义创建Bean try { Class clazz = Class.forName(bd.getClazz()); //通过反射使用无参数构造器创建Bean return clazz.getConstructor().newInstance(); } catch (ClassNotFoundException e) { throw new RuntimeException("没有找到Bean[" + bd.getId() + "]类"); } catch (Exception e) { throw new RuntimeException("创建Bean[" + bd.getId() + "]失败"); } } }
@Test public void testPrototype () throws Exception { //1.创建Bean工厂 DefaultBeanFactory bf = new DefaultBeanFactory(); //2.创建原型 Bean定义 BeanDefinition bd = new BeanDefinition(); bd.setId("bean"); bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bd.setClazz(HelloImpl2.class.getName()); bf.registerBeanDefinition(bd); //对于原型Bean每次应该返回一个全新的Bean System.out.println(bf.getBean("bean") != bf.getBean("bean")); }
单例存在于缓存之中,原型每次都会重新建立。
相关文章推荐
- 【转】跟我一起学Spring 3(4)–深入理解IoC(控制反转)和DI(依赖注入)
- (转)关于spring的两个概念:IoC(控制反转) ,DI(依赖注入)
- Spring 使用注解的方式实现IOC和DI(控制反转和依赖注入)
- Spring原理机制资源汇总(控制反转(IoC)与依赖注入(DI)主题)
- Spring_控制反转(IOC)/依赖注入(DI)
- Spring的IoC(控制反转) 、DI(依赖注入)
- Spring.net step by step——依赖注入(DI)和控制反转(IOC)
- Spring的IOC(控制反转)和DI(依赖注入)
- Spring学习—控制反转(IOC)Spring依赖注入(DI)和控制反转(IOC)
- Spring IoC和DI(反转控制和依赖注入)原理
- 【Spring】Spring的IOC(控制反转)/DI(依赖注入)原理(三):Spring启动加载配置文件源码分析
- 切面(aop)控制反转和依赖注入(IOC,DI)和spring的事务隔离和传播行为
- spring(3)------控制反转(IOC)/依赖注入(DI)
- Spring的Ioc(控制反转)和DI(依赖注入)
- Java面试-Spring IOC(控制反转)和DI(依赖注入)
- 依赖注入(DI)和控制反转(IOC)的详细讲解 spring容器(spring注解实现,而不是xml配置文件)
- Spring——DI(依赖注入)/Ioc(控制反转)
- Spring IoC(控制反转)和DI(依赖注入)的理解
- Spring 深入理解IOC(控制反转)和DI(依赖注入)
- Spring---IOC(控制反转)和DI(依赖注入)