IOC及DI功能分析与设计
Posted 踩踩踩从踩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IOC及DI功能分析与设计相关的知识,希望对你有一定的参考价值。
前言
本篇文章会从IOC分析,IOC设计实现 ,包括 bean工厂 bean定义 以及bean定义注册接口;以及di进行分析,BeanReference 如何找到对应的类进行依赖注入,构造参数依赖定义,并且怎么判断出bean工厂中参数依赖的问题。
IOC分析
- 代码更简洁,不需要去new要使用的对象了。
- 面向接口编程,使用者与具体类解耦, 易扩展、替换实现者。
- 可以方便进行AOP增强。
IOC容器设计&实现
实现一个ioc容器需要下面三个元素
- bean工厂接口,ioc容器是实现抽象接口,向用户提供获取bean基本入口。
/**
* Bean工厂,IOC容器最顶层的抽象
*/
public interface BeanFactory
/**
* 用户端获取Bean的方法
* @param beanName
* @return
*/
Object getBean(String beanName) throws Exception;
- bean定义接口,向ioc容器规定bean创建成什么样。
- bean定义的注册接口,如何将bean定义注册进入bean工厂,这就是bean定义注册接口的含义,也是为什么需要这个接口。
/**
* Bean定义注册接口,用来完成Bean定义和Bean工厂之间沟通
*/
public interface BeanDefinitionRegistry
/**
* 想Bean工厂,注册Bean定义信息
* @param beanName
* @param beanDefinition
*/
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegisterException;
/**
* 获取已经注册的BeanDefinition
* @param beanName
* @return
*/
BeanDefinition getBeanDefinition(String beanName);
/**
* 是否包含了已经定义的beanName
* @param beanName
* @return
*/
boolean containsBeanDefinition(String beanName);
为什么要用beanDefinitionReistery也是为了解决降低耦合,不至于强一致性。如果直接往里面注册,一旦bean定义修改了什么,则会修改具体的beanfactory.这里很像门面模式。
设计ioc时,一定是多用接口去隔绝开,从而进行扩展。这是spring的经典思想。
BeanDefinition
获得获得类的实例的方式
- new 构造方法
Person p = new Person();
- 工厂 静态方法
public class PersonFactory
public static Person getPerson()
return new Person();
- 工厂 方法
public class PersonFactory
public Person getPerson()
return new Person();
以及通过反射也好,还是 直接采用new ,这里 在工厂中。一般需要知道。
- new 构造方法 需要知道 类名
- 静态工厂方法 需要知道 工厂类名、工厂方法名
- 成员工厂方法 需要知道 工厂bean名 工厂方法名

- 获取bean的类名:getBeanClass() :Class
- 获取工厂方法名:getFactoryMethodName():String
- 获取工厂bean名:getFactoryBeanName():String
- 是否是单例等方法:getScope():String isSingleton() isPrototype()
- 比如创建对象后可能需要进行一些初始化
- 还有有些对象在销毁时可能要进行一些特定的销毁逻辑(如释放资源)
- 那就在Bean定义中提供让用户可指定初始化、销毁方法。
- 对Bean工厂就需提供 getInitMethodName() getDestroyMethodName()

这里在bean定义的接口上可以知道的。
/**
* Bean定义,用来指定Bean构建信息接口
*/
public interface BeanDefinition
String SCOPE_SINGLETON = "singleton";
String SCOPE_PROTOTYPE = "prototype";
/**
* 获取Bean的类名
* @return
*/
Class<?> getBeanClass();
/**
*
* @param beanClass
*/
void setBeanClass(Class<?> beanClass);
String getScope();
void setScope(String scope);
boolean isSingleton();
boolean isPrototype();
String getFactoryMethodName();
void setFactoryMethodName(String factoryMethodName);
String getFactoryBeanName();
void setFactoryBeanName(String factoryBeanName);
void setInitMethod(String initMethod);
String getInitMethod();
void setDestroyMethod(String destroyMethod);
String getDestroyMethod();
default boolean validate()
// 没有BeanCalss信息,只能通过成员工厂来构建对象
if(getBeanClass() == null)
if(StringUtils.isBlank(getFactoryBeanName()) || StringUtils.isBlank(getFactoryMethodName()))
return false;
// Class存在的情况,还指定FactoryBeanName,构建对象的方式冲突
if(getBeanClass() != null && StringUtils.isNotBlank(getFactoryBeanName()))
return false;
return true;
;
定义的beandefinition bean定义。
这是最常见的bean定义实现主要作的也是 定义的最多的 方法实现。获取到 这个在spring中 会解析并产生生成 bean定义。
在 默认的 DefaultBeanFactory 中 需要实现BeanFactory BeanDefinitionRegistry 接口来实现 的
public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable
这里实现 beanfactory工厂时,首先需要存储 bean定义的容器
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
其次在实现时注册bean定义时, 判断 bean定义 是否可以添加
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegisterException
Objects.requireNonNull(beanName, "注册bean需要指定beanName");
Objects.requireNonNull(beanDefinition, "注册bean需要指定beanDefinition");
if(!beanDefinition.validate())
throw new BeanDefinitionRegisterException("名字为["+beanName+"]的bean定义不合法:"+beanDefinition);
if(containsBeanDefinition(beanName))
throw new BeanDefinitionRegisterException("名字为["+beanName+"]已存在:"+getBeanDefinition(beanName));
beanDefinitionMap.put(beanName, beanDefinition);
然后以及获取 bean定义的方法 这些都比较简单。
@Override
public BeanDefinition getBeanDefinition(String beanName)
return beanDefinitionMap.get(beanName);
@Override
public boolean containsBeanDefinition(String beanName)
return beanDefinitionMap.containsKey(beanName);
在 获取 getBean时, 并创建 对应object对象。
先去缓存里面判断一下beanName对应的对象已经创建好了 这里需要创建一个全局的 bean
private Map<String, Object> beanMap = new ConcurrentHashMap<>();
构建的方式有三种:构造函数、静态工厂、成员工厂 在进行判断是否 创建 bean等功能
@Override
public Object getBean(String beanName) throws Exception
return doGetBean(beanName);
private Object doGetBean(String beanName) throws Exception
Objects.requireNonNull(beanName, "beanName不能为空");
// 先去缓存里面判断一下beanName对应的对象已经创建好了
Object bean = beanMap.get(beanName);
if(bean != null) return bean;
// 构建的方式有三种:构造函数、静态工厂、成员工厂
BeanDefinition bd = beanDefinitionMap.get(beanName);
Objects.requireNonNull(bd, "找不到["+beanName+"]的bean定义信息");
Class<?> type = bd.getBeanClass();
if(type != null)
// 通过构造函数
if(StringUtils.isBlank(bd.getFactoryMethodName()))
bean = createBeanByConstructor(bd);
else // 通过静态工厂方式构建对象
bean = createBeanByStaticFactory(bd);
else
// 成员工厂方式构建对象
bean = createBeanByFactoryBean(bd);
// 开始Bean生命周期
if(StringUtils.isNotBlank(bd.getInitMethod()))
doInitMethod(bean, bd);
// 对单例bean的处理
if(bd.isSingleton())
beanMap.put(beanName, bean);
return bean;
通过判断beanclass去判断 使用构造方法去创建对应的 类。
private Object createBeanByConstructor(BeanDefinition bd) throws Exception
Object instance = bd.getBeanClass().newInstance();
return instance;
通过静态工厂方式构建对象 因为是静态方法 则可以采用直接采用 class.getmethod获取到 invoke方法。
private Object createBeanByStaticFactory(BeanDefinition bd) throws Exception
Class<?> type = bd.getBeanClass();
Method method = type.getMethod(bd.getFactoryMethodName(), null);
return method.invoke(type, null);
成员工厂方式构建对象 要获取到 factorybean 通过getbean的方式去获取。
private Object createBeanByFactoryBean(BeanDefinition bd) throws Exception
String factoryBeanName = bd.getFactoryBeanName();
Object factoryBean = getBean(factoryBeanName);
Method method = factoryBean.getClass()
.getMethod(bd.getFactoryMethodName(), null);
return method.invoke(factoryBean, null);
最终去创建出一个正常的bean出来。
以及 最后的 开始Bean生命周期
// 开始Bean生命周期
if(StringUtils.isNotBlank(bd.getInitMethod()))
doInitMethod(bean, bd);
private void doInitMethod(Object bean, BeanDefinition bd) throws Exception
Method method = bean.getClass().getMethod(bd.getInitMethod(), null);
method.invoke(bean, null);
对单例bean的处理
// 对单例bean的处理
if(bd.isSingleton())
beanMap.put(beanName, bean);
这里都需要作的操作。
最后添加针对单例Bean执行销毁方法
@Override
public void close() throws IOException
// 针对单例Bean执行销毁方法
for(Map.Entry<String, BeanDefinition> e : beanDefinitionMap.entrySet())
String beanName = e.getKey();
BeanDefinition definition = e.getValue();
if(definition.isSingleton() && StringUtils.isNotBlank(definition.getDestroyMethod()))
Object instance = beanMap.get(beanName);
if(instance == null) continue;
Method m = null;
try
m = instance.getClass().getMethod(definition.getDestroyMethod(), null);
m.invoke(instance, null);
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex)
ex.printStackTrace();
设计一个简单的 ioc容器 。
DI分析
这里就涉及到依赖的分析,包括构造参数依赖 属性依赖 。
依赖注入的本质 是给入构造参数值,给属性赋值
因为赋值,才要考虑怎么 赋值给某个值。
对于参数值、属性值 有可能是 直接值、bean依赖
对于这种构造参数 的注入 需要找到依赖关系。
- 基本数据类型值、String
- 数组、集合
- Properties
- map
在DI设计时,如何告诉bean工厂该给入什么构造参数值?即如何来定义参数依赖?
- 集合:List
- 可以,也只能用Object了。
- List<Object> constructorArgumentValues
/**
* 表示Bean依赖的类
*/
public class BeanReference
private String beanName;
public BeanReference(String beanName)
this.beanName = beanName;
public String getBeanName()
return beanName;

DI实现
private Object[] getConstructorArgumentValues(BeanDefinition bd) throws Exception
List<?> args = bd.getConstructorArgumentValues();
return getRealVaues(args);
整个值的解析转换。
这里对于ioc进行创建出来的对象, 如果带有参数的情况。 则需要 创建类进行修改。
private Object createBeanByConstructor(BeanDefinition bd) throws Exception
Object instance = null;
if(CollectionUtils.isEmpty(bd.getConstructorArgumentValues()))
instance = bd.getBeanClass().newInstance();
else
Object[] args = getConstructorArgumentValues(bd);
if(args == null)
instance = bd.getBeanClass().newInstance();
else
return determineConstructor(bd, args).newInstance(args);
return instance;
找对应的构造函数,有参数了,如何断定是哪个构造方法、哪个工厂方法
- 方法是可以重载的
- 形参定义时可能是接口或父类,实参则是具体的子实现
- 反射提供的获取的构造方法、方法的API
- 1 先根据参数的类型进行精确匹配查找,如未找到,则进行第2步查找;
- 2获得所有的构造方法,遍历,通过参数数量过滤,再比对形参类型与实参类型。
private Constructor determineConstructor(BeanDefinition bd, Object[] args) throws Exception
找到无参构造方法 进行判断返回
Constructor ct = null;
if(args == null) return bd.getBeanClass().getConstructor(null);
Class<?>[] paramType = new Class[args.length];
对于原型Bean,从第二次开始获取Bean实例时,可以直接从第一次缓存中获取构造方法
// 对于原型Bean,从第二次开始获取Bean实例时,可以直接从第一次缓存中获取构造方法
ct = bd.getConstructor();
if(ct != null) return ct;
根据参数类型获取构造方法
int j = 0;
for(Object p : args)
paramType[j++] = p.getClass();
找到对应的class.
根据 类型 找到 对应的构造方法
ct = bd.getBeanClass().getConstructor(paramType);
当没找到 时,遍历所有的 构造参数,判断形参跟实参进行类型匹配
if(ct == null)
Constructor<?>[] cts = bd.getBeanClass().getConstructors();
// 判断逻辑:先判断参数数量,依次判断形参跟实参进行类型匹配
outer: for(Constructor<?> c : cts)
Class<?>[] paramterTypes = c.getParameterTypes();
if(paramterTypes.length == args.length)
for(int i = 0; i < paramterTypes.length; i++)
if(!paramterTypes[i].isAssignableFrom(args[i].getClass()))
continue outer;
ct = c;
break outer;
最后进行调用构造方法
if(ct != null)
if(bd.isPrototype())
bd.setConstructor(ct);
else
throw new Exception("找不到对应的构造方法:"+bd);
这里主要两点 主要是要怎么确认 对于bean参数的类型怎么用来区分,以及构造实例时,对参数怎么判断,选择使用那个构造参数 ,判断的逻辑就是 先根据参数的类型进行精确匹配查找,找到了最好,如果找不到 则对比形参类型与实参类型,进行判断。
也就是说,对于原型bean,我们可以缓存下这个构造方法或工厂方法。
这都需要做的。
private Object createBeanByStaticFactory(BeanDefinition bd) throws Exception
Class<?> type = bd.getBeanClass();
Object[] realArgs = getRealVaues(bd.getConstructorArgumentValues());
Method method = determineFactoryMethod(bd, realArgs, type);
return method.invoke(type, realArgs);
determineFactoryMethod 也需要判断具体的方法
循环依赖如何处理
属性依赖
某个属性依赖某个值
描述属性依赖
/**
* 属性值类型,用作属性依赖注入
*/
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;
创建bean成功过后,需要 执行 setPropertyDIVAlues(bd, bean); 完成属性注入。
private void setPropertyDIVAlues(BeanDefinition bd, Object instance) throws Exception
if (CollectionUtils.isEmpty(bd.getPropertyValues()))
return;
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;
p.set(instance, v);
针对属性注入是相对来说简单的。只需要判断需要引入的类型就可以。
以上是关于IOC及DI功能分析与设计的主要内容,如果未能解决你的问题,请参考以下文章