手写DI

  • 提前实例化单例Bean
  • DI分析
  • DI的实现
    • 构造参数依赖
      • 一:定义分析
      • 二:定义一个类BeanReference
      • 三:BeanDefinition接口及其实现类
      • 四:DefaultBeanFactory类增加方法
      • 五:构造参数注入实现
      • 六:构造参数依赖测试
    • 循环依赖的处理
    • 属性依赖
      • 一:属性依赖的定义
      • 二:BeanDefinition接口及其实现类
      • 三:DefaultBeanFactory类中实现属性依赖
      • 四:属性依赖测试

上一篇文章中说到,如何手写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<>();@Overridepublic 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注入,构造器注入等,那么就可以知道,有下面两种依赖:

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

依赖注入的本质就是给值,给入构造参数的值,给属性赋值

参数值和属性值是不是可以是基本的一些值,也有可能是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;@Overridepublic List<?> getConstructorArgumentValues() {return constructorArgumentValues;}@Overridepublic 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 {//参数为空,直接返回nullif (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;@Overridepublic Constructor<?> getConstructor() {return constructor;}@Overridepublic void setConstructor(Constructor<?> constructor) {this.constructor = constructor;}@Overridepublic Method getFactoryMethod() {return factoryMethod;}@Overridepublic 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;}@Overridepublic void sayLove() {if (girl == null) {System.out.println("没有对象好难过!" + hashCode());} else {System.out.println("我爱你,亲爱的!" + girl);}}@Overridepublic 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;}@Overridepublic String toString() {return "MagicGril{" +"name='" + name + '\'' +'}';}
}
public interface Money {void pay();
}
public class Renminbi implements Money {@Overridepublic 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();/** 构造函数注入测试 */@Testpublic void testConstructorDI() throws Exception {GenericBeanDefinition definition = new GenericBeanDefinition();//设置beanClassdefinition.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();//设置beanClassdefinition.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();}

执行结果:

静态工厂注入测试:

    /** 静态工厂注入测试 */@Testpublic void testStaticFactoryDI() throws Exception {GenericBeanDefinition definition = new GenericBeanDefinition();//设置beanClassdefinition.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();//设置beanClassdefinition.setBeanClass(Renminbi.class);bf.registerBeanDefinition("rmb", definition);bf.preInstantiateSingletons();Boy boy = (Boy) bf.getBean("nl");boy.play();}

执行结果:

成员工厂注入测试:

/** 成员工厂注入测试 */@Testpublic void testFactoryMethodDI() throws Exception {GenericBeanDefinition definition = new GenericBeanDefinition();String fBeanName = "boyFactoryBean";//设置工厂beandefinition.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();//设置beanClassdefinition.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;}
}

测试类:

    @Testpublic void testCycleDI() throws Exception {GenericBeanDefinition definition = new GenericBeanDefinition();//设置beanClassdefinition.setBeanClass(NiuLang.class);//设置构造函数的参数List<Object> args = new ArrayList<>();args.add(new BeanReference("zv"));definition.setConstructorArgumentValues(args);bf.registerBeanDefinition("nl", definition);definition = new GenericBeanDefinition();//设置beanClassdefinition.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;@Overridepublic List<PropertyValue> getPropertyValues() {return propertyValues;}@Overridepublic 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方法

    @Overridepublic 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())) {//通过构造函数构建beanbean = createBeanByConstructor(bd);} else {//通过静态工厂构建beanbean = createBeanByStaticFactoryMethod(bd);}} else {//通过成员工厂构建beanbean = createBeanByFactoryBean(bd);}//实例创建完成后进行删除beans.remove(beanName);//对单例bean处理if (bd.isSingleton()) {beanMap.put(beanName, bean);}return bean;}

四:属性依赖测试

    /** 属性注入测试 */@Testpublic void testPropertyDI() throws Exception {GenericBeanDefinition definition = new GenericBeanDefinition();//设置beanClassdefinition.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();//设置beanClassdefinition.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就结束了,希望本文对您有所帮助!

手写Spring DI依赖注入,嘿,你的益达!相关推荐

  1. Spring DI(依赖注入)注解篇

    1 课程内容介绍 我之前写的的一篇博客Spring核心功能DI(依赖注入)xml篇主要介绍了如何通过配置xml的方式来实现依赖注入,今天我们来介绍如何通过注解方式完成我们的依赖注入操作. 2 注入基本 ...

  2. Spring DI依赖注入讲解

    DI:dependency injection 依赖注入 在spring框架负责创建Bean对象时,动态将依赖对象注入到Bean组件. public class UserServiceImpl imp ...

  3. Spring DI(依赖注入)

    DI依赖注入 IoC(Inversion Of Control)控制翻转,Spring反向控制应用程序所需要使用的外部资源 DI(Dependency Injection)依赖注入,应用程序运行依赖的 ...

  4. Spring DI(依赖注入)Xml篇

    1 DI(依赖注入)简单介绍 如果您对Spring了解甚少,建议先移步我的另一篇博客Spring核心功能IOC之HelloWorld因为下面的内容是在该文章基础上进行阐述的 .我们可以通过一段简单代码 ...

  5. Spring DI[依赖注入]

    依赖注入(Dependency Injection,简称DI)意思是由容器或者框架将被调用类注入给调用对象,以此来降低调用对象和被调用类之间的依赖关系. 依赖注入主要有2种不同的实现形式: 1. 构造 ...

  6. Spring DI依赖注入方式

    1.构造器注入 2.Set方式注入[重点] 依赖注入:Set注入 依赖:bean对象的创建依赖于容器 注入:bean对象中的所有属性,由容器来注入. [环境搭建] Student package co ...

  7. Spring DI(依赖注入)构造器注入篇

    Spring 在不使用自动装配的方式进行注入需要我们必须为成员属性提供setter方法,这种方式相对比较繁琐,除了setter方法注入方式外,Spring还为我们提供了构造器配置的注入方式. 构造器默 ...

  8. 手把手教你手写Spring框架

    手写spring准备工作: 新建一个maven工程: 架构 新建类: package com.spring;public class keweiqinApplicationContext {priva ...

  9. 万字博客带你全面剖析Spring的依赖注入

    1.写在前面 前面的博客我们已经写了Spring的依赖查找,这篇博客我们来了解写Spring的依赖注入. 2.依赖注入的模式和类型 手动模式 - 配置或者编程的方式, 提前安排注入规则 XML 资源配 ...

最新文章

  1. c语言中结构体的用法
  2. 如何有效提高数据中心PUE?
  3. 主板19针接口_【新品上市】D4双通道还能组RAID!华南B365D4主板6/7/8/9代全兼容!...
  4. 从偏远的小山村出来的孩子,一路的 “辛酸史”
  5. boost::geometry::math::equals用法的测试程序
  6. 前瞻:Java能否畅行未来?
  7. 【渝粤题库】广东开放大学 会展概论 形成性考核
  8. 部署WEB项目到服务器(三)安装mysql到linux服务器(Ubuntu)详解
  9. 前端:JS/18/JS运算符(算术运算符,赋值运算符,字符串运算符,比较运算符,逻辑运算符,三元运算符),window.prompt()
  10. 每日关键词-170304-.net,c#分层架构常识
  11. c++ 字符减去‘0’_字符串为什么减‘0’成整数
  12. 数字水印--给我的文件充当保护神
  13. 用超级鹰来识别B站图片验证
  14. 通过usb线ssh连接iPhone
  15. Dart 实现字符串 进行 gbk编码的 urlencode
  16. 看我如何用云函数撸一个PC小程序代码包在线解密工具
  17. mysql 获取农历年份_iOS 获取公历、农历日期的年月日
  18. 软件项目管理-第三章生存期模型
  19. 英语语法长难句——名词性从句
  20. Amazon EC2 Deep Dive 亚马逊EC2深度解析 Lynda课程中文字幕

热门文章

  1. Oracle高级查询,over 用法
  2. 鲁大师html5是什么,鲁大师怎么样
  3. 网站流量日志系统知识详解----【点击流事件详解】
  4. BZOJ 1038: [ZJOI2008]瞭望塔 半平面交
  5. Codeforces Gym100543L:Outer space invaders(区间DP)
  6. AddressSanitizer 页面
  7. PCIE/GPU/显卡参数性能查看工具搜集
  8. 地球上空首次绽放焰火汉字“未来”
  9. NTLM认证原理及其过程
  10. centos7parted分区_Linux-centos7超过2TB使用parted命令分区