手写Spring DI依赖注入,嘿,你的益达!
上一篇文章中说到,如何手写Spring IOC容器,有了IOC,下面就是手写DI了,根据上一篇文章中的代码继续往下进行,手写Spring IOC入口:点击链接
提前实例化单例Bean
对于单例Bean,可以使用下面的方法进行提前实例化
/**
* 提前构建单例bean的工程
*/
public class PreBuildBeanFactory extends DefaultBeanFactory {
private final Logger logger = LoggerFactory.getLogger(getClass());
private List<String> beanNames = new ArrayList<>();
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionRegisterException {
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);
}
}
}
}
}
}
DI分析
哪些地方会有依赖呢,都知道DI依赖注入有setter注入,构造器注入等,那么就可以知道,有下面两种依赖:
- 构造参数依赖
- 属性依赖
依赖注入的本质就是给值,给入构造参数的值,给属性赋值
参数值和属性值是不是可以是基本的一些值,也有可能是bean依赖,比如会有基本数据类型值、String、数组、集合、map、properties等
不论是参数值还是属性值,这些都是值,bean工厂在进行依赖注入的时候就是给入值
DI的实现
构造参数依赖
一:定义分析
看下面一段代码:
public class Girl {
public Girl(String name, int age, Boy boy) {
//.......
}
}
上面代码就是简单的一个类,构造方法里面有一些参数,那么平时创建Girl的时候就是通过new的方式,即:
Boy boy = new Boy("小明");
Girl girl = new Girl("小芳", 18, boy);
把值直接给Girl,就可以创建一个Girl了,就是这么的简单
那定义构造参数依赖的话,完全可以按照下面这样:
- 第一个参数值是:"小芳"
- 第二个参数值是:18
- 第三个参数值是:Boy,是一个bean依赖
构造参数值可以有多个,而且是有顺序的,如果顺序错了,那么参数的类型可能就对应不上,就会出错,java中的list可以用来存储构造参数,它是有序的对吧;因为参数值可以是直接的值,比如基本类型、string、map等,也可以是一个bean依赖,那么只能用Object来表示了
用Object来表示的话,还会有一个问题,就是bean依赖怎么去区分它呢
可以自己定义一种数据类型来表示bean依赖,当bean工厂在构造Bean实例的时候,遍历参数,判断参数是不是自己定义的数据类型,如果是的话,就替换成依赖的bean实例
如果说参数值是集合、数组,它们中也有bean依赖的话,同样的,需要对它们进行遍历,然后替换
二:定义一个类BeanReference
BeanReference这个类就是用来说明Bean依赖的,依赖的是哪一个bean
/**
* @className: BeanReference
* @description: 在依赖注入中描述bean依赖的
* @date: 2021/4/6 13:25
* @author: jinpeng.sun
*/
public class BeanReference {
/** beanName */
private String beanName;
public BeanReference(String beanName) {
super();
this.beanName = beanName;
}
/** 获取beanName */
public String getBeanName() {
return this.beanName;
}
}
三:BeanDefinition接口及其实现类
定义好了构造参数后,就需要在bean工厂中进行注入,首先要在BeanDefinition接口中增加获取构造参数的接口,然后在实现类中实现它
BeanDefinition增加下面两个接口:
/** 获取构造函数的参数 */
List<?> getConstructorArgumentValues();
/** 设置构造函数的参数 */
void setConstructorArgumentValues(List<?> constructorArgumentValues);
GeneralBeanDefinition实现类中增加如下实现代码:
//构造参数集合
private List<?> constructorArgumentValues;
@Override
public List<?> getConstructorArgumentValues() {
return constructorArgumentValues;
}
@Override
public void setConstructorArgumentValues(List<?> constructorArgumentValues) {
this.constructorArgumentValues = constructorArgumentValues;
}
到此,我们就可以获取到构造参数依赖了,下面就是实现构造参数依赖的注入了
四:DefaultBeanFactory类增加方法
根据上面的内容可以知道,构造参数中会存在bean依赖,那么首先就是需要把bean定义中的构造参数引用转为真实的值,在DefaultBeanFactory类中增加一个方法来做这件事情
/** 获取构造参数值 */
private Object[] getConstructorArgumentValues(BeanDefinition bd) throws Exception {
return this.getRealValues(bd.getConstructorArgumentValues());
}
/** 获取真实的参数值 */
private Object[] getRealValues(List<?> args) throws Exception {
//参数为空,直接返回null
if (CollectionUtils.isEmpty(args)) {
return null;
}
//定义一个数组,长度和传过来的参数值集合大小一致
Object[] values = new Object[args.size()];
int i = 0;
Object v = null;
//遍历参数值,替换bean依赖的实例
for (Object rv : args) {
if (rv == null) {
v = null;
} else if (rv instanceof BeanReference) {
//TODO 获取引用的bean依赖的实例
v = 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 {
//TODO 基本类型
v = rv;
}
values[i++] = v;
}
return values;
}
五:构造参数注入实现
参数有了后,又怎么知道哪个是构造方法,哪个是工厂方法呢?
方法是可以重载的;而且形参定义的可能是接口和父类,实参则是具体的子类实现的
反射提供的获取构造方法和基本方法的API如下:
判断逻辑:
- 先使用第一个方法,根据参数的类型进行精确的匹配查找,如果没有找到就使用下一步
- 获得所有的构造方法,进行遍历,先通过参数数量过滤,再对比形参类型和实参类型
当判断出构造方法或者工厂方法后,对于原型bean,即多例的bean,可以缓存下这个构造方法或者工厂方法,当下一次获取的时候,可以直接从缓存中获取
在BeanDefinition接口中增加如下接口:
/** 获取构造方法 */
Constructor<?> getConstructor();
/** 设置构造方法 */
void setConstructor(Constructor<?> constructor);
/** 获取工厂方法 */
Method getFactoryMethod();
/** 设置工厂方法 */
void setFactoryMethod(Method factoryMethod);
GeneralBeanDefinition实现类中进行实现:
//构造方法
private Constructor<?> constructor;
//工厂方法
private Method factoryMethod;
@Override
public Constructor<?> getConstructor() {
return constructor;
}
@Override
public void setConstructor(Constructor<?> constructor) {
this.constructor = constructor;
}
@Override
public Method getFactoryMethod() {
return factoryMethod;
}
@Override
public void setFactoryMethod(Method factoryMethod) {
this.factoryMethod = factoryMethod;
}
下面就是实现寻找构造方法或者工厂方法了
/** 查找构造方法 */
private Constructor determineConstructor(BeanDefinition bd, Object[] args) throws Exception {
Constructor ct = null;
//参数为空的情况
if (args == null) {
return bd.getBeanClass().getConstructor(null);
}
//定义一个参数数组,长度为传过来的构造参数集合大小
Class<?>[] paramTypes = new Class[args.length];
//对于原型bean,从第二次开始获取Bean实例开始,可以从缓存中获取
ct = bd.getConstructor();
if (ct != null) {
return ct;
}
//1.根据构造参数类型获取构造方法
int i = 0;
for (Object p : args) {
paramTypes[i++] = p.getClass();
}
ct = bd.getBeanClass().getConstructor(paramTypes);
//2.获取所有的构造方法,遍历
if (ct == null) {
Constructor<?>[] cts = bd.getBeanClass().getConstructors();
//先判断参数数量,然后依次判断形参和实参
outer: for (Constructor c : cts) {
//获取构造方法中的参数
Class<?>[] parameterTypes = c.getParameterTypes();
if (parameterTypes.length == args.length) {
for (int j =0; i< parameterTypes.length; j++) {
if (!parameterTypes[i].isAssignableFrom(args[j].getClass())) {
continue outer;
}
}
ct = c;
break outer;
}
}
}
if (ct != null) {
//对于原型bean,缓存起来
if (bd.isProtoType()) {
bd.setConstructor(ct);
}
} else {
throw new Exception("找不到对应的构造方法:" + bd);
}
return ct;
}
修改通过构造函数构建bean实例的方法:
/** 通过构造函数构建bean */
private Object createBeanByConstructor(BeanDefinition bd) throws Exception {
Object object = null;
if (CollectionUtils.isEmpty(bd.getConstructorArgumentValues())) {
//获得的构造参数值是空的,就不传参
object = bd.getBeanClass().newInstance();
} else {
//获得的参数值是空的,就不传参
Object[] args = getConstructorArgumentValues(bd);
if (args == null) {
object = bd.getBeanClass().newInstance();
} else {
//调用方法,实现构造参数依赖注入
return determineConstructor(bd, args).newInstance(args);
}
}
return object;
}
构造方法的方式写好后,按照上面的逻辑来实现静态工厂和工厂方法获取bean实例的方法
private Method determineFactoryMethod(BeanDefinition bd, Object[] realArgs,
Class<?> type) throws Exception {
if (type == null) {
type = bd.getBeanClass();
}
//获取工厂方法名
String factoryMethodName = bd.getFactoryMethodName();
if (realArgs == null) {
return type.getMethod(factoryMethodName, null);
}
Method m = null;
//对于原型bean,从第二次开始获取Bean实例开始,可以从缓存中获取
m = bd.getFactoryMethod();
if (m != null) {
return m;
}
//1.根据参数类型精确匹配方法
Class[] paramTypes = new Class[realArgs.length];
int i = 0;
for (Object p : realArgs) {
paramTypes[i++] = p.getClass();
}
try {
m = type.getMethod(factoryMethodName, paramTypes);
} catch (Exception e) {
//不做任何处理
m = null;
}
//2.获取所有的方法,然后遍历
if (m == null) {
//先判断参数数量,然后依次判断形参和实参
outer: for (Method m0 : type.getMethods()) {
//方法名称不一样,直接继续遍历
if (!m0.getName().equals(factoryMethodName)) {
continue ;
}
//获取找到方法的所有参数
Class<?>[] parameterTypes = m0.getParameterTypes();
if (parameterTypes.length == realArgs.length) {
for (int j =0; j< parameterTypes.length; j++) {
if (!parameterTypes[j].isAssignableFrom(realArgs[j].getClass())) {
continue outer;
}
}
m = m0;
break outer;
}
}
}
if (m != null) {
//对于原型bean,缓存下方法
if (bd.isProtoType()) {
bd.setFactoryMethod(m);
}
} else {
throw new Exception("找不到对应的方法:" + bd);
}
return m;
}
然后下面修改通过静态工厂获取Bean和成员工厂获取Bean的方法
/** 通过静态工厂构建bean */
private Object createBeanByStaticFactoryMethod(BeanDefinition bd) throws Exception {
Class<?> type = bd.getBeanClass();
Object[] realArgs = getRealValues(bd.getConstructorArgumentValues());
Method method = determineFactoryMethod(bd, realArgs, type);
Object object = method.invoke(type, realArgs);
return object;
}
/** 通过成员工厂构建bean */
private Object createBeanByFactoryBean(BeanDefinition bd) throws Exception {
String factoryBeanName = bd.getFactoryBeanName();
Object factoryBean = getBean(factoryBeanName);
Object[] realArgs = getRealValues(bd.getConstructorArgumentValues());
Method method = determineFactoryMethod(bd, realArgs, factoryBean.getClass());
Object object = method.invoke(factoryBean, realArgs);
return object;
}
六:构造参数依赖测试
测试使用的几个类:
public interface Boy {
void sayLove();
void play();
}
public class Lad implements Boy {
private String name;
private Girl girl;
private Money money;
public Lad(String name) {
this.name = name;
}
public Lad(String name, Girl gf) {
this.name = name;
this.girl = gf;
System.out.println("调用了含有Girl参数的构造方法");
}
public Lad(String name, MagicGirl gf) {
this.name = name;
this.girl = gf;
System.out.println("调用了含有MMagicGirll参数的构造方法");
}
public Lad(String name, Money m) {
this.name = name;
this.money = m;
System.out.println("调用了含有Money参数的构造方法");
}
public Girl getFriend() {
return girl;
}
public void setFriend(Girl girl) {
this.girl = girl;
}
@Override
public void sayLove() {
if (girl == null) {
System.out.println("没有对象好难过!" + hashCode());
} else {
System.out.println("我爱你,亲爱的!" + girl);
}
}
@Override
public void play() {
if (money == null) {
System.out.println("没有钱怎么玩!" + hashCode());
} else {
System.out.println("有了钱开心的玩耍!" + money);
}
}
public void init() {
System.out.println("老天赐予我一个对象吧!");
}
public void destroy() {
System.out.println("自古多情空余恨,此恨绵绵无绝期!");
}
}
public interface Girl {
}
public class MagicGirl implements Girl {
private String name;
private Boy friend;
public MagicGirl(){}
public MagicGirl(String name) {
this.name = name;
}
public Boy getFriend() {
return friend;
}
public void setFriend(Boy friend) {
this.friend = friend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MagicGril{" +
"name='" + name + '\'' +
'}';
}
}
public interface Money {
void pay();
}
public class Renminbi implements Money {
@Override
public void pay() {
System.out.println("使用人民币成功进行了支付");
}
}
public class BoyFactory {
public static Boy getBean(String name, Money money) {
return new Lad(name, money);
}
}
public class BoyFactoryBean {
public Boy buildBoy(String name, Girl girl) {
return new Lad(name, girl);
}
}
构造函数注入测试:
static PreBuildBeanFactory bf = new PreBuildBeanFactory();
/** 构造函数注入测试 */
@Test
public void testConstructorDI() throws Exception {
GenericBeanDefinition definition = new GenericBeanDefinition();
//设置beanClass
definition.setBeanClass(Lad.class);
//设置构造函数的参数
List<Object> args = new ArrayList<>();
args.add("孙悟空");
args.add(new BeanReference("magicGirl"));
definition.setConstructorArgumentValues(args);
bf.registerBeanDefinition("swk", definition);
definition = new GenericBeanDefinition();
//设置beanClass
definition.setBeanClass(MagicGirl.class);
//设置构造函数的参数
args = new ArrayList<>();
args.add("白骨精");
definition.setConstructorArgumentValues(args);
bf.registerBeanDefinition("magicGirl", definition);
bf.preInstantiateSingletons();
Lad lad = (Lad) bf.getBean("swk");
lad.sayLove();
}
执行结果:
静态工厂注入测试:
/** 静态工厂注入测试 */
@Test
public void testStaticFactoryDI() throws Exception {
GenericBeanDefinition definition = new GenericBeanDefinition();
//设置beanClass
definition.setBeanClass(BoyFactory.class);
//设置方法名
definition.setFactoryMethodName("getBean");
//设置构造函数的参数
List<Object> args = new ArrayList<>();
args.add("牛郎");
args.add(new BeanReference("rmb"));
definition.setConstructorArgumentValues(args);
bf.registerBeanDefinition("nl", definition);
definition = new GenericBeanDefinition();
//设置beanClass
definition.setBeanClass(Renminbi.class);
bf.registerBeanDefinition("rmb", definition);
bf.preInstantiateSingletons();
Boy boy = (Boy) bf.getBean("nl");
boy.play();
}
执行结果:
成员工厂注入测试:
/** 成员工厂注入测试 */
@Test
public void testFactoryMethodDI() throws Exception {
GenericBeanDefinition definition = new GenericBeanDefinition();
String fBeanName = "boyFactoryBean";
//设置工厂bean
definition.setFactoryBeanName(fBeanName);
//设置方法名
definition.setFactoryMethodName("buildBoy");
//设置构造函数的参数
List<Object> args = new ArrayList<>();
args.add("猪八戒");
args.add(new BeanReference("xlv"));
definition.setConstructorArgumentValues(args);
bf.registerBeanDefinition("zbj", definition);
definition = new GenericBeanDefinition();
definition.setBeanClass(BoyFactoryBean.class);
bf.registerBeanDefinition("boyFactoryBean", definition);
definition = new GenericBeanDefinition();
//设置beanClass
definition.setBeanClass(MagicGirl.class);
bf.registerBeanDefinition("xlv", definition);
bf.preInstantiateSingletons();
Boy boy = (Boy) bf.getBean("zbj");
boy.sayLove();
}
执行结果:
循环依赖的处理
当我们构建对象的时候,可以循环依赖吗?
写个例子测试一下:
public class NiuLang {
private ZhiNv zhiNv;
public NiuLang(ZhiNv zhiNv) {
this.zhiNv = zhiNv;
}
}
public class ZhiNv {
private NiuLang niuLang;
public ZhiNv(NiuLang niuLang) {
this.niuLang = niuLang;
}
}
测试类:
@Test
public void testCycleDI() throws Exception {
GenericBeanDefinition definition = new GenericBeanDefinition();
//设置beanClass
definition.setBeanClass(NiuLang.class);
//设置构造函数的参数
List<Object> args = new ArrayList<>();
args.add(new BeanReference("zv"));
definition.setConstructorArgumentValues(args);
bf.registerBeanDefinition("nl", definition);
definition = new GenericBeanDefinition();
//设置beanClass
definition.setBeanClass(Renminbi.class);
//设置构造函数的参数
args = new ArrayList<>();
args.add(new BeanReference("nl"));
definition.setConstructorArgumentValues(args);
bf.registerBeanDefinition("zv", definition);
bf.preInstantiateSingletons();
}
运行结果:
可以看到,出现了错误
如果构建实例对象时循环依赖了,就会陷入僵死的局面,所以这个是不允许的
那么,可以在工厂的实现类中,加入一个正在构建的Bean的记录,当这个Bean正在构建的时候,就加入到这个记录中,构建完成就删除这个记录;如果有依赖,就看这个依赖是否在构建中,如果是的话就构成了循环依赖,就抛出异常就可以了
/** 正在创建的bean */
private Set<String> buildingBeans = Collections.newSetFromMap(new ConcurrentHashMap());
在doGetBean方法中加入如下代码:
Set<String> beans = buildingBeans;
//检测循环依赖
if (beans.contains(beanName)) {
throw new Exception(beanName + "循环依赖" + beans);
}
beans.add(beanName);
//bean实例创建完后删除
beans.remove(beanName);
//对单例bean的处理
if (bd.isSingleton()) {
beanMap.put(beanName, bean);
}
再次运行结果:
属性依赖
属性依赖即是某个属性依赖于某个值
如果需要描述一个属性依赖,就是属性名+值,那么可以定义一个类来表示属性依赖
如果有多个属性,也是使用List进行存储,属性依赖的处理情况和构造参数值基本上是一样的
public class Girl {
private String name;
private Integer age;
private Boy boy;
}
一:属性依赖的定义
定义类PropertyValue用来表示属性依赖
**
* @className: PropertyValue
* @description: 属性值类型,用做属性依赖注入
* @date: 2021/4/7 09:11
* @author: jinpeng.sun
*/
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;
}
}
二:BeanDefinition接口及其实现类
BeanDefinition类增加下面两个接口:
/** 获取属性值 */
List<PropertyValue> getPropertyValues();
/** 设置属性值 */
void setPropertyValues(List<PropertyValue> propertyValues);
GeneralBeanDefinition实现类中增加相应的实现代码:
//属性值
private List<PropertyValue> propertyValues;
@Override
public List<PropertyValue> getPropertyValues() {
return propertyValues;
}
@Override
public void setPropertyValues(List<PropertyValue> propertyValues) {
this.propertyValues = propertyValues;
}
三:DefaultBeanFactory类中实现属性依赖
/** 属性依赖 */
private void setPropertyDIValues(BeanDefinition bd, Object bean) throws Exception {
//获取不到属性值,直接返回
if (CollectionUtils.isEmpty(bd.getPropertyValues())) {
return;
}
//遍历所有属性值
for (PropertyValue pv : bd.getPropertyValues()) {
String name = pv.getName();
if (StringUtils.isBlank(name)) {
continue;
}
Class<?> classz = bean.getClass();
//获取属性
Field p = classz.getDeclaredField(name);
p.setAccessible(true);
Object rv = pv.getValue();
Object v = null;
if (rv == null) {
v = null;
} else if (rv instanceof BeanReference) {
v = 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(bean, v);
}
}
属性依赖是在bean实例创建完成之后,bean初始化之前调用的,所以需要改下doGetBean方法
@Override
public Object getBean(String beanName) throws Exception {
//获取bean定义
BeanDefinition bd = beanDefinitionMap.get(beanName);
Object bean = doGetBean(beanName);
//属性注入
setPropertyDIValues(bd, bean);
//bean的生命周期
if (StringUtils.isNotBlank(bd.getInitMethodName())) {
doInitMethod(bean,bd);
}
return doGetBean(beanName);
}
public Object doGetBean(String beanName) throws Exception {
Object bean = beanMap.get(beanName);
if (bean != null) {
return bean;
}
//获取bean定义
BeanDefinition bd = beanDefinitionMap.get(beanName);
Objects.requireNonNull(bd, "找不到["+beanName+"]的bean定义信息");
Class<?> type = bd.getBeanClass();
//检测循环依赖
Set<String> beans = this.buildingBeans;
if (beans.contains(beanName)) {
throw new Exception(beanName + "循环依赖" + beans);
}
beans.add(beanName);
if (type != null) {
if (StringUtils.isBlank(bd.getFactoryMethodName())) {
//通过构造函数构建bean
bean = createBeanByConstructor(bd);
} else {
//通过静态工厂构建bean
bean = createBeanByStaticFactoryMethod(bd);
}
} else {
//通过成员工厂构建bean
bean = createBeanByFactoryBean(bd);
}
//实例创建完成后进行删除
beans.remove(beanName);
//对单例bean处理
if (bd.isSingleton()) {
beanMap.put(beanName, bean);
}
return bean;
}
四:属性依赖测试
/** 属性注入测试 */
@Test
public void testPropertyDI() throws Exception {
GenericBeanDefinition definition = new GenericBeanDefinition();
//设置beanClass
definition.setBeanClass(Lad.class);
//设置构造函数的参数
List<Object> args = new ArrayList<>();
args.add("孙悟空");
args.add(new BeanReference("bgj"));
definition.setConstructorArgumentValues(args);
bf.registerBeanDefinition("swk", definition);
definition = new GenericBeanDefinition();
//设置beanClass
definition.setBeanClass(MagicGirl.class);
//设置属性注入的参数值
List<PropertyValue> propertyValues = new ArrayList<>();
propertyValues.add(new PropertyValue("name", "白骨精"));
propertyValues.add(new PropertyValue("friend", new BeanReference("swk")));
definition.setPropertyValues(propertyValues);
bf.registerBeanDefinition("bgj", definition);
bf.preInstantiateSingletons();
MagicGirl magicGirl = (MagicGirl) bf.getBean("bgj");
System.out.println(magicGirl.getName() + ":" + magicGirl.getFriend());
magicGirl.getFriend().sayLove();
}
执行结果:
到此,手写Spring DI就结束了,希望本文对您有所帮助!