【Spring框架学习】IoC/DI机制 注解式 基本实现

Youzg Logo

首先,本人来给出两个类:

public class OneClass {
	private TwoClass two;
	
	public OneClass() {
	}

	public void doOneThing() {
		System.out.println(two);
	}
	
}
public class TwoClass {
	
	public TwoClass() {
	}
	
	@Override
	public String toString() {
		return "这是一个TwoClass的对象";
	}
	
}

那么,现在本人若是给出一个测试类,来new一个OnClass对象:

public class Demo {

	public static void main(String[] args) {
		OneClass one = new OneClass();
		one.doOneThing();
	}

}

那么,本人来展示一下运行结果:
运行结果 展示
Demo类的主函数执行结果,输出为null。
这是必然的,也很容易理解:

我们虽然在OneClass中设置并输出了TwoClass的对象two,
但是并没有为OneClass类的two成员并没有初始化,
所以我们输出的结果是null

如果想这么一种办法:
对于OneClass类中的two成员,通过一种工具,
自动地 完成对two成员的初始化(注入)
那么,Demo类的主函数执行结果就完美了!
那么,这,就是本人在之后的内容中所要进行实现的IoC/DI机制

那么,我们来通过反射机制对OneClass类中的two成员进行注入;

package edu.youzg.about_mpring.core;

import java.lang.reflect.Field;

public class Demo {

	public static void main(String[] args) {
		OneClass one = new OneClass();

		Class<?> klass = OneClass.class;
		try {
			Field field = klass.getDeclaredField("two");
			field.setAccessible(true);
			field.set(one, new TwoClass());
			
			one.doOneThing();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
	
}

那么,现在本人来展示下运行结果:
反射机制 展示

上面的做法,完成了最初步的DI
不过,我们每一次new一个OneClass时候,就要用反射机制注入two对象
若是OneClass中有很多非八大基本类型成员对象,那我们岂不是要写很多重复冗余的代码
由此可见,上述的手法过于简单朴实

思路:

现在考虑这么一种思路:

某应用所涉及及其对象,都集中存储到一个集合(池子)中;
凡是在这个集合中的类,尤其是这些类的成员类型也在这个池子中,
则,这些成员的初始化都从池子中的对象来实现!


例如:
A类、B类和C类都在集合中;
另外,A类有B类类型的成员,有C类类型的成员;B类有C类类的成员……
那么,A类的B类类型成员,C类类型成员,还有B类中的C类类型成员,
都由一段代码自动完成“初始化”。
以后使用这些类的对象时,一律从这个集合(池子)中取。

从另一个角度思考上述问题:
构建一个容器(上下文),在这个容器中存储类及其对象
使用这些类的对象时,基本上都是从这个容器中获取的;

若这些类的成员其类型也在容器中
则,它们将被自动初始化,且用容器中的对象完成初始化。
需要说明的是,对于类中的成员的初始化选择,应该由用户决定
选择权的实现有两种具体的方法:

  1. 通过XML配置映射关系;
  2. 通过注解配置映射关系。

上述的两种实现方法,各有利弊:

  • XML配置
  • 优势:
    不侵害源代码,
    保证了“开闭原则”
  • 不足:
    代码量巨大,若要配置一个大项目,则配置文件就变得十分复杂
  • 注解配置
  • 优势:
    程序可读性强
    无需额外代码(开发效率高)
  • 不足:
    会对源代码进行修饰,
    实现起来比较麻烦

那么,本着“累死我一个,幸福千万家”的原则,
本人就来实现下 注解配置的代码:


注解配置方式:

首先,我们来构造一个类,来存储需要进行注入的类的信息:
我们现在思考下,这个类需要哪些信息:

  1. 这个类的Class对象信息,以便我们之后的反射机制的使用
  2. 这个类的对象,以便我们之后执行它的某个方法,或者 将它注入到容器(上下文)
  3. 一个用于标识是不是单例的类的标识
  4. 一个用于标识是否注入此类的标识

BeanDefinition类:

package edu.youzg.ioc_impl.core;

/**
 * 用于保存一个bean的基本信息
 */
public class BeanDefination {
    private Class<?> klass; // 该bean的 Class对象(方便之后的反射调用)
    private Object object;  // 该bean的 实例化个体
    private boolean singleton;  // 是否单例
    private boolean inject; // 是否自动注入

    public BeanDefination() {
        this.singleton = true;
        this.inject = false;
    }

    Class<?> getKlass() {
        return klass;
    }

    void setKlass(Class<?> klass) {
        this.klass = klass;
    }

    Object getObject() {
        return object;
    }

    void setObject(Object object) {
        this.object = object;
    }

    boolean isSingleton() {
        return singleton;
    }

    void setSingleton(boolean singleton) {
        this.singleton = singleton;
    }

    boolean isInject() {
        return inject;
    }

    void setInject(boolean inject) {
        this.inject = inject;
    }

}

现在,我们需要一个 @Componnet注解 ,以便区分要进行注入的容器类

@Componnet注解:

对于这个注解,我们肯定是要有一个名称标识,以便我们区分
但是,我们使用 IoC/DI 机制时,有时所要注入的对象是单例的,有时不是单例的
那么,针对这种情况,我们在该注解中增加一个标识,
以便我们区分要注入的类是不是单例的,方便处理

那么,本人现在来展示下 @Componnet注解 的内容:

package edu.youzg.ioc_impl.core;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-17 16:01
 * @Description: 带你深究Java的本质!
 */
@Retention(RUNTIME)
@Target({TYPE, ANNOTATION_TYPE})
public @interface Component {
    String value() default "";
    boolean singleton() default true;
}

在我们准备好容器之后,还需要识别 哪些方法成员需要自动注入
那么,我们先用一个注解,来标识 可以从容器中获得 成员的构造方法 的成员

@Autowired注解:

package edu.youzg.ioc_impl.core;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-17 16:12
 * @Description: 带你深究Java的本质!
 */
@Retention(RUNTIME)
@Target({FIELD, METHOD})
public @interface Autowired {
}

包扫描工具 —— PackageScanner类:

请观看本人博文 —— 《【小工具】—— 包扫描》


BeanFactory类基础 (核心步骤):

那么,前戏都已经做好了,我们先来通过“包扫描”技术,
将所有带有 @Compent注解 的类的信息保存到一个map中:

//用这个map,以 类的名字 为键,类所对应的BeanDefinition类对象为值,
//来保存所有需要注入的类的信息
private static final Map<String, BeanDefinition> beanPool = new HashMap<>();

//通过“包扫描”来将所有含有 @Component注解 的类存入beanPool中
public static void scanPackage(String packageName) {
	new PackageScanner() {
		@Override
		public void dealClass(Class<?> klass) {
			if (klass.isPrimitive()
					|| klass == String.class
					|| klass.isAnnotation()
					|| klass.isArray()
					|| klass.isInterface()
					|| !klass.isAnnotationPresent(Component.class)) {
				return;
			}
			// 将这个类实例化,并将其放到beanPool中
			try {
				Object object = klass.newInstance();
				BeanDefinition beanDefinition = new BeanDefinition();
				beanDefinition.setKlass(klass);
				beanDefinition.setObject(object);
				
				beanPool.put(klass.getName(), beanDefinition);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}.scanPackage(packageName);
}

上述操作完成了对含有 @Componnet注解 的类的类的实例化
并将其放到池子中的操作,

但是,没有实现对这个实例中有 @Autowired注解 成员的注入工作
这种情况下,在没有完全扫描完之前,是没有办法确定依赖关系的完整性的!
因此,在包扫描解析XML配置时,不能急于处理依赖关系
即,不能急于完成“注入”工作
完成注入的最佳实际,应该延迟到GetBean()时
(即:在我们所要调用这个类时)
而这种将工作延迟到使用时的思想,也对应了本人之前博文中所讲解的一种设计模式 —— 懒汉模式

@Autowired注解 用于方法,则,使用者应该保证使用在setter方法上

因此,setter()方法如下基本特征

setter()方法基本特征

  1. 一个参数
  2. 无返回值(或是 返回本类型对象)

根据上文所述,@Autowired注解 若用在方法身上,
那么,这个方法应该是public修饰的否则我们无法通过反射机制来调用它


在这里,本人再来给出一个注解 —— @Bean注解

@Bean注解:

package edu.youzg.ioc_impl.core;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-17 16:13
 * @Description: 带你深究Java的本质!
 */
@Retention(RUNTIME)
@Target(METHOD)
public @interface Bean {
    String value() default "";
    boolean singleton() default true;
}

那么,现在,本人来讲解下为什么要给出 @Bean注解
答曰:

  1. @Bean注解的第一个应用场合
    @Autowired注解是存在缺陷的
    @Autowired注解 的缺陷:只能获取池子中的对象
    而池中对象都是需要给对应的类@Component注解
    对于不可更改的Jar包中的类,就没有办法增加@Component注解
    也就不能实现“注入”操作

那么,现在,再来给出一个 @Bean注解
@Bean注解 就是为了解决这样的问题存在的。
给一个方法增加 @Bean注解
而将这个方法的返回值对象返回值对象类型作为键值对,存储到beanPool中!

  1. @Bean注解的第二个应用场合
    若相关类没有提供可用的构造方法
    所谓的没有提供可用的构造方法包括相关构造方法是private的
    或者,构造方法不能直接调用
    或者,构造方法不能直接生成对象
    在这种情况下,由于对于Component注解的处理是通过调用相关类的无参构造产生的,
    那么,对于@Autowired注解,就不能产生这个类的对象!

此时,可以通过@Bean注解,调用合适的获取该类对象的方法,
取得这个类的对象,并加入beanPool中!

  1. @Bean注解的第三个应用场合
    相关类的对象,不是简单无参构造就能直接使用的
    意思是:这个类虽然存在无参构造
    但是,无参构造出来的对象不能直接使用

那么,在这种情况下,通过@Bean注解的方法,
完成其对象所必须的基础数据,从而使得该对象可用!


有@Bean注解含有参数的方法的处理:
这里的关键是:方法所依赖的参数是否满足
如果满足,则,该方法是可以执行得到一个BeanDefinition
如果多个方法的参数形成了循环依赖关系
则,应将这种循环依赖环,告知用户

核心是:

检测依赖是否满足

这里可以先考虑构造MethodDefinition
MethodDefinition中应该存储方法反射执行所需要的内容:

  1. 对象
  2. 方法本身
  3. 参数对象集合

那么,依据上述思想,本人现在来给出实现代码:

MethodDefinition类:

package edu.youzg.ioc_impl.core;

import java.lang.reflect.Method;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-17 16:17
 * @Description: 带你深究Java的本质!
 */
public class MethodDefination {
    private Object object;  // 执行该方法的对象
    private Method method;  // 该方法的Method对象
    private int paraCount;  // 未满足“依赖关系” 的参数个数

    public MethodDefination() {
        this.paraCount = 0;
    }

    //将这个方法所依赖的参数个数减1
    int sub() {
        return --this.paraCount;
    }

    Object getObject() {
        return object;
    }

    void setObject(Object object) {
        this.object = object;
    }

    Method getMethod() {
        return method;
    }

    void setMethod(Method method) {
        this.method = method;
    }

    int getParaCount() {
        return paraCount;
    }

    void setParaCount(int paraCount) {
        this.paraCount = paraCount;
    }

}

MethodDependence类:

这里最难解决的问题是:
参数所需要的对象可能是随机满足

所有不满足依赖关系的方法中的参数形成一个Map
此Map的参数类型,而MethodDefinition所形成的List
再准备一个满足要求MethodDefinition的列表

综上所述

准备3种东西

  1. 不能满足依赖关系的MethodDefinition 列表:列表1;
  2. 参数类型为键MethodDefinition为值的Map
  3. 满足了依赖关系的MethodDefinition 列表:列表2
private static final List<MethodDefinition> uninvokeMethodList
	= new ArrayList<MethodDefinition>();
private static final List<MethodDefinition> invokeableMethodList
		= new LinkedList<MethodDefinition>();
private static final Map<Class<?>, List<MethodDefinition>>
		dependenceMethodPool = new HashMap<Class<?>, List<MethodDefinition>>();

如下图所示:
3个容器初始状态 展示
处理步骤

  1. 遇到一个带参方法,先检测其所有参数
    1.1. 若参数满足要求,则,暂不处理(可以将count--)
    这里的count是“参数个数
    1.2. 若参数不能得到满足,则,将这个参数类型作为
    这个MethodDefinition作为存储到Map中
    1.3. 当对所有参数都进行了检测,若存在未满足的参数
    则,将MethodDefinition存储到列表1
    1.4. 当所有参数都满足要求,则,将其存储到列表2
  2. 每处理完一个Bean,都扫描Map
    依赖这个BeanMethodDefinitioncount--
    count为0了,则,将其存储到列表2中

addUninvokeMethod()方法实现思路
addUninvokeMethod()方法的执行 展示
而剩下两个列表也会做出相应的改动:
checkDependence()方法 和 invokeDependenceMethod()方法 实现思路
checkDependence()方法 和 invokeDependenceMethod()方法 的执行

那么,有了上述思路,我们现在来编写下 MethodDependence类

package edu.youzg.ioc_impl.core;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-17 16:42
 * @Description: 带你深究Java的本质!
 */
public class MethodDependence {
    // 三个容器
    private static final List<MethodDefination> uninvokeMethodList
            = new ArrayList<>();
    private static final List<MethodDefination> invokeMethodList
            = new ArrayList<>();
    private static final Map<String, List<MethodDefination>> dependenceMethodPool
            = new HashMap<>();

    public MethodDependence() {
    }

    /**
     * 向“依赖关系不满足”的方法列表 中存储数据
     * @param methodDefination
     * @param paraTypePool
     */
    static void addUninvokeMethod(MethodDefination methodDefination,
                                  Map<String, Boolean> paraTypePool) {
        uninvokeMethodList.add(methodDefination);

        // 遍历存储不满足依赖关系的dependenceMethodPool,将这个参数类型都存储进去
        // 再将依赖该参数的方法设置进去
        for (String beanName : paraTypePool.keySet()) {
            if (!dependenceMethodPool.containsKey(beanName)) {
                List<MethodDefination> methodList = new ArrayList<>();
                dependenceMethodPool.put(beanName, methodList);
            }
            List<MethodDefination> methodList = dependenceMethodPool.get(beanName);
            methodList.add(methodDefination);
        }
    }

    /**
     * 检查是否有方法所依赖的参数完成了注入,<br/>
     * 将完成所有依赖参数的注入的方法(即:可执行参数),<br/>
     * 从uninvokeMethodList转移到uninvokeMethodList中
     * @param beanName
     */
    static void satisfyDependence(String beanName) {
        // 获取依赖该参数的 方法列表
        List<MethodDefination> mdList = dependenceMethodPool.get(beanName);
        if (mdList == null) {
            return;
        }
        List<MethodDefination> okMethodList = new ArrayList<>();
        // 遍历该方法列表,
        // 若注入该参数后,遍历到的方法无未完成注入的依赖参数,就加入到okMethodList中
        for (MethodDefination md : mdList) {
            if (md.sub() == 0) {
                okMethodList.add(md);
            }
        }
        // 将okMethodList中的方法,
        // 从uninvokeMethodList转移到invokeableMethodList中
        if (!okMethodList.isEmpty()) {
            for (MethodDefination method : okMethodList) {
                uninvokeMethodList.remove(method);
                invokeMethodList.add(method);
            }
        }
        dependenceMethodPool.remove(beanName);
    }

    /**
     * 调用 invokeableMethodList中的方法(满足依赖关系的方法)
     */
    static void invokeDependenceMethod() {
        while (!invokeMethodList.isEmpty()) {
            MethodDefination methodDefination = invokeMethodList.get(0);
            Object object = methodDefination.getObject();
            // object是null
            //Class<?> klass = object.getClass();
            Method method = methodDefination.getMethod();
            invokeMethodList.remove(0);

            Parameter[] parameters = method.getParameters();
            HashSet<String> paraNames = new HashSet<>();
            System.out.println("循环前");
            for (Parameter parameter : parameters) {
                if (parameter.isAnnotationPresent(Qualifire.class)) {
                    Qualifire qualifire = parameter.getAnnotation(Qualifire.class);
                    paraNames.add(qualifire.value());
                } else {
                    String paraFullName = parameter.getType().getName();
                    int lastIndex = paraFullName.lastIndexOf(".");
                    paraNames.add(paraFullName.substring(lastIndex+1));
                }
            }
            System.out.println("循环结束");
            Bean bean = method.getAnnotation(Bean.class);
            String beanName = bean.value().equals("") ? method.getName() : bean.value();
            BeanFactory.invokeMethodWithPara(object, method, paraNames, beanName);
        }
    }

    /**
     * 获取依赖关系字符串(用于在发生“循环依赖”现象后进行处理展示)
     * @return
     */
    static String getUndependence() {
        StringBuffer str = new StringBuffer();

        for (String beanName : dependenceMethodPool.keySet()) {
            List<MethodDefination> mdList = dependenceMethodPool.get(beanName);
            for (MethodDefination md : mdList) {
                str.append(md.getMethod())
                        .append(" --> ").append(beanName)
                        .append("为名的bean\n");
            }
        }

        return str.append("等无法注入!").toString();
    }

}

在上文中的讲述中,其实还是有一点瑕疵:
若是我们的容器,所存储的同一个类非单例成员对象多个
那么,在注入的时候,就会有问题
在这里,本人再来给出一个注解,来解决这个问题。


因为我们想要区分哪一个对象是某处所需要注入的
所以,我们需要用“别名”来区分这些对象,
所以,再来给出一个注解中,来实现“别名”的区分,这个注解中需要一个name标识:

@Qualifier注解:

package edu.youzg.ioc_impl.core;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-17 16:15
 * @Description: 带你深究Java的本质!
 */
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER})
public @interface Qualifire {
    String value();
}

那么,有了上述的铺垫,本人现在来给出 核心的类 —— BeanFactory类
BeanFactory类是本次代码的核心类,它通过“包扫描技术”,来获取满足要求的类和方法,
并对其进行封装,再将其放入一个beanPool(存储每一个能完成依赖注入的方法的信息):

BeanFactory类:

package edu.youzg.ioc_impl.core;

import edu.youzg.ioc_impl.util.PackageScanner;

import java.lang.reflect.*;
import java.util.*;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-17 18:04
 * @Description: 带你深究Java的本质!
 */
public class BeanFactory {
    private static final Map<String, BeanDefination> beanPool
            = new HashMap<>();

    public BeanFactory() {
    }

    private static String getKlassName(String fullName) {
        // edu.youzg.ioc_impl.model.DemoConfigration
        int lastDotIndex = fullName.lastIndexOf(".");
        return fullName.substring(lastDotIndex + 1);
    }
    
    private static void processBean(boolean singleton, Class<?> klass, Object object, String name) {
        BeanDefination beanDefination = new BeanDefination();

        beanDefination.setSingleton(singleton);
        beanDefination.setKlass(klass);
        beanDefination.setObject(object);   // 此处我们的目的是不给非单例的赋值,以便我们在之后获取时候能够重新赋值(保证多例性)

        beanPool.put(name, beanDefination);
        MethodDependence.satisfyDependence(name);
    }

    private static void dealComponent(Class<?> klass) {
        Component component = klass.getAnnotation(Component.class);
        boolean singleton = component.singleton();
        String name = component.value(); // 若用户未赋值,则为"",方便我们在下面处理

        String klassFullName = klass.getName();
        int lastIndex = klassFullName.lastIndexOf(".");
        name = name.equals("") ? klassFullName.substring(lastIndex+1) : name;
        Object object = null;
        if (singleton) {
            try {
                object = klass.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        processBean(singleton, klass, object, name);
    }

    private static BeanDefination getBeanObject(String klassname) {
        BeanDefination bean = beanPool.get(klassname);
        if (bean == null) {
            return null;
        }
        Object object = null;

        if (!bean.isSingleton()) {
            Class<?> klass = bean.getKlass();
            try {
                object = klass.newInstance();
                bean.setObject(object);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return bean;
    }

    private static Map<String, Boolean> getMethodPara(Method method) {
        Map<String, Boolean> paraPool = new HashMap<>();
        List<String> paraList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            String name = null;
            if (parameter.isAnnotationPresent(Qualifire.class)) {   // 按照名称注入
                Qualifire qualifire = parameter.getAnnotation(Qualifire.class);
                name = qualifire.value();
            } else {    // 按照类型注入
                // java.lang.String
                String paraFullName = parameter.getType().getName();
                int lastDotIndex = paraFullName.lastIndexOf(".");
                name = paraFullName.substring(lastDotIndex + 1);
            }
            if (!paraList.contains(name)) {
                paraList.add(name);
            }
        }

        for (String beanName : paraList) {
            BeanDefination beanDefinition = beanPool.get(beanName);
            if (beanDefinition != null) {
                paraPool.put(beanName, true);
            } else {
                paraPool.put(beanName, false);
            }
        }

        return paraPool;
    }

    static void invokeMethodWithPara(Object object, Method method, Set<String> paraNames, String name) {
        int paraCount = paraNames.size();
        Object[] paraValues = new Object[paraCount];

        int index = 0;
        Iterator<String> iterator = paraNames.iterator();
        while (iterator.hasNext()) {
            String beanName = iterator.next();
            BeanDefination beanDefinition = getBeanObject(beanName);
            paraValues[index] = beanDefinition.getObject();
        }

        try {
            Class<?> beanClass = method.getReturnType();
            Object bean = method.invoke(object, paraValues);
            BeanDefination beanDefinition = new BeanDefination();
            beanDefinition.setKlass(beanClass);
            beanDefinition.setObject(bean);
            beanPool.put(name, beanDefinition);

            //检查依赖关系,将由于该方法的执行 而满足依赖关系的方法 转移到可执行列表中,以便我们之后的执行
            MethodDependence.satisfyDependence(name);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private static void dealMethodWithPara(Object object, Method method, String name) {
        //获取该方法所需参数的map
        Map<String, Boolean> paraPool = getMethodPara(method);

        //若取到的map没有存储任何对象,则执行该方法
        Set<String> paraNames = paraPool.keySet();
        if (!paraPool.values().contains(false)) {
            invokeMethodWithPara(object, method, paraNames, name);
            return;
        }

        //将该方法的信息,设置进uninvokeMethodList中
        MethodDefination methodDefinition = new MethodDefination();
        methodDefinition.setObject(object);
        methodDefinition.setMethod(method);
        methodDefinition.setParaCount(paraPool.size());

        MethodDependence.addUninvokeMethod(methodDefinition, paraPool);
    }

    private static void dealConfigration(Class<?> klass) {
        Object object = null;
        try {
            object = klass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        Method[] methods = klass.getMethods();
        for (Method method : methods) {
            if (!method.isAnnotationPresent(Bean.class)) {
                continue;
            }
            Bean bean = method.getAnnotation(Bean.class);
            boolean singleton = bean.singleton();
            String name = bean.value(); // 若用户未赋值,则为"",方便我们在下面处理
            Class<?> returnClass = method.getReturnType();

            name = name.equals("") ? method.getName() : name;

            if (method.getParameterCount() > 0) {
                // 处理带参数的方法;
                dealMethodWithPara(object, method, name);
                continue;
            }
            try {
                Object beanObject = method.invoke(object);
                processBean(singleton, returnClass, beanObject, name);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    public static void scanPackage(String packageName) {
        new PackageScanner() {
            @Override
            public void dealClass(Class<?> klass) {
                if (klass.isPrimitive()
                        || klass == String.class
                        || klass.isAnnotation()
                        || klass.isArray()
                        || klass.isInterface()
                        || !(klass.isAnnotationPresent(Component.class) || klass.isAnnotationPresent(Configration.class))) {
                    return;
                }
                if (klass.isAnnotationPresent(Configration.class)) {
                    dealConfigration(klass);
                } else {
                    dealComponent(klass);
                }
            }
        }.scanPackage(packageName);
        MethodDependence.invokeDependenceMethod();
    }

    private void showCircleDependency() {
        System.out.println(MethodDependence.getUndependence());
    }

    private void injectMethod(Class<?> klass, Object object) {
        Method[] methods = klass.getDeclaredMethods();
        for (Method method : methods) {
            String methodName = method.getName();
            int parameterCount = method.getParameterCount();
            int modify = method.getModifiers();
            //执行该对象的setXXX()方法
            if (!methodName.startsWith("set")
                    || parameterCount != 1
                    || !Modifier.isPublic(modify)
                    || !method.isAnnotationPresent(Autowired.class)) {
                continue;
            }

            Object value = null;
            Parameter parameter = method.getParameters()[0];    // 由于是setXXX()方法,所以是单参
            if (parameter.isAnnotationPresent(Qualifire.class)) {
                Qualifire qualifire = parameter.getAnnotation(Qualifire.class);
                String beanName = qualifire.value();
                value = getBean(beanName);
            } else {
                // 对这个方法进行反射调用
                // 方法的反射调用,需要得到两个基本数据:
                // 1、对象;这个数据已经拥有,即,object
                // 2、参数;
                Class<?> paraType = parameter.getType();
                value = getBean(paraType);
            }
            try {
                method.invoke(object, new Object[]{value});
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    private void injectField(Class<?> klass, Object object) {
        Field[] fields = klass.getDeclaredFields();
        for (Field field : fields) {
            //筛选出带有 @Autowired注解 的成员对象
            if (!field.isAnnotationPresent(Autowired.class)) {
                continue;
            }
            Object value = null;
            if (field.isAnnotationPresent(Qualifire.class)) {
                Qualifire qualifire = field.getAnnotation(Qualifire.class);
                String beanName = qualifire.value();
                value = getBean(beanName);
            } else {
                Class<?> fieldClass = field.getType();
                value = getBean(fieldClass);
            }
            field.setAccessible(true);
            try {
                //为该成员赋值(即:注入该成员)
                field.set(object, value);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private void inject(BeanDefination bean) {
        Object object = bean.getObject();
        Class<?> klass = bean.getKlass();

        injectField(klass, object);
        injectMethod(klass, object);
    }

    //通过所传类名,来获取该类的对象
    @SuppressWarnings("unchecked")
    public <T> T getBean(String beanName) {
        BeanDefination bean = getBeanObject(beanName);
        //若未取到,则表明该类并没有存入beanPool中(即:发生了“循环依赖”)
        if (bean == null) {
            showCircleDependency();
            try {
                throw new BeanNotFoundException("Bean[" + beanName + "]不存在!");
            } catch (BeanNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
        Object object = bean.getObject();

        if (!bean.isInject() || !bean.isSingleton()) {
            //设置该类 “已被注入”
            bean.setInject(true);
            // 这里完成对object中需要注入的成员的初始化工作!
            inject(bean);
        }

        return (T) object;
    }

    public <T> T getBean(Class<?> klass) {
        String fullName = klass.getName();
        int lastIndex = fullName.lastIndexOf(".");
        return getBean(fullName.substring(lastIndex + 1));
    }

}

那么,到这里,我们就基本实现了 IoC/DI机制


测试:

现在,我们来测试下所给出的工具是否能完成 依赖注入 的功能:
首先,本人来给出四个类:

OneClass类:

package edu.youzg.ioc_impl.test;

import java.util.Calendar;

import edu.youzg.ioc_impl.core.Autowired;
import edu.youzg.ioc_impl.core.Component;
import edu.youzg.ioc_impl.core.Qualifire;

@Component(singleton = false)
public class OneClass {
	@Autowired
	private TwoClass two;

	@Autowired
	@Qualifire("getCalendar")
	private Calendar date;

	@Autowired
	ThreeClass threeClass;

	public OneClass() {
	}

	public void setTwo(TwoClass two) {
		this.two = two;
	}

	public TwoClass getTwo() {
		return two;
	}

	public ThreeClass getThreeClass() {
		return threeClass;
	}

	public void setThreeClass(ThreeClass threeClass) {
		this.threeClass = threeClass;
	}

	public void doOneThing() {
		System.out.println(two);
		System.out.println(date.getTimeInMillis());
		System.out.println(threeClass);
		System.out.println(threeClass.getTwo());
	}
	
}

TwoClass类:

package edu.youzg.ioc_impl.test;

import edu.youzg.ioc_impl.core.Component;

@Component
public class TwoClass {
	
	public TwoClass() {
	}
	
	@Override
	public String toString() {
		return "这是一个TwoClass的对象";
	}
	
}

ThreeClass类:

package edu.youzg.ioc_impl.test;

import edu.youzg.ioc_impl.core.Autowired;
import edu.youzg.ioc_impl.core.Component;

@Component
public class ThreeClass {
	@Autowired
	private TwoClass two;
	
	public ThreeClass() {
	}

	public TwoClass getTwo() {
		return two;
	}

	public void setTwo(TwoClass two) {
		this.two = two;
	}
	
}

ForthClass类:

package edu.youzg.ioc_impl.test;

public class ForthClass {
	private OneClass one;
	
	public ForthClass() {
	}

	public OneClass getOne() {
		return one;
	}

	public void setOne(OneClass one) {
		this.one = one;
	}

}

现在,本人来给出一个配置类

配置类 测试:

package edu.youzg.ioc_impl.test;

import edu.youzg.ioc_impl.core.Bean;
import edu.youzg.ioc_impl.core.Configration;
import edu.youzg.ioc_impl.core.Qualifire;

import java.util.Calendar;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-18 12:49
 * @Description: 带你深究Java的本质!
 */
@Configration
public class TestConfigration {

    @Bean
    public Calendar getCalendar() {
        Calendar calendar = Calendar.getInstance();
        return calendar;
    }

    @Bean("four")
    public ForthClass getForth(@Qualifire("OneClass") OneClass one) {
        ForthClass res = new ForthClass();
        res.setOne(one);
        System.out.println("这是获取ForthClass对象的方法");

        return res;
    }

}

现在,本人最后来给出一个测试类:

package edu.youzg.ioc_impl.test;

import edu.youzg.ioc_impl.core.BeanFactory;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-18 12:14
 * @Description: 带你深究Java的本质!
 */
public class Test {

    public static void main(String[] args) {
        BeanFactory.scanPackage("edu.youzg.ioc_impl.test");
        BeanFactory beanFactory = new BeanFactory();
        
        // 测试 普通获取
        OneClass one = beanFactory.getBean("OneClass");
        System.out.println(one);

        // 测试 “依赖注入”
        one.doOneThing();

        // 测试 配置类的两个注解 ———— @Qualifire 和 @Bean 是否生效
        ForthClass four = beanFactory.getBean("four");
        System.out.println(four);

        // 测试 多例性
        Object one2 = beanFactory.getBean("OneClass");
        System.out.println(one==one2);

        // 测试 单例性
        ThreeClass three = beanFactory.getBean("ThreeClass");
        System.out.println(three == one.getThreeClass());
    }

}

现在,我们来看一下运行结果:
测试结果 展示
可以看到:
我们的 注解式 的IoC/DI机制 基本上完成了!!!


当然,我们所写的这点代码,
是不可能完全考虑到各种情况,实现Spring的功能的
那么,在这里,本人总结下存在的Bug和比起Spring的IoC机制所欠缺的功能:

Bug 及 不足:

配置类中的Bean的参数 只能有一个:

@Bean("four")
public ForthClass getForth(@Qualifire("OneClass") OneClass one1, @Qualifire("OneClass") OneClass one2) {
    ForthClass res = new ForthClass();
    res.setOne(one1);
    res.setOne(one2);
    System.out.println("这是获取ForthClass对象的方法");

    return res;
}

bug1 展示


无法像Spring那么高效:

Spring的上下文容器BeanFactory所采用了很多的算法
解决了效率问题


考虑的问题较少:

Spring Framework 的兼容性非常高
而我们所编写的IoC机制,仅仅是能简单实现IoC的基本功能
但是在之后的与别的框架的集成中,很可能会出现很大的问题!


本文主旨是提高同学们的编程思想
以及初步模拟下Spring的IoC机制的基本实现
希望同学们能够有所收获!

posted @ 2020-04-28 17:49  在下右转,有何贵干  阅读(62)  评论(0编辑  收藏  举报