简单自定义实现IoC

自定义 IoC

基于配置类加载

基于反射机制实现

MyAnnotationConfigApplicationContext

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.kk.ioc;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class MyAnnotationConfigApplicationContext {

private Map<String, Object> ioc = new HashMap<>();

public MyAnnotationConfigApplicationContext(Class clazz) {
// 判断目标类是否是一个配置类(看其是否有@Configuration注解)
Annotation annotation = clazz.getAnnotation(Configuration.class);
if (annotation == null) {
throw new RuntimeException(clazz.getName() + "不是一个配置类");
}
// System.out.println(annotation);
//获取配置类中的方法
Method[] declaredMethods = clazz.getDeclaredMethods();
Bean annotation1;
for (Method declaredMethod : declaredMethods) { // 遍历需要注入的方法(标注了@bean的方法)
annotation1 = declaredMethod.getAnnotation(Bean.class);
if (annotation1 != null) {
try {
// 获取目标方法返回值(调用方法)
Object invoke = declaredMethod.invoke(clazz.getConstructor(null).newInstance(), null);
// 把对象注入到IoC
String[] value = annotation1.value();
String key;
if(value.length == 0) { // @bean中没有指定value
// 把目标方法的方法名作为key
key = declaredMethod.getName();
} else {
// @bean注解中的value值作为key
key = value[0];
}
ioc.put(key, invoke);
} catch (IllegalAccessException | NoSuchMethodException | InstantiationException |
InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}

public Object getBean(String name) {
Object o = ioc.get(name);
if (o == null) {
throw new RuntimeException("NoSuchBeanException");
}
return o;
}
}

基于注解加载

  1. 传入覆盖着目标类的目标包;
  2. 目标类要添加相应注解。

MyAnnotationApplicationContext

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.kk.ioc;

import com.kk.utils.MyTools;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MyAnnotationApplicationContext {

private Map<String, Object> ioc = new HashMap<>();

public MyAnnotationApplicationContext(String packageName) {
// 找到包下所有的类(遍历硬盘)
Set<Class<?>> classes = MyTools.getClasses(packageName);
// 遍历所有类,找到目标类(标注了@Component的类)
Iterator<Class<?>> iterator = classes.iterator();
while (iterator.hasNext()) {
Class<?> aClass = iterator.next();
//找到目标类
Component annotation = aClass.getAnnotation(Component.class);
if(annotation != null){
try {
// 创建目标类对象(调无参构造)
Constructor<?> constructor = aClass.getConstructor(null);
Object o = constructor.newInstance(null);
// 获取当前类的所有属性
Field[] declaredFields = aClass.getDeclaredFields();
// 遍历所有属性,为目标属性(标注了@Value的属性)赋值
for (Field declaredField : declaredFields) {
Value annotation1 = declaredField.getAnnotation(Value.class);
if(annotation1 != null){
String value = annotation1.value();
// 赋值:拼接set方法名,数据类型转换,赋值
String fieldName = declaredField.getName();
String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Method declaredMethod = aClass.getDeclaredMethod(methodName, declaredField.getType());
if(declaredField.getType().equals(Integer.class)){
declaredMethod.invoke(o, Integer.parseInt(value));
}
if(declaredField.getType().equals(String.class)){
declaredMethod.invoke(o,value);
}
}
}
// 处理key
String value = annotation.value();
String key;
if(!value.equals("")){
key = value;
} else {
String className = aClass.getName();
className = className.replaceAll(packageName+".","");
key = className.substring(0, 1).toLowerCase()+className.substring(1);
}
//将对象注入到IoC
ioc.put(key, o);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

public Object getBean(String key) {
Object o = ioc.get(key);
if(o == null) throw new RuntimeException("NoSuchBeanException");
return o;
}
}

遍历硬盘的工具类比较复杂,可以直接拿来使用

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package com.kk.utils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class MyTools {

public static Set<Class<?>> getClasses(String pack) {

// 第一个class类的集合
Set<Class<?>> classes = new LinkedHashSet<>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
System.out.println("jar类型的扫描");
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
findClassesInPackageByJar(packageName, entries, packageDirName, recursive, classes);
} catch (IOException e) {
// log.error("在扫描用户定义视图时从jar包获取文件出错");
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}

private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, final boolean recursive, Set<Class<?>> classes) {
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// .error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}
}

private static void findClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
// log.warn("用户定义包名 " + packageName + " 下没有任何文件");
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
@Override
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// log.error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}

简单自定义实现IoC
http://example.com/2022/08/28/自定义IoC/
作者
fkxia
发布于
2022年8月28日
许可协议