
- 创建者
- 皮埃尔·德·顾拜旦(法国人)
- 外文名
- International Olympic Committee(英),Comité international olympique (法)
- 简 称
- 国际奥委会(IOC)
- 成 员
- 206国家和地区
- 总 部
- 瑞士洛桑(Lausanne)
- 中文名
- 国际奥林匹克委员会
- 官 网
- http://www.olympic.org/
- 成立时间
- 1894年6月23日
- 现任主席
- 托马斯·巴赫
- 官方语言
- 英语、法语
-
对IOC的相关理解总结
2020-04-20 13:54:07一、对IOC和DI的基本认识 (一)理解IoC,即“控制反转” (二)IoC具体做什么? (三)理解IoC和DI的关系 二、对IOC容器初始化的理解 四、对DI依赖注入的理解(主要是) 参考书籍、文献和资料 一、对IOC和DI...目录
当前没有去添加对应的源码,只是自己的一些总结,可能理解有错误或不到位,还请指出。
当前没有去添加对应的源码,只是自己的一些总结,可能理解有错误或不到位,还请指出。
一、对IOC和DI的基本认识
(一)理解IoC,即“控制反转”
在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建以及外部资源获取(不只是对象包括比如文件等)。
●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象:由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转,依赖对象的获取被反转了。
(二)IoC具体做什么?
IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。
- 传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;
- 有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
- IoC对编程实现由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
(三)理解IoC和DI的关系
DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
●谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
二、对IOC容器初始化的理解
IOC容器初始化的基本步骤主要是两个方面:
- 初始化的入口由容器实现中的refresh()方法调用来完成。
- 对Bean定义载入IOC容器使用的方法是loadBeanDefinition()。
大致过程如下:
- 通过ReasourceLoader来完成资源文件的定位,DefaultResourceLoader是默认的实现,同时上下文本身就给出了ResourceLoader的实现,可以通过类路径、文件系统、URL等方式来定位资源。
- 如果XmlBeanFactory作为IOC容器,那么需要为它指定Bean定义的资源,也就是说Bean定义文件是通过抽象成Resource来被IOC容器处理,容器通过BeanDefinitionReader来完成定义信息的解析和Bean信息的注册,往往使用XmlBeanDefinitionReader来解析Bean的XML定义文件---实际的处理过程是委托给BeanDefinitionParserDelegate来完成的,从而得到Bean的定义信息,这些信息在Spring中使用BeanDefinition来表示(这个名字可以让我们想到loadBeanDefinition()、registerBeanDefinition()这些相关的方法,他们都是为处理BeanDefinition服务的)。
- 解析得到BeanDefinition以后,需要在IOC容器中注册,这由IOC实现BeanDefinitionRegister接口来实现,注册过程就是在IOC容器内容维护一个HashMap来保存得到的BeanDefinition的过程,这个HashMap是IOC容器持有Bean信息的场所,以后Bean的操作都是围绕这个HashMap来实现。
- 之后我们通过BeanFactory和ApplicationContext来享受Spring IOC的服务了,在使用IOC容器的时候我们注意到,除了少量粘合代码,绝大多数以正确IOC风格编写的应用程序代码完全不关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在了一起,基本的策略是把工厂放到已知的地方,最好放在对预期使用的上下文有意义的地方,以及代码要实际访问工厂的地方。
- Spring本身提供了对声明式载入Web应用程序用法的应用程序上下文,并将其存储在ServletContext的框架实现中。
三、对DI依赖注入的理解
当Spring IOC容器完成了Bean定义资源的定位、载入和解析注册,IOC容器就可以管理Bean定义的相关数据了,但是此时IOC容器还没有对所管理的Bean进行依赖注入,依赖注入 在以下两种情况下发生:
- 用户第一次调用getBean()方法时,IOC容器触发依赖注入。
- 当用户在配置文件中将<bean>元素配置了lazy-init=false属性时,即让容器在解析注册Bean定义时进行预实例化,触发依赖注入。
Beanfactory接口定义了Spring IOC容器的基本功能规范,是Spring IOC容器所应遵守的最低层和最基本的编程规范。BeanFactory接口中定义了几个getBean()方法,用于用户向IOC容器索取被管理的Bean的方法,通过分析其子类的具体实现来理解Spring IOC容器在用户索取Bean时如何完成依赖注入。
- getBean方法肯定不陌生,必经之路,然后调用doGetBean,进来以后首先会执行transformedBeanName找别名,看你的Bean上面是否起了别名。然后进行很重要的一步,getSingleton,这段代码就是从你的单例缓存池中获取Bean的实例。那么你第一次进来肯定是没有的,缓存里肯定是拿不到的。也就是一级缓存里是没有的。那么它怎么办呢?他会尝试去二级缓存中去拿,但是去二级缓存中拿并不是无条件的,首先要判断isSingletonCurrentlyInCreation(beanName)他要看你这个对象是否正在创建当中,如果不是直接就退出该方法,如果是的话,他就会去二级缓存earlySingletonObjects里面取,如果没拿到,它还接着判断allowEarlyReference这个东西是否为true。它的意思是说,是否允许让你从单例工厂对象缓存中去拿对象。默认为true。好了,此时如果进来那么就会通过singletonFactory.getObject()去单例工厂缓存中去拿。然后将缓存级别提升至二级缓存也就早期暴露的缓存。
- getSingleton执行完以后会走dependsOn方法,判断是否有dependsOn标记的循环引用,有的话直接卡死,抛出异常。比如说A依赖于B,B依赖于A 通过dependsOn注解去指定。此时执行到这里就会抛出异常。这里所指并非是构造函数的循环依赖。
- beforeSingletonCreation在这里方法里,就把你的对象标记为了早期暴露的对象,提前暴露对象用于创建Bean的实例。
- 紧接着就走创建Bean的流程开始。在创建Bean之前执行了一下resolveBeforeInstantiation。它的意思是说,代理AOPBean定义注册信息但是这里并不是实际去代理你的对象,因为对象还没有被创建。只是代理了Bean定义信息,还没有被实例化。把Bean定义信息放进缓存,以便我想代理真正的目标对象的时候,直接去缓存里去拿。
- 接下来就真正的走创建Bean流程,首先走进真正做事儿的方法doCreateBean然后找到createBeanInstance这个方法,在这里面它将为你创建你的Bean实例信息(Bean的实例)。如果说创建成功了,那么就把你的对象放入缓存中去(将创建好的提前曝光的对象放入singletonFactories三级缓存中)将对象从二级缓存中移除因为它已经不是提前暴露的对象了。但是。如果说在createBeanInstance这个方法中在创建Bean的时候它会去检测你的依赖关系,会去检测你的构造器。然后,如果说它在创建A对象的时候,发现了构造器里依赖了B,然后它又会重新走getBean的这个流程,当在走到这里的时候,又发现依赖了A此时就会抛出异常。为什么会抛出异常,因为,走getBean的时候他会去从你的单例缓存池中去拿,因为你这里的Bean还没有被创建好。自然不会被放进缓存中,所以它是在缓存中拿不到B对象的。反过来也是拿不到A对象的。造成了死循环故此直接抛异常。这就是为什么Spring IOC不能解决构造器循环依赖的原因。因为你还没来的急放入缓存你的对象是不存在的。所以不能创建。同理@Bean标注的循环依赖方法也是不能解决的,跟这个同理。那么多例就更不能解决了。为什么?因为在走createBeanInstance的时候,会判断是否是单例的Bean定义信息mbd.isSingleton();如果是才会进来。所以多例的Bean压根就不会走进来,而是走了另一段逻辑,这里不做介绍。至此,构造器循环依赖和@Bean的循环依赖还有多例Bean的循环依赖为什么不能解决已经解释清楚。然后如果说,Bean创建成功了。那么会走后面的逻辑。
- 将创建好的Bean放入缓存,addSingletonFactory方法就是将你创建好的Bean放入三级缓存中,并且移除早期暴露的对象。
- 通过populateBean给属性赋值,我们知道,创建好的对象,并不是一个完整的对象,里面的属性还没有被赋值。所以这个方法就是为创建好的Bean为它的属性赋值。并且调用了我们实现的的XXXAware接口进行回调初始化,然后调用我们实现的Bean的后置处理器,给我们最后一次机会去修改Bean的属性。
参考书籍、文献和资料
1.https://www.iteye.com/blog/jinnianshilongnian-1413846
2.《Sring 5 核心原理与30个类手写实战》,谭勇徳,中国公信出版社,2019.
-
IOC
2018-05-19 11:51:46[IOC] 1. ioc 简介 1.1 ioc 定义 ioc (Inversion of Control, 控制反转)把创建对象的操作交给框架,亦被称为 DI(Dependency Injection, 依赖注入)。 为什么叫做 “控制反转” 呢?之前,我们想要一个对象...[IOC]
1. ioc 简介
1.1 ioc 定义
ioc
(Inversion of Control, 控制反转)把创建对象的操作交给框架,亦被称为DI
(Dependency Injection, 依赖注入)。为什么叫做 “控制反转” 呢?之前,我们想要一个对象都是
new
出来的,天天需要new
对象是不是感觉有点麻烦。有人就想到了,把这些简单重复的工作也交给框架做。本来需要我们向框架 “射入” 对象,现在框架自己能产生对象了,这不正是 控制反转 吗?于是,就有了这个响亮的名字。1.2 ioc 目标
其实定义中已经把
ioc
的目标说清楚了。这里,再强调一下:把创建对象的操作交给框架,“解放程序员的生产力”。下面代码是
ioc
的使用用例,它可实现customerService
变量的自动注入。本文的目标是实现ioc
,也就是使得下面代码可用。// 使用了基于注解实现的 ioc 代码 @Controller public class CustomerController { // 这里可以自动注入 CustomerService 对象 @InJect private CustomerService customerService; ... }
ioc
的实现主要有两种形式:1)声明式;2)注解。其中,声明式是基于xml
配置文件来做的,这种方式对jdk
版本无要求,兼容性强,但相注解方式较冗杂;而注解方式,需要jdk1.5
以上(因为从 jdk1.5 才有了注解),但它更灵活、更简洁。鉴于现在很少有人在使用jdk1.5
之前的版本,本文将基于注解实现ioc
。2. ioc 实现
2.1 实现关键
- 如何标识出类中哪些变量需要注入;
- 怎样生成可用于注入的对象实例以及如何存储它们;
- 何时注入;
2.2 详细过程
1) 创建注解
InJect
用它标识类中需要注入的属性;// InJect.java package top.inotwant.annocation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) // 元注解,表示 InJect 作用于属性之上 @Retention(RetentionPolicy.RUNTIME) // 元注解,用于描述 InJect 生存周期 public @interface InJect { }
2) 创建
Controller
Service
注解,它们都作用于类上(比如,1.2 节中@Controller
作用于CustomerController
之上)。被它们修饰的类将要求产生对象实例,这个过程将会用到 反射,下面再详细描述该过程。(使用过Spring
框架的是不是对这两个注解很熟悉)// Controller.java package top.inotwant.annocation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) // 元注解,表示 Controller 作用于类之上 @Retention(RetentionPolicy.RUNTIME) public @interface Controller { } // Service.java package top.inotwant.annocation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Service { }
3) 自定义类加载器加载包下的所有
java
文件,并从中获取被上面两个注解之一修饰的类的元类Class<?>
。 有了这些元类,我们就可以使用反射产生对应的对象实例了。ClassUtil
工具类中的loadClassSet(String packageName)
静态方法实现了该逻辑。它将返回packageName
指定的包下所有类的元类。ClassHelper
帮助类实现了对上面两个注解之一修饰类的元类的抽取。// ClassUtil.java package top.inotwant.utils; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; public final class ClassUtil { /** * 获取类加载器 */ public static ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); } /** * 加载指定类 */ public static Class<?> loadClass(String className) { try { // TODO 注意 class.forName 与 ClassLoader.loadClass 的区别(关于 static 是否静态初始化) // 注意第二个参数,一定要设置为 true 。否则,在加载类时不会运行 static 块,这将影响一部分类的初始化。 return Class.forName(className, true, getClassLoader()); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } /** * 加载包下所有类 * @param packageName 为包名,比如 “top.inotwant.sample” */ public static Set<Class<?>> loadClassSet(String packageName) { String packagePath = packageName.replace(".", "/"); try { Set<Class<?>> result = new HashSet<>(); // getResources 的使用请看 API Enumeration<URL> resources = getClassLoader().getResources(packagePath); while (resources.hasMoreElements()) { URL currentURL = resources.nextElement(); if ("file".equals(currentURL.getProtocol())) { String path = currentURL.getPath().replaceAll("%20", " "); addClass(path, packageName, result); } else if ("jar".equals(currentURL.getProtocol())) { // 此处为了加载文件夹下 `jar` 中所包含的所有类文件,若不理解可忽略此处,不影响总体 JarURLConnection conn = (JarURLConnection) currentURL.openConnection(); JarFile jarFile = conn.getJarFile(); Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); String jarEntryName = jarEntry.getName(); if (jarEntryName.endsWith(".class")) { String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); result.add(loadClass(className)); } } } } return result; } catch (IOException e) { throw new RuntimeException(e); } } private static void addClass(String path, String packageName, Set<Class<?>> result) { File[] files = new File(path).listFiles(new FileFilter() { @Override public boolean accept(File file) { return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory(); } }); if (files != null) { for (File file : files) { if (file.isFile()) { String subFileName = file.getName(); String className = subFileName.substring(0, subFileName.lastIndexOf(".")); result.add(loadClass(packageName + "." + className)); } else if (file.isDirectory()) { // 递归 addClass(file.getAbsolutePath(), packageName + "." + file.getName(), result); } } } } } // ClassHelper.java package top.inotwant.helper; import top.inotwant.annocation.Controller; import top.inotwant.annocation.Service; import top.inotwant.utils.ClassUtil; import java.util.HashSet; import java.util.Set; /** * Class 帮助类 */ public final class ClassHelper { private static Set<Class<?>> CLASS_SET; static { String basePackage = "指定包"; CLASS_SET = ClassUtil.loadClassSet(basePackage); } /** * 加载 base package 下的所有类 */ public static Set<Class<?>> getClassSet() { return CLASS_SET; } /** * 获取加载的由 service 标注的所有类 */ public static Set<Class<?>> getServiceClassSet() { Set<Class<?>> result = new HashSet<>(); for (Class<?> clazz : CLASS_SET) { if (clazz.isAnnotationPresent(Service.class)) result.add(clazz); } return result; } /** * 获取加载的由 controller 标注的所有类 */ public static Set<Class<?>> getControllerClassSet() { Set<Class<?>> result = new HashSet<>(); for (Class<?> clazz : CLASS_SET) { if (clazz.isAnnotationPresent(Controller.class)) result.add(clazz); } return result; } /** * 获取框架管理的 bean (包含, service controller) */ public static Set<Class<?>> getBeanClassSet() { Set<Class<?>> result = new HashSet<>(); result.addAll(getServiceClassSet()); result.addAll(getControllerClassSet()); return result; } }
4) 利用上面的元类,使用反射生成它们对应的对象实例。
BeanHelper
中的静态块中实现这个逻辑。这些对象实例中包含了可用于注入的(与@Service
对应的),它们被存储在map
中,以<Class,Object>
键值对的形式,是不是很容易理解。// BeanHelper.java package top.inotwant.helper; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * bean 帮助类 */ public final class BeanHelper { private final static Map<Class<?>, Object> BEAN_MAP = new HashMap<>(); static { Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet(); for (Class<?> clazz : beanClassSet) { BEAN_MAP.put(clazz, getInstance(clazz)); } } /** * 获取类对应的实例 */ @SuppressWarnings("unchecked") private static <T> T getInstance(Class<?> clazz) { try { return (T) clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } /** * 获取 bean 映射 */ public static Map<Class<?>, Object> getBeanMap() { return BEAN_MAP; } /** * 获取 bean */ @SuppressWarnings("unchecked") public static <T> T getBean(Class<T> clazz) { Object obj = BEAN_MAP.get(clazz); if (obj != null) return (T) obj; else { throw new RuntimeException("can't get bean by class: " + clazz.getSimpleName()); } } }
5) 注入
IocHelper
帮助类的 static 块中完成最后一步 注入。其中的逻辑很简单,我们把所有的bean
遍历一遍,每一次迭代查看该bean
的各个属性是否标注InJect
。如果标注了,就从beanMap
中获取相应的对象实例进行注入。package top.inotwant.helper; import top.inotwant.annocation.InJect; import top.inotwant.utils.ArrayUtil; import top.inotwant.utils.CollectionUtil; import top.inotwant.utils.ReflectUtil; import java.lang.reflect.Field; import java.util.Map; /** * ioc 实现类 */ public final class IocHelper { static { Map<Class<?>, Object> beanMap = BeanHelper.getBeanMap(); if (CollectionUtil.isNotEmpty(beanMap)) { // 判断集合是否为空 for (Map.Entry<Class<?>, Object> entry : beanMap.entrySet()) { Class<?> clazz = entry.getKey(); Object obj = entry.getValue(); // TODO 切勿调成 getFields() ,它不能获取私有属性 Field[] fields = clazz.getDeclaredFields(); if (ArrayUtil.isNotEmpty(fields)) { // 判断数组是否 for (Field field : fields) { if (field.isAnnotationPresent(InJect.class)) { Object newValue = BeanHelper.getBean(field.getType()); ReflectUtil.modifyField(obj, field, newValue); // 注入 } } } } } } }
6) 初始化 ioc
前面 5 步已经实现了
ioc
,并且可通过系统初始化时加载IocHelper.class
让它起到作用(想一想为什么只需要加载一个类就可以?)。但是,我建议再创建如下一个类,让加载更集中、初始化的逻辑更清晰。package top.inotwant; import top.inotwant.helper.*; import top.inotwant.utils.ClassUtil; /** * 加载相关的 helper 类 */ public final class HelperLoader { public static void init() { Class<?>[] classes = { ClassHelper.class, BeanHelper.class, IocHelper.class }; for (Class<?> clazz : classes) { ClassUtil.loadClass(clazz.getName()); } } }
至此,1.2 节中的代码已经可以使用。
3. 参考
-
ioc
2018-09-23 09:29:14IOC:比如有一个类,在类中有一个非静态的方法,当我们要在另一个类中去调用这个类的方法,创建这个类的对象,使用对象点的方式去调用方法,创建类对象的过程是new出来的 而ioc不是通过new来创建对象,而是交给...IOC:比如有一个类,在类中有一个非静态的方法,当我们要在另一个类中去调用这个类的方法,创建这个类的对象,使用对象点的方式去调用方法,创建类对象的过程是new出来的
而ioc不是通过new来创建对象,而是交给spring的配置文件去创建对象
Spring是一站式框架,在其三层结构中,他的每一层都提供了不同的技术
web层 springmvc
Service层 spring ioc
dao层 jdbc模板
Spring 的IOC操作
1.ioc的配置方式
2.ioc的注解方式
ioc底层原理用到的技术
1).xml配置文件
2).dom4j解析xml
3).工厂设计模式
4).反射
注解的写法 @注解名称(属性名=属性值)
注解可以使用在类上面,可以使用在方法、属性上面
使用注解可以完成功能
使用注解必须导入aop包
-
IoC
2014-03-07 13:20:41IoC: Inversion of Control,控制反转, 控制权从应用程序转移到框架(如IoC容器),是框架共有特性 1、为什么需要IoC容器 1.1、应用程序主动控制对象的实例化及依赖装配 Java代码 ...IoC: Inversion of Control,控制反转, 控制权从应用程序转移到框架(如IoC容器),是框架共有特性1、为什么需要IoC容器1.1、应用程序主动控制对象的实例化及依赖装配- A a = new AImpl();
- B b = new BImpl();
- a.setB(b);
缺点:更换实现需要重新编译源代码很难更换实现、难于测试- A a = AFactory.createA();
- B b = BFactory.createB();
- a.setB(b);
本质:创建对象,被动实例化,间接获取依赖,主动装配 (简单工厂)缺点:更换实现需要重新编译源代码很难更换实现、难于测试- A a = Factory.create(“a”);
- B b = Factory.create(“b”);
- a.setB(b);
<!—配置.properties-->- a=AImpl
- b=BImpl
本质:创建对象,被动实例化,间接获取依赖, 主动装配(工厂+反射+properties配置文件、缺点:冗余的依赖装配逻辑我想直接://返回装配好的a- A a = Factory.create(“a”);
1.2、可配置通用工厂:工厂主动控制,应用程序被动接受,控制权从应用程序转移到工厂- //返回装配好的a
- A a = Factory.create(“a”);
- <bean id=“a” class=“AImpl”>
- <property name=“b” ref=“b”/>
- </bean>
- <bean id=“b” class=“BImpl”/>
被动实例化,被动接受依赖,被动装配(工厂+反射+xml配置文件)缺点:不通用步骤:1、读取配置文件根据配置文件通过反射创建AImpl2、发现A需要一个类型为B的属性b3、到工厂中找名为b的对象,发现没有,读取配置文件通过反射创建BImpl4、将b对象装配到a对象的b属性上【组件的配置与使用分离开(解耦、更改实现无需修改源代码、易于更好实现) 】1.3、 IoC(控制反转)容器:容器主动控制- //返回装配好的a
- A a = ApplicationContext.getBean(“a”);
- <bean id=“a” class=“AImpl”>
- <property name=“b” ref=“b”/>
- </bean>
- <bean id=“b” class=“BImpl”/>
本质:创建对象和装配对象、管理对象生命周期被动实例化,被动接受依赖,被动装配(工厂+反射+xml配置文件)通用
IoC容器:实现了IoC思想的容器就是IoC容器2、IoC容器特点【1】无需主动new对象;而是描述对象应该如何被创建即可IoC容器帮你创建,即被动实例化;【2】不需要主动装配对象之间的依赖关系,而是描述需要哪个服务(组件),IoC容器会帮你装配(即负责将它们关联在一起),被动接受装配;【3】主动变被动,好莱坞法则:别打电话给我们,我们会打给你;【4】迪米特法则(最少知识原则):不知道依赖的具体实现,只知道需要提供某类服务的对象(面向抽象编程),松散耦合,一个对象应当对其他对象有尽可能少的了解,不和陌生人(实现)说话【5】IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。3、理解IoC容器问题关键:控制的哪些方面被反转了?1、谁控制谁?为什么叫反转? ------ IoC容器控制,而以前是应用程序控制,所以叫反转2、控制什么? ------ 控制应用程序所需要的资源(对象、文件……)3、为什么控制? ------ 解耦组件之间的关系4、控制的哪些方面被反转了? ------ 程序的控制权发生了反转:从应用程序转移到了IoC容器。思考:1: IoC/DI等同于工厂吗?2: IoC/DI跟以前的方式有什么不一样?领会:主从换位的思想4、实现了IoC思想的容器就是轻量级容器吗?如果仅仅因为使用了控制反转就认为这些轻量级容器与众不同,就好象在说我的轿车与众不同因为它有四个轮子?容器:提供组件运行环境,管理组件声明周期(不管组件如何创建的以及组件之间关系如何装配的);IoC容器不仅仅具有容器的功能,而且还具有一些其他特性---如依赖装配控制反转概念太广泛,让人迷惑,后来Martin Fowler 提出依赖注入概念Martin Fowler Inversion of Control Containers and the Dependency Injection patternhttp://martinfowler.com/articles/injection.htmlDI2、什么是DIDI:依赖注入(Dependency Injection) :用一个单独的对象(装配器)来装配对象之间的依赖关系 。2、理解DI问题关键谁依赖于谁? ------- 应用程序依赖于IoC容器为什么需要依赖? ------- 应用程序依赖于IoC容器装配类之间的关系依赖什么东西? ------- 依赖了IoC容器的装配功能谁注入于谁? ------- IoC容器注入应用程序注入什么东西? ------- 注入应用程序需要的资源(类之间的关系)
更能描述容器其特点的名字——“依赖注入”(Dependency Injection)IoC容器应该具有依赖注入功能,因此也可以叫DI容器3、DI优点【1】帮你看清组件之间的依赖关系,只需要观察依赖注入的机制(setter/构造器),就可以掌握整个依赖(类与类之间的关系)。【2】组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。【3】依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不用关心具体的资源来自何处、由谁实现。使用DI限制:组件和装配器(IoC容器)之间不会有依赖关系,因此组件无法从装配器那里获得更多服务,只能获得配置信息中所提供的那些。4、实现方式1、构造器注入2、setter注入3、接口注入:在接口中定义需要注入的信息,并通过接口完成注入@Autowiredpublic void prepare(MovieCatalog movieCatalog,CustomerPreferenceDao customerPreferenceDao) {this.movieCatalog = movieCatalog;this.customerPreferenceDao = customerPreferenceDao;}使用IoC/DI容器开发需要改变的思路1、应用程序不主动创建对象,但要描述创建它们的方式。2、在应用程序代码中不直接进行服务的装配,但要配置文件中描述哪一个组件需要哪一项服务。容器负责将这些装配在一起。其原理是基于OO设计原则的The Hollywood Principle:Don‘t call us, we’ll call you(别找我,我会来找你的)。也就是说,所有的组件都是被动的(Passive),所有的组件初始化和装配都由容器负责。组件处在一个容器当中,由容器负责管理。IoC容器功能:实例化、初始化组件、装配组件依赖关系、负责组件生命周期管理。本质:IoC:控制权的转移,由应用程序转移到框架;IoC/DI容器:由应用程序主动实例化对象变被动等待对象(被动实例化);DI: 由专门的装配器装配组件之间的关系;IoC/DI容器:由应用程序主动装配对象的依赖变应用程序被动接受依赖关于IoC/DI与DIP之间的关系 详见 http://www.iteye.com/topic/1122310?page=5#2335746
IoC/DI与迪米特法则 详见http://www.iteye.com/topic/1122310?page=5#2335748
-
SpringIoc 实现原理
2019-04-20 16:44:33什么是SpringIOC spring ioc指的是控制反转,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。交由Spring容器统一进行管理,从而实现松耦合 “控制反转”,不是什么技术,而是一种设计思想... -
IOC
2011-05-17 17:06:54IOC(控制反转,也叫依赖注入):是一种设计模式而不是编程技术。使用IOC后,我们不需要自己去创建某个类的实例,而由IOC容器去创建,当我们需要使用某个对象时,直接到容器中去获取就可以了。 ... -
Ioc
2008-06-11 16:08:00控制反转(Ioc)模式(又称DI:Dependency Injection)就是Inversion of Control,控制反转。在Java开发中,IoC意味着将你设计好的类交给系统去控制,而不是在你的类内部控制。这称为控制反转。 IoC(Inversion of ... -
浅谈IOC--说清楚IOC是什么
2018-03-29 16:22:44什么是IOC3.IOC也叫依赖注入(DI)4.IOC的优缺点5.IOC容器的技术剖析6.IOC容器的一些产品7.参考博文本文旨在用语言(非代码)说清楚IOC到底是什么,没有什么高深的技术,园中的老牛、大虾们看到这里可以绕行了,以免浪费... -
【Spring-IOC基础】深入浅出Spring-IOC
2020-04-20 16:20:03IOC(Inversion Of Control) 1.概念: 控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency ... -
IoC之IoC基础
2019-06-02 19:33:22上文提到Spring的核心是IoC和AOP,今天开始学习核心之一的IoC,同样是带着IoC是什么,为什么,怎么用来学习IoC IoC是什么 先看看IoC的全名是什么。IoC——Inversion of Control,即“控制反转”。这个可不是什么技术... -
谈谈对Spring IOC的理解
2016-09-21 13:42:59学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring... -
浅谈 IOC 什么是 IOC?
2019-05-27 14:19:26本文以文字为主题,并没有代码案例,仅限于记录自己对于 IOC 的理解,文中也啥特别高深的东西,大牛和大佬可以忽略本文,以免浪费您宝贵的时间 什么是 IOC ? IOC (Inversion Of Control,控制倒转),是spring... -
IoC之IoC容器基本原理
2019-06-02 22:10:51上一篇学习了IoC的基础知识,明白了IoC是什么,为什么需要使用IoC以及IoC和DI的区别,接下来让我们继续往下学习IoC容器的基本原理。 IoC容器的概念 IoC容器就是具有依赖注入功能的容器,IoC具有负责实例化,定位,... -
IOC详解
2020-09-04 23:51:00IOC概念 什么是IOC: 控制反转,把对象创建和对象间的调用过程,交给Spring进行管理。 使用IOC可以降低耦合度。 IOC底层原理:XML解析,工厂模式,反射。 图解IOC底层原理: IOC思想基于IOC容器完成的,IOC容器... -
SpringIOC
2020-04-10 13:15:11SpringIOC开发第一个Spring程序(IOC)springIOC发展史IOC控制反转/依赖注入图解三种方式的区别 开发第一个Spring程序(IOC) ApplicationContext conext = new ClassPathXmlApplicationContext(... -
ioc编程
2020-03-29 12:02:35一、什么是ioc? 首先要强调的是ioc是一种编程思想,而不是技术。ioc的全称叫inversion of control,即控制反转的意思。那控制反转又是什么意思呢?通俗点来说就是原来你控制的事情,现在不再由你来控制。在java... -
ioc本质
2020-05-08 16:57:18学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring... -
Spring IoC
2019-10-22 17:35:22文章目录控制反转(IoC)依赖倒置原则依赖注入(DI)控制反转、依赖倒置、依赖注入的关系Spring IoC的实现方式XML方式注解方式 控制反转(IoC) IoC(Inversion of Control),控制反转,IoC不是一门技术,而是一... -
spring IOC
2019-06-04 17:45:06Spring IOC的初始化过程: Spring IOC的初始化过程 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语言中也有应用,并非 Spirng 特有。 IoC 容器是 Spring 用来实现 IoC 的载体, ... -
Spring IOC
2020-02-23 10:24:44Spring IOC IOC容器是一个管理bean的容器,在spring的定义中,他要求所有的ioc容器都需要实现接口BeanFactory,他是一个顶级容器接口。 在IOC容器中,默认情况下,Bean都是以单例形式存在的,也就是说getBean方法... -
Sping IOC
2019-03-05 16:45:42Sping IOCIoC是什么IoC能做什么IoC和DI IoC是什么 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象...
-
2016年下半年 信息系统监理师 上午试卷 综合知识 软考真题【含答案和答案解析】
-
2021 PHP租车系统 毕业设计 毕设源码 源代码使用教程
-
2015年上半年 信息系统管理工程师 上午试卷 综合知识 软考真题【含答案和答案解析】
-
jz2440-----Ubuntu与开发板之间互传文件(三)
-
2014年上半年 信息系统监理师 上午试卷 综合知识 软考真题【含答案和答案解析】
-
白话:java从入门到实战
-
生成、添加用于操作多个 Git 账户的 SSH 公钥配置
-
kafka中kafkaConsumer的poll拉取方法
-
MaxScale 实现 MySQL 读写分离与负载均衡
-
产品需求分析神器:KANO模型分析法
-
RootCluster.github.io:组织网站-源码
-
一天学完MySQL数据库
-
挑战程序设计9.2 二叉搜索树——插入中root和NIl的问题
-
NearFi应用-源码
-
【学习札记NO.00004】Linux Kernel Pwn学习笔记 I:一切开始之前
-
[记录]VisualStudio Color Theme
-
百亿级日志系统架构设计及优化
-
2015年下半年 信息系统监理师 上午试卷 综合知识 软考真题【含答案和答案解析】
-
APPKIT打造稳定、灵活、高效的运营配置平台
-
【黑苹果EFI】联想昭阳E40-80的自制EFI,Opencore 0.6.6