什么是依赖注入?
平常的Java开发中,程序员在某个类中需要依赖其它类的方法。我们通常是new一个依赖类再调用类实例的方法set进去,这种开发存在的问题是new的类实例不好统一管理。所以Spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过Spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。
依赖注入的另一种说法是"控制反转"。通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员而控制反转是指new实例工作不由我们程序员来做而是交给Spring容器来做。
注意在这篇文章中我们用注解的方法去模拟,采用懒加载方式。
其中涉及到一个扫描包中其他Java类的扫描工具类
模拟的思路如下:
- @Component注解类:用来注解需要哪些类的定义信息,被扫描生成BeanDefination,可以拥有别名。
- @Autowired注解类:用来注解依赖类成员,表示该成员需要注入,可以根据别名注入,可以生成单例或非单例的实例;
- @Qualifier注解类:当某个要被注入的成员是接口或者父类时,确定要注入哪个子类,ImplementationClass记录父类名称。
- BeanDefinition:记录一个类的信息,包含三个成员:klass是类的类型,object是记录已经注入的实例,isinject表示是否已经注入,它的作用有两个,一是在getbean时不会再次依赖注入,二是巧妙解决循环依赖。
- BeanFactory:顾名思义就是生产bean的地方了,我这里实现的比较粗糙,将扫描和依赖注入均放在这里,它有两个成员beanPool:用来存放BeanDefinition,alias用来存放别名,beanfactory会先扫描包启动容器,然后在调用getBean时进行依赖注入(这里的依赖注入实际就是用递归表达出来的)。
具体代码
- 三个注解类:
package annotation;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(TYPE)
public @interface Component {
String name() default "";
}
package annotation;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(FIELD)
public @interface Autowired {
String name() default "";
boolean isSinglton() default true;
}
package annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 如果要向接口注入,需要确定注入接口实现类的哪一个
* @author quan
* @create 2020-06-25 12:52
*/
@Retention(RUNTIME)
@Target(FIELD)
public @interface Qualifier {
String ImplementationClass() default "";
}
- BeanDefinition: 类的定义,以及生成的单例也会包含在这里
package core;
/**
* 类的定义
*/
public class BeanDefinition {
private Class<?> klass;
private Object object;
private boolean inject;
public BeanDefinition() {
}
protected Class<?> getKlass() {
return klass;
}
protected void setKlass(Class<?> klass) {
this.klass = klass;
}
protected Object getObject() {
return object;
}
protected Object getPropertyObject() throws IllegalAccessException, InstantiationException {
return klass.newInstance();
}
protected void setObject(Object object) {
this.object = object;
}
protected boolean isInject() {
return inject;
}
protected void setInject(boolean inject) {
this.inject = inject;
}
@Override
public String toString() {
return "[klass=" + klass.getSimpleName(