手写IOC容器
实现:Java 反射 + 注解
-
创建两个注解:
-
@Bean
:创建对象1
2
3
4// 作用在类对象上
// 运行时生效
public Bean {
} -
@Di
:属性注入1
2
3
4// 使用在属性上
// 运行时生效
public Di {
}
-
-
创建Bean容器接口 ApplicationContext,定义方法,返回对象;
1
2
3
4public interface ApplicationContext {
// 返回Bean对象
Object getBean(Class clazz) throws NoSuchMethodException;
} -
实现Bean容器接口
- 返回对象
- 创建有参构造器,传递包路径,设置包扫描规则
- 根据包规则加载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
37public class AnnotationApplicationContext implements ApplicationContext {
// 创建map集合,存放Bean对象
private Map<Class,Object> beanFactory = new HashMap<>();
private String rootPath; // 前路径
// fa
public Object getBean(Class clazz) throws NoSuchMethodException {
return beanFactory.get(clazz);
}
// 创建有参构造器,传递包路径,设置包扫描规则
// 当前包及子包中,如果哪个类有@Bean注解,则通过反射将其实例化
public AnnotationApplicationContext(String basePackage){
try {
// 1. 将.替换为\
String packagePath = basePackage.replaceAll("\\.","\\\\");
// 2. 获取包的绝对路径
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
.getResources(packagePath);
while(urls.hasMoreElements()){
URL url = urls.nextElement();
String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
// 截取前路径
rootPath = filePath.substring(0, filePath.length() - packagePath.length());
// 包扫描
loadBean(new File(filePath));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
// 属性注入
loadDi();
} -
包扫描加载
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
43
44
45
46
47// 包扫描过程,实例化
public void loadBean(File file) throws Exception {
// 判断当前内容是否是一个文件夹
if(file.isDirectory()) {
// 获取文件夹中的所有内容
File[] subFiles = file.listFiles();
// 判断文件夹是否为空
if(subFiles==null||subFiles.length==0){
return;
}
// 遍历文件夹所有内容
for(File subfile:subFiles){
// 递归遍历文件夹及子文件夹
if(subfile.isDirectory()){
// 如果是文件夹,递归遍历
loadBean(subfile);
}else{
// 得到包路径+类名
String pathWithClass = subfile.getAbsolutePath().substring(rootPath.length()-1);
// 判断当前文件类型是否为.class
if(pathWithClass.endsWith(".class")){
// 把\换成. 并去掉.class后缀
String allName = pathWithClass.replaceAll("\\\\",".").replace(".class","");
// 对有注解 @Bean的类进行实例化
Class<?> clazz = Class.forName(allName);
// 判断是否为接口
if(!clazz.isInterface()){
// 判断有无注解
Bean annotation = clazz.getAnnotation(Bean.class);
if(annotation != null){
// 实例化对象
Object instance = clazz.getConstructor().newInstance();
// 实例化后的对象放入到map集合中
// 如果有接口,接口作为key
if(clazz.getInterfaces().length>0){
beanFactory.put(clazz.getInterfaces()[0],instance);
}else{
beanFactory.put(clazz,instance);
}
}
}
}
}
}
}
} -
属性注入实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public void loadDi(){
// 遍历 map 集合
for(Map.Entry<Class,Object> entry:beanFactory.entrySet()){
Object obj = entry.getValue();
Class<?> clazz = obj.getClass();
// 获取每个对象的属性
Field[] fields = clazz.getDeclaredFields();
// 遍历得到对象的属性数组,得到每个属性
for(Field field:fields){
// 判断属性上是否有 @Di 注解,把对象进行注入
Di annotation = field.getAnnotation(Di.class);
if(annotation!=null){
field.setAccessible(true);
try {
field.set(obj,beanFactory.get(field.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
评论