手写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就结束了,希望本文对您有所帮助!
手写Spring DI依赖注入,嘿,你的益达!的更多相关文章
- Spring DI - 依赖注入
1.IOC(DI) - 控制反转(依赖注入) 所谓的IOC称之为控制反转,简单来说就是将对象的创建的权利及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不再需要关注对象的创建和生 ...
- spring Di依赖注入
依赖注入有两种方式 通过 get set 方法 Person.java package cn.itcast.spring.sh.di.set; import java.util.List; imp ...
- 手写Spring AOP,快来瞧一瞧看一看撒!
目录 AOP分析 Advice实现 定义Advice接口 定义前置.后置.环绕和异常增强接口 Pointcut实现 定义PointCut接口 定义正则表达式的实现类:RegExpressionPoin ...
- 手写Spring Config,最终一战,来瞅瞅撒!
上一篇说到了手写Spring AOP,来进行功能的增强,下面本篇内容主要是手写Spring Config.通过配置的方式来使用Spring 前面内容链接: 我自横刀向天笑,手写Spring IOC容器 ...
- Spring详解(三)------DI依赖注入
上一篇博客我们主要讲解了IOC控制反转,也就是说IOC 让程序员不在关注怎么去创建对象,而是关注与对象创建之后的操作,把对象的创建.初始化.销毁等工作交给spring容器来做.那么创建对象的时候,有可 ...
- 一) Spring 介绍、IOC控制反转思想与DI依赖注入
一.spring介绍1.IOC反转控制思想(Inversion of Control)与DI依赖注入(Dependency Injection)2.AOP面向切面的编程思想与动态代理3.作用:项目的粘 ...
- 【Spring】Spring之依赖注入(DI)传递参数的方式
DI依赖注入传入参数的方式,这里介绍了基本数据类型,集合,符合数据类型的传递(String类型比较特殊,其传递值和基本数据类型的方法一致) 注入基本数据类型和String类型 通过setter方法注入 ...
- 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。
轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战 ...
- 三大框架 之 Spring(IOC控制反转、DI依赖注入)
目录 常用词汇 left join与left outer join的区别 Struts2的标签库导入 Spring Spring概述 什么是Spring spring特点 下载 IOC 什么IOC 传 ...
随机推荐
- JPEG解码——(4)霍夫曼解码
本篇是该系列的第四篇,主要介绍霍夫曼解码相关内容. 承接上篇,文件头解析完毕后,就进入了编码数据区域,即SOS的tag后的区域,也是图片数据量的大头所在. 1. 解码过程规则描述 a)从此颜色分量单元 ...
- 解决margin-top无效问题
当两个空的块级元素嵌套时,如果内部的块设置有margin-top属性,而且父元素没有下边解决方法所述的特征,那么内部块的margin-top属性会绑架父元素(即将margin-top传递凌驾给了父元素 ...
- Hystrix熔断器的使用步骤
1.添加熔断器依赖 2.在配置文件中开启熔断器 feign.hystrix.enabled=true 3.写接口的实现类VodFileDegradeFeignClient,在实现类中写如果出错了输出的 ...
- 如何使用GraphQL Client: Apollo Android
如何使用GraphQL Client: Apollo Android 一个Android app, 如何使用GraphQL. 本文以最流行的Apollo Android为例来说明. 添加依赖 首先, ...
- C# 使用 Index 和 Range 简化集合操作
C# 使用 Index 和 Range 简化集合操作 Intro 有的语言数组的索引值是支持负数的,表示从后向前索引,比如:arr[-1] 从 C# 8 开始,C# 支持了数组的反向 Index,和 ...
- 医学图像 | DualGAN与儿科超声心动图分割 | MICCAI
文章转自微信公众号:「机器学习炼丹术」 作者:炼丹兄(已授权) 联系方式:微信cyx645016617(欢迎交流共同进步) 论文名称:"Dual Network Generative Adv ...
- 六. SpringCloud网关
1. Gateway概述 1.1 Gateway是什么 服务网关还可以用Zuul网关,但是Zuul网关由于一些维护问题,所以这里我们学习Gateway网关,SpringCloud全家桶里有个很重要的组 ...
- java 集合 + 常见面试题
1.1. 集合概述 1.1.1. Java 集合概览 从下图可以看出,在 Java 中除了以 Map 结尾的类之外, 其他类都实现了 Collection 接口. 并且,以 Map 结尾的类都实现了 ...
- 记录自己第一次搭建本地fabric框架
写在前,第一次搭建fabric框架,对于小白的我很是艰辛,参考了很多博主的博客才最终完成,在此记录一下搭建过程. 参考的网站 https://blog.csdn.net/smallone233/art ...
- Java 重入锁和读写锁
本文部分摘自<Java 并发编程的艺术> 重入锁 重入锁 ReentrantLock,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁.除此之外,该锁还支持获取锁时 ...