Spring part 1:IoC和DI
http://repo.springsource.org/libs-release-local/
这是Spring所包含的模块,但随着Spring的发展壮大,模块也是越来越多
传统的实例化对象方式是,我需要什么对象,我来new,SpringIoC实例化对象的思想是,你需要什么对象?告诉我,我给你(注入)。
注入的方式有三种:接口注入、方法注入、构造注入
SpringIoC职责:对象的构建与管理、对象间依赖绑定
管理对象间依赖方式:编码、配置文件、注解(元数据)
导入所需要的底层4个核心jar包
spring-beans-3.2.5.RELEASE.jar spring-context-3.2.5.RELEASE.jar spring-core-3.2.5.RELEASE.jar spring-expression-3.2.5.RELEASE.jar
还需要日志包
commons.logging-1.1.1.jar log4j-1.2.15.jar
创建两个配置文件
log4j.properties applictionContext.xml
工程目录结构如下
案例1:创建一个实例对象,将该对象交由SpringIoC容器管理,通过SpringIoC容器为其初始化
public class InstanceBean { public InstanceBean() { System.out.println("..."); } public void method() { System.out.println("InstanceBean--method()"); } }
在applicationContext.xml中配置InstanceBean
<?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 id="instanceBean" class="ioc.InstanceBean"></bean> </beans>
该配置文件中的schema可以通过官方文档找到,以Spring3.2.5为例
spring-framework-3.2.5.RELEASE\docs\spring-framework-reference\html\xsd-config.html
该文档中有所有关于Spring配置的xml约束,bean的约束在xsd-config.html的最下方
使用SpringIoC容器初始化InstanceBean
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:ioc/applictionContext.xml"); InstanceBean instance = context.getBean("instanceBean", InstanceBean.class); instance.getMethod();
我把applicationContext.xml放在了src下的ioc包中,所以路径前加上了“ioc/”,编译后的项目结构为
案例二:SpringIoC容器通过构造实例化对象,案例一中的方式是通过默认构造器实例化InstanceBean,也可以使用有参数的构造器实例化InstanceBean
public class InstanceBean { private String info; public InstanceBean() { System.out.println("..." + this); } public InstanceBean(String info) { this.info = info; } }
applicationContext.xml配置,采用index+value方式,index表示构造器中参数位置,value表示参数的值
<!-- 默认构造器 --> <bean id="instaceBean" class="ioc.InstanceBean"> </bean> <!-- 指定构造器 --> <bean id="constructor" class="ioc.InstanceBean"> <constructor-arg index="0" value="springIoC"></constructor-arg> </bean>
案例三:静态工厂初始化
创建一个工厂类,有两个静态方法,一个使用默认构造器,一个使用有参数的构造器
public class StaticFactory { public static InstanceBean getInstanceBean() { return new InstanceBean(); } public static InstanceBean getInstanceBeanPara(String info) { return new InstanceBean(info); } }
对静态工厂进行配置,使用SpringIoC初始化
<!-- 静态工厂 --> <bean id="staticFactory" class="ioc.StaticFactory" factory-method="getInstanceBean"> </bean> <!-- 静态工厂 有参数 --> <bean id="staticFactoryParam" class="ioc.StaticFactory" factory-method="getInstanceBeanPara"> <constructor-arg index="0" value="staticFacotryParam"></constructor-arg> </bean>
测试
@Test public void testIoCStaticFactory() { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:ioc/applicationContext.xml"); InstanceBean instance = context.getBean("staticFactory",InstanceBean.class); instance.getMethod(); } @Test public void testIoCStaticFactoryParam() { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:ioc/applicationContext.xml"); InstanceBean instance = context.getBean("staticFactoryParam",InstanceBean.class); System.out.println(instance.getInfo()); }
案例三:实例工厂
创建实例工厂类
public class InstanceFacotry { public InstanceBean getInstanceBean() { return new InstanceBean(); } }
配置时需要先配置实例工厂,然后通过factory-bean引用实例工厂的id,通过factory-method指定调用实例工厂中的哪个方法
<!-- 实例工厂 --> <bean id="instanceFactory" class="ioc.InstanceFacotry"></bean> <bean id="insFactory" factory-bean="instanceFactory" factory-method="getInstanceBean"></bean>
案例四:setter注入
创建一个包含setter()的类
public class InstanceBean { private String info; public InstanceBean() { System.out.println("..." + this); } public String getInfo() { return this.info; } public void setInfo(String info) { this.info = info; } }
配置
<!-- setter注入 --> <bean id="instanceBeanSetter" class="ioc.InstanceBean"> <property name="info" value="setInfo()"></property> </bean>
案例五:集合注入
创建一个类,包含常用的集合容器,生成setter(),使用SpringIoC容器注集合容器的内容
public class CollectionBean { private String[] arr; private String[][] array; private List<String> list; private Set<Integer> set; private Properties properties; private Collection<String> collection; private Map<String, String> map; }
配置集合注入
<!-- 集合注入 --> <bean id="collectionDI" class="ioc.CollectionBean"> <!-- 一维数组 --> <property name="arr"> <array> <value>arr1</value> <value>arr2</value> </array> </property> <!-- 二维数组 --> <property name="array"> <array> <array> <value>array1</value> </array> <array> <value>array2</value> </array> </array> </property> <!-- List集合 --> <property name="list"> <!-- value-type:可选,相当于泛型 --> <list value-type="java.lang.String" merge="default"> <value>list1</value> <value>list2</value> <value>list3</value> </list> </property> <!-- Set集合 --> <property name="set"> <set value-type="java.lang.Integer"> <value>1</value> <value>2</value> <value>3</value> </set> </property> <!-- Properties --> <property name="properties"> <props> <prop key="1">pop1</prop> <prop key="2">pop2</prop> </props> </property> <!-- Collection --> <property name="collection"> <set value-type="java.lang.String"> <value>collction1</value> <value>collction2</value> <value>collction3</value> </set> </property> <!-- Map集合 --> <property name="map"> <map key-type="java.lang.String" value-type="java.lang.String"> <!-- 复杂配置 --> <entry> <key> <value>key1</value> </key> <value>value1</value> </entry> <!-- 简化配置 --> <entry key="key2" value="value2"></entry> </map> </property> </bean>
案例六:depends-on配置,在依赖情况下,depends-on配置的bean要先与当前bean初始化,晚于当前bean销毁
创建Resources、Dependency两个类,Dependency依赖Resources
public class Resources { public Resources() { super(); System.out.println("Resources Constructor() "); } public void init(){ System.out.println("Resources init()"); } public void destroy(){ System.out.println("Resources destroy()"); } } public class Dependency { Resources resources; public Dependency() { super(); System.out.println("Dependency Constructor() "); } public void init() { System.out.println("Dependency init()"); } public void destroy() { System.out.println("Dependency destroy()"); } public Resources getResources() { return resources; } public void setResources(Resources resources) { this.resources = resources; } }
配置
<bean id="resources" class="ioc.Resources" init-method="init" destroy-method="destroy"></bean> <bean id="dependency" class="ioc.Dependency" init-method="init" destroy-method="destroy" depends-on="resources"> <property name="resources" ref="resources"></property> </bean>
测试时使用ClassPathXmlApplicationContext获得IoC容器,因为只有容器个关闭才能看到调用destroy()
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Dependency dependency = context.getBean("dependency",Dependency.class); context.close(); /**output: Resources Constructor() Resources init() Dependency Constructor() Dependency init() Dependency destroy() Resources destroy() */
案例七:单例bean
方式一:单例bean构造私有化
public class Singletion { private Singletion() { } public static class SingletionHandler { public static final Singletion INSTANCE = new Singletion(); } public static Singletion getInstance() { return SingletionHandler.INSTANCE; } }
方式二:将单例的bean通过唯一键注册到注册表,然后通过键值来获取,通过实现SingletonBeanRegistry接口来实现,不考虑多线程问题,Spring采用这种方式实现singleton
public class SingleonBeanRegister implements SingletonBeanRegistry { private final Map<String, Object> BEANS = new HashMap<String, Object>(); public void registerSingleton(String beanName, Object singletonObject) { if (BEANS.containsKey(beanName)) { throw new RuntimeException("[" + beanName + "] already exist"); } BEANS.put(beanName, singletonObject); } public Object getSingleton(String beanName) { return BEANS.get(beanName); } public boolean containsSingleton(String beanName) { return BEANS.containsKey(beanName); } public String[] getSingletonNames() { return BEANS.keySet().toArray(new String[0]); } public int getSingletonCount() { return BEANS.size(); } }
Spring配置方式,通过SpringIoC容器获得单例bean
定义bean
public class ScopeBean {}
配置,不配置scope时默认就是singleton
<bean id="singleton" class="bean.ScopeBean" scope="singleton"></bean>
测试
ScopeBean singleton1 = context.getBean("singleton",ScopeBean.class); ScopeBean singleton2 = context.getBean("singleton",ScopeBean.class); ScopeBean singleton3 = context.getBean("singleton",ScopeBean.class); System.out.println(singleton1); System.out.println(singleton2); System.out.println(singleton3); /** bean.ScopeBean@30db7df3 bean.ScopeBean@30db7df3 bean.ScopeBean@30db7df3 */
案例八:prototype bean
模拟Spring实现,定义个bean
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; }
这侧表类
public class BeanDifinitionRegister { private final Map<String, BeanDefinition> DEFINITIONS = new HashMap<String, BeanDefinition>(); public void registerBeanDefinition(String beanName, BeanDefinition bd) { if (DEFINITIONS.containsKey(bd.getId())) { throw new RuntimeException("[" + beanName + "] already exist"); } DEFINITIONS.put(bd.getId(), bd); } public BeanDefinition getBeanDefinition(String beanName) { return DEFINITIONS.get(beanName); } public boolean containsBeanDefinition(String beanName) { return DEFINITIONS.containsKey(beanName); } }
定义工厂类用于获取bean实例
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 + "] already exist"); } // 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 = 20000 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() + "]失败"); } } }
测试
public class PrototypeTest { DefaultBeanFactory factory = null; @Before public void initContext() { factory = new DefaultBeanFactory(); } @Test public void testPrototype() { BeanDefinition bd = new BeanDefinition(); bd.setId("bean"); bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bd.setClazz(BeanDefinition.class.getName()); factory.registerBeanDefinition(bd); System.out.println(factory.getBean("bean")); System.out.println(factory.getBean("bean")); System.out.println(factory.getBean("bean")); System.out.println(factory.getBean("bean")); } }
Spring配置prototype
<bean id="singleton" class="bean.ScopeBean" scope="prototype"></bean>
Spring中的bean的作用于出路Singleton和Prototype外还有三个与web有关的作用于分别为request(一起请求,比如一次表单提交)、session、global session,也可以通过实现Scope接口自己实现作用域范围
构造器注入:1、构造器参数索引 2、构造器参数类型 3、构造器参数类型
构造器注入通过<constructor-arg>标签配置,案例1、2、3使用第一种方式
SpringIoC容器依赖有两层含义:Bean初始化依赖于容器、容器注入Bean的依赖资源
二、DI
当一个对象的创建依赖于另外一个对象时,依赖就发生了,Spring的依赖是方法级的与对象中的属性无关
class A { void setName(String name){ // 就可以说 A 对象 依赖 String 类型参数 } void setB(B b){ // 就可以说 A 对象 依赖 B 类型参数 } }
实例bean,可以没有info属性,但必须有setXxxx()
public class InstanceBean { private String info; public InstanceBean() { System.out.println("..."); } public void method() { System.out.println("InstanceBean--method()"); } public String getInfo() { return this.info; } public void setInfo(String info) { this.info = info; } }
配置
<bean id="instance" class="demo01.InstanceBean"> <property name="info" value="注入"></property> </bean>
通过DI的方式为InstanceBean注入String类型对象
@Test public void demo2(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); InstanceBean bean = (InstanceBean) applicationContext.getBean("instance"); System.out.println(bean.getInfo()); }
这种对象间的依赖关系,通过Spring的使用配置文件的方式注入到InstanceBean中
三、Spring配置文件读取
一般有两个位置:
1、src根目录
2、WEB-INF下
最好命名为applicationContext.xml
读取方式
加载classpath: ew ClassPathXmlApplicationContext("applicationContext.xml"); 加载磁盘路径: FileSystemXmlApplicationContext("WebContent/WEB-INF/applicationContext.xml");
四、Spring实例化Bean的三种方式
1、使用构造器
实例化对象必须提供默认构造方法,配置如下
<bean id="instance" class="demo01.InstanceBean"></bean>
实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); InstanceBean bean = applicationContext.getBean("instance",InstanceBean.class);
2、静态工厂方法
工厂类
public class BeanFactory { public static InstanceBean getInstanceBean() { return new InstanceBean(); } }
配置
<bean id="instance1" class="demo01.BeanFactory" factory-method="getInstanceBean"> <property name="info" value="静态工厂注入"></property> </bean>
实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); InstanceBean bean = applicationContext.getBean("instance1",InstanceBean.class);
3、实力工厂
工厂
public class BeanFactory { public InstanceBean getInstanceBeen() { return new InstanceBean(); } }
配置
<bean id="beanFactory" class="demo01.BeanFactory"></bean> <bean id="instance2" factory-bean="beanFactory" factory-method="getInstanceBeen"> <property name="info" value="实例工厂注入"></property> </bean>
实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); InstanceBean bean = applicationContext.getBean("instance2",InstanceBean.class); System.out.println(bean.getInfo());
五、Spring中Bean的作用域
Spring加载配置文件时,会默认为文件中所配置的所有bean实例化,实例化后,默认bean的实例为单利模式
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); InstanceBean bean1 = applicationContext.getBean("instance",InstanceBean.class); InstanceBean bean2 = applicationContext.getBean("instance",InstanceBean.class); System.out.println(bean1); System.out.println(bean2);
还有其他几种类型
- Spring 1 IoC 3 DI
- Spring--IoC--基于注解的DI-基本使用
- Spring--IoC--基于注解的DI-使用Spring的JUnit4测试
- 戏说Spring里的【IoC与DI】
- Spring模拟(DI,IOC)
- Spring(1)IOC/DI
- spring的IoC和DI
- Spring的注入,Ioc,DI详解
- Spring IOC和DI原理讲解并制作LazyCoder版的Spring (一)
- spring的工作原理以及AOP,IOC,DI等概念
- Spring 中的Bean配置(IOC & DI )
- spring的Ioc控制反转和DI依赖注入讲解
- maven、java 内存泄漏与spring Ioc DI
- Spring的IOC和DI的区别
- 关于Spring IOC (DI-依赖注入)你需要知道的一切
- Spring入门--控制反转(IOC)与依赖注入(DI)
- spring IOC DI简单理解
- 浅谈对Spring IOC以及DI的理解
- Spring的IoC/DI的理解
- Spring 之IOC和DI