本文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】自定义容器
public interface BeanFactory {T getBean(Class clazz); T getBean(String beanName); }
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); } @Override publicT getBean(Class clazz) { return (T) SpringContextSupport.container.get(clazz.getName()); } @Override public T getBean(String beanName) { return (T) SpringContextSupport.container.get(beanName); } }
public class SpringContextSupport { List【2.3】工具allClazzNameList; Map defineAnnotationMap; List beanClazzNameList; static ConcurrentMap container = new ConcurrentHashMap<>(); public SpringContextSupport(String appRootPath) { // 扫描所有文件的clazz this.allClazzNameList = scanAllFileClazz(appRootPath); // 扫描所有自定义注解 this.defineAnnotationMap = scanAnnotation(allClazzNameList); // 剔除注解类clazz this.beanClazzNameList = allClazzNameList.stream().filter(x->!defineAnnotationMap.containsKey(x)).collect(Collectors.toList()); this.loadBean(); } 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()值为beanName String beanName = (String)defineAnnoBean.getClass().getDeclaredMethod("value").invoke(defineAnnoBean); // 若value方法值为空,则取类名首字母小写 beanName = !BusiStringUtils.isBlank(beanName) ? beanName : BusiStringUtils.lowerFirstChar(beanClazz.getSimpleName()); // 若bean在容器中存在,直接break if (container.containsKey(beanName)) break; // 实例化bean之前先扫描是否有属性bean,若有被 MyAutowired修饰的属性,则注入 Object beanObj = beanClazz.newInstance(); // 装配bean属性 this.autowireField(beanClazz, beanObj); // 注入bean SpringContext.insertBean(beanName, beanObj); SpringContext.insertBean(beanClazz.getName(), beanObj); break; } catch(Exception e) { e.printStackTrace(); } } } } catch (Exception e) { throw new IllegalStateException("类" + x+ "实例化失败", e); } } } 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方法值 // 装配bean try { // 若容器不存在该bean,则创建并放入容器 if (!container.containsKey(beanNameOfMyAutowire)) { Object beanObjOfAutowire = field.getType().newInstance(); // 注入属性bean SpringContext.insertBean(beanNameOfMyAutowire, beanObjOfAutowire); SpringContext.insertBean(field.getClass().getName(), beanObjOfAutowire); } field.setAccessible(true); // 设置可以访问 field.set(beanObj, container.get(beanNameOfMyAutowire)); // 注入bean } catch (Exception e) { } } } } public static Map scanAnnotation(List clazzNameList) { Map 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; } public static List scanAllFileClazz(String appRoot) { // class文件列表 List clazzFileList = new ArrayList<>(); // 文件夹队列 linkedList 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; } public static String prcFilePath(String filePath) { String flag = "target\classes\"; return filePath.substring(filePath.indexOf(flag)+flag.length(), filePath.lastIndexOf(".class")).replace('\', '.'); } }
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); } 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
@MyStep("VWPAMQRY") public class VWPAMQRY { @MyAutowire ParamDAO paramDAO; public String doBusi(String key) { // 其他逻辑 XXX // 调用dao层api查询参数值 return paramDAO.qryValueByKey(key); } }【2.4.2】dao 层
@MyRepository("diyParamDao") public class ParamDAO { private static Mapparams = new HashMap<>(); static { params.put("k1", "v1"); params.put("k2", "v2"); } public String qryValueByKey(String key) { return params.get(key); } }
【3】main程序 拉起整个应用
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 // 从容器获取 step VWPAMQRY vwpamqry = springContext.getBean(VWPAMQRY.class); System.out.println(vwpamqry.doBusi("k1"));// v1 System.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
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)