【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

diy实现spring依赖注入相关推荐

  1. arg是什么函数_java后端开发三年!你还不了解Spring 依赖注入,凭什么给你涨薪...

    前言 前两天和一个同学吃饭的时候同学跟我说了一件事,说他公司有个做了两年的人向他提出要涨薪资,他就顺口问了一个问题关于spring依赖注入的,那个要求涨薪的同学居然被问懵了...事后回家想了想这一块确 ...

  2. Java程序员进阶——Spring依赖注入原理分析

    Spring依赖注入原理分析 下面谈谈Spring是如何实现反转模式IOC或依赖注入模式DI: 平时,我们需要生成一个对象,使用new语法,如一个类为A public class A{public v ...

  3. spring 依赖注入

    Technorati 标记: spring,依赖注入,DI,ioc 平常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的 ...

  4. java接口注入对象的意义_Java Web系列:Spring依赖注入基础

    一.Spring简介 1.Spring简化Java开发 Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构.基础设施和常用功能性组件,而是 ...

  5. spring依赖注入原理(转载)

    关于spring依赖注入原理的文章在网络上已经有很多,我要写的这篇文章原文出自http://taeky.iteye.com/blog/563450,只所以再一次写下来只是为了一为自己收藏,方便以后的复 ...

  6. Spring依赖注入:注解注入总结

    更多11 spring 依赖注入 注解 java 注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired.Resource.Qualifier.Service.C ...

  7. spring依赖注入_Spring源码阅读:Spring依赖注入容器

    依赖注入 依赖注入是Spring框架最核心的能力,Spring框架提供的AOP,WebMVC等其它功能都是以依赖注入容器作为基础构建的,Spring依赖注入容器类似于一个用于组装对象的框架内核,任何应 ...

  8. spring依赖注入_Spring依赖注入

    spring依赖注入 介绍: 在设计良好的Java应用程序中,这些类应尽可能独立. 这样的设计提高了组件的可重用性. 它还使对各个组件进行单元测试变得更加容易. 依赖注入的概念促进了Java对象之间的 ...

  9. spring依赖注入_Spring的依赖注入陷阱

    spring依赖注入 Spring框架中有三种注入变量: 基于二传手的注射 基于构造函数的注入 基于现场的注入 这些机制中的每一种都有优点和缺点,并且不仅只有一种正确的方法. 例如现场注入: @Aut ...

最新文章

  1. 2022-2028年中国数码相机行业投资分析及前景预测报告
  2. 关于Office 365 域名绑定问题
  3. 什么是NIO?NIO的原理是什么机制?
  4. 骑芯供应链(T 面试)
  5. Socket的三个关联函数
  6. nonzero的用法一则例子
  7. UNIX网络编程:I/O复用技术(select、poll、epoll)
  8. 互联网日报 | 3月2日 星期二 | ​互联网人薪资报告:2021 开年薪资环比增长 7%...
  9. 决定成败的人生细节(转)
  10. 3D数学之四元组应用及实现
  11. 太极计算机 审计厅,湖北省审计厅举办2010年春节联欢会
  12. dis的前缀单词有哪些_学好单词得靠词根词缀来帮忙
  13. 织梦留言板模板 .php,织梦DEDECMS留言板功能制作及调用标签
  14. 四叉树(QuadTree)图例、应用、实现
  15. 4.1关系运算符和逻辑运算符
  16. 华为路由器交换机快捷键大全
  17. android studio listview点击事件,android如何响应listview上不同按钮点击事件
  18. 计算机中的ip地址是什么 ???
  19. Android程序的升级
  20. 鲜肉大葱包子的做法和配方

热门文章

  1. P3714 [BJOI2017]树的难题(点分治/线段树/单调队列)
  2. NWERC 2018 C. Circuit Board Design 树 + 构造
  3. D - Counting Stars HDU - 7059
  4. 牛客题霸 转圈打印矩阵 C++题解/答案
  5. P8208-[THUPC2022 初赛]骰子旅行【dp】
  6. P8207-[THUPC2022 初赛]最小公倍树【Kruskal】
  7. Dubbo(六)之属性配置
  8. Scala与Java差异(一)之基础语法
  9. SpringBoot整合kafka(实现producer和consumer)
  10. 理解大型分布式网站你必须知道这些概念