当前位置: 首页 > news >正文

Spring源码之手写DI

Spring源码之手写DI

我们来回顾一下前面手写IOC的内容。

image.png

一、DI介绍

DI(Dependency injection)依赖注入。对象之间的依赖由容器在运行期决定,即容器动态的将某个依赖注入到对象之中。说直白点就是给Bean对象的成员变量赋值。

在这里我们就需要明白几个问题。

1. 哪些地方会有依赖

  1. 构造参数依赖
  2. 属性依赖

2. 依赖注入的本质是什么?

依赖注入的本质是 赋值。赋值有两种情况

  1. 给有参构造方法赋值
  2. 给属性赋值

3. 参数值、属性值有哪些?

具体赋值有两种情况:直接值和Bean依赖。比如

public clsss Girl{public Girl(String name, int age, Clouth clouth){private String name;private Integer age;private Clothes clothes;}
}

4. 直接赋值有哪些?

  1. 基本数据类型:String、int 等
  2. 数组,集合
  3. map

二、构造注入

我们先来看看构造参数注入的情况应该要如何解决。

1.构造注入分析

我们应该如何定义构造参数的依赖?也就是我们需要通过构造方法来创建实例,然后对应的构造方法我们需要传入对应的参数。如果不是通过IoC来处理,我们可以直接通过如下的代码实现。

    public static void main(String[] args) {Clouth clouth  = new Dress();Girl girl = new Girl("小龙女", 18, clouth);}

我们通过直接赋值的方式就可以了。但是在IOC中我们需要通过反射的方式来进行通用处理。在使用反射操作的时候就需要能获取到对应的构造参数的依赖了,这时我们得分析怎么来存储我们的构造参数的依赖了。构造参数的依赖有两个特点:

  1. 顺序
  2. 数量
  3. 类型

上面例子中构造函数的参数

  1. 小丽, String类型
  2. 20, int 类型
  3. clouth, 自定义的Clouth类型,是一个依赖Bean

参数可以有多个,我们可以通过List集合来存储,这样构造参数的顺序就可以通过控制往List集合中add元素的顺来保证。但是需要注意的是,依赖Bean如何表示呢?

基本类型的值直接添加到集合中就可以了,但是依赖Bean的实例对象可能还没有创建,那么这时我们可以抽象出一个类,用来描述依赖Bean的信息。

2. BeanReference

注意:

BeanReference其实就是用来描述依赖Bean信息的

image.png

/*** 用于依赖注入中描述bean依赖*/
public class BeanReference {private String beanName;private Class<?> type;public BeanReference(Class<?> type) {this.type = type;}public String getBeanName() {return beanName;}public void setBeanName(String beanName) {this.beanName = beanName;}public Class<?> getType() {return type;}public void setType(Class<?> type) {this.type = type;}
}

可以根据name来依赖,也可以按照Type来依赖。另外还有一点需要考虑,就是如何来区分是直接值还是依赖Bean呢?有了上面的设计其实就很容易判断了。

if ( obj instanceOf BeanReference)

但是下面这种直接值是数组或者集合等,同时容器中的元素是依赖Bean的情况:

import java.io.Serializable;
import java.util.List;public class Girl implements Serializable {private String name;private Integer age;private List<Clothes> clothesList;public Girl(String name, Integer age, List<Clothes> clothesList) {this.name = name;this.age = age;this.clothesList = clothesList;}
}

这种情况下元素值仍然可以用BeanReference来处理。Bean工厂在处理时需要遍历替换。

3. BeanDefinition实现

接下来我们看看如何具体的来实现DI基于构造函数参数依赖的相关操作。首先是BeanDefinition定义的相关处理。需要在 BeanDefinition中增加构造参数的获取的方法。

image.png

然后我们需要在默认的实现GenericBeanDefinition中增加对应的方法来处理。

image.png

BeanDefinition接口

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;import org.apache.commons.lang3.StringUtils;/*** bean定义接口*/
public interface BeanDefinition {String SCOPE_SINGLETON = "singleton";String SCOPE_PROTOTYPE = "prototype";/*** 类*/Class<?> getBeanClass();/*** Scope*/String getScope();/*** 是否单例*/boolean isSingleton();/*** 是否原型*/boolean isPrototype();/*** 工厂bean名*/String getFactoryBeanName();/*** 工厂方法名*/String getFactoryMethodName();/*** 初始化方法*/String getInitMethodName();/*** 销毁方法*/String getDestroyMethodName();boolean isPrimary();/*** 校验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;}/*** 获得构造参数定义*/List<?> getConstructorArgumentValues();/*** 属性依赖*/List<PropertyValue> getPropertyValues();/*** 下面的四个方法是供beanFactory中使用的*/public Constructor<?> getConstructor();public void setConstructor(Constructor<?> constructor);public Method getFactoryMethod();public void setFactoryMethod(Method factoryMethod);}

BeanDefinition的通用实现GernericBeanDefinition


import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;import org.apache.commons.lang3.StringUtils;public class GenericBeanDefinition implements BeanDefinition {private Class<?> beanClass;private String scope = BeanDefinition.SCOPE_SINGLETON;private String factoryBeanName;private String factoryMethodName;private String initMethodName;private String destroyMethodName;private boolean primary;private Constructor<?> constructor;private Method factoryMethod;private List<?> constructorArgumentValues;private List<PropertyValue> propertyValues;public void setBeanClass(Class<?> beanClass) {this.beanClass = beanClass;}public void setScope(String scope) {if (StringUtils.isNotBlank(scope)) {this.scope = scope;}}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;}@Overridepublic Class<?> getBeanClass() {return this.beanClass;}@Overridepublic String getScope() {return this.scope;}@Overridepublic boolean isSingleton() {return BeanDefinition.SCOPE_SINGLETON.equals(this.scope);}@Overridepublic boolean isPrototype() {return BeanDefinition.SCOPE_PROTOTYPE.equals(this.scope);}@Overridepublic String getFactoryBeanName() {return this.factoryBeanName;}@Overridepublic String getFactoryMethodName() {return this.factoryMethodName;}@Overridepublic String getInitMethodName() {return this.initMethodName;}@Overridepublic String getDestroyMethodName() {return this.destroyMethodName;}public void setPrimary(boolean primary) {this.primary = primary;}@Overridepublic boolean isPrimary() {return this.primary;}public List<?> getConstructorArgumentValues() {return constructorArgumentValues;}public void setConstructorArgumentValues(List<?> constructorArgumentValues) {this.constructorArgumentValues = constructorArgumentValues;}public List<PropertyValue> getPropertyValues() {return propertyValues;}public void setPropertyValues(List<PropertyValue> propertyValues) {this.propertyValues = propertyValues;}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;}@Overridepublic String toString() {return "GenericBeanDefinition [beanClass=" + beanClass + ", scope=" + scope + ", factoryBeanName="+ factoryBeanName + ", factoryMethodName=" + factoryMethodName + ", initMethodName=" + initMethodName+ ", destroyMethodName=" + destroyMethodName + "]";}@Overridepublic 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;}@Overridepublic 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;}}

定义后我们来测试一下对应的应用,定义个TestOneBean,依赖了TestTwoBean

public class TestOneBean {private String name;private TestTwoBean testTwoBean;public TestOneBean(String name, TestTwoBean testTwoBean) {super();this.name = name;this.testTwoBean = testTwoBean;System.out.println("调用了含有TestTwoBean参数的构造方法");}public TestOneBean(String name, TestThreeBean testThreeBean) {super();this.name = name;this.testThreeBean = testThreeBean;System.out.println("调用了含有TestThreeBean参数的构造方法");}public ABean(TestTwoBean testTwoBean) {super();this.testTwoBean = testTwoBean;}public void doSomthing() {System.out.println(String.format("TestOneBean.doSomthing(): %s TestOneBean.name= %s", this.name, this.testTwoBean.getName()));}public void init() {System.out.println("TestOneBean.init() 执行了");}public void destroy() {System.out.println("TestOneBean.destroy() 执行了");}
}

然后在实例化时我们需要做相关的绑定

GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(TestOneBean.class);
// 定义的构造参数的依赖
List<Object> args = new ArrayList<>();
args.add("testOneBean");
// 依赖Bean 通过BeanReference来处理
args.add(new BeanReference("testTwoBean"));
bd.setConstructorArgumentValues(args);
bf.registerBeanDefinition("testOneBean", bd);

构造参数传递后,接下来需要在 BeanFactory中来实现构造参数的注入了

4.BeanFactory实现

前面我们在BeanFactory中实现Bean对象的创建有几种方式

  1. 构造方法创建
  2. 工厂静态方法
  3. 工厂成员方法
import java.util.Map;public interface BeanFactory {Object getBean(String name) throws Exception;<T> T getBean(Class<T> type)throws Exception;<T> Map<String,T> getBeansOfType(Class<T> type)throws Exception;Class<?> getType(String name) throws Exception;
}

BeanFactory的默认实现DefaultBeanFactory

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;/*** 1.构造参数依赖注入的: 参数真实值获取实现,构造方法方式的构造方法的判定实现* 2.缓存原型bean的构造方法、工厂方法,增加了 静态工厂方法、工厂bean工厂方法的参数依赖实现* 3.构造循环依赖检测* 4.属性依赖实现*/
@Slf4j
public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {protected Map<String, BeanDefinition> beanDefintionMap = new ConcurrentHashMap<>(256);private final Map<String, Object> singletonBeanMap = new ConcurrentHashMap<>(256);private final Map<Class<?>, Set<String>> typeMap = new ConcurrentHashMap<>(256);private final ThreadLocal<Set<String>> buildingBeansRecorder = new ThreadLocal<>();/*** Spring 中属性依赖的情况: 如果循环依赖中不存在单例,则不可以,否则可以*/private final ThreadLocal<Map<String, Object>> earlyExposeBuildingBeans = new ThreadLocal<>();@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionRegistException {Objects.requireNonNull(beanName, "注册bean需要指定beanName");Objects.requireNonNull(beanDefinition, "注册bean需要指定beanDefinition");// 校验给入的bean是否合法if (!beanDefinition.validate()) {throw new BeanDefinitionRegistException("名字为[" + beanName + "] 的bean定义不合法:" + beanDefinition);}if (this.containsBeanDefinition(beanName)) {throw new BeanDefinitionRegistException("名字为[" + beanName + "] 的bean定义已存在:" + this.getBeanDefinition(beanName));}this.beanDefintionMap.put(beanName, beanDefinition);}public void registerTypeMap() throws Exception {for (String name : this.beanDefintionMap.keySet()) {Class<?> type = this.getType(name);//映射本类this.registerTypeMap(name, type);//父类this.registerSuperClassTypeMap(name, type);//接口this.registerInterfaceTypeMap(name, type);}}private void registerInterfaceTypeMap(String name, Class<?> type) {Class<?>[] interfaces = type.getInterfaces();for (Class<?> interfaceClasses : interfaces) {this.registerTypeMap(name, interfaceClasses);//递归找父接口this.registerInterfaceTypeMap(name, interfaceClasses);}}private void registerSuperClassTypeMap(String name, Class<?> type) {Class<?> superClass = type.getSuperclass();if (superClass != null && !superClass.equals(Object.class)) {this.registerTypeMap(name, superClass);//递归找父类this.registerSuperClassTypeMap(name, superClass);//找父类实现的接口注册this.registerInterfaceTypeMap(name, superClass);}}private void registerTypeMap(String name, Class<?> type) {Set<String> names2type = this.typeMap.computeIfAbsent(type, k -> new HashSet<>());names2type.add(name);}@Overridepublic Class<?> getType(String name) throws Exception {BeanDefinition beanDefinition = this.getBeanDefinition(name);Class<?> type = beanDefinition.getBeanClass();if (type != null) {if (StringUtils.isBlank(beanDefinition.getFactoryMethodName())) {// 构造方法来构造对象的,Type就是beanClass,不需做什么。} else {// 静态工厂方法方式的,反射获得Method,再获取Method的返回值类型type = type.getDeclaredMethod(beanDefinition.getFactoryMethodName(), null).getReturnType();}} else {// 获得工厂Bean的Classtype = this.getType(beanDefinition.getFactoryBeanName());// 获取工厂方法的返回值类型type = type.getDeclaredMethod(beanDefinition.getFactoryMethodName(), null).getReturnType();}return type;}@Overridepublic BeanDefinition getBeanDefinition(String beanName) {return this.beanDefintionMap.get(beanName);}@Overridepublic boolean containsBeanDefinition(String beanName) {return this.beanDefintionMap.containsKey(beanName);}@Overridepublic Object getBean(String name) throws Exception {return this.doGetBean(name);}@Overridepublic <T> T getBean(Class<T> type) throws Exception {/** 逻辑:* 1. 获取其对应的所有的BeanDefinition* 2. 如果只有一个,直接获取bean实例返回,否则* 3. 遍历找出Primary的* 4. 如果primary没有,或大于1个,抛出异常* 5. 返回Primary的实例*/Set<String> names = this.typeMap.get(type);if (names != null) {if (names.size() == 1) {return (T) this.getBean(names.iterator().next());} else {//找PrimaryBeanDefinition beanDefinition = null;String primaryName = null;StringBuilder nameStrings = new StringBuilder();for (String name : names) {beanDefinition = this.getBeanDefinition(name);if (beanDefinition != null && beanDefinition.isPrimary()) {if (primaryName != null) {String mess = type + " 类型的Bean存储多个Primary[" + primaryName + "," + name + "]";log.error(mess);throw new Exception(mess);} else {primaryName = name;}}nameStrings.append(" ").append(name);}if (primaryName != null) {return (T) this.getBean(primaryName);} else {String mess = type + " 类型的Bean存在多个[" + nameStrings + "] 但无法确定Primary";log.error(mess);throw new Exception(mess);}}}return null;}@Overridepublic <T> Map<String, T> getBeansOfType(Class<T> type) throws Exception {Set<String> names = this.typeMap.get(type);if (names != null) {Map<String, T> map = new HashMap<>();for (String name : names) {map.put(name, (T) this.getBean(name));}return map;}return null;}protected Object doGetBean(String beanName) throws Exception {Objects.requireNonNull(beanName, "beanName不能为空");Object instance = singletonBeanMap.get(beanName);if (instance != null) {return instance;}instance = this.getFromEarlyExposeBuildingBeans(beanName);if (instance != null) { //这是属性依赖时的循环引用,返回提前暴露的实例return instance;}BeanDefinition beanDefinition = this.getBeanDefinition(beanName);Objects.requireNonNull(beanDefinition, "beanDefinition不能为空");// 检测构造参数循环依赖Set<String> buildingBeans = this.buildingBeansRecorder.get();if (buildingBeans == null) {buildingBeans = new HashSet<>();this.buildingBeansRecorder.set(buildingBeans);}if (buildingBeans.contains(beanName)) {throw new Exception(beanName + " 循环依赖!" + buildingBeans);}// 记录正在创建的BeanbuildingBeans.add(beanName);if (beanDefinition.isSingleton()) { //如果是单例synchronized (this.singletonBeanMap) { //加锁instance = this.singletonBeanMap.get(beanName);if (instance == null) {//第二次检查instance = doCreateInstance(beanName, beanDefinition);this.singletonBeanMap.put(beanName, instance);}}} else {instance = doCreateInstance(beanName, beanDefinition);}// 创建好实例后,移除创建中记录buildingBeans.remove(beanName);return instance;}private Object getFromEarlyExposeBuildingBeans(String beanName) {Map<String, Object> earlyExposeBuildingBeansMap = earlyExposeBuildingBeans.get();return earlyExposeBuildingBeansMap == null ? null : earlyExposeBuildingBeansMap.get(beanName);}private Object doCreateInstance(String beanName, BeanDefinition beanDefinition) throws Exception {Class<?> type = beanDefinition.getBeanClass();Object instance;if (type != null) {if (StringUtils.isBlank(beanDefinition.getFactoryMethodName())) {// 构造方法来构造对象instance = this.createInstanceByConstructor(beanDefinition);} else {// 静态工厂方法instance = this.createInstanceByStaticFactoryMethod(beanDefinition);}} else {// 工厂bean方式来构造对象instance = this.createInstanceByFactoryBean(beanDefinition);}this.doEarlyExposeBuildingBeans(beanName, instance);// 给入属性依赖this.setPropertyDIValues(beanDefinition, instance);this.removeEarlyExposeBuildingBeans(beanName, instance);// 执行初始化方法this.doInit(beanDefinition, instance);return instance;}private void removeEarlyExposeBuildingBeans(String beanName, Object instance) {earlyExposeBuildingBeans.get().remove(beanName);}private void doEarlyExposeBuildingBeans(String beanName, Object instance) {Map<String, Object> earlyExposeBuildingBeansMap = earlyExposeBuildingBeans.get();if (earlyExposeBuildingBeansMap == null) {earlyExposeBuildingBeansMap = new HashMap<>();earlyExposeBuildingBeans.set(earlyExposeBuildingBeansMap);}earlyExposeBuildingBeansMap.put(beanName, instance);}// 给入属性依赖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);p.set(instance, this.getOneArgumentRealValue(pv.getValue()));}}/*** 构造方法来构造对象** @param beanDefinition bean定义* @return bean实例对象* @throws Exception 异常*/private Object createInstanceByConstructor(BeanDefinition beanDefinition)throws Exception {/*构造参数依赖注入,这里需要做些什么?1 得到真正的参数值,因为List<?> constructorArgumentValues = beanDefinition.getConstructorArgumentValues();constructorArgumentValues 中可能有 BeanReference*/Object[] args = this.getConstructorArgumentValues(beanDefinition);// 2 判定应该调用哪个构造方法来创建实例return this.determineConstructor(beanDefinition, args).newInstance(args);}private Object[] getConstructorArgumentValues(BeanDefinition bd) throws Exception {List<?> constructorArgumentValues = bd.getConstructorArgumentValues();if (CollectionUtils.isEmpty(constructorArgumentValues)) {return null;}Object[] values = new Object[constructorArgumentValues.size()];int i = 0;for (Object originalValue : constructorArgumentValues) {values[i++] = getOneArgumentRealValue(originalValue); //获取真正参数值的逻辑应该是怎样的?}return values;}private Object getOneArgumentRealValue(Object originalValue) throws Exception {//获取真正参数值,主要是处理BeanReference,得到真正的Bean实例Object realValue = null;if (originalValue != null) {if (originalValue instanceof BeanReference) {BeanReference beanReference = (BeanReference) originalValue;if (StringUtils.isNotBlank(beanReference.getBeanName())) {realValue = this.getBean(beanReference.getBeanName());} else if (beanReference.getType() != null) {realValue = this.getBean(beanReference.getType());}} else if (originalValue instanceof Object[]) {// 处理数组中的bean引用Object[] originalValueNew = (Object[]) originalValue;for (int i = 0; i < originalValueNew.length; i++) {Object attr = originalValueNew[i];Class<?> attrClass = attr.getClass();String simpleName = attrClass.getSimpleName();BeanReference beanReference = new BeanReference(attrClass);beanReference.setBeanName(getBeanName(simpleName));originalValueNew[i] = beanReference;}realValue = originalValueNew;} else if (originalValue instanceof Collection) {// 处理集合中的bean引用Type genericSuperclass = originalValue.getClass().getGenericSuperclass();Class<? extends Type> genericClass = genericSuperclass.getClass();BeanReference beanReference = new BeanReference(genericClass);String simpleName = genericClass.getSimpleName();String beanName = getBeanName(simpleName);beanReference.setBeanName(beanName);Collection<BeanReference> originalValueNew = new ArrayList<>();originalValueNew.add(beanReference);realValue = originalValueNew;} else if (originalValue instanceof Properties) {// 处理properties中的bean引用Properties originValueNew = new Properties();originValueNew.put(new BeanReference(null), new BeanReference(null));realValue = originValueNew;} else if (originalValue instanceof Map) {// 处理Map中的bean引用Map<BeanReference, BeanReference> originValueNew = new HashMap<>();originValueNew.put(new BeanReference(null), new BeanReference(null));realValue = originValueNew;} else {realValue = originalValue;}}return realValue;}private String getBeanName(String simpleName) {if (simpleName == null) {throw new IllegalArgumentException("simpleName不能为空");}String firstChar = simpleName.charAt(0) + "";return firstChar.toLowerCase() + simpleName.substring(1);}private Constructor<?> determineConstructor(BeanDefinition bd, Object[] args) throws Exception {/*判定构造方法的逻辑应是怎样的?1 先根据参数的类型进行精确匹配查找,如未找到,则进行第2步查找;2获得所有的构造方法,遍历,通过参数数量过滤,再比对形参类型与实参类型。* */Constructor<?> ct = null;//没有参数,则用无参构造方法if (args == null) {return bd.getBeanClass().getConstructor(null);}// 对于原型bean,从第二次开始获取bean实例时,可直接获得第一次缓存的构造方法。ct = bd.getConstructor();if (ct != null) {return ct;}// 1 根据参数类型获取精确匹配的构造方法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) {// 2 没有精确参数类型匹配的,获得所有的构造方法,遍历,通过参数数量过滤,再比对形参类型与实参类型。// 判断逻辑:先判断参数数量,再依次比对形参类型与实参类型outer:for (Constructor<?> ct0 : bd.getBeanClass().getConstructors()) {Class<?>[] paramterTypes = ct0.getParameterTypes();if (paramterTypes.length == args.length) {   //通过参数数量过滤for (int i = 0; i < paramterTypes.length; 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);}}private Method determineFactoryMethod(BeanDefinition bd, Object[] args, Class<?> type) throws Exception {/*判定工厂方法的逻辑同构造方法的判定逻辑1 先根据实参的类型进行精确匹配查找,如未找到,则进行第2步查找;2 获得所有方法,遍历,通过方法名、参数数量过滤,再比对形参类型与实参类型。* */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;}// 1 先根据实参的类型进行精确匹配查找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) {// 没有精确参数类型匹配的,则遍历匹配所有的方法// 2 获得所有方法,遍历,通过方法名、参数数量过滤,再比对形参类型与实参类型。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++) {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);}}// 静态工厂方法private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception {Object[] realArgs = this.getConstructorArgumentValues(bd);Class<?> type = bd.getBeanClass();Method m = this.determineFactoryMethod(bd, realArgs, type);return m.invoke(type, realArgs);}// 工厂bean方式来构造对象private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception {Object[] realArgs = this.getConstructorArgumentValues(bd);Method m = this.determineFactoryMethod(bd, realArgs, this.getType(bd.getFactoryBeanName()));Object factoryBean = this.doGetBean(bd.getFactoryBeanName());return m.invoke(factoryBean, realArgs);}/*** 执行初始化方法** @param beanDefinition bean定义* @param instance       实例对象* @throws Exception 异常*/private void doInit(BeanDefinition beanDefinition, Object instance) throws Exception {// 执行初始化方法if (StringUtils.isNotBlank(beanDefinition.getInitMethodName())) {Method m = instance.getClass().getMethod(beanDefinition.getInitMethodName(), null);m.invoke(instance, null);}}@Overridepublic void close() throws IOException {// 执行单例实例的销毁方法for (Entry<String, BeanDefinition> e : this.beanDefintionMap.entrySet()) {String beanName = e.getKey();BeanDefinition bd = e.getValue();if (bd.isSingleton() && StringUtils.isNotBlank(bd.getDestroyMethodName())) {Object instance = this.singletonBeanMap.get(beanName);try {Method m = instance.getClass().getMethod(bd.getDestroyMethodName(), null);m.invoke(instance, null);} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException| InvocationTargetException e1) {log.error("执行bean[" + beanName + "] " + bd + " 的 销毁方法异常!", e1);}}}}
}

我们通过构造方法创建其实是通过无参构造方法来处理的,这时我们需要改变这块的逻辑,通过有参构造方法来实现。

// 构造方法来构造对象
private Object createInstanceByConstructor(BeanDefinition bd)throws InstantiationException, IllegalAccessException {try {return bd.getBeanClass().newInstance();} catch (SecurityException e1) {log.error("创建bean的实例异常,beanDefinition:" + bd, e1);throw e1;}
}

我们就需要对上面的方法做出改变。

// 构造方法来构造对象
private Object createInstanceByConstructor(BeanDefinition bd)throws InstantiationException, IllegalAccessException {// 1. 得到真正的参数值List<?> constructorArgumentValues = bd.getConstructorArgumentValues(); // 2.根据对应的构造参数依赖获取到对应的 Constructor Constructor  constructor = 得到对应的构造方法// 3.用实际参数值调用构造方法创建对应的对象return constructor.newInstance(Object ... 实参值); 
}

通过上面的分析我们需要获取对应的构造器。这块我们需要通过反射来获取了。下面是具体的实现逻辑

image.png

根据上面的分析,实现逻辑可分为两步

  1. 先根据参数的类型进行精确匹配查找,如果没有找到,继续执行第二步操作
  2. 获得所有的构造方法,遍历构造方法,通过参数数量过滤,再比对形参与实参的类型

因为这里有个情况,实参是Boy,构造方法的形参是Person,第一种精确匹配就没有办法关联了。

image.png

具体的实现代码如下:

private Constructor<?> determineConstructor(BeanDefinition bd, Object[] args) throws Exception {/*判定构造方法的逻辑应是怎样的?1 先根据参数的类型进行精确匹配查找,如未找到,则进行第2步查找;2获得所有的构造方法,遍历,通过参数数量过滤,再比对形参类型与实参类型。* */Constructor<?> ct = null;//没有参数,则用无参构造方法if (args == null) {return bd.getBeanClass().getConstructor(null);}// 1 先根据参数的类型进行精确匹配查找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) {// 2 没有精确参数类型匹配的,获得所有的构造方法,遍历,通过参数数量过滤,再比对形参类型与实参类型。// 判断逻辑:先判断参数数量,再依次比对形参类型与实参类型outer:for (Constructor<?> ct0 : bd.getBeanClass().getConstructors()) {Class<?>[] paramterTypes = ct0.getParameterTypes();if (paramterTypes.length == args.length) {   //通过参数数量过滤for (int i = 0; i < paramterTypes.length; i++) { //再依次比对形参类型与实参类型是否匹配if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) {continue outer; //参数类型不可赋值(不匹配),跳到外层循环,继续下一个}}ct = ct0;  //匹配上了break outer;}}}if (ct != null) {return ct;} else {throw new Exception("不存在对应的构造方法!" + bd);}
}

上面我们考虑的是BeanFactory通过构造器来获取对象的逻辑,那如果我们是通过静态工厂方法或者成员工厂方法的方式来处理的,那么构造参数依赖的处理是否和前面的是一样的呢?其实是差不多的,我们需要根据对应的构造参数来推断对应的工厂方法

// 静态工厂方法
private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception {Object[] realArgs = this.getConstructorArgumentValues(bd);Class<?> type = bd.getBeanClass();Method m = this.determineFactoryMethod(bd, realArgs, type);return m.invoke(type, realArgs);
}// 工厂bean方式来构造对象
private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception {Object[] realArgs = this.getConstructorArgumentValues(bd);Method m = this.determineFactoryMethod(bd, realArgs, this.getType(bd.getFactoryBeanName()));Object factoryBean = this.doGetBean(bd.getFactoryBeanName());return m.invoke(factoryBean, realArgs);
}

5.缓存功能

对于上面的处理过程相信大家应该清楚了,我们通过推断也得到了对应的构造方法或者对应的工厂方法,那么我们可以不可以在下次需要再次获取的时候省略掉推导的过程呢?显然我们可以在BeanDefinition中增加缓存方法可以实现这个需求。

image.png

6. 循环依赖问题

image.png

  上图是循环依赖的三种情况,虽然方式有点不一样,但是循环依赖的本质是一样的,就你的完整创建要依赖于我,我的完整创建也依赖于你。相互依赖从而没法完整创建造成失败。

  我们通过构造参数依赖是完全可能出现上面的情况的,那么这种情况我们能解决吗?构造依赖的情况我们是解决不了的。

public class Test01 {public static void main(String[] args) {new TestService1();}
}class TestService1{private TestService2 testService2 = new TestService2();
}class TestService2{private  TestService1 testService1 = new TestService1();
}

既然解决不了,那么我们在程序中如果出现了,应该要怎么来解决呢?

其实我们可以在创建一个Bean的时候记录下这个Bean,当这个Bean创建完成后我们再移除这个Bean,然后我们在getBean的时候判断记录中是否有该Bean,如果有就判断为循环依赖,并抛出异常。数据结构我们可以通过Set集合来处理。

image.png

到此构造注入的实现就告一段落了。

三、属性注入

  上面搞定了构造注入的方式。接下来我们再看看属性注入的方式有什么需要注意的地方。

1. 属性依赖分析

  属性依赖就是某个属性依赖某个值。

public class Girl {private String name;private int age;private List<Clothes> ClothesList;// ....
}

  那么在获取实例对象后如何根据相关的配置来给对应的属性来赋值呢?这时我们可以定义一个实体类 PropertyValue来记录相关的属性和值。

image.png

2.BeanDefinition实现

  这时我们就需要在BeanDefinition中关联相关属性信息了。

image.png

3.BeanFactory实现

  然后我们在BeanFactory的默认实现DefaultBeanFactory中实现属性值的依赖注入。

// 创建好实例对象
// 给属性依赖赋值
this.setPropertyDIValues(bd,instance);
// 执行初始化相关方法
this.doInit(bd,instance);

  具体的实现代码如下:

// 给入属性依赖
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());//暴力访问  privatep.setAccessible(true);p.set(instance, this.getOneArgumentRealValue(pv.getValue()));}
}

4.循环依赖问题

  在构造参数依赖中我们发现没有办法解决,在属性依赖中同样会存在循环依赖的问题,这时我们能解决吗?

image.png

  其实这种循环依赖的情况,不在IOC场景下非常好解决。如下

Boy b = new Boy();
Girl g = new Girl();
b.setGirl(g);
g.setBoy(b);

  但是在IOC好像不是太好解决:

image.png

  针对这种情况我们需要通过 提前暴露来解决这个问题,具体看代码!!!

private void doEarlyExposeBuildingBeans(String beanName, Object instance) {Map<String,Object> earlyExposeBuildingBeansMap = earlyExposeBuildingBeans.get();if(earlyExposeBuildingBeansMap == null) {earlyExposeBuildingBeansMap = new HashMap<>();earlyExposeBuildingBeans.set(earlyExposeBuildingBeansMap);}earlyExposeBuildingBeansMap.put(beanName,instance);
}

最后现阶段已经实现的类图结构

image.png

相关文章:

Spring源码之手写DI

Spring源码之手写DI 我们来回顾一下前面手写IOC的内容。 一、DI介绍 DI(Dependency injection)依赖注入。对象之间的依赖由容器在运行期决定&#xff0c;即容器动态的将某个依赖注入到对象之中。说直白点就是给Bean对象的成员变量赋值。 在这里我们就需要明白几个问题。 1.…...

从零入手人工智能(5)—— 决策树

1.前言 在上一篇文章《从零入手人工智能&#xff08;4&#xff09;—— 逻辑回归》中讲述了逻辑回归这个分类算法&#xff0c;今天我们的主角是决策树。决策树和逻辑回归这两种算法都属于分类算法&#xff0c;以下是决策树和逻辑回归的相同点&#xff1a; 分类任务&#xff1…...

go语言:两协程并发交替打印数字和字母(代码逐行注释)

要求&#xff1a; 并发两协程交替打印数字和字母。一个协程打印数字&#xff0c;一个协程打印字母。 输出&#xff1a; 12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728 思路&#xff1a; 两个 channel 控制两个协程进行交替打印。sync.WaitG…...

TWM论文阅读笔记

这是ICLR2023的一篇world model论文&#xff0c;用transformer来做世界模型的sequence prediction。文章贡献是transformer-based world model&#xff08;不同于以往的如transdreamer的world model&#xff0c;本文的transformer-based world model在inference 的时候可以丢掉…...

探索ChatTTS项目:高效的文字转语音解决方案

文章目录 &#x1f4d6; 介绍 &#x1f4d6;&#x1f4d2; ChatTTS &#x1f4d2;&#x1f4dd; 项目介绍&#x1f4dd; 项目亮点&#x1f4dd; UI &#x1f388; 项目地址 &#x1f388; &#x1f4d6; 介绍 &#x1f4d6; 在AI技术迅速发展的今天&#xff0c;文本到语音&…...

[Django学习]Q对象实现多字段模糊搜索

一、应用场景 假设我们现在有一个客房系统,前端界面上展示出来了所有客房的所有信息。用户通过客房的价格、面积、人数等对客房进行模糊搜索&#xff0c;如检索出价格在50到100元之间的客房&#xff0c;同时检索面积在20平方米到30平方米之间的客房&#xff0c;此时后端可以借助…...

transdreamer 论文阅读笔记

这篇文章是对dreamer系列的改进&#xff0c;是一篇world model 的论文改进点在于&#xff0c;dreamer用的是循环神经网络&#xff0c;本文想把它改成transformer&#xff0c;并且希望能利用transformer实现并行训练。改成transformer的话有个地方要改掉&#xff0c;dreamer用ht…...

AIGC技术的发展现状与未来趋势探讨

AIGC技术的发展现状与未来趋势探讨 随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;AI生成内容&#xff08;AI-Generated Content&#xff0c;AIGC&#xff09;成为了一项颠覆性的技术&#xff0c;它能够自动生成文本、图像、音频和视频等多种内容。本文将…...

Postman Postman接口测试工具使用简介

Postman这个接口测试工具的使用做个简单的介绍&#xff0c;仅供参考。 插件安装 1&#xff09;下载并安装chrome浏览器 2&#xff09;如下 软件使用说明...

Java开发笔记Ⅱ(Jsoup爬虫)

Jsoup 爬虫 Java 也能写爬虫&#xff01;&#xff01;&#xff01; Jsoup重要对象如下&#xff1a; Document&#xff1a;文档对象&#xff0c;每个html页面都是一个Document对象 Element&#xff1a;元素对象&#xff0c;一个Document对象里有多个Element对象 Node&#…...

一五三、MAC 安装MongoDB可视化工具连接

若没有安装brew包管理工具&#xff0c;在命令行输入安装命令 /bin/bash -c “$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)”上面步骤安装完成后&#xff0c;开始安装MongoDB&#xff0c;输入安装命令&#xff1a; brew tap mongodb/brewbrew u…...

ULTRAINTERACT 数据集与 EURUS 模型:推动开源大型语言模型在推理领域的新进展

在人工智能的浪潮中&#xff0c;大型语言模型&#xff08;LLMs&#xff09;已经成为推动自然语言处理技术发展的关键力量。它们在理解、生成语言以及执行复杂任务方面展现出了巨大的潜力。然而&#xff0c;尽管在特定领域内取得了显著进展&#xff0c;现有的开源LLMs在处理多样…...

【leetcode刷题】面试经典150题 , 27. 移除元素

leetcode刷题 面试经典150 27. 移除元素 难度&#xff1a;简单 文章目录 一、题目内容二、自己实现代码2.1 方法一&#xff1a;直接硬找2.1.1 实现思路2.1.2 实现代码2.1.3 结果分析 2.2 方法二&#xff1a;排序整体删除再补充2.1.1 实现思路2.1.2 实现代码2.1.3 结果分析 三、…...

红队内网攻防渗透:内网渗透之内网对抗:横向移动篇PTH哈希PTT票据PTK密匙Kerberoast攻击点TGTNTLM爆破

红队内网攻防渗透 1. 内网横向移动1.1 首要知识点1.2 PTH1.2.1 利用思路第1种:利用直接的Hash传递1.2.1.1、Mimikatz1.2.2 利用思路第2种:利用hash转成ptt传递1.2.3 利用思路第3种:利用hash进行暴力猜解明文1.2.4 利用思路第4种:修改注册表重启进行获取明文1.3 PTT1.3.1、漏…...

springBoot不同module之间互相依赖

在 Spring Boot 多模块项目中&#xff0c;不同模块之间的依赖通常是通过 Maven 或 Gradle 来管理的。以下是一个示例结构和如何设置这些依赖的示例。 项目结构 假设我们有一个多模块的 Spring Boot 项目&#xff0c;结构如下&#xff1a; my-springboot-project │ ├── p…...

[modern c++] 类型萃取 type_traits

前言&#xff1a; #include <type_traits> type_traits 又叫类型萃取&#xff0c;是一个在编译阶段用于进行 类型判断/类型变更 的库&#xff0c;在c11中引入。因为其工作阶段是在编译阶段&#xff0c;因此被大量应用在模板编程中&#xff0c;同时也可以结合 constexpr…...

函数模板和类模板的区别

函数模板和类模板在C中都是重要的泛型编程工具&#xff0c;但它们之间存在一些显著的区别。以下是它们之间的主要区别&#xff1a; 实例化方式&#xff1a; 函数模板&#xff1a;隐式实例化。当函数模板被调用时&#xff0c;编译器会根据传递给它的参数类型自动推断出模板参数…...

ChatGPT 提示词技巧一本速通

目录 一、基本术语 二、提示词设计的基本原则 三、书写技巧 2.1 赋予角色 2.2 使用分隔符 2.2 结构化输出 2.3 指定步骤 2.4 提供示例 2.5 指定长度 2.6 使用或引用参考文本 2.7 提示模型进行自我判断 2.8 思考问题的解决过程 ​编辑 2.10 询问是否有遗漏 2.11 …...

【windows解压】解压文件名乱码

windows解压&#xff0c;文件名乱码但内容正常。 我也不知道什么时候设置出的问题。。。换了解压工具也没用&#xff0c;后来是这样解决的。 目录 1.环境和工具 2.打开【控制面板】 3.点击【时钟和区域】 4.选择【区域】 5.【管理】中【更改系统区域设置】 6.选择并确定…...

使用Flink CDC实时监控MySQL数据库变更

在现代数据架构中&#xff0c;实时数据处理变得越来越重要。Flink CDC&#xff08;Change Data Capture&#xff09;是一种强大的工具&#xff0c;可以帮助我们实时捕获数据库的变更&#xff0c;并进行处理。本文将介绍如何使用Flink CDC从MySQL数据库中读取变更数据&#xff0…...

学生课程信息管理系统

摘 要 目前&#xff0c;随着科学经济的不断发展&#xff0c;高校规模不断扩大&#xff0c;所招收的学生人数越来越 多&#xff1b;所开设的课程也越来越多。随之而来的是高校需要管理更多的事务。对于日益增 长的学生相关专业的课程也在不断增多&#xff0c;高校对其管理具有一…...

如何看待鸿蒙HarmonyOS?

鸿蒙系统&#xff0c;自2019年8月9日诞生就一直处于舆论风口浪尖上的系统&#xff0c;从最开始的“套壳”OpenHarmony安卓的说法&#xff0c;到去年的不再兼容安卓的NEXT版本的技术预览版发布&#xff0c;对于鸿蒙到底是什么&#xff0c;以及鸿蒙的应用开发的讨论从来没停止过。…...

【论文复现|智能算法改进】一种基于多策略改进的鲸鱼算法

目录 1.算法原理2.改进点3.结果展示4.参考文献5.代码获取 1.算法原理 SCI二区|鲸鱼优化算法&#xff08;WOA&#xff09;原理及实现【附完整Matlab代码】 2.改进点 混沌反向学习策略 将混沌映射和反向学习策略结合&#xff0c;形成混沌反向学习方法&#xff0c;通过该方 法…...

yarn安装配置及使用教程

Yarn 是一款 JavaScript 的包管理工具&#xff0c;是 Facebook, Google, Exponent 和 Tilde 开发的一款新的 JavaScript 包管理工具&#xff0c;它提供了确定性、依赖关系树扁平化等特性&#xff0c;并且与 npm 完全兼容。以下是 Yarn 的安装及使用教程&#xff1a; Yarn 安装…...

有那么点道理。

...

蔚蓝资源包和数据分析

代码如下 /* * COMPUTER GENERATED -- DO NOT EDIT* */#include <windows.h>static FARPROC __Init_Fun_2__; int __RestartAppIfNecessary__Fun() {return 0; } int Init_Fun() {__Init_Fun_2__();return 1; }static FARPROC __GameServer_BSecure__; static FARPROC _…...

MySQL----利用Mycat配置读写分离

首先确保主从复制是正常的&#xff0c;具体步骤在MySQL----配置主从复制。MySQL----配置主从复制 环境 master(CtenOS7)&#xff1a;192.168.200.131 ----ifconfig查看->ens33->inetslave(win10)&#xff1a;192.168.207.52 ----ipconfig查看->无线局域网适配器 WLA…...

【科学计算与可视化】2. pandas 基础

1. 安装 Pandas 首先&#xff0c;确保你已经安装了 Pandas。你可以使用以下命令安装&#xff1a;pip install pandas 2. 导入 Pandas 在开始使用 Pandas 之前&#xff0c;你需要先导入它&#xff1a;import pandas as pd 3. 创建数据结构 Pandas 主要有两种数据结构&#…...

医学记录 --- 腋下异味

逻辑图地址 症状 病因 汗液分泌旺盛&#xff1a;由于天气炎热、活动出汗、肥胖等因素导致汗液分泌旺盛&#xff0c;可引起腋下有异味表现。在这种情况下&#xff0c;建议保持身体清洁&#xff0c;特别是在炎热和潮湿的环境下。可以使用抗菌洗液、喷雾或霜剂来帮助减少细菌滋…...

【Linux】进程间通信_1

文章目录 七、进程间通信1. 进程间通信分类管道 未完待续 七、进程间通信 进程间由于 进程具有独立性 &#xff0c;所以不可以直接进行数据传递。但是我们通常需要多个进程协同&#xff0c;共同完成一件事&#xff0c;所以我们需要进程间通信的手段。进程间通信的本质就是先让…...