IOC:Inversion of Control控制反转,也称依赖倒置(反转)java
问题:如何理解控制反转?git
反转:依赖对象的得到被反转了。由本身建立,反转为从IOC容器中获取(和自动注入)github
1)代码更简洁,不须要去new须要使用的对象了。spring
2)面向接口编程,使用者与具体类解耦,易扩展、替换实现者。apache
3)能够方便进行AOP加强。进行AOP的前提是有IOC编程
IOC容器的工做:负责建立、管理类实例,向使用者提供实例。数组
是的。IOC容器负责来建立类实例对象,使用者须要实例就从IOC容器中get。也称IOC容器为bean工厂。缓存
问:bean是什么?安全
bean是组件,就是类对象!ide
1)IOC容器应该具有什么行为(对外接口)?
对外提供bean实例,getBean()方法
2) 这个getBean()方法是否须要参数?须要几个参数、什么类型的参数?
简单工厂模式中,当工厂能建立不少类产品时,如要建立某类产品,须要告诉工厂。
3)这个getBean()方法的返回值应是什么类型?
各类类型的bean,那就只能是Object了。
通过上面的问题Bean工厂的接口就能够定义出来了!!!
Bean工厂代码:
package com.study.spring.beans; /** * * @Description: IOC容器(bean工厂)接口:负责建立bean实例 * @author leeSmall * @date 2018年11月29日 * */ public interface BeanFactory { /** * 获取bean * * @param name bean的名字 * * @return bean 实例 * @throws Exception */ Object getBean(String name) throws Exception; }
Bean工厂怎么知道该如何建立bean?
如何告诉Bean工厂?
就是一个定义注册,咱们能够给它定义一个bean定义注册接口
Bean定义注册接口代码:
package com.study.spring.beans; /** * * @Description: bean定义BeanDefinition定义好了就须要告诉IOC容器(bean工厂): * 经过bean定义注册接口BeanDefinitionRegistry把bean定义BeanDefinition注册到IOC容器(bean工厂)BeanFactory * @author leeSmall * @date 2018年11月29日 * */ public interface BeanDefinitionRegistry { //注册bean定义 void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException; //获取bean定义 BeanDefinition getBeanDefinition(String beanName); //判断是否包含bean定义 boolean containsBeanDefinition(String beanName); }
问题1:bean定义的用途是什么?
告诉Bean工厂该如何建立某类bean
问题2:得到类的实例的方式有哪些?
new 构造方法
Person p = new Person();
工厂方法
静态的
public class PersonFactory { public static Person getPerson() { return new Person(); } }
成员方法
public class PersonFactory { public Person getPerson() { return new Person(); } }
问题3:Bean工厂帮咱们建立bean时,它须要获知哪些信息?
1)new 构造方法的方式建立bean时,Bean工厂须要知道要建立的类的类名
2)静态工厂方法的方式建立bean时,Bean工厂须要知道工厂类名、工厂方法名
3)成员工厂方法的方式建立bean时,Bean工厂须要知道工厂类名(工厂bean名)、工厂方法名
由于须要获取工厂类对象去调用工厂方法名建立bean,因此直接给工厂bean名先建立工厂bean对象
问题4:每次从Bean工厂获取bean实例时,是否都须要建立一个新的?
否,有的只须要单实例
问题5:Bean定义是给Bean工厂建立bean用的,那么Bean定义接口应向Bean工厂提供哪些方法?
new 构造方法的方式建立bean时,须要告诉bean工厂怎么获取类的名称——获取bean的类名:getBeanClass (): Class
静态工厂方法的方式建立bean时,须要告诉bean工厂怎么获取工厂方法名:getFactoryMethodName() : String
成员工厂方法的方式建立bean时,须要告诉bean工厂怎么获取工厂bean名:getFactoryBeanName() : String
是不是单例等方法:getScope() : Sting、isSingleton()、isPrototype()
问题6:类对象交给IOC容器来管理,类对象的生命周期中还可能有什么生命阶段事情要作吗?
好比建立对象后可能须要进行一些初始化
还有一些对象在销毁时可能要进行一些特定的销毁逻辑(如释放资源)
那就在Bean定义中提供让用户可指定初始化、销毁方法。
对Bean工厂就需提供getInitMethodName()、getDestroyMethodName()
bean定义接口BeanDefinition代码:
package com.study.spring.beans; import org.apache.commons.lang3.StringUtils; /** * * @Description: bean定义接口:要IOC容器(bean工厂)建立bean实例,就得告诉IOC容器(bean工厂)须要建立什么样的bean-BeanDefinition * @author leeSmall * @date 2018年11月29日 * */ public interface BeanDefinition { String SCOPE_SINGLETION = "singleton"; String SCOPE_PROTOTYPE = "prototype"; /** * 类:new 构造方法的方式建立bean时,须要告诉bean工厂怎么获取类的名称 */ Class<?> getBeanClass(); /** * Scope */ String getScope(); /** * 是否单例 */ boolean isSingleton(); /** * 是否原型 */ boolean isPrototype(); /** * 工厂bean名:成员工厂方法的方式建立bean时,须要告诉bean工厂怎么获取工厂bean名 */ String getFactoryBeanName(); /** * 工厂方法名:静态工厂方法的方式建立bean时,须要告诉bean工厂怎么获取工厂方法名 */ String getFactoryMethodName(); /** * 初始化方法 */ String getInitMethodName(); /** * 销毁方法 */ String getDestroyMethodName(); /** * 校验bean定义的合法性 */ default boolean validate() { // 没定义class,工厂bean或工厂方法没指定,则不合法。 if (this.getBeanClass() == null) { if (StringUtils.isBlank(getFactoryBeanName()) || StringUtils.isBlank(getFactoryMethodName())) { return false; } } // 定义了类,又定义工厂bean,不合法 if (this.getBeanClass() != null && StringUtils.isNotBlank(getFactoryBeanName())) { return false; } return true; } }
实现bean定义接口BeanDefinition实现一个通用的bean定义GenericBeanDefinition
package com.study.spring.beans; import org.apache.commons.lang3.StringUtils; /** * * @Description: bean定义有了就须要实现bean定义接口BeanDefinition实现一个通用的bean * 定义GenericBeanDefinition,用来承载数据 * @author leeSmall * @date 2018年11月29日 * */ public class GenericBeanDefinition implements BeanDefinition { //bean的名称 private Class<?> beanClass; //scope 默认单例 private String scope = BeanDefinition.SCOPE_SINGLETION; //工厂bean名 private String factoryBeanName; //工厂方法名 private String factoryMethodName; //初始化方法 private String initMethodName; //销毁方法 private String destroyMethodName; //设置bean的名称 public void setBeanClass(Class<?> beanClass) { this.beanClass = beanClass; } //设置scope public void setScope(String scope) { if (StringUtils.isNotBlank(scope)) { this.scope = scope; } } //设置工厂bean名 public void setFactoryBeanName(String factoryBeanName) { this.factoryBeanName = factoryBeanName; } //设置工厂方法名 public void setFactoryMethodName(String factoryMethodName) { this.factoryMethodName = factoryMethodName; } //设置初始化方法 public void setInitMethodName(String initMethodName) { this.initMethodName = initMethodName; } //设置销毁方法 public void setDestroyMethodName(String destroyMethodName) { this.destroyMethodName = destroyMethodName; } @Override public Class<?> getBeanClass() { return this.beanClass; } @Override public String getScope() { return this.scope; } @Override public boolean isSingleton() { return BeanDefinition.SCOPE_SINGLETION.equals(this.scope); } @Override public boolean isPrototype() { return BeanDefinition.SCOPE_PROTOTYPE.equals(this.scope); } @Override public String getFactoryBeanName() { return this.factoryBeanName; } @Override public String getFactoryMethodName() { return this.factoryMethodName; } @Override public String getInitMethodName() { return this.initMethodName; } @Override public String getDestroyMethodName() { return this.destroyMethodName; } @Override public String toString() { return "GenericBeanDefinition [beanClass=" + beanClass + ", scope=" + scope + ", factoryBeanName=" + factoryBeanName + ", factoryMethodName=" + factoryMethodName + ", initMethodName=" + initMethodName + ", destroyMethodName=" + destroyMethodName + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((beanClass == null) ? 0 : beanClass.hashCode()); result = prime * result + ((destroyMethodName == null) ? 0 : destroyMethodName.hashCode()); result = prime * result + ((factoryBeanName == null) ? 0 : factoryBeanName.hashCode()); result = prime * result + ((factoryMethodName == null) ? 0 : factoryMethodName.hashCode()); result = prime * result + ((initMethodName == null) ? 0 : initMethodName.hashCode()); result = prime * result + ((scope == null) ? 0 : scope.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; GenericBeanDefinition other = (GenericBeanDefinition) obj; if (beanClass == null) { if (other.beanClass != null) return false; } else if (!beanClass.equals(other.beanClass)) return false; if (destroyMethodName == null) { if (other.destroyMethodName != null) return false; } else if (!destroyMethodName.equals(other.destroyMethodName)) return false; if (factoryBeanName == null) { if (other.factoryBeanName != null) return false; } else if (!factoryBeanName.equals(other.factoryBeanName)) return false; if (factoryMethodName == null) { if (other.factoryMethodName != null) return false; } else if (!factoryMethodName.equals(other.factoryMethodName)) return false; if (initMethodName == null) { if (other.initMethodName != null) return false; } else if (!initMethodName.equals(other.initMethodName)) return false; if (scope == null) { if (other.scope != null) return false; } else if (!scope.equals(other.scope)) return false; return true; } }
咱们继续看下面的图:
说明:bean定义BeanDefinition经过bean定义注册接口BeanDefinitionRegistry注册到Bean工厂BeanFactory,Bean工厂BeanFactory负责建立bean
实现一个最基础的默认bean工厂:DefaultBeanFactory
说明:
6.1 实现bean定义信息注册接口
问题1:bean定义信息如何存放?
Map
问题2:bean定义是否能够重名?重名怎么办?
重名抛异常
6.2 实现bean工厂
问题1:建立的bean用什么存放,方便下次获取?
Map,由于getBean是经过名字来取的,放在Map中更好
问题2:在getBean方法中要作哪些事?
建立bean实例,进行初始化
6.3 Bean工厂实现代码:
package com.study.spring.beans; import java.io.Closeable; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * @Description: bean定义BeanDefinition有了,bean定义注册BeanDefinitionRegistry也有了, * 就能够实现一个默认的bean工厂DefaultBeanFactory来建立bean实例了,DefaultBeanFactory除了须要实现BeanFactory外, * 还须要实现Bean定义注册接口BeanDefinitionRegistry,由于要把bean定义注册到bean工厂里面 * @author leeSmall * @date 2018年11月29日 * */ public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable { private final Log logger = LogFactory.getLog(getClass()); //用Map来存放bean定义信息 private Map<String, BeanDefinition> beanDefintionMap = new ConcurrentHashMap<>(256); //用Map来存放建立的bean实例,注意这里只是存放单例bean,多实例每次都要建立新的,不须要存放 private Map<String, Object> beanMap = new ConcurrentHashMap<>(256); //注册bean定义 @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException { //判断给入的beanName和beanDefinition不能为空 Objects.requireNonNull(beanName, "注册bean须要给入beanName"); Objects.requireNonNull(beanDefinition, "注册bean须要给入beanDefinition"); // 校验给入的bean是否合法 if (!beanDefinition.validate()) { throw new BeanDefinitionRegistException("名字为[" + beanName + "] 的bean定义不合法:" + beanDefinition); } //若是已存在bean定义就抛异常 if (this.containsBeanDefinition(beanName)) { throw new BeanDefinitionRegistException( "名字为[" + beanName + "] 的bean定义已存在:" + this.getBeanDefinition(beanName)); } //把bean定义放到Map里面 this.beanDefintionMap.put(beanName, beanDefinition); } //根据bean的名字从Map里面获取bean定义 @Override public BeanDefinition getBeanDefinition(String beanName) { return this.beanDefintionMap.get(beanName); } //根据bean的名字判断Map里面是否包含bean定义 @Override public boolean containsBeanDefinition(String beanName) { return this.beanDefintionMap.containsKey(beanName); } //根据bean的名字获取bean实例,里面主要作的工做是建立bean实例和对bean实例进行初始化 @Override public Object getBean(String name) throws Exception { return this.doGetBean(name); } //根据bean的名字获取bean实例,里面主要作的工做是建立bean实例和对bean实例进行初始化 protected Object doGetBean(String beanName) throws Exception { //判断给入的bean名字不能为空 Objects.requireNonNull(beanName, "beanName不能为空"); //先从beanMap里面获取bean实例 Object instance = beanMap.get(beanName); //若是beanMap里面已存在bean实例就直接返回,不须要走后面的流程了 if (instance != null) { return instance; } //从beanDefintionMap里面获取bean定义信息 BeanDefinition bd = this.getBeanDefinition(beanName); //bean定义信息不能为空 Objects.requireNonNull(bd, "beanDefinition不能为空"); //获取bean的类型 Class<?> type = bd.getBeanClass(); if (type != null) { //若是bean的类型不为空,而且工厂方法名为空,说明是使用构造方法的方式来建立bean实例 if (StringUtils.isBlank(bd.getFactoryMethodName())) { // 构造方法来构造对象 instance = this.createInstanceByConstructor(bd); } //若是bean的类型不为空,而且工厂方法名不为空,说明是使用静态工厂方法的方式来建立bean实例 else { // 静态工厂方法 instance = this.createInstanceByStaticFactoryMethod(bd); } } //若是bean的类型为空,说明是使用工厂bean的方式来建立bean实例 else { // 工厂bean方式来构造对象 instance = this.createInstanceByFactoryBean(bd); } // 执行初始化方法 this.doInit(bd, instance); //存放单例的bean到beanMap if (bd.isSingleton()) { beanMap.put(beanName, instance); } return instance; } // 构造方法来构造对象 反射 private Object createInstanceByConstructor(BeanDefinition bd) throws InstantiationException, IllegalAccessException { try { //拿到bean的类型,而后调用newInstance经过反射来建立bean实例 return bd.getBeanClass().newInstance(); } catch (SecurityException e1) { logger.error("建立bean的实例异常,beanDefinition:" + bd, e1); throw e1; } } // 静态工厂方法 反射 private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception { //拿到bean的类型 Class<?> type = bd.getBeanClass(); //经过静态工厂方法方法的名字getFactoryMethodName反射出bean的方法m建立bean实例 Method m = type.getMethod(bd.getFactoryMethodName(), null); return m.invoke(type, null); } // 工厂bean方式来构造对象 反射 private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception { //经过bean定义信息中工厂bean的名字获取工厂bean的实例 Object factoryBean = this.doGetBean(bd.getFactoryBeanName()); //经过bean定义信息中工厂方法的名字反射出工厂bean的方法m建立bean实例 Method m = factoryBean.getClass().getMethod(bd.getFactoryMethodName(), null); return m.invoke(factoryBean, null); } //执行初始化方法 private void doInit(BeanDefinition bd, Object instance) throws Exception { // 获取bean定义中的初始化方法,若是存在初始化方法就经过反射去执行初始化方法 if (StringUtils.isNotBlank(bd.getInitMethodName())) { Method m = instance.getClass().getMethod(bd.getInitMethodName(), null); m.invoke(instance, null); } } //销毁 @Override public void close() throws IOException { // 执行单例实例的销毁方法 for (Entry<String, BeanDefinition> e : this.beanDefintionMap.entrySet()) { String beanName = e.getKey(); BeanDefinition bd = e.getValue(); //获取bean定义中的销毁方法,若是存在销毁方法就经过反射去执行销毁方法 if (bd.isSingleton() && StringUtils.isNotBlank(bd.getDestroyMethodName())) { Object instance = this.beanMap.get(beanName); try { Method m = instance.getClass().getMethod(bd.getDestroyMethodName(), null); m.invoke(instance, null); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) { logger.error("执行bean[" + beanName + "] " + bd + " 的 销毁方法异常!", e1); } } } } }
6.4 扩展DefaultBeanFactory
对于单例bean,咱们能够提早实例化,这样作的好处是不用在须要的时候再取获取了,能够保证线程安全,提升性能
package com.study.spring.beans; import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * @Description: 提早实例化单例bean * @author leeSmall * @date 2018年11月27日 * */ public class PreBuildBeanFactory extends DefaultBeanFactory { private final Log logger = LogFactory.getLog(getClass()); private List<String> beanNames = new ArrayList<>(); @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException { super.registerBeanDefinition(beanName, beanDefinition); synchronized (beanNames) { beanNames.add(beanName); } } public void preInstantiateSingletons() throws Exception { synchronized (beanNames) { for (String name : beanNames) { BeanDefinition bd = this.getBeanDefinition(name); if (bd.isSingleton()) { this.doGetBean(name); if (logger.isDebugEnabled()) { logger.debug("preInstantiate: name=" + name + " " + bd); } } } } } }
6.5 测试IOC容器(bean工厂)建立bean实例
须要建立的bean实例ABean
package com.study.spring.samples; /** * * @Description: 须要建立的bean实例ABean * @author leeSmall * @date 2018年11月29日 * */ public class ABean { public void doSomthing() { System.out.println(System.currentTimeMillis() + " " + this); } public void init() { System.out.println("ABean.init() 执行了"); } public void destroy() { System.out.println("ABean.destroy() 执行了"); } }
建立bean实例ABean的工厂ABeanFactory
package com.study.spring.samples; /** * * @Description: 建立bean实例ABean的工厂ABeanFactory * @author leeSmall * @date 2018年11月29日 * */ public class ABeanFactory { //静态工厂方式 public static ABean getABean() { return new ABean(); } //工厂bean的方式 public ABean getABean2() { return new ABean(); } }
测试IOC容器(bean工厂)建立bean实例ABean
package v1; import org.junit.AfterClass; import org.junit.Test; import com.dn.spring.beans.BeanDefinition; import com.dn.spring.beans.DefaultBeanFactory; import com.dn.spring.beans.GenericBeanDefinition; import com.dn.spring.samples.ABean; import com.dn.spring.samples.ABeanFactory; /** * * @Description: 测试IOC容器(bean工厂)建立bean实例ABean * @author leeSmall * @date 2018年11月29日 * */ public class DefaultBeanFactoryTest { static DefaultBeanFactory bf = new DefaultBeanFactory(); //测试构造方法方式建立bean实例 @Test public void testRegist() throws Exception { //建立bean定义 GenericBeanDefinition bd = new GenericBeanDefinition(); //设置bean的类名 bd.setBeanClass(ABean.class); //设置是否单例 bd.setScope(BeanDefinition.SCOPE_SINGLETION); // bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); //设置bean的初始化方法 bd.setInitMethodName("init"); //设置bean的销毁方法 bd.setDestroyMethodName("destroy"); //把bean定义注册到bean工厂DefaultBeanFactory bf bf.registerBeanDefinition("aBean", bd); } //静态工厂方法的方式建立bean实例 @Test public void testRegistStaticFactoryMethod() throws Exception { //建立bean定义 GenericBeanDefinition bd = new GenericBeanDefinition(); //设置工厂bean的名字 bd.setBeanClass(ABeanFactory.class); //设置工厂方法名 bd.setFactoryMethodName("getABean"); //把bean定义注册到bean工厂DefaultBeanFactory bf bf.registerBeanDefinition("staticAbean", bd); } //工厂bean的方式建立bean实例 @Test public void testRegistFactoryMethod() throws Exception { //建立工厂bean定义 GenericBeanDefinition bd = new GenericBeanDefinition(); //设置工厂bean的名字 bd.setBeanClass(ABeanFactory.class); String fbname = "factory"; //把工厂bean注册到bean工厂DefaultBeanFactory bf bf.registerBeanDefinition(fbname, bd); //建立bean定义 bd = new GenericBeanDefinition(); //设置工厂bean的名字 bd.setFactoryBeanName(fbname); //设置工厂bean的方法名 bd.setFactoryMethodName("getABean2"); //设置是不是单列 bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); //把bean定义注册到bean工厂DefaultBeanFactory bf bf.registerBeanDefinition("factoryAbean", bd); } //获取bean实例并调用里面的方法 @AfterClass public static void testGetBean() throws Exception { System.out.println("构造方法方式------------"); for (int i = 0; i < 3; i++) { ABean ab = (ABean) bf.getBean("aBean"); ab.doSomthing(); } System.out.println("静态工厂方法方式------------"); for (int i = 0; i < 3; i++) { ABean ab = (ABean) bf.getBean("staticAbean"); ab.doSomthing(); } System.out.println("工厂方法方式------------"); for (int i = 0; i < 3; i++) { ABean ab = (ABean) bf.getBean("factoryAbean"); ab.doSomthing(); } //销毁 bf.close(); } }
输出结果:
构造方法方式------------ ABean.init() 执行了 1543478493599 com.dn.spring.samples.ABean@46fbb2c1 1543478493599 com.dn.spring.samples.ABean@46fbb2c1 1543478493599 com.dn.spring.samples.ABean@46fbb2c1 静态工厂方法方式------------ 1543478493599 com.dn.spring.samples.ABean@5ef04b5 1543478493599 com.dn.spring.samples.ABean@5ef04b5 1543478493599 com.dn.spring.samples.ABean@5ef04b5 工厂方法方式------------ 1543478493599 com.dn.spring.samples.ABean@5f4da5c3 1543478493600 com.dn.spring.samples.ABean@443b7951 1543478493600 com.dn.spring.samples.ABean@14514713 ABean.destroy() 执行了
DI(Dependency Injection)依赖注入分析
问题1:哪些地方会有依赖?
构造参数依赖
属性依赖
问题2:依赖注入的本质是什么?
赋值,给入构造参数值,给属性赋值
问题3:参数值、属性值多是什么值?
直接值、bean依赖
举例:
public class Girl{ public Girl(String name,int age,char cup,Boy boyfriend){ } }
name,age,cup都是直接值,boyfriend是bean依赖
问题4:直接值会有哪几种状况?
基本数据类型、String
数组、集合
Properties
Map
本质:参数值、属性值都是值。bean工厂在进行依赖注入时,就是给入值。
public class Girl{ public Girl(String name,int age,char cup,Boy boyfriend){ } }
问题1:咱们要建立一个Girl是如何建立的?
Boy leeSmall = new Boy("leeSmall"); Girl girl = new Girl("小青",18,'D',leeSmall);
直接把值传入构造函数便可
问题2:咱们可不能够这样来定义构造参数依赖?
第一个参数值是:"小青"
第二个参数值是:18
第三个参数值是:'D'
第四个参数值是:依赖一个Boy Bean
彻底能够!
问题1:参数能够有多个,用什么存储?
集合:List
问题2:参数有顺序,如何体现顺序?
按参数顺序放入List
问题3:参数能够值直接值,也能够是bean依赖,如何表示?
由于能够有多种值,那就只能用Object
List<Object> constructorArgumentValues
问题4:若是用Object来表示值,如何区分是Bean依赖?
为Bean依赖定义一种数据类型BeanReference,bean工厂在构造Bean实例时,遍历判断参数是不是BeanReference,若是是则替换为依赖的bean实例。
问题5:若是直接值是数组、集合等,它们的元素中有的是bean依赖,怎么处理?
元素值仍是用BeanReference,一样bean工厂在使用时需遍历替换。
BeanReference就是用来讲明bean依赖的:依赖哪一个Bean
package com.dn.spring.beans; /** * 用于依赖注入中描述bean依赖 */ public class BeanReference { private String beanName; public BeanReference(String beanName) { super(); this.beanName = beanName; } /** * 得到引用的beanName */ public String getBeanName() { return this.beanName; } }
1.3 在BeanDefinition中增长得到构造参数值的方法
List<?> getConstructorArgumentValues();
在GenericBeanDefinition中增长对应的实现
//构造参数存放属性 private List<?> constructorArgumentValues; //得到构造参数值 public List<?> getConstructorArgumentValues() { return constructorArgumentValues; } //设置构造参数值 public void setConstructorArgumentValues(List<?> constructorArgumentValues) { this.constructorArgumentValues = constructorArgumentValues; }
构造参数依赖有了,下面就能够来实现构造参数依赖注入了!
代码实现:
//把bean定义中的构造参数引用转为真实的值 private Object[] getConstructorArgumentValues(BeanDefinition bd) throws Exception { return this.getRealValues(bd.getConstructorArgumentValues()); } //把bean定义中的构造参数引用转为真实的值 private Object[] getRealValues(List<?> defs) throws Exception { if (CollectionUtils.isEmpty(defs)) { return null; } Object[] values = new Object[defs.size()]; int i = 0; Object v = null; for (Object rv : defs) { if (rv == null) { v = null; } else if (rv instanceof BeanReference) { v = this.doGetBean(((BeanReference) rv).getBeanName()); } else if (rv instanceof Object[]) { // TODO 处理集合中的bean引用 } else if (rv instanceof Collection) { // TODO 处理集合中的bean引用 } else if (rv instanceof Properties) { // TODO 处理properties中的bean引用 } else if (rv instanceof Map) { // TODO 处理Map中的bean引用 } else { v = rv; } values[i++] = v; } return values; }
问题:有参数了,如何判定是哪一个构造方法、哪一个工厂方法?须要考虑下面的状况
a)方法是能够重载的
b)形参定义时多是接口或者父类,实参则是具体的子实现
c)能够经过反射获取提供的构造方法、方法,以下:
java.lang.Class
反射获取构造方法:
//获取全部构造方法 @CallerSensitive public Constructor<?>[] getConstructors() throws SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return copyConstructors(privateGetDeclaredConstructors(true)); } //获取指定构造方法 @CallerSensitive public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return getConstructor0(parameterTypes, Member.PUBLIC); }
反射获取方法:
//获取全部方法 @CallerSensitive public Method[] getMethods() throws SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return copyMethods(privateGetPublicMethods()); } //获取指定方法 @CallerSensitive public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); Method method = getMethod0(name, parameterTypes, true); if (method == null) { throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); } return method; }
判断逻辑:
a)先根据参数的类型进行精确匹配查找,如未找到,则进行第二步查找;
b)得到全部的构造方法遍历,经过参数数量过滤,再比对形参类型与实参类型
也就是说,对于原型Bean,咱们能够缓存下这个构造方法或工厂方法。如何实现?
在BeanDefinition中增长缓存的方法:
/* 下面的四个方法是供beanFactory中使用的 */ //获取构造方法 public Constructor<?> getConstructor(); //缓存构造方法 public void setConstructor(Constructor<?> constructor); //获取工厂方法 public Method getFactoryMethod(); //缓存工厂方法 public void setFactoryMethod(Method factoryMethod);
在GenericBeanDefinition中增长对应的实现:
/* 下面的四个方法是供beanFactory中使用的 */ //获取构造方法 public Constructor<?> getConstructor() { return constructor; } //缓存构造方法 public void setConstructor(Constructor<?> constructor) { this.constructor = constructor; } //获取工厂方法 public Method getFactoryMethod() { return factoryMethod; } //缓存工厂方法 public void setFactoryMethod(Method factoryMethod) { this.factoryMethod = factoryMethod; }
1)在DefaultBeanFactory中增长查找构造方法的方法
//查找构造方法的方法 private Constructor<?> determineConstructor(BeanDefinition bd, Object[] args) throws Exception { Constructor<?> ct = null; //若是参数为空,则返回无参的构造方法 if (args == null) { return bd.getBeanClass().getConstructor(null); } // 对于原型bean,从第二次开始获取bean实例时,可直接得到第一次缓存的构造方法。 ct = bd.getConstructor(); if (ct != null) { return ct; } // 先根据参数类型获取精确匹配的构造方法 Class<?>[] paramTypes = new Class[args.length]; int j = 0; for (Object p : args) { paramTypes[j++] = p.getClass(); } try { ct = bd.getBeanClass().getConstructor(paramTypes); } catch (Exception e) { // 这个异常不须要处理 } //若是根据参数类型进行精确批次查找没有找到构造方法,就得到全部的构造方法遍历,经过参数数量过滤,再比对形参类型与实参类型 if (ct == null) { // 没有精确参数类型匹配的,则遍历匹配全部的构造方法 // 判断逻辑:先判断参数数量,再依次比对形参类型与实参类型 outer: for (Constructor<?> ct0 : bd.getBeanClass().getConstructors()) { Class<?>[] paramterTypes = ct0.getParameterTypes(); if (paramterTypes.length == args.length) { for (int i = 0; i < paramterTypes.length; i++) { //isAssignableFrom方法表示是否能够把args[i].getClass()赋值给paramterTypes[i] if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) { continue outer; } } ct = ct0; break outer; } } } //若是找到构造方法了,而且是原型的就缓存起来 if (ct != null) { // 对于原型bean,能够缓存找到的构造方法,方便下次构造实例对象。在BeanDefinfition中获取设置所用构造方法的方法。 // 同时在上面增长从beanDefinition中获取的逻辑。 if (bd.isPrototype()) { bd.setConstructor(ct); } return ct; } else { throw new Exception("不存在对应的构造方法!" + bd); } }
2)修改DefaultBeanFactory中用构造方法建立实例的代码调用determineConstructor
// 构造方法来构造对象 private Object createInstanceByConstructor(BeanDefinition bd) throws Exception { try { //获取bean定义中的构造参数 Object[] args = this.getConstructorArgumentValues(bd); //若是构造参数为空就使用无参构造函数 if (args == null) { return bd.getBeanClass().newInstance(); } //查找有参构造函数并返回 else { // 决定构造方法 return this.determineConstructor(bd, args).newInstance(args); } } catch (SecurityException e1) { logger.error("建立bean的实例异常,beanDefinition:" + bd, e1); throw e1; } }
3)按照增长查找构造方法的方式修改静态工厂方法、工厂方法方式的参数依赖
增长查找工厂方法的方法
//查找工厂方法的方法 private Method determineFactoryMethod(BeanDefinition bd, Object[] args, Class<?> type) throws Exception { if (type == null) { type = bd.getBeanClass(); } //获取bean定义中国工厂方法的名字 String methodName = bd.getFactoryMethodName(); //若是参数为空就返回无参的方法 if (args == null) { return type.getMethod(methodName, null); } Method m = null; // 对于原型bean,从第二次开始获取bean实例时,可直接得到第一次缓存的构造方法。 m = bd.getFactoryMethod(); if (m != null) { return m; } // 先根据参数类型获取精确匹配的方法 Class[] paramTypes = new Class[args.length]; int j = 0; for (Object p : args) { paramTypes[j++] = p.getClass(); } try { m = type.getMethod(methodName, paramTypes); } catch (Exception e) { // 这个异常不须要处理 } //若是根据参数类型进行精确批次查找没有找到工厂方法,就得到全部的构造方法遍历,经过参数数量过滤,再比对形参类型与实参类型 if (m == null) { // 没有精确参数类型匹配的,则遍历匹配全部的方法 // 判断逻辑:先判断参数数量,再依次比对形参类型与实参类型 outer: for (Method m0 : type.getMethods()) { if (!m0.getName().equals(methodName)) { continue; } Class<?>[] paramterTypes = m.getParameterTypes(); if (paramterTypes.length == args.length) { for (int i = 0; i < paramterTypes.length; i++) { //isAssignableFrom方法表示是否能够把args[i].getClass()赋值给paramterTypes[i] if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) { continue outer; } } m = m0; break outer; } } } //若是找到构造方法了,而且是原型的就缓存起来 if (m != null) { // 对于原型bean,能够缓存找到的方法,方便下次构造实例对象。在BeanDefinfition中获取设置所用方法的方法。 // 同时在上面增长从beanDefinition中获取的逻辑。 if (bd.isPrototype()) { bd.setFactoryMethod(m); } return m; } else { throw new Exception("不存在对应的构造方法!" + bd); } }
修改DefaultBeanFactory中用工厂方法建立实例的代码调用determineFactoryMethod
// 静态工厂方法 private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception { Class<?> type = bd.getBeanClass(); Object[] realArgs = this.getRealValues(bd.getConstructorArgumentValues()); Method m = this.determineFactoryMethod(bd, realArgs, null); return m.invoke(type, realArgs); } // 工厂bean方式来构造对象 private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception { Object factoryBean = this.doGetBean(bd.getFactoryBeanName()); Object[] realArgs = this.getRealValues(bd.getConstructorArgumentValues()); Method m = this.determineFactoryMethod(bd, realArgs, factoryBean.getClass()); return m.invoke(factoryBean, realArgs); }
参数依赖注入测试
package v2; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.dn.spring.beans.BeanReference; import com.dn.spring.beans.GenericBeanDefinition; import com.dn.spring.beans.PreBuildBeanFactory; import com.dn.spring.samples.ABean; import com.dn.spring.samples.ABeanFactory; import com.dn.spring.samples.CBean; import com.dn.spring.samples.CCBean; /** * * @Description: 参数依赖注入测试 * @author leeSmall * @date 2018年12月1日 * */ public class DItest { static PreBuildBeanFactory bf = new PreBuildBeanFactory(); //构造函数参数依赖注入 @Test public void testConstructorDI() throws Exception { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(ABean.class); List<Object> args = new ArrayList<>(); args.add("abean01"); args.add(new BeanReference("cbean")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("abean", bd); bd = new GenericBeanDefinition(); bd.setBeanClass(CBean.class); args = new ArrayList<>(); args.add("cbean01"); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("cbean", bd); bf.preInstantiateSingletons(); ABean abean = (ABean) bf.getBean("abean"); abean.doSomthing(); } //静态工厂方法参数依赖注入 @Test public void testStaticFactoryMethodDI() throws Exception { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(ABeanFactory.class); bd.setFactoryMethodName("getABean"); List<Object> args = new ArrayList<>(); args.add("abean02"); args.add(new BeanReference("cbean02")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("abean02", bd); bd = new GenericBeanDefinition(); bd.setBeanClass(CBean.class); args = new ArrayList<>(); args.add("cbean02"); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("cbean02", bd); bf.preInstantiateSingletons(); ABean abean = (ABean) bf.getBean("abean02"); abean.doSomthing(); } //普通工厂方法的参数依赖注入 @Test public void testFactoryMethodDI() throws Exception { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setFactoryBeanName("abeanFactory"); bd.setFactoryMethodName("getABean2"); List<Object> args = new ArrayList<>(); args.add("abean03"); args.add(new BeanReference("cbean02")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("abean03", bd); bd = new GenericBeanDefinition(); bd.setBeanClass(ABeanFactory.class); bf.registerBeanDefinition("abeanFactory", bd); bf.preInstantiateSingletons(); ABean abean = (ABean) bf.getBean("abean03"); abean.doSomthing(); } }
输出结果:
调用了含有CBean参数的构造方法 1543644406604 abean01 cb.name=cbean01 调用了含有CBean参数的构造方法 1543644406607 abean02 cb.name=cbean02 调用了含有CBean参数的构造方法 1543644406608 abean03 cb.name=cbean02
问题:构造对象时能够循环依赖吗/
构造实例对象时的循环依赖,会陷入僵死局面,是不容许构造实例时的循环依赖的。那么怎么发现循环依赖呢?
发现循环依赖的方法:加入一个正在构造的bean的记录,每一个bean开始构造时加入该记录中,构造完成后从记录中移除。若是有依赖,先看依赖的bean是否在构造中,如是就构成了循环依赖,抛出异常
代码实现:
在DefaultBeanFactory增长以下代码:
//记录正在构造的bean private ThreadLocal<Set<String>> buildingBeans = new ThreadLocal<>(); // 记录正在建立的Bean Set<String> ingBeans = this.buildingBeans.get(); if (ingBeans == null) { ingBeans = new HashSet<>(); this.buildingBeans.set(ingBeans); } // 检测循环依赖 由于若是已bean正在构造,这时发现有其余的bean依赖它,此时它又没有建立很久须要抛异常了 if (ingBeans.contains(beanName)) { throw new Exception(beanName + " 循环依赖!" + ingBeans); } // 记录正在建立的Bean ingBeans.add(beanName); ..................... ...................... // 建立好实例后,移除建立中记录 ingBeans.remove(beanName);
循环依赖测试代码
package v2; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.dn.spring.beans.BeanReference; import com.dn.spring.beans.GenericBeanDefinition; import com.dn.spring.beans.PreBuildBeanFactory; import com.dn.spring.samples.DBean; import com.dn.spring.samples.EBean; /** * * @Description: 循环依赖测试 * @author leeSmall * @date 2018年12月1日 * */ public class CirculationDiTest { static PreBuildBeanFactory bf = new PreBuildBeanFactory(); //DBean依赖EBean,EBean又依赖DBean 抛异常 @Test public void testCirculationDI() throws Exception { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(DBean.class); List<Object> args = new ArrayList<>(); args.add(new BeanReference("ebean")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("dbean", bd); bd = new GenericBeanDefinition(); bd.setBeanClass(EBean.class); args = new ArrayList<>(); args.add(new BeanReference("dbean")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("ebean", bd); bf.preInstantiateSingletons(); } }
问题1:属性依赖是什么?
某个属性依赖某个值,须要外部给入这个值
问题2:该如何来描述一个属性依赖?
属性名、值,定义一个来来表示这两个值
问题3:会有多个属性依赖,怎么存放?
List存放
问题4:属性值得状况和构造参数值得状况同样吗?
同样
属性依赖实体类类图以下:
属性依赖实体类代码:
package com.study.spring.beans; /** * * @Description: 属性值依赖实体 * @author leeSmall * @date 2018年12月1日 * */ public class PropertyValue { private String name; private Object value; public PropertyValue(String name, Object value) { super(); this.name = name; this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } }
/** * 得到属性依赖定义的方法 * */ List<PropertyValue> getPropertyValues();
//存放属性依赖 private List<PropertyValue> propertyValues; //获取属性依赖 public List<PropertyValue> getPropertyValues() { return propertyValues; } //设置属性依赖 public void setPropertyValues(List<PropertyValue> propertyValues) { this.propertyValues = propertyValues; }
//属性依赖实现 private void setPropertyDIValues(BeanDefinition bd, Object instance) throws Exception { //bean定义中没有属性依赖就直接返回 if (CollectionUtils.isEmpty(bd.getPropertyValues())) { return; } //若是bean定义中有属性依赖就设置值,设置方式和构造参数的设置同样 for (PropertyValue pv : bd.getPropertyValues()) { //属性依赖没有给名字就直接跳过 if (StringUtils.isBlank(pv.getName())) { continue; } //获取类对象 Class<?> clazz = instance.getClass(); //经过属性名获取类对象里面声明的字段 Field p = clazz.getDeclaredField(pv.getName()); //设置字段可访问 p.setAccessible(true); //把属性值转化为真正的值,和构造参数同样 Object rv = pv.getValue(); Object v = null; if (rv == null) { v = null; } else if (rv instanceof BeanReference) { v = this.doGetBean(((BeanReference) rv).getBeanName()); } else if (rv instanceof Object[]) { // TODO 处理集合中的bean引用 } else if (rv instanceof Collection) { // TODO 处理集合中的bean引用 } else if (rv instanceof Properties) { // TODO 处理properties中的bean引用 } else if (rv instanceof Map) { // TODO 处理Map中的bean引用 } else { v = rv; } //把真正的值v设置到属性p里面 即属性p依赖的值v p.set(instance, v); } }
在doGetBean(String beanName)中增长对设置属性依赖的调用
// 给入属性依赖 this.setPropertyDIValues(bd, instance);
注意:属性依赖是容许循环依赖的,由于是在实例建立好以后才设置属性依赖的值的!!!
package v2; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.dn.spring.beans.BeanReference; import com.dn.spring.beans.GenericBeanDefinition; import com.dn.spring.beans.PreBuildBeanFactory; import com.dn.spring.beans.PropertyValue; import com.dn.spring.samples.ABean; import com.dn.spring.samples.CBean; import com.dn.spring.samples.FBean; /** * * @Description: 属性依赖测试 * @author leeSmall * @date 2018年12月1日 * */ public class PropertyDItest { static PreBuildBeanFactory bf = new PreBuildBeanFactory(); @Test public void testPropertyDI() throws Exception { //构造参数依赖 GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(ABean.class); List<Object> args = new ArrayList<>(); args.add("abean01"); args.add(new BeanReference("cbean")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("abean", bd); //构造参数依赖 bd = new GenericBeanDefinition(); bd.setBeanClass(CBean.class); args = new ArrayList<>(); args.add("cbean01"); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("cbean", bd); //属性依赖 bd = new GenericBeanDefinition(); bd.setBeanClass(FBean.class); List<PropertyValue> propertyValues = new ArrayList<>(); propertyValues.add(new PropertyValue("name", "FFBean01")); propertyValues.add(new PropertyValue("age", 18)); propertyValues.add(new PropertyValue("aBean", new BeanReference("abean"))); bd.setPropertyValues(propertyValues); bf.registerBeanDefinition("fbean", bd); bf.preInstantiateSingletons(); FBean fbean = (FBean) bf.getBean("fbean"); System.out.println("设置的属性依赖值: " + fbean.getName() + " " + fbean.getAge()); fbean.getaBean().doSomthing(); } }
输出结果:
调用了含有CBean参数的构造方法
设置的属性依赖值: FFBean01 18
1543649718902 abean01 cb.name=cbean01
完整代码获取地址:
Spring IOC分析和手写实现Spring IOC代码:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v1
Spring DI分析和手写实现Spring DI代码:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v2