【Spring之手写一个依赖注入容器】

Spring之手写一个依赖注入容器

  • 1. 创建两个自定义注解
    • 1.1 Component注解
    • 1.2 DI注解
  • 2. ApplicationContext接口与实现类
    • 2.1 ApplicationContext 接口
    • 2.2 实现类:DefaultListableApplicationContext
  • 3. 定义DAO层和Service层及其实现
  • 4. 定义异常信息类
    • 4.1 InjectBeanException
    • 4.2 NotExistsBean
    • 4.3 NotSupportMoreSuperInterface
  • 5. 测试自定义bean容器(带依赖注入)
    • 5.1 新建测试类TestUser
    • 5.2 输出结果

1. 创建两个自定义注解

1.1 Component注解

@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {String value() default "";
}

1.2 DI注解

@Target(value = ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DI {String value() default "";}

2. ApplicationContext接口与实现类

2.1 ApplicationContext 接口

public interface ApplicationContext {// 获取beanObject getBean(String name);// 获取bean<T> T getBean(String name, Class<T> clazz);// 判断bean是否存在boolean exists(String name);boolean exists(String name, Class<?> clazz);
}

2.2 实现类:DefaultListableApplicationContext

// 实现类
public class DefaultListableApplicationContext implements ApplicationContext {/*** 存放bean对象容器*/private Map<String, Object> objectsMap = new ConcurrentHashMap<>(128);private Map<Class, Object> objectsTypeMap = new ConcurrentHashMap<>(128);private Logger log = LoggerFactory.getLogger(DefaultListableApplicationContext.class);// 扫描的基础路径private String basePath;public DefaultListableApplicationContext(String basePackage) {//public static void pathDemo1(String basePackageName){String packagePath = basePackage.replaceAll("\\.", "\\\\");try {URL url = Thread.currentThread().getContextClassLoader().getResource(packagePath);if (StringUtils.hasText(url.getPath())) {String filePath = URLDecoder.decode(url.getFile(), "UTF8");// 扫描的基础路径basePath = filePath.substring(0, filePath.length() - packagePath.length());//包扫描loadBeans(new File(filePath));// 依赖注入loadDI();}} catch (IOException e) {throw new RuntimeException(e);}}@Override@Nullablepublic Object getBean(String name) {return objectsMap.get(name);}@Override@Nullablepublic <T> T getBean(String name, Class<T> clazz) {Object obj = objectsMap.get(name);if (obj == null) {Object bean;if ((bean = objectsTypeMap.get(clazz)) == null) {for (Class<?> interfaceClazz : clazz.getInterfaces()) {if ((bean = objectsTypeMap.get(interfaceClazz)) != null)return (T) bean;getBean(name, interfaceClazz);}return null;}return (T) bean;}return clazz.isInstance(obj) ? clazz.cast(obj) : null;}@Overridepublic boolean exists(String name) {return objectsMap.get(name) == null ? false : true;}@Overridepublic boolean exists(String name, Class<?> clazz) {if (objectsMap.get(name) == null) {if (objectsTypeMap.get(clazz) == null) {for (Class<?> interfaceClazz : clazz.getInterfaces()) {if (objectsTypeMap.get(interfaceClazz) != null)return true;exists(name, interfaceClazz);}return false;}return true;}return true;}/*** 包扫描** @param file*/private void loadBeans(File file) {// 1 判断当前文件是否文件夹if (file.isDirectory()) {File[] childrenFiles = file.listFiles();// 2. 获取文件夹里面的所有内容// 3. 判断文件夹为空,直接返回if (childrenFiles == null || childrenFiles.length == 0) {return;}// 4. 如果文件夹不为空,遍历文件夹内容for (File childFile : childrenFiles) {// 4.1  遍历得到每个file对象,继续判断。如果是文件夹,递归if (childFile.isDirectory()) {loadBeans(childFile);} else {// 4.2 遍历得到的file不是文件夹,是文件// 4.3 得到包路径 + 类名称部分// C:/desktop/IdeaProjects/spring-mytest/src/main/java/com/ypy/contextString pathWithClass = childFile.getAbsolutePath().substring(basePath.length() - 1);// 4.4 判断当前文件类型是否为.classif (pathWithClass.endsWith(".class")) {// 4.5 如果是.class类型,把路径\替换成., 把.class去掉// com.ypy.context.beans.UserServiceImplString allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");// 4.6 使用反射实例化try {Class<?> clazz = Class.forName(allName);if (!clazz.isInterface()) {// 4.6.1 判断是否有@Component注解,有if (clazz.getAnnotation(Component.class) != null) {Object instance = clazz.getConstructor().newInstance();// 优先使用用户自定义的bean名称if (StringUtils.hasText(clazz.getAnnotation(Component.class).value())) {String beanName = clazz.getAnnotation(Component.class).value();objectsMap.put(beanName, instance);log.warn(">>> objectsMap store bean(name,obj) ===> (" + beanName + "," + instance + ")");if (clazz.getInterfaces().length == 1) {objectsTypeMap.put(clazz.getInterfaces()[0], instance);log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");} else if (clazz.getInterfaces().length == 0) {objectsTypeMap.put(clazz, instance);log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");} else {throw new NotSupportMoreSuperInterface("Not support the bean that has more than two super interfaces.");}}// 其次使用父接口的类名作为bean名称else if (clazz.getInterfaces().length > 0) {String interfaceName = clazz.getInterfaces()[0].getSimpleName();String beanName = lowercaseFirstLetter(interfaceName);// 放入容器中objectsMap.put(beanName, instance);log.warn(">>> objectsMap store bean(name,obj) ===> (" + beanName + "," + instance + ")");// 可能出现bugobjectsTypeMap.put(clazz.getInterfaces()[0], instance);log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");}// 如果说没有父接口,就使用类名作为bean名称else {String beanName = lowercaseFirstLetter(clazz.getSimpleName());// 放入容器中objectsMap.put(beanName, instance);log.warn(">>> objectsMap store bean(name,obj) ===> (" + beanName + "," + instance + ")");objectsTypeMap.put(clazz, instance);log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");}}}} catch (Exception e) {throw new RuntimeException(e);}}}}}}private void loadDI() {for (Map.Entry<String, Object> entry : objectsMap.entrySet()) {Object obj = entry.getValue();Class<?> clazz = obj.getClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);if (field.getAnnotation(DI.class) != null) {Class<?>[] interfaces = field.getType().getInterfaces();String needWiredBeanName;Object autoWiredBean;// 优先使用注解DI的value注入if (StringUtils.hasText(needWiredBeanName = field.getAnnotation(DI.class).value())) {autoWiredBean = objectsMap.get(needWiredBeanName);if (autoWiredBean != null) {field.set(obj, autoWiredBean);log.warn("<<< DI: Class " + clazz.getSimpleName() + " of field named " + field.getName() + " is injected with value of " + autoWiredBean + " from " + "objectsMap, by value of annotation @DI");continue;} /*else {throw new NotExistsBean("Not exists the bean named <" + needWiredBeanName + "> to inject as property");}*/}// 没有父接口needWiredBeanName = lowercaseFirstLetter(field.getType().getSimpleName());if ((autoWiredBean = objectsMap.get(needWiredBeanName)) != null || (autoWiredBean = objectsTypeMap.get(field.getType())) != null) {field.set(obj, autoWiredBean);log.warn("<<< DI: Class " + clazz.getSimpleName() + " of field named " + field.getName() + " is injected with value of " + autoWiredBean + " , by value or type of property itself ");continue;}// 使用父接口的名字从bean容器中查找,注入if (interfaces.length > 0) {for (Class<?> interfaceClazz : interfaces) {String interfaceClazzName = interfaceClazz.getSimpleName();if (interfaceClazz.isAssignableFrom(field.getType())) {needWiredBeanName = lowercaseFirstLetter(interfaceClazzName);if ((autoWiredBean = objectsMap.get(needWiredBeanName)) != null || (autoWiredBean = objectsTypeMap.get(interfaceClazz)) != null) {field.set(obj, autoWiredBean);log.warn("<<< DI: Class " + clazz.getSimpleName() + " of field named " + field.getName() + " is injected with value of " + autoWiredBean + ", by value or type of super interface");}}}continue;}throw new InjectBeanException("There occurs an Exception while injecting property filed [" + field.getName() + "] of Class <" + clazz.getSimpleName() + "> , because bean factory doesn't exist the bean named " + needWiredBeanName);}} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}}private String lowercaseFirstLetter(String name) {return name.substring(0, 1).toLowerCase() + name.substring(1);}}

3. 定义DAO层和Service层及其实现

// 3.1 dao层
public interface UserDao {void addUser(User user);
}/** 
*  userDao实现
*/
@Component("userDao")
public class UserDaoImpl implements UserDao {@Overridepublic void addUser(User user) {System.out.println();System.out.println("------------------>>>执行UserDaoImpl中的addUser方法开始<<<<------------------");System.out.println("\t\t\t\t\t\t名字\t\t\t\t年龄");System.out.println("\t\t\t\t\t\t"+user.getName()+"\t\t\t\t"+user.getAge());System.out.println("------------------>>>执行UserDaoImpl中的addUser方法完毕<<<<------------------");}
}// 3.2 service层public interface UserService {void add();
}// service实现
@Component("userService")
public class UserServiceImpl implements UserService {@DI("userDao")private UserDaoImpl userDao;@Overridepublic void add() {System.out.println("》》》 user service impl execute add method...");User user = new User("张三", 25);userDao.addUser(user);}
}

4. 定义异常信息类

4.1 InjectBeanException

public class InjectBeanException extends RuntimeException{public InjectBeanException() {super();}public InjectBeanException(String message) {super(message);}
}

4.2 NotExistsBean

public class NotExistsBean extends RuntimeException{public NotExistsBean() {}public NotExistsBean(String message) {super(message);}
}

4.3 NotSupportMoreSuperInterface

public class NotSupportMoreSuperInterface extends RuntimeException{public NotSupportMoreSuperInterface() {super();}public NotSupportMoreSuperInterface(String message) {super(message);}
}

5. 测试自定义bean容器(带依赖注入)

5.1 新建测试类TestUser

public class TestUser {public static void main(String[] args) {DefaultListableApplicationContext context = new DefaultListableApplicationContext("com.ypy.context");UserService userService = context.getBean("aaa", UserServiceImpl.class);UserDao userDao = context.getBean("userDaoImpl", UserDao.class);boolean existFlag = context.exists("aaa"/*, UserService.class*/);System.out.println("UserService exists ? " + existFlag);System.out.println("从Bean容器中获取aaa对象地址: " + userService);System.out.println("从Bean容器中获取userDao对象地址: " + userDao);userService.add();}
}

5.2 输出结果

一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsMap store bean(name,obj) ===> (userDao,com.ypy.context.dao.impl.UserDaoImpl@2b193f2d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsTypeMap store bean(class,obj) ===> (UserDao,com.ypy.context.dao.impl.UserDaoImpl@2b193f2d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsMap store bean(name,obj) ===> (userService,com.ypy.context.service.impl.UserServiceImpl@7a81197d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsTypeMap store bean(class,obj) ===> (UserService,com.ypy.context.service.impl.UserServiceImpl@7a81197d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: <<< DI: Class UserServiceImpl of field named userDao is injected with value of com.ypy.context.dao.impl.UserDaoImpl@2b193f2d from objectsMap, by value of annotation @DI
UserService exists ? false
从Bean容器中获取aaa对象地址: com.ypy.context.service.impl.UserServiceImpl@7a81197d
从Bean容器中获取userDao对象地址: com.ypy.context.dao.impl.UserDaoImpl@2b193f2d
》》》 user service impl execute add method...------------------>>>执行UserDaoImpl中的addUser方法开始<<<<------------------名字				年龄张三				25
------------------>>>执行UserDaoImpl中的addUser方法完毕<<<<------------------Process finished with exit code 0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/635847.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ETL概念

ETL ETLELT 技术原理ETL 模式应用场景常见工具ETL未来发展方向 ETL 在BI项目中ETL会花掉整个项目至少1/3的时间&#xff0c; ETL设计的好坏直接关接到BI项目的成败。ETL(Extract-Transform-Load) : 用来描述将数据从来源端经过抽取&#xff08;extract&#xff09;、转换&…

深入理解Rust引用与借用

文章目录 一、概述二、引用与解引用三、不可变引用四、可变引用4.1、可变引用同时只能存在一个4.2、可变引用与不可变引用不能同时存在4.3、悬垂引用&#xff08;Dangling References&#xff09; 团队博客: 汽车电子社区 一、概述 获取变量的引用&#xff0c;称之为借用(borro…

day3:基于UDP模型的简单文件下载

思维导图 tftp文件下载客户端实现 #include <head.h> #define SER_PORT 69 #define SER_IP "192.168.125.223" int link_file() {int sfdsocket(AF_INET,SOCK_DGRAM,0);if(sfd-1){perror("socket error");return -1;}return sfd; } int filedownloa…

智慧校园大数据应用系统介(3)

智能巡课系统 巡课系统是一种新的课堂观察记录工具,它将学校或区域内全体教师作为一个整体,采用数字化手段描述教师和学生的课堂行为。通过移动端实时记录和通用性的统计分析,使教育者更容易发现教学过程与教学成果之间的联系,有助于改变课堂生态、提高教学有效性、提升教…

《剑指 Offer》专项突破版 - 面试题 18、19 和 20 - 详解回文字符串(C++ 实现)

目录 前言 面试题 18 : 有效的回文 面试题 19 : 最多删除一个字符得到回文 面试题 20 : 回文子字符串的个数 前言 回文是一类特殊的字符串。不管是从头到尾读取一个回文&#xff0c;还是颠倒过来从尾到头读取一个回文&#xff0c;得到的内容是一样的。 英语中有很多回文单…

【WPF.NET开发】以编程方式打印XPS文件

本文内容 可以使用 AddJob 方法的一个重载来打印 XML 纸张规范 (XPS) 文件&#xff0c;而根本无需打开 PrintDialog 或任何用户界面 (UI)&#xff08;从原理上讲&#xff09;。 还还可以使用多种 XpsDocumentWriter.Write 和 XpsDocumentWriter.WriteAsync 方法打印 XPS 文件…

04--JdbcTemplate模版

1、JdbcTemplate模版 1.1 概述 Spring JDBC Spring框架对JDBC的简单封装。提供了一个JdbcTemplate对象简化JDBC的开发&#xff08;后面专门讲spring框架) 1.2 实现步骤 1. 导入jar包 4 1 2. 创建JdbcTemplate对象。依赖于数据源DataSource JdbcTemplate template new JdbcTe…

【论文阅读 SIGMOD18】Query-based Workload Forecasting for Self-Driving

Query-based Workload Forecasting for Self-Driving Database Management Systems My Summary ABSTRACT Autonomous DBMS的第一步就是能够建模并预测工作负载&#xff0c;以前的预测技术对查询的资源利用率进行建模。然而&#xff0c;当数据库的物理设计和硬件资源发生变化…

Windows如何部署TortoiseSVN客户端

文章目录 前言1. TortoiseSVN 客户端下载安装2. 创建检出文件夹3. 创建与提交文件4. 公网访问测试 前言 TortoiseSVN是一个开源的版本控制系统&#xff0c;它与Apache Subversion&#xff08;SVN&#xff09;集成在一起&#xff0c;提供了一个用户友好的界面&#xff0c;方便用…

Spring第六天(注解开发第三方Bean)

注解开发管理第三方Bean 显然&#xff0c;我们无法在第三方Bean中写入诸如service这样的注解&#xff0c;所以&#xff0c;Spring为我们提供了Bean这一注解来让我们通过注解管理第三方Bean 第二种导入方式由于可读性太低&#xff0c;故只介绍第一种导入方式&#xff0c;这里我…

自动化工具实践操作-注入自定义代码

功能 采集掘金左边每个tab下文章的标题。人为操作就是点击一个tab&#xff0c;复制文字标题&#xff0c;重复以上操作。根据这个&#xff0c;我们可以转换成自己的代码 开始设计 如上文操作基本一致。新建任务、设计点击事件如出一辙。 自定义循环事件 操作循环节点&#…

R语言【cli】——ansi_has_any():检查字符串里是否存在ANSI格式

Package cli version 3.6.0 Usage ansi_has_any(string, sgr TRUE, csi TRUE, link TRUE) Arguments 参数【string】&#xff1a;要检查的字符串。它也可以是字符向量 参数【sgr】&#xff1a;是否查找SGR(样式化)控制序列。 参数【csi】&#xff1a;是否查找非sgr控制序…

ubuntu下常见查看库信息的指令

要查看共享库&#xff08;例如 liblog4cplus.so&#xff09;的信息&#xff0c;可以使用一些工具来获取有关库的详细信息。以下是一些常用的方法&#xff1a; 1. 使用 nm 命令&#xff1a; nm 命令用于显示目标文件或共享库的符号表。你可以运行以下命令查看 liblog4cplus.so…

Mysql:重点且常用的操作和理论知识整理 ^_^

目录 1 基础的命令操作 2 DDL 数据库定义语言 2.1 数据库操作 2.2 数据表操作 2.2.1 创建数据表 2.2.2 修改和删除数据表 2.2.3 添加外键 3 DML 数据库操作语言 3.1 插入语句(INSERT) 3.2 修改语句(UPDATE) 3.3 删除语句 3.3.1 DELETE命令 3.3.2 TRUNCATE命令 4 …

第1周:Day 3 - PyTorch与TensorFlow的异同介绍(入门级)

第1周&#xff1a;Day 3 - PyTorch介绍 学习目标 理解PyTorch的基本概念和主要特点。 成功安装PyTorch环境。 PyTorch简介 PyTorch 是一个开源的机器学习库&#xff0c;广泛用于计算机视觉和自然语言处理等领域。 它由Facebook的人工智能研究团队开发&#xff0c;提供了丰富的A…

idea中使用git提交代码报 Nothing To commit No changes detected

问题描述 在idea中右键&#xff0c;开始将变更的代码进行提交的时候&#xff0c;【Commit Directory】点击提交的时候 报 Nothing To commit No changes detected解决方案 在这里点击Test 看看是不是能下面显示git版本&#xff0c;不行的话 会显示一个 fix的字样&#xff0c;行…

【日常聊聊】边缘计算的挑战和机遇

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; 日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 边缘计算的挑战和机遇 一&#xff1a;数据安全与隐私保护 二&#xff1a;网络稳定性与可靠性 三&#xff1a;实时性与性能优…

Unity中的协程

定义&#xff1a;协程使得任务的执行可以分配到多个帧中完成&#xff0c;在Unity中&#xff0c;协程从开始执行到第一个yield return 语句后将调用权归还Unity主线程&#xff0c;并在紧随的下一帧继续从上次结束调用的代码上下文位置恢复执行。 常见应用场景&#xff1a;HTTP请…

【Vue】属性计算 computed

<script setup>import { ref,computed} from vue let hbs ref([]); //装爱好的值const publishHbsMessagecomputed(()>{return hbs.value.length>0?Yes:No}) </script><template><div>吃 <input type"checkbox" name"hbs&qu…

C++入门学习(八)sizeof关键字

sizeof 是 C 和 C 中的一个运算符&#xff0c;用于确定特定类型或对象的内存大小&#xff08;以字节为单位&#xff09;。 1、查看数据类型占据内存大小 #include <iostream> using namespace std; int main() {short a 1;int b 1;long c 1;long long d 1;cout<…