Spring是一种 Java 开发框架,其主要功能有两个:IoC(DI)和AOP。《模拟实现Spring IOC》是本人的一个编程训练项目,为了提升本人的编程能力、JAVA 编程思想,基于框架的角度出发,对 Spring IoC有一个更深层次的认识, DI (依赖注入)的底层实现逻辑有更深的理解。
博主本人初入 Java 不久,能力有限,只将 Spring IoC 完成到:发现Bean注解的循环依赖;
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
反转:程序本身不创建对象,而变成被动的接收对象。
依赖注入:就是利用 set 方法来进行注入的。
一个APP中的类对象,不是直接在代码中初始化的,而是通过自动工具(Spring)完成初始化(对象的生成)。
假设某个APP中存在多个类:A、B、C,A 类中存在一个成员,其类型是B,B类中存在一个成员,其类型是C。
A 类:
public class A {
private B b;
……无参、单参构造,getter,setter
@Override
public String toString() {
return "A [b=" + b + "]";
}
}
B 类:
public class B {
private C c;
……无参、单参构造,getter,setter
@Override
public String toString() {
return "B [c=" + c + "]";
}
}
C 类:
public class C {
…………
}
那么,A、B、C这三个类的成员,将由一个“Bean工厂”自动生成,且,其中的各成员所需对象也是由这个工厂自动“注入”的;并将这些生成的对象,存储到一个Bean工厂中。
在使用时,由自动工具完成从Bean工厂中取出相关对象的操作。
IoC(DI)所处理的类的成员类型主要是类类型,或者说,非八大基本类型和String类型。
包扫描技术的实现
上述代码中涉及到包扫描的工具使用,这里给出具体实现过程,HB个人博客:包扫描工具实现https://blog.csdn.net/SwaggerHB/article/details/130990812?spm=1001.2014.3001.5501
自动注入的实现
为了模拟实现 IoC 中的自动注入,博主这里定义了一个**@Autowired 注解**
具体实现在下文中对@Autowired 注解的建立和使用中。
循环依赖的处理
问题描述:
如果带有@Bean注解的方法是无参方法,当然可以直接执行,并生成一个Bean;
如果该方法是带参的(当然,这个参数的值(实参),必须能从BeanFactory中获取),那么,这个方法的反射执行,就必须先从BeanFactory中获取一个Bean,然后才能执行之,并获得一个 Bean。但,对于方法在反射机制的扫描过程,不能完全保证应有的顺序!
更普遍的现象可能会是:多个@Bean注解的方法都是带参数的,而且,这些参数对应的Bean对象还没有生成,可能会在以后的扫描过程中才逐步生成,那就必须处理这样的问题。
这个问题就等价于说:多个Bean对象,其生成顺序不定(且都是通过带有@Bean注解的方法生成的),那么,这些方法所需
参数,就形成了所谓的“依赖关系”。
解决方案:
对于带参的@Bean注解的方法,需要考察期参数依赖;如果参数依赖都能够满足,那么可以执行这个方法,并得到响应的Bean对象;
如果相关参数不能得到满足,就需要先将这些方法存储起来;并在每生成一个Bean对象时,扫描所有依赖不满足的方法,更改依赖关系,直到某个方法依赖已经满足,则,执行这个方法!
上述算法虽然可以解决问题,但是,其时间复杂度比较高。为降低时间复杂度,可以考虑按照所需依赖的类型,创建一个Map:
ClassB -> getClassA(), getClassE()
ClassC -> getClassA(), getClassB()
ClassD -> getClassB(), getClassC(), getClassE()
ClassA -> getClassE()
所需注解定义:
Component 注解:
这个注解的意思是:对于这个类,我是否产生一个 Bean 放到 BeanFactory 里面去。
//如果某一个类带了 Component 注解,那么就需要生成一个对象放到 BeanFactory 里。
@Retention(RUNTIME)
@Target(TYPE)
public @interface Component {
String value() default "";
}
Autowired 注解:
@Retention(RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD}) //只用于成员和方法
public @interface Autowired {
String value() default "";
}
这个注解的意思是:某个成员被自动注入,例如:
public class B {
@Autowired
private C c;
…………
}
表明 C 成员应该由自动工具自动完成初始化,也就是说从 BeanFactory 中取出 C 的一个成员完成对他的初始化,即注入。
Bean 注解:
上述两个注解:@Component注解、@Autowried注解,这些注解需要书写到类和成员上,才能实现其功能。依靠这两个注解,IoC的基本功能已经可以实现,并且避免了循环依赖造成的无限递归;但是,功能还不完善。
这时候我们也发现了一个问题,对于已经编译成 .class
的类,我们无法将上述两个注解“书写”进这些类中,因此,对于已经编译成.class的类,是不能执行 IoC 功能的。这就是最大的缺陷!
为解决上述问题,专门引入一个注解:@Bean注解,@Bean 注解专门处理已生成的类的IoC问题。
@Retention(RUNTIME)
@Target(METHOD) //用于方法
public @interface Bean {
}
举个例子,如下:
//说明:此处的Timer是自己编写的定时器,用来当作已无法更改其源代码的类。
@Bean
public Timer getTimer() {
Timer timer = new Timer();
// 在这个方法中,生成了一个Timer类的对象,而且可以预见:
// 在这个方法中,我们可以对timer对象进行更复杂的操作!
timer.setDelayTime(1500);
// 这种操作对于前面实现的Autowired注入方式是无法实现的!
// 这相当于增强了IoC时的逻辑操作!
return timer;
}
我们则需要在 BeanFactory 的 scan 中,去扫描到带有 @Bean 的方法,执行它,并将执行的结果以返回值类型的名字作为 id,和它的返回值形成BeanDefinition
BeanFactory 用来存放众多的 Bean ,对所有的 Bean进行封装。
BeanDefinition
public class BeanDefinition {
private Class<?> klass; //Bean的类型
private Object object; //该类的一个对象
private boolean isInject; //是否被注入的标志
…………无参构造、一系列getter,setter
}
为后续的所要产生 Bean 的类,都产生一个对象,并且封装成 BeanDefinition。
BeanFactory
包扫描:扫描所有包下,带有Component注解的类
public class BeanFactory {
//单例模式的 beanPool;
private static final Map<String, BeanDefinition> beanPool;
static {
beanPool = new HashMap<>();
}
public BeanFactory() {
}
//
public static void scanBeanByAnnotation(String packageName) throws Exception {
PackageScanner scanner = new PackageScanner()
.addClassDealer(new IClassDealer() {
@Override
public void classDealer(Class<?> klass) {
//如果没有带Component注解,返回
if (!klass.isAnnotationPresent(Component.class)) {
return;
}
//如果带有Component注解,我们需要生成一个对象,放到BeanPool中去
try {
Object object = klass.newInstance();
BeanDefinition beanDefinition = addBean(klass, object);
dealBeanAnnotation(beanDefinition);
dealDependencyMethod();
} catch (Exception e) {
e.printStackTrace();
}
}
});
scanner.scanPackage(packageName);
}
…………
}
获取封装在BeanFactory中的指定类的对象:
@SuppressWarnings("unchecked")
public <T> T getBean(String name) {
//从 beanPool 中根据 name 获取 beanDefinition;
BeanDefinition beanDefinition = beanPool.get(name);
if (beanDefinition == null) {
return null;
}
if (!beanDefinition.isInject()) {
//设置已经被注入
beanDefinition.setInject(true);
doInject(beanDefinition);
}
return (T) beanDefinition.getObject();
}
//重载,从 beanPool 中根据 klass 获取 name;
public <T> T getBean(Class<?> klass) {
return getBean(klass.getName());
}
注入操作的实现
private void doInject(BeanDefinition bean) {
Class<?> klass = bean.getKlass();
Object object = bean.getObject();
//获取这个类的每一个成员,判断是否有Autowired注解
Field[] fields = klass.getDeclaredFields();
for (Field field : fields) {
//如果没有Autowired注解,放弃
if (!field.isAnnotationPresent(Autowired注解.class)) {
continue;
}
//如果有Autowired注解,对这个成员进行注入
Class<?> fieldType = field.getType(); //获取这个成员的类型
/**
*使用下面的代码,Bean对象就是单例的;
*/
Object fieldValue = getBean(fieldType); //根据成员的类型,获取在BeanFactory存放的这个成员的对象
/**
*如果发现用户要求非单例,则需要根据fieldType,再次生成一个对象,
*而且还需要进一步完成这个对象的“注入”工作
*/
field.setAccessible(true);
// 上面的操作有违Java编程精神,有违面向对象编程思想。
// private权限就是为了保护成员和方法的,而setAccessible()方法彻底使得
// 保护权限失去其作用!所以,这种注入方法不提倡!
// 建议:对setter方法使用@Autowired注解!
try {
field.set(object, fieldValue);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
@Bean注解的处理:
private static void dealBeanAnnotation(BeanDefinition beanDefinition) throws Exception {
Class<?> klass = beanDefinition.getKlass();
Object object = beanDefinition.getObject();
Method[] methods = klass.getDeclaredMethods();
for (Method method : methods) {
if (!method.isAnnotationPresent(Bean.class)) {
continue;
}
MethodDependencyPool.addBeanMethodDefinition(object, method);
}
}
@Bean注解的循环依赖
将 Bean 封装后,添加到BeanPool中:
private static BeanDefinition addBean(Class<?> klass, Object object) {
BeanDefinition bean = new BeanDefinition();
bean.setKlass(klass);
bean.setObject(object);
bean.setInject(false);
beanPool.put(klass.getName(), bean);
MethodDependencyPool.adjustDependency(klass);
return bean;
}
判断beanPool中是否存在这个类的Bean
static boolean isBeanExist(Class<?> klass) {
return beanPool.containsKey(klass.getName());
}
反射执行带有@Bean注解的方法:
private static void doBeanMethod(BeanMethodDefinition beanMethodDefinition) throws Exception {
Object object = beanMethodDefinition.getObject();
Method method = beanMethodDefinition.getMethod();
Class<?> beanClass = method.getReturnType();
Object[] parameterValues = getParameterValues(method);
Object bean = method.invoke(object, parameterValues);
addBean(beanClass, bean);
}
处理MethodDependencyPool
池子中的方法:
private static void dealDependencyMethod() throws Exception {
while (MethodDependencyPool.hasNext()) {
BeanMethodDefinition beanMethodDefinition = MethodDependencyPool.next();
doBeanMethod(beanMethodDefinition);
}
}
获取这个方法的所有参数的值列表:
private static Object[] getParameterValues(Method method) {
int parameterCount = method.getParameterCount();
if (parameterCount <= 0) {
return new Object[0];
}
Object[] parameterValues = new Object[parameterCount];
Parameter[] parameters = method.getParameters();
for (int index = 0; index < parameterCount; index++) {
Parameter parameter = parameters[index];
Class<?> parameterType = parameter.getType();
BeanDefinition beanDefinition = beanPool.get(parameterType.getName());
parameterValues[index] = beanDefinition.getObject();
}
return parameterValues;
}
@Bean注解的循环依赖基础数据创建
BeanMethodDefinition
类的创建:
public class BeanMethodDefinition {
private Object object;
private Method method;
private List<Class<?>> parameterTypeList;
public BeanMethodDefinition() {
this.parameterTypeList = new LinkedList<Class<?>>();
}
…………一系列getter、setter
void addParameterType(Class<?> parameterType) {
this.parameterTypeList.add(parameterType);
}
void removeParameterType(Class<?> parameterType) {
if (this.parameterTypeList.contains(parameterType)) {
this.parameterTypeList.remove(parameterType);
}
}
boolean isParameterReady() {
return this.parameterTypeList.isEmpty();
}
}
MethodDependencyPool
类的创建:
public class MethodDependencyPool {
/**
* 尚未满足依赖关系的Bean方法的列表
*/
private static final List<BeanMethodDefinition> beanMethodList = new ArrayList<>();
/**
* 键:尚未生成的Bean对象类型名称;
* 值:方法列表;其中的每一个方法,都依赖“键”所表示的Bean对象。
*/
private static final Map<Class<?>, List<BeanMethodDefinition>> dependencyList
= new HashMap<Class<?>, List<BeanMethodDefinition>>();
/**
* 已经满足依赖关系的,可以直接执行的方法列表。
*/
private static final List<BeanMethodDefinition> readyMethodList = new LinkedList<>();
MethodDependencyPool() {
}
/**
* 增加一个存在依赖关系的方法。
* @param object 相关方法在执行时所需的对象
* @param method 方法
*/
static void addBeanMethodDefinition(Object object, Method method) {
BeanMethodDefinition definition = new BeanMethodDefinition();
definition.setObject(object);
definition.setMethod(method);
int parameterCount = method.getParameterCount();
if (parameterCount <= 0) {
// 对于无参方法,依赖关系已经满足,可以直接加到readyMethodList中。
readyMethodList.add(definition);
return;
}
Map<Class<?>, Integer> parameterTypePool = new HashMap<Class<?>, Integer>();
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
Class<?> parameterType = parameter.getType();
if (!BeanFactory.isBeanExist(parameterType)) {
parameterTypePool.put(parameterType, 0);
}
}
if (parameterTypePool.isEmpty()) {
readyMethodList.add(definition);
return;
}
for (Class<?> clazz : parameterTypePool.keySet()) {
definition.addParameterType(clazz);
if (!dependencyList.containsKey(clazz)) {
dependencyList.put(clazz, new ArrayList<>());
}
List<BeanMethodDefinition> methodDependencyList = dependencyList.get(clazz);
methodDependencyList.add(definition);
}
}
static boolean hasNext() {
return !readyMethodList.isEmpty();
}
static BeanMethodDefinition next() {
return readyMethodList.remove(0);
}
/**
* 根据新生成的Bean对象,调整方法依赖关系。
* @param klass 新生成的Bean对象的类型
*/
static void adjustDependency(Class<?> klass) {
if (!dependencyList.containsKey(klass)) {
return;
}
List<BeanMethodDefinition> findBeanMethodList = dependencyList.remove(klass);
for (BeanMethodDefinition beanMethod : findBeanMethodList) {
beanMethod.removeParameterType(klass);
if (beanMethod.isParameterReady()) {
beanMethodList.remove(beanMethod);
readyMethodList.add(beanMethod);
}
}
}
}