手写Spring DI依赖注入,嘿,你的益达!
手写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注入,构造器注入等,那么就可以知道,有下面两种依赖:
- 构造参数依赖
- 属性依赖
依赖注入的本质就是给值,给入构造参数的值,给属性赋值
参数值和属性值是不是可以是基本的一些值,也有可能是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依赖注入,嘿,你的益达!相关推荐
- Spring DI(依赖注入)注解篇
1 课程内容介绍 我之前写的的一篇博客Spring核心功能DI(依赖注入)xml篇主要介绍了如何通过配置xml的方式来实现依赖注入,今天我们来介绍如何通过注解方式完成我们的依赖注入操作. 2 注入基本 ...
- Spring DI依赖注入讲解
DI:dependency injection 依赖注入 在spring框架负责创建Bean对象时,动态将依赖对象注入到Bean组件. public class UserServiceImpl imp ...
- Spring DI(依赖注入)
DI依赖注入 IoC(Inversion Of Control)控制翻转,Spring反向控制应用程序所需要使用的外部资源 DI(Dependency Injection)依赖注入,应用程序运行依赖的 ...
- Spring DI(依赖注入)Xml篇
1 DI(依赖注入)简单介绍 如果您对Spring了解甚少,建议先移步我的另一篇博客Spring核心功能IOC之HelloWorld因为下面的内容是在该文章基础上进行阐述的 .我们可以通过一段简单代码 ...
- Spring DI[依赖注入]
依赖注入(Dependency Injection,简称DI)意思是由容器或者框架将被调用类注入给调用对象,以此来降低调用对象和被调用类之间的依赖关系. 依赖注入主要有2种不同的实现形式: 1. 构造 ...
- Spring DI依赖注入方式
1.构造器注入 2.Set方式注入[重点] 依赖注入:Set注入 依赖:bean对象的创建依赖于容器 注入:bean对象中的所有属性,由容器来注入. [环境搭建] Student package co ...
- Spring DI(依赖注入)构造器注入篇
Spring 在不使用自动装配的方式进行注入需要我们必须为成员属性提供setter方法,这种方式相对比较繁琐,除了setter方法注入方式外,Spring还为我们提供了构造器配置的注入方式. 构造器默 ...
- 手把手教你手写Spring框架
手写spring准备工作: 新建一个maven工程: 架构 新建类: package com.spring;public class keweiqinApplicationContext {priva ...
- 万字博客带你全面剖析Spring的依赖注入
1.写在前面 前面的博客我们已经写了Spring的依赖查找,这篇博客我们来了解写Spring的依赖注入. 2.依赖注入的模式和类型 手动模式 - 配置或者编程的方式, 提前安排注入规则 XML 资源配 ...
最新文章
- c语言中结构体的用法
- 如何有效提高数据中心PUE?
- 主板19针接口_【新品上市】D4双通道还能组RAID!华南B365D4主板6/7/8/9代全兼容!...
- 从偏远的小山村出来的孩子,一路的 “辛酸史”
- boost::geometry::math::equals用法的测试程序
- 前瞻:Java能否畅行未来?
- 【渝粤题库】广东开放大学 会展概论 形成性考核
- 部署WEB项目到服务器(三)安装mysql到linux服务器(Ubuntu)详解
- 前端:JS/18/JS运算符(算术运算符,赋值运算符,字符串运算符,比较运算符,逻辑运算符,三元运算符),window.prompt()
- 每日关键词-170304-.net,c#分层架构常识
- c++ 字符减去‘0’_字符串为什么减‘0’成整数
- 数字水印--给我的文件充当保护神
- 用超级鹰来识别B站图片验证
- 通过usb线ssh连接iPhone
- Dart 实现字符串 进行 gbk编码的 urlencode
- 看我如何用云函数撸一个PC小程序代码包在线解密工具
- mysql 获取农历年份_iOS 获取公历、农历日期的年月日
- 软件项目管理-第三章生存期模型
- 英语语法长难句——名词性从句
- Amazon EC2 Deep Dive 亚马逊EC2深度解析 Lynda课程中文字幕
热门文章
- Oracle高级查询,over 用法
- 鲁大师html5是什么,鲁大师怎么样
- 网站流量日志系统知识详解----【点击流事件详解】
- BZOJ 1038: [ZJOI2008]瞭望塔 半平面交
- Codeforces Gym100543L:Outer space invaders(区间DP)
- AddressSanitizer 页面
- PCIE/GPU/显卡参数性能查看工具搜集
- 地球上空首次绽放焰火汉字“未来”
- NTLM认证原理及其过程
- centos7parted分区_Linux-centos7超过2TB使用parted命令分区