当前位置: 首页 > news >正文

手写ioc容器(简易版)

一、概述

主要是通过仿照Beanfactory的思想,利用反射来实现一个非常简单的ioc容器。大体思想为:

  1. 自定义注解
  2. 实现ioc容器的管理功能,通过识别注解标识的类,利用反射创建对象并加入beanfactory中
  3. 实现依赖注入,为容器中对象,若其属性有注解标示,则为其赋值

以上就是大体的思路了,我们在实现的时候只关注核心功能,一些细节之处便不再关注,如异常处理,这里便直接throws Exception了

二、创建注解

我们首先创建两个注解,@Bean@di分别用于表示bean的管理和依赖的注入

  • @Bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
  • @di
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}

这里面有几个要注意的点:

@Target表示注解作用的范围,ElementType.TYPE和ElementType.FIELD分别表示作用于类本身和类的字段上。

@ 是 Java 注解的一个元注解,用于指定注解的保留策略。RetentionPolicy 枚举类型定义了注解的保留策略,有三种可能的值:

  1. SOURCE:注解仅保留在源代码级别,编译时会被丢弃,不会出现在字节码中。
  2. CLASS:注解会保留在编译后的字节码中,但在运行时不可见(即无法通过反射获取)。
  3. RUNTIME:注解会保留在编译后的字节码中,并且在运行时可以通过反射获取

三、创建父接口ApplicationContext

核心功能就一个,根据传入的类的class对象获得bean

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

四、创建实现类,实现主要的功能

  • 首先是有两个属性,beanFactory是最核心的一个字段,存储了类class对象和类对象的一个映射。BasePath存储的是类的绝对路径的前半部分,主要是便于后续操作。
  • 通过有参构造,传入包的全路径,表示扫描该包及其子包中的类。
public class AnnotationApplicationContext implements ApplicationContext {public   Map<Class,Object> beanFactory=new HashMap<Class,Object>();private  String BasePath;public AnnotationApplicationContext(String basePackage) throws Exception {}public Object getBean(Class clazz) {return beanFactory.get(clazz);}
}
  • 现在来具体实现以下步骤
    • 将传入包的类路径转换成文件资源的绝对路径
    • 扫描该路径下的资源,找到.class文件
    • 判断是否携带有注解,有则创建对象,加入beanFactory中
public AnnotationApplicationContext(String basePackage) throws Exception {//替换包包路径中中的.为\,注意要进行转义String basePackagePath = basePackage.replaceAll("\\.", "\\\\");//获得绝对路径Enumeration<URL> resources= Thread.currentThread().getContextClassLoader().getResources(basePackagePath);while (resources.hasMoreElements()) {URL url = resources.nextElement();//这里有一个细节,由于我们之前将.替换为了\,此时的\为编码值,还未进行解码String absolutePath = URLDecoder.decode(url.getFile(), "UTF-8");BasePath=absolutePath.substring(0,absolutePath.length()-basePackagePath.length());//传入该路径的File对象,后续核心代码在loadBean中进行,避免构造函数冗余loadBean(new File(absolutePath));}loadDi();
}private void loadBean(File baseFile) throws Exception {//判断是否为文件夹if(baseFile.isDirectory()){//获取子资源File[] childFiles = baseFile.listFiles();for (File childFile : childFiles) {//如果与子资源也是一个文件夹,就继续递归调用if(childFile.isDirectory()){loadBean(childFile);}else if(childFile==null||childFile.length()==0){return;}else{//此时为不空的文件资源String absolutePath = childFile.getAbsolutePath();//判断是否为.class文件if(!absolutePath.endsWith(".class")){return;}//拿到类的全名称String classPath = absolutePath.substring(BasePath.length() - 1).replaceAll(".class","").replaceAll("\\\\",".");Class clazz = Class.forName(classPath);//获得非接口带注解的类对象if(clazz.isInterface()){return;}Annotation annotation = clazz.getAnnotation(Bean.class);if(annotation==null){return;}Object o = clazz.getDeclaredConstructor().newInstance();//判断其是否实现有接口,有的话默认以第一个接口作为map的keyClass[] interfaces = clazz.getInterfaces();if(interfaces!=null){beanFactory.put(interfaces[0],o);}else{beanFactory.put(clazz,o);}}}}
}
  • 实现依赖注入功能

    这个就比较简单了,大体思路是先遍历beanFactory中的对象,再判断每个类对象的属性中是否有@Di注解,有则为其赋值

private void loadDi() throws Exception{//遍历map集合for (Map.Entry<Class, Object> beanEntry : beanFactory.entrySet()) {//获取类对象,然后通过对象获得它的class对象,再通过反射获得其所有的字段Class beanClazz= beanEntry.getValue().getClass();for (Field field : beanClazz.getDeclaredFields()) {//判断是否有注解Di di = field.getAnnotation(Di.class);if (di != null) {//设置访问权限field.setAccessible(true);//field.getType()获得该字段的class对象,再通过beanFactory进行赋值field.set(beanEntry.getValue(),beanFactory.get(field.getType()));}}}
}

五、小结

以上便是对spring中ioc容器的一个简易的实现,总的来说还是比较简单,就是利用反射技术。大家可以先理清思路,再写一遍会更加清晰。


http://www.mrgr.cn/news/61362.html

相关文章:

  • 探索 C++ 与 LibUSB:开启 USB 设备交互的奇幻之旅
  • zerox - 使用视觉模型将 PDF 转换为 Markdown
  • 【优选算法篇】:深入浅出位运算--性能优化的利器
  • Linux电源管理——Suspend-to-Idle(s2idle) 流程
  • CentOS 7 下 Nginx 的详细安装与配置
  • 1.两数之和--力扣
  • 【jvm】如何设置堆内存大小
  • 事务学习一
  • 年薪百万打工人自爆:我的大厂生存指南!
  • 使用DeepLabV3实现植叶病害检测
  • File类踩坑记录
  • 细胞核荧光探针(一):一种红色发光、NADPH响应的的喹啉基
  • 【点云异常点检测数据集】Real3D-AD数据集介绍
  • 基于SSM大学生互动交流网站设计与实现
  • 四元数各个旋转API的使用
  • 【JSON相关漏洞(Hijacking+Injection)挖掘技巧及实战案例全汇总】
  • mongo实操笔记
  • 美团外卖霸王餐系统如何对接?有哪些具体步骤?
  • Java Lock LockSupport 源码
  • 代码学习:如何阅读开源代码
  • 网络搜索引擎Shodan(6)
  • 今日总结10.29
  • 重学SpringBoot3-怎样优雅停机
  • 基于ssm+jsp639实验室排课系统设计与实现
  • 力扣每日一题 3211. 生成不含相邻零的二进制字符串
  • 代码随想录 | Day35 | 动态规划 :最小花费爬楼梯不同路径不同路径II