“ DI是IOC的一种重要实现,Spring能够动态的向某个对象提供它所需要的其他对象,就是通过DI(Dependency Injection,依赖注入)来实现的”
在上一篇文章中,对Spring的AOP进行了简单的实现,其中一部分代码其实就有注入的影子,但是我没有具体的提取出来,今天我们就来具体的实现注入。
01
—
工厂类
我们都知到在Spring项目中,所有的实例对象的获取不应该直接new,而是通过某个工厂获取。在Spring中的工厂有两个:BeanFactory和ApplicationContext,其中BeanFactory在获取Bean的时候才会创建类的实例,ApplicationContext是在配置文件被加载的时候就创建类的实例,此外ApplicationContext接口作为BeanFactory的派生,因而提供BeanFactory所有的功能。
这里我们自己实现一个工厂类,同DefaultListableBeanFactory一样将Bean放入到HashMap中:
public class MyApplicationContext {
/**
* 此Map容器用于存储类定义对象
*/
private Map<String, Class<?>> beanDefinationFacotry = new ConcurrentHashMap<>();
/**
* 此Map容器用于存储单例对象
*/
private Map<String, Object> singletonbeanFactory = new ConcurrentHashMap<>();
/**
* 有参构造方法,参数类型为指定要扫描加载的包名
*/
public MyApplicationContext(String packageNames) {
System.out.println("开始扫描包:" + packageNames);
/**扫描指定的包路径*/
getClasses(packageNames);
/**进行DI依赖注入*/
dependencyInjection();
}
}
其中getClassess方法我就不详细展示了,还是老规矩去小程序知识模块去复制,这里展示一点改动的地方:
Class<?> c = Thread.currentThread().getContextClassLoader()
.loadClass(packageName + '.' + className);
//判定这个类上是否有MyComponent注解
if (c.isAnnotationPresent(MyComponent.class)) {
//将类对象存储到map容器中
beanDefinationFacotry.put(beanId, c);
}
beanDefinationFacotry.put(beanId, c);
这里是在我们遍历class文件,如果该class文件存在注解MyComponent的时候,我将对象放入到beanDefinationFacotry这个存储对象的Map中。
dependencyInjection方法我们来看一下,首先是获取beanDefinationFacotry中所有对象,然后再遍历对象定义的字段,如果字段上使用了MyAutowired注解我们就通过field.set方法将值赋到字段上面去,如此我们就注入成功了。
private void dependencyInjection() {
//获取容器中所有的类定义对象
Collection<Class<?>> classes = beanDefinationFacotry.values();
//遍历每一个类对象
for (Class<?> cls : classes) {
//获取类对象的名字全称(包名+类名)
String clsName = cls.getName();
//获取类名
clsName = clsName.substring(clsName.lastIndexOf(".") + 1);
//将类名(通常为大写开头)的第一个字母转换小写
String beanId = String.valueOf(clsName.charAt(0)).toLowerCase() + clsName.substring(1);
//获取类中所有的属性
Field[] fields = cls.getDeclaredFields();
//遍历每一个属性
for (Field field : fields) {
//如果这个属性上有MyAutowired注解,进行注入操作
if (field.isAnnotationPresent(MyAutowired.class)) {
try {
//获取属性名
String fieldName = field.getName();
System.out.println("属性名:" + fieldName);
//定义为属性注入的bean对象(此对象从容器中获取)
Object fieldBean = null;
//首先根据属性名从容器中取出对象,如果不为null,则赋值给fieldBean对象
if (beanDefinationFacotry.get(fieldName) != null) {
fieldBean = getBean(fieldName, field.getType());
} else { //否则按照属性的类型从容器中取出对象进行注入
//获取属性的类型(包名+类名)
String type = field.getType().getName();
//截取最后的类名
type = type.substring(type.lastIndexOf(".") + 1);
//将类名(通常为大写开头)的第一个字母转换小写
String fieldBeanId = String.valueOf(type.charAt(0)).toLowerCase() + type.substring(1);
System.out.println("属性类型ID:" + fieldBeanId);
//根据转换后的类型beanId,从容器中获取对象并赋值给fieldBean对象
fieldBean = getBean(fieldBeanId, field.getType());
}
System.out.println("要为属性注入的值:" + fieldBean);
//如果fieldBean对象不为空,则为该属性进行注入
if (fieldBean != null) {
//获取此类定义的对象的实例对象
Object clsBean = getBean(beanId, cls);
//设置此属性可访问
field.setAccessible(true);
//为该属性注入值
field.set(clsBean, fieldBean);
System.out.println("注入成功!");
} else {
System.out.println("注入失败!");
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
02
—
定义注解
在上面我们看到工厂类中看到了两个注解MyAutowired和MyComponent,我们具体来看
MyAutowired定义是放在FIELD上
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}
MyComponent的scope方法我们在获取bean的时候会用到,用于判断是否单例
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
public String scope() default "";
}
03
—
Service
这里我们就具体的来使用注解,然后测试效果
@MyComponent
public class Service {
public void service() {
System.out.println("service.....");
}
}
测试类
@MyComponent
public class TestDI {
private static MyApplicationContext ctx;
@MyAutowired
private static Service service;
public static void main(String[] args) {
ctx = new MyApplicationContext("com.example.demo.di");
service = ctx.getBean("service", Service.class);
service.service();
}
}
测试结果:
Connected to the target VM, address: '127.0.0.1:56106', transport: 'socket'
开始扫描包:com.example.demo.di
Disconnected from the target VM, address: '127.0.0.1:56106', transport: 'socket'
属性名:service
要为属性注入的值:com.example.demo.di.Service@1c2c22f3
注入成功!
service.....
完整代码进入小程序进行查看: