【Spring框架学习】IoC/DI机制 注解式 基本实现
首先,本人来给出两个类:
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类类型成员,
都由一段代码自动完成“初始化”。
以后使用这些类的对象时,一律从这个集合(池子)中取。
从另一个角度思考上述问题:
构建一个容器(上下文),在这个容器中存储类及其对象;
在使用这些类的对象时,基本上都是从这个容器中获取的;
若这些类的成员,其类型也在容器中,
则,它们将被自动初始化,且用容器中的对象完成初始化。
需要说明的是,对于类中的成员的初始化选择,应该由用户决定。
选择权的实现有两种具体的方法:
- 通过XML配置映射关系;
- 通过注解配置映射关系。
上述的两种实现方法,各有利弊:
- XML配置:
- 优势:
不侵害源代码,
保证了“开闭原则”- 不足:
代码量巨大,若要配置一个大项目,则配置文件就变得十分复杂
- 注解配置:
- 优势:
程序可读性强
无需额外代码(开发效率高)- 不足:
会对源代码进行修饰,
实现起来比较麻烦
那么,本着“累死我一个,幸福千万家”的原则,
本人就来实现下 注解配置的代码:
注解配置方式:
首先,我们来构造一个类,来存储需要进行注入的类的信息:
我们现在思考下,这个类需要哪些信息:
- 这个类的Class对象信息,以便我们之后的反射机制的使用
- 这个类的对象,以便我们之后执行它的某个方法,或者 将它注入到容器(上下文)中
- 一个用于标识是不是单例的类的标识
- 一个用于标识是否注入此类的标识
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()方法 的 基本特征:
- 一个参数;
- 无返回值(或是 返回本类型对象)
根据上文所述,@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注解?
答曰:
- @Bean注解的第一个应用场合:
@Autowired注解是存在缺陷的,
@Autowired注解 的缺陷:只能获取池子中的对象,
而池中对象都是需要给对应的类以 @Component注解;
对于不可更改的Jar包中的类,就没有办法增加@Component注解,
也就不能实现“注入”操作
那么,现在,再来给出一个 @Bean注解,
@Bean注解 就是为了解决这样的问题存在的。
给一个方法增加 @Bean注解,
而将这个方法的返回值对象和返回值对象类型作为键值对,存储到beanPool中!
- @Bean注解的第二个应用场合:
若相关类没有提供可用的构造方法;
所谓的没有提供可用的构造方法包括相关构造方法是private的,
或者,构造方法不能直接调用,
或者,构造方法不能直接生成对象!
在这种情况下,由于对于Component注解的处理是通过调用相关类的无参构造产生的,
那么,对于@Autowired注解,就不能产生这个类的对象!
此时,可以通过@Bean注解,调用合适的获取该类对象的方法,
取得这个类的对象,并加入beanPool中!
- @Bean注解的第三个应用场合:
相关类的对象,不是简单无参构造就能直接使用的;
意思是:这个类虽然存在无参构造,
但是,无参构造出来的对象不能直接使用
那么,在这种情况下,通过@Bean注解的方法,
完成其对象所必须的基础数据,从而使得该对象可用!
有@Bean注解且含有参数的方法的处理:
这里的关键是:方法所依赖的参数是否满足?
如果满足,则,该方法是可以执行并得到一个BeanDefinition
如果多个方法的参数形成了循环依赖关系,
则,应将这种循环依赖环,告知用户
其核心是:
检测依赖是否满足
这里可以先考虑构造MethodDefinition
MethodDefinition中应该存储方法反射执行所需要的内容:
- 对象;
- 方法本身;
- 参数对象集合
那么,依据上述思想,本人现在来给出实现代码:
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种东西:
- 不能满足依赖关系的MethodDefinition 列表:列表1;
- 参数类型为键,MethodDefinition为值的Map;
- 满足了依赖关系的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>>();
如下图所示:
处理步骤:
- 遇到一个带参方法,先检测其所有参数;
1.1. 若参数满足要求,则,暂不处理(可以将count--)
这里的count是“参数个数”
1.2. 若参数不能得到满足,则,将这个参数类型作为键,
这个MethodDefinition作为值,存储到Map中;
1.3. 当对所有参数都进行了检测,若存在未满足的参数,
则,将MethodDefinition存储到列表1;
1.4. 当所有参数都满足要求,则,将其存储到列表2;- 每处理完一个Bean,都扫描Map,
将依赖这个Bean的MethodDefinition的count--;
若count为0了,则,将其存储到列表2中
addUninvokeMethod()方法实现思路:
而剩下两个列表也会做出相应的改动:
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;
}
无法像Spring那么高效:
Spring的上下文容器和BeanFactory所采用了很多的算法
解决了效率问题
考虑的问题较少:
Spring Framework 的兼容性非常高
而我们所编写的IoC机制,仅仅是能简单实现IoC的基本功能
但是在之后的与别的框架的集成中,很可能会出现很大的问题!
本文主旨是提高同学们的编程思想
以及初步模拟下Spring的IoC机制的基本实现
希望同学们能够有所收获!