diy实现spring依赖注入

【README】

本文diy代码实现了 spring 依赖注入,一定程度上揭示了依赖注入原理;


【1】控制反转-Inversion of Control

是一种编码思想,简而言之就是 应用程序A可以使用组件B,但A无法控制B的生命周期(如创建,内部属性赋值,销毁(若需)等等),而交由第三方控制,如容器;

  • 这里的容器不仅仅是spring容器,spring容器只是一种实现方式; 还有其他容器,如 PicoContainer,参见文末引用资料;
  • 这里的组件指的是 封装了属性和方法的java类实例,如业务逻辑处理类实例,dao层实例,数据源,日志发送器,http客户端,kafka客户端,各种客户端等等很多;

这样做的原因 是可以把 应用程序A 与 组件B 解耦,1可以简化代码开发,2提高代码复用,3代码易于维护;

补充:控制反转,反转的是 组件或javabean的创建,存储与管理权到第三方容器;by wikipedia  https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC

【早期控制反转例子】

spring容器出现前的IOC例子(即 IOC 在spring之前就已经提出来了,IOC并非是spring提出,而是 spring使用了IOC的编程思想):

关于命令行用户界面的主控权:命令行用户界面交互的主控权由应用程序来控制,应用程序可以设计一系列命令,如linux上的zk,kafka客户端命令等,应用程序逐条输出信息和给出响应;如 window命令行界面,linux vim编辑器,zk客户端,kafka客户端等;

而在图形用户界面环境下(包括CS,BS),UI框架将负责执行一个主循环监听事件,应用程序只需要提供处理函数即可(代码适配浏览器),无需关心界面交互方式(界面IO,事件等),它也没法关心,因为每个浏览器有自己特有的交互方式(有少许差别,这才会产生浏览器兼容性问题), 这样的UI框架如浏览器。显然,前端应用程序如html,js无法控制界面响应(只能适配),而由底层UI框架来控制;这时,交互控制权发生了反转,从应用程序转移到了UI框架;

虽然应用程序无法控制界面响应,但它可以使用UI组件渲染前端;

小结:可以理解 在IoC的编程思想下,应用程序对使用的组件只有使用权,没有所有权,所有权由第三方容器或框架控制*

如何理解所有权? 即 当应用程序消亡时,其使用的组件并没有随它而消亡,而是继续存在;因为前者对后者没有所有权,无法管理后者的生命周期;


【1.1】实现控制反转主要有两种方式:依赖注入和依赖查找。

1)依赖注入DI(被动接受属性对象赋值):在类A实例a的创建过程中同时创建了类A依赖对象b(仅创建对象,没有赋值),然后第三方容器通过类型或名称把 b 注入(赋值)给类A实例的属性;这个过程叫做依赖注入

【代码1-依赖注入代码示例】

A a = new A(); // 主类A实例 步骤1
B b = new B(); // 依赖实例b 步骤2
a.setB(b); // 第三方容器注入bean或赋值给实例a的属性 步骤3, 
// 以上3步均由 第三方容器通过反射+工厂来完成 

这里反转的是 类A实例对依赖属性对象b的创建,存储和管理权利(或生命周期权),而由第三方容器来管理;因为按照传统编程方式,类A依赖属性对象b,那属性b就应该由类A来创建和赋值;

2)依赖查找(主动索取对象):主动索取相应类型的对象,获得依赖对象的时间也可以在代码中自由控制。

【1.2】自动装配 Autowire

表示第三方容器通过类型或名称把创建的依赖对象 赋值给主类实例的属性对象的过程,前提是属性对象被Autowire注解标识;

【小结】

上文详细阐述了  IoC, DI, autowire 的概念,这是spring核心概念,应该是比较清楚了;

  • IoC:是一种编程模型,讲的是 依赖对象不由 使用者创建,交由 第三方容器来创建和管理;
  • DI:是IoC思想的一种实现, 讲的是  使用者实例,依赖对象实例都由 第三方容器来创建,实例创建完成后, 容器通过类型或名称把依赖对象赋值给 使用者实例的属性对象,以便建立依赖关系的过程;
  • autowire:是DI过程的一部分,讲的是   容器通过类型或名称把依赖对象赋值给 使用者实例的属性对象的过程;

【2】diy代码实现 spring  依赖注入

【2.1】自定义4个注解

// 标识 controller后的步骤 
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyStep {public String value() default "";
}// 标识 dao bean 
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRepository {public String value() default "";
}// 标识bean 
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBean {public String value() default "";
}// 标识自动注入bean 
@Documented
@Inherited
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowire {public String value() default "";
}

【2.2】自定义容器

/*** @Description bean工厂 * @author xiao tang* @version 1.0.0* @createTime 2021年12月16日*/
public interface BeanFactory {<T> T getBean(Class<T> clazz);<T> T getBean(String beanName);
}
/*** @Description spring容器 * @author xiao tang* @version 1.0.0* @createTime 2021年12月16日*/
public class SpringContext implements BeanFactory {/** 类路径根目录 */private static String appRoot = "D:\\workbench_idea\\study4vw\\vwstudy10\\target\\classes";private static SpringContextSupport springContextSupport = new SpringContextSupport(appRoot);public static void insertBean(String key, Object value) {SpringContextSupport.container.put(key, value);}@Overridepublic <T> T getBean(Class<T> clazz) {return (T) SpringContextSupport.container.get(clazz.getName());}@Overridepublic <T> T getBean(String beanName) {return (T) SpringContextSupport.container.get(beanName);}
}
/*** @Description spring容器支持类,扫描注解,使用反射创建bean,注入bean* @author xiao tang* @version 1.0.0* @createTime 2021年12月19日*/
public class SpringContextSupport {/** clazz名称列表  */List<String> allClazzNameList;/** 自定义注解映射 */Map<String, Class> defineAnnotationMap;/** bean clazz名称列表 */List<String>  beanClazzNameList;/** spring容器 */static ConcurrentMap<String, Object> container = new ConcurrentHashMap<>();public SpringContextSupport(String appRootPath) {// 扫描所有文件的clazzthis.allClazzNameList = scanAllFileClazz(appRootPath);// 扫描所有自定义注解this.defineAnnotationMap = scanAnnotation(allClazzNameList);// 剔除注解类clazzthis.beanClazzNameList = allClazzNameList.stream().filter(x->!defineAnnotationMap.containsKey(x)).collect(Collectors.toList());this.loadBean();}/*** @description 加载bean* @author xiao tang* @date 2021/12/25*/private void loadBean() {// 扫描被注解修饰的类,并添加到容器for (String x : beanClazzNameList) {try {Class beanClazz = Class.forName(x);for (Class defineAnnoClazz : defineAnnotationMap.values()) {if (beanClazz.isAnnotationPresent(defineAnnoClazz)) { // bean是否被定义的注解修饰Annotation defineAnnoBean = beanClazz.getAnnotation(defineAnnoClazz);try {// 获取注解的value方法值(value方法值==bean名称) ,可以认为这里约定注解的value()值为beanNameString beanName = (String)defineAnnoBean.getClass().getDeclaredMethod("value").invoke(defineAnnoBean);// 若value方法值为空,则取类名首字母小写beanName = !BusiStringUtils.isBlank(beanName) ? beanName :  BusiStringUtils.lowerFirstChar(beanClazz.getSimpleName());// 若bean在容器中存在,直接breakif (container.containsKey(beanName)) break;// 实例化bean之前先扫描是否有属性bean,若有被 MyAutowired修饰的属性,则注入Object beanObj = beanClazz.newInstance();// 装配bean属性this.autowireField(beanClazz, beanObj);// 注入beanSpringContext.insertBean(beanName, beanObj);SpringContext.insertBean(beanClazz.getName(), beanObj);break;} catch(Exception e) {e.printStackTrace();}}}} catch (Exception e) {throw new IllegalStateException("类" + x+ "实例化失败", e);}}}/*** @description 装配属性bean* @param beanClazz bean class对象* @author xiao tang* @date 2021/12/25*/private void autowireField(Class beanClazz, Object beanObj) {for (Field field : beanClazz.getDeclaredFields()) {if (field.isAnnotationPresent(MyAutowire.class)) { // 若属性被 MyAutowire 修饰String beanNameOfMyAutowire = field.getAnnotation(MyAutowire.class).value(); // 获取 MyAutowire的value方法值beanNameOfMyAutowire = !BusiStringUtils.isBlank(beanNameOfMyAutowire) ? beanNameOfMyAutowire : field.getType().getName(); // 获取autowire的value方法值// 装配beantry {// 若容器不存在该bean,则创建并放入容器if (!container.containsKey(beanNameOfMyAutowire)) {Object beanObjOfAutowire = field.getType().newInstance();// 注入属性beanSpringContext.insertBean(beanNameOfMyAutowire, beanObjOfAutowire);SpringContext.insertBean(field.getClass().getName(), beanObjOfAutowire);}field.setAccessible(true); // 设置可以访问field.set(beanObj, container.get(beanNameOfMyAutowire)); // 注入bean} catch (Exception e) {}}}}/*** @description 扫描所有注解* @param clazzNameList 类名列表* @return 注解集合* @author xiao tang* @date 2021/12/19*/public static Map<String, Class> scanAnnotation(List<String> clazzNameList) {/** 自定义注解名称 */Map<String, Class> annotationMap = new HashMap<>();for (String x : clazzNameList) {try {Class clazz = Class.forName(x);if (Class.forName(x).isAnnotation()) { // 若为注解,添加到缓存annotationMap.put(clazz.getName(), clazz);}} catch (ClassNotFoundException e) {}}return annotationMap;}/*** @description 扫描所有文件* @param appRoot 根目录* @return 类名列表* @author xiao tang* @date 2021/12/17*/public static List<String> scanAllFileClazz(String appRoot) {// class文件列表List<String> clazzFileList = new ArrayList<>();// 文件夹队列LinkedList<File> dirList = new LinkedList<>();dirList.add(new File(appRoot));// 遍历文件夹while (!dirList.isEmpty()) {File[] files = dirList.removeFirst().listFiles();for (File tempFile : files) {// 文件夹if (tempFile.isDirectory()) {dirList.add(tempFile);} else if (tempFile.getName().endsWith(".class")) {// 非文件夹,且以 .class 结尾,添加到文件列表clazzFileList.add(prcFilePath(tempFile.getAbsolutePath()));}}}return clazzFileList;}/*** @description 加工文件名,获取全限定类名* @param filePath 文件绝对路径* @return 全限定类名* @author xiao tang* @date 2021/12/25*/public static String prcFilePath(String filePath) {String flag = "target\\classes\\";return filePath.substring(filePath.indexOf(flag)+flag.length(), filePath.lastIndexOf(".class")).replace('\\', '.');}
}

【2.3】工具  

/*** @Description 字符串工具 * @author xiao tang* @version 1.0.0* @createTime 2021年12月17日*/
public class BusiStringUtils {private BusiStringUtils(){}public static String lowerFirstChar(String raw) {raw.charAt(0);char[] charArr = raw.toCharArray();charArr[0] += 32;return new String(charArr);}/** * @description 判断字符串是否为空* @param * @return * @author xiao tang * @date 2021/12/25 */public static boolean isBlank(String raw) {if (raw ==null) return true;for (int i = 0; i < raw.length(); i++) {if (!Character.isWhitespace(raw.charAt(i))) return false;}return true; }
}

【2.4】 使用以上自定义容器

【2.4.1】自定义 step

/*** @Description 查询参数* @author xiao tang* @version 1.0.0* @createTime 2021年12月25日*/
@MyStep("VWPAMQRY")
public class VWPAMQRY {@MyAutowireParamDAO paramDAO;/*** @description 业务逻辑 * @param key 键* @return 响应报文* @author xiao tang * @date 2021/12/19 */public String doBusi(String key) {// 其他逻辑 XXX// 调用dao层api查询参数值 return paramDAO.qryValueByKey(key);}
}

【2.4.2】dao 层

/*** @Description dao层 * @author xiao tang* @version 1.0.0* @createTime 2021年12月19日*/
@MyRepository("diyParamDao")
public class ParamDAO {private static Map<String, String> params = new HashMap<>();static {params.put("k1", "v1");params.put("k2", "v2");}/*** @description 根据key查询value* @param key 键* @return 值* @author xiao tang* @date 2021/12/19*/public String qryValueByKey(String key) {return params.get(key);}
}

 

【3】main程序 拉起整个应用

/*** @Description spring ioc 实现机制* @author xiao tang* @version 1.0.0* @createTime 2021年12月16日*/
public class Topic16Main {public static void main(String[] args) {SpringContext springContext = new SpringContext();// 通过clazz 获取ParamDAO paramDAO = springContext.getBean(ParamDAO.class);String value = paramDAO.qryValueByKey("k1");System.out.println(value); // v1// 通过 name 获取paramDAO = springContext.getBean("diyParamDao");value = paramDAO.qryValueByKey("k1");System.out.println(value); // v1// 从容器获取 stepVWPAMQRY vwpamqry = springContext.getBean(VWPAMQRY.class);System.out.println(vwpamqry.doBusi("k1"));// v1System.out.println(vwpamqry.doBusi("k2")); // v2}
}

打印结果:

v1
v1
v1
v2


【Reference】

IoC容器和Dependency Injection模式 - Thoughtworks洞见https://insights.thoughtworks.cn/injection/依赖注入和控制反转的理解,写的太好了。_路在脚下-CSDN博客_依赖注入和控制反转的区别学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring Ioc的理解。一、分享Iteye的开涛对Ioc的精彩讲解  首先要分享的是Iteye的开涛这位技术牛人https://blog.csdn.net/bestone0213/article/details/47424255

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

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

相关文章

html 中一个格子拆分成两个_一个效果惊人的数字游戏

安爸曾多次讲过数学推理能力对孩子成长的重要性&#xff0c;听到有位家长说自己用扔骰子的方法教孩子数学等式。步骤大致是扔骰子时&#xff0c;如果骰子是3&#xff0c;就在棋盘上从0出发走3步&#xff0c;并且写出033的加法等式。扔到负数就后退&#xff0c;写出减法等式。科…

JAVA面试常考系列八

转载自 JAVA面试常考系列八 题目一 JDBC是什么&#xff1f; JDBC&#xff08;Java DataBase Connectivity,java数据库连接&#xff09;是一种用于执行SQL语句的Java API&#xff0c;可以为多种关系数据库提供统一访问&#xff0c;由一组用Java语言编写的类和接口组成。JDBC提供…

【广州/深圳 活动】 MVP社区巡讲

紧跟当今的技术发展趋势还远远不够&#xff0c;我们要引领变革&#xff01;加入本地技术专家社区&#xff0c;获取真实案例、实况培训演示以及探讨新一代解决方案。在此活动中&#xff0c;您将&#xff1a; 了解如何运用开源&#xff08;OSS&#xff09;技术、Microsoft 技术及…

java socket实现简单即时通讯

【1】socket服务器 /*** Description 即时消息服务器* author xiao tang* version 1.0.0* createTime 2022年01月23日*/ public class IMSocketServer {private static int PORT 13;public static void main(String[] args) {ServerSocket server null;try {// 开启端口serv…

蝌蚪网课助手mac_疫情期间如何录网课?(干货教程)手把手教你录出高质量网课。...

鉴于国外疫情的持续爆发&#xff0c;中小学开学日期进一步延期&#xff0c;我们的网课教学也同样面临持续后延。我们的很多教师朋友&#xff0c;可能此时他们正需要这么一个教程来熟悉网课的录制方法。于是这篇文章就应运而生了&#xff0c;希望它能给各位老师带来些许帮助。​…

JAVA面试常考系列九

转载自 JAVA面试常考系列九 题目一 RMI架构层的结构是如何组成的&#xff1f; RMI体系结构由三层组成&#xff0c;分别是&#xff1a; 存根和骨架层&#xff08;Stub and Skeleton Layer&#xff09; 远程引用层&#xff08;Remote Reference Layer&#xff09; 传输层&#xf…

WebAPI前置知识:HTTP与RestfulAPI

对HTTP协议的基本了解是能理解并使用RestFul风格API的基础&#xff0c;在了解了这些基础之后&#xff0c;使用各种RestFul的开发框架才能得心应手。我一开始使用WebApi的时候就因为对这些知识缺乏了解&#xff0c;觉得用起来各种不顺手&#xff0c;直到熟悉了这些HTTP的知识后&…

Java三种代理模式-静态代理,动态代理和cglib代理

【README】 本文阐述了3种代理模式的定义&#xff0c;并编写了代码测试案例&#xff1b; 代理其实是一种设计模式&#xff0c;可以在访问目标对象的方法上下文添加处理逻辑&#xff08;扩展目标对象的功能&#xff09;&#xff0c;是 切面编程的基石&#xff1b; 【举个例子】…

python遗传算法工具箱的使用_遗传算法的python实现,非常值得看的一篇文章

遗传算法是一种智能优化算法&#xff0c;通常用于求解复杂的数学问题。相比于传统方法&#xff0c;遗传算法摒弃了盲目的穷举或完全随机的求解策略&#xff0c;借鉴了自然界优胜劣汰、自然进化的思想&#xff0c;快速逼近最优解。上文对遗传算法的基本内容进行了介绍&#xff0…

JAVA面试常考系列十一

转载自 JAVA面试常考系列十一 题目一 什么是JSP&#xff1f; JSP(Java Server Page)是一个文本文档&#xff0c;是一种将静态内容和动态生成内容混合在一起的技术。 JSP包含两种类型的文本&#xff1a;静态数据和JSP元素。静态数据可以用任何基于文本的格式表示&#xff0c;如H…

.NET跨平台实践:用C#开发Linux守护进程

Linux守护进程&#xff08;Daemon&#xff09;是Linux的后台服务进程&#xff0c;它脱离了与控制终端的关联&#xff0c;直接由Linux init进程管理其生命周期&#xff0c;即使你关闭了控制台&#xff0c;daemon也能在后台正常工作。 一句话&#xff0c;为Linux开发与控制台无关…

ThreadLocalRandom与Random区别

转自&#xff1a; 一文秒懂 Java ThreadLocalRandom - Java 一文秒懂 - 简单教程&#xff0c;简单编程随机数生成是一个非常常见的操作&#xff0c;而且 Java 也提供了 java.util.Random 类用于生成随机数&#xff0c;而且呢&#xff0c;这个类也是线程安全的&#xff0c;就是…

python自动配置文件_【python接口自动化】- ConfigParser配置文件的使用

前言&#xff1a;目前我们使用的绝大多数计算机程序&#xff0c;无论是办公软件&#xff0c;浏览器&#xff0c;甚至游戏、视频都是通过菜单界面系统配置的&#xff0c;它几乎成了我们使用机器的默认方式。而在python中&#xff0c;也有这样的一个配置模块可以把代码可配置化。…

JAVA面试常考系列十

转载自 JAVA面试常考系列十 题目一 Servlet是什么&#xff1f; Servlet&#xff08;Server Applet&#xff09;是Java Servlet的简称&#xff0c;称为小服务程序或服务连接器&#xff0c;是用Java编写的服务器端程序&#xff0c;主要的作用是处理客户端请求并生成动态Web内容。…

DotNet 资源大全

Awesome DotNet&#xff0c;这又是一个 Awesome XXX 系列的资源整理&#xff0c;由 quozd 发起和维护。内容包括&#xff1a;编译器、压缩、应用框架、应用模板、加密、数据库、反编译、IDE、日志、风格指南等。 伯乐在线已在 GitHub 上发起「DotNet 资源大全中文版」的整理。欢…

javabean与json转换(fastjson与jackson两个版本)

【README】 本文演示了 javabean与json转换的开发方式&#xff1b; 要想 javabean的属性名 与 json的字段名不一致&#xff0c;也是可以转换的&#xff1b; 之前需要引入 ali.fastjson <dependency><groupId>com.alibaba</groupId><artifactId>fas…

mysql数据库新建一个递增的_分享一个mysql实验—基于数据库事务隔离级别RR及RC的测试...

概述今天主要分享一个最近做的实验&#xff0c;主要是测试mysql数据库在RR和RC不同事务隔离级别下的表现。MySQL使用可重复读来作为默认隔离级别的主要原因是语句级的Binlog。RR能提供SQL语句的写可串行化&#xff0c;保证了绝大部分情况(不安全语句除外)的DB/DR一致。下面以my…

直面Java第45期

转载自 直面Java第45期

ABP框架搭建项目系列教程基础版

我现在要着手一个新的项目&#xff0c;也打算用这个框架&#xff0c;所以想要将我一步一步用这个框架的做项目的步骤和想法跟大家分享出来。 经过前面十二篇的基础教程&#xff0c;现在终于该做个总结了。 第一篇&#xff0c;我们建议新手朋友们先通过ABP官网的启动模板生成解决…

字节数组转jsonobject(如读取HttpServletRequest.inputstream到jsonobject)

【README】 本文po出了 如何读取 字节数组到jsonobject&#xff1b; 字节数组如何获取&#xff0c;本文不再赘述&#xff1b; 【1】代码 /*** Description 字节数组转json演示* author xiao tang* version 1.0.0* createTime 2022年02月11日*/ public class ByteArr2JsonDem…