手写实现Spring中的IoC

布鸽不鸽 Lv4

前言

依赖反转(IoC)是Spring中一个非常重要的功能。我们基于java反射机制,来尝试实现一个简单的IoC。

原文地址:https://xuedongyun.cn/post/35893/

回顾反射

获取Class对象

  1. 类名.class
1
Class clazz = Car.class;
  1. 对象.getClass
1
2
Car car = new Car();
Class clazz = car.getClass();
  1. Class.forName + 全类名
1
Class clazz = Class.forName("com.xuedongyun.project.Car");

获取构造方法

  1. 获取所有构造方法
1
2
3
4
5
// public
Constructor[] constructors = clazz.getConstructors();

// public 和 private
Constructor[] constructors = clazz.getDeclaredConstructors();
  1. 指定获取有参构造器
1
2
3
4
5
6
7
8
// public
Constructor c = clazz.getConstructor(String.class, int.class, String.class);

// 无参构造器不写参数即可
Constructor c = clazz.getConstructor();

// public 和 private
Constructor c = clazz.getDeclaredConstructor(String.class, int.class, String.class);
  1. 创建对象
1
2
3
// 设置允许访问
constructor.setAccessible(true);
Car car = constructor.newInstance();

获取属性

  1. 获取所有属性
1
2
3
4
5
// public
Field[] fields = clazz.getFields();

// public 和 private
Field[] fields = clazz.getDeclaredFields();
  1. 获取某个属性
1
2
3
4
5
// public
Field field = clazz.getField("name");

// public 和 private
Field field = clazz.getDeclaredField("name");
  1. 修改属性
1
2
3
// 设置允许访问
field.setAccessible(true);
field.set(car,"五菱宏光");

获取方法

  1. 获取所有方法
1
2
3
4
5
// public
Method[] methods = clazz.getMethods();

// public 和 private
Method[] methods = clazz.getDeclaredMethods();
  1. 获得某一个方法
1
2
3
4
5
// public
Method m = clazz.getMethod("func", String.class, int.class);

// public 和 private
Method m = clazz.getDeclaredMethod("func", String.class, int.class);
  1. 执行方法
1
2
3
// 设置允许访问
m.setAccessible(true);
Object result = m.invoke(car, "test", 123);

int.class表示基本数据类型int的Class对象,以便在反射时使用。它等价于Integer.TYPE

实现Spring的IoC

创建所需的注解

Bean注解

1
2
3
4
5
6
package org.example.annotation;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}

依赖注入的注解

1
2
3
4
5
6
package org.example.annotation;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}

定义IoC容器

定义容器接口

1
2
3
4
5
package org.example.context;

public interface ApplicationContext {
Object getBean(Class<?> clazz);
}

定义容器实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.example.context;

public class AnnotationApplicationContext implements ApplicationContext{
private Map<Class<?>, Object> beanFactory = new HashMap<>();

public AnnotationApplicationContext(String basePackage) {
// TODO
}

@Override
public Object getBean(Class<?> clazz) {
return beanFactory.get(clazz);
}
}

扫描Bean功能

获取packageDirName的绝对路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public AnnotationApplicationContext(String basePackage) {
String packageDirName = basePackage.replace(".", "\\");
try {
Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
while (dirs.hasMoreElements()) {
URL url = dirs.nextElement();
String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
String rootPath = filePath.substring(0, filePath.length() - packageDirName.length());
loadBean(new File(rootPath));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}

遍历所有.class文件,若标注有@Bean注解,则将其实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public void loadBean(File fileParent, String rootPath) {
// 遍历所有子文件
if (fileParent.isDirectory()) {
File[] childFiles = fileParent.listFiles();
if (childFiles == null || childFiles.length == 0) {
return;
}
// 子文件若为文件夹,递归访问
for (File childFile : childFiles) {
if (childFile.isDirectory()) {
loadBean(childFile, rootPath);
continue;
}
// 获得文件相对路径
String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);
if (!pathWithClass.contains(".class")) {
continue;
}
try {
// 对所有.class文件,获得其类路径
String fullName = pathWithClass.replace("\\", ".").replace(".class", "");
Class<?> clazz = Class.forName(fullName);
if (clazz.isAnnotation() || clazz.isInterface()) {
continue;
}
// 如果标注了@Bean注解,放入容器中
Bean annotation = clazz.getAnnotation(Bean.class);
if (annotation != null) {
Object instance = clazz.getConstructor().newInstance();
// 如果实现了接口,以接口为key
if (clazz.getInterfaces().length > 0) {
beanFactory.put(clazz.getInterfaces()[0], instance);
} else {
beanFactory.put(clazz, instance);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}

依赖注入

1
2
3
4
5
6
public AnnotationApplicationContext(String basePackage) {
// ...

// 所有Bean放入容器后,执行依赖注入
loadDi();
}

实现依赖注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void loadDi() {
for (Map.Entry<Class<?>, Object> entry : beanFactory.entrySet()) {
Object obj = entry.getValue();
Class<?> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Autowired annotation = field.getAnnotation(Autowired.class);
if (annotation != null) {
field.setAccessible(true);
try {
field.set(obj, beanFactory.get(field.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
package org.example.test;

@Bean
public class Pet {
private String name = "dodo";

@Override
public String toString() {
return "Pet{" +
"name='" + name + '\'' +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.example.test;

@Bean
public class User {

@Autowired
private Pet pet;

private String name = "xdy";

@Override
public String toString() {
return "User{" +
"pet=" + pet +
", name='" + name + '\'' +
'}';
}
}
1
2
3
4
5
6
7
8
9
package org.example;

public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationApplicationContext("org.example");
Object bean = context.getBean(User.class);
System.out.println("bean = " + bean);
}
}
  • 标题: 手写实现Spring中的IoC
  • 作者: 布鸽不鸽
  • 创建于 : 2023-06-14 23:01:01
  • 更新于 : 2023-08-28 18:59:35
  • 链接: https://xuedongyun.cn//post/35893/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论