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

  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; @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依赖注入,嘿,你的益达!的更多相关文章

  1. Spring DI - 依赖注入

    1.IOC(DI) - 控制反转(依赖注入) 所谓的IOC称之为控制反转,简单来说就是将对象的创建的权利及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不再需要关注对象的创建和生 ...

  2. spring Di依赖注入

    依赖注入有两种方式 通过 get   set 方法 Person.java package cn.itcast.spring.sh.di.set; import java.util.List; imp ...

  3. 手写Spring AOP,快来瞧一瞧看一看撒!

    目录 AOP分析 Advice实现 定义Advice接口 定义前置.后置.环绕和异常增强接口 Pointcut实现 定义PointCut接口 定义正则表达式的实现类:RegExpressionPoin ...

  4. 手写Spring Config,最终一战,来瞅瞅撒!

    上一篇说到了手写Spring AOP,来进行功能的增强,下面本篇内容主要是手写Spring Config.通过配置的方式来使用Spring 前面内容链接: 我自横刀向天笑,手写Spring IOC容器 ...

  5. Spring详解(三)------DI依赖注入

    上一篇博客我们主要讲解了IOC控制反转,也就是说IOC 让程序员不在关注怎么去创建对象,而是关注与对象创建之后的操作,把对象的创建.初始化.销毁等工作交给spring容器来做.那么创建对象的时候,有可 ...

  6. 一) Spring 介绍、IOC控制反转思想与DI依赖注入

    一.spring介绍1.IOC反转控制思想(Inversion of Control)与DI依赖注入(Dependency Injection)2.AOP面向切面的编程思想与动态代理3.作用:项目的粘 ...

  7. 【Spring】Spring之依赖注入(DI)传递参数的方式

    DI依赖注入传入参数的方式,这里介绍了基本数据类型,集合,符合数据类型的传递(String类型比较特殊,其传递值和基本数据类型的方法一致) 注入基本数据类型和String类型 通过setter方法注入 ...

  8. 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。

    轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战   ...

  9. 三大框架 之 Spring(IOC控制反转、DI依赖注入)

    目录 常用词汇 left join与left outer join的区别 Struts2的标签库导入 Spring Spring概述 什么是Spring spring特点 下载 IOC 什么IOC 传 ...

随机推荐

  1. 20201228 买卖股票的最佳时机 IV(困难)

    给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你最多可以完成 k 笔交易. 注意:你不能同时参 ...

  2. 还原Oracle数据库dmp文件(Win系统)

    准备工作: 1.核对数据字符集:   一般Oracle在安装的时候默认是选择ZHS16GBK,如有改动,使用 select userenv('language') from dual;语句查看使用的字 ...

  3. dategrip的使用技巧

    原文链接:https://blog.csdn.net/weixin_44421461/article/details/109541903 数据表复制,可以直接用sql语句 1.复制表结构及数据到新表 ...

  4. molloc堆区的动态内存分配

    [前言]前面有一篇文章介绍了堆区栈区的区别.栈区的核心主要集中在操作一个栈结构,一般由操作系统维护.堆区,主要是我们程序员来维护,核心就是动态内存分配. 这篇笔记结束就不在高新CSAPP的读书笔记了, ...

  5. 上百本电子书(Java/Hadoop/Spark/Linux/机器学习/)免费分享 百度云持续更新

    分享一下自己整理的超多电子书, 其中包括:Java,Hadoop,Spark,Linux,Hbase,Hive,机器学习,区块链 目录如下: 1 Java 基础 2 Java 虚拟机 3 Java 并 ...

  6. new String("abc"),到底在不在常量池中存储"abc"?

    String str = new String("Hello World"); 问之:这行代码到底有没有在字符串常量池中创建"Hello World"字符串呢? ...

  7. JS table排序

    <html lang="en"> <head> <meta charset="UTF-8"> <meta http-e ...

  8. 阿里的Easyexcel读取Excel文件(最新版本)

      本篇文章主要介绍一下使用阿里开源的Easyexcel工具处理读取excel文件,因为之前自己想在网上找一下这个简单的立即上手的博客,发现很多文章的教程都针对比较旧的版本的Easyexcel,没有使 ...

  9. SpringMVC-05 Json交互处理

    SpringMVC-05 Json交互处理 Json 1.什么是JSON? JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式,目前使用特别 ...

  10. 如何在 ASP.Net Core 中使用 Serilog

    记录日志的一个作用就是方便对应用程序进行跟踪和排错调查,在实际应用上都是引入 日志框架,但如果你的 日志文件 包含非结构化的数据,那么查询起来将是一个噩梦,所以需要在记录日志的时候采用结构化方式. 将 ...