学习视频地址:https://time.geekbang.org/course/detail/100042601-184049
IOC容器的职责
- 依赖处理
- 依赖查找
- 依赖注入
- 容器
- 托管的资源(Java Beans 或其他资源[1])
- 配置
- 容器
- 外部化配置
- 托管的资源(Java Beans或其他资源[1:1])Ioc容器的实现
- JavaSE
- Java Beans
- Java ServiceLoader SPI[2]
- JNDI(Java Naming and Directory Interface)
- Java EE
- EJB(Enterprise Java Beans)
- Servlet
- 开源
- Apache Avalon(http://avalon.apache.org/closed.html)
- PicoContainer(http://picocontainer.com)
- Google Guice(https://github.com/google/guice)
- Spring Framework(https://spring.io/projects/spring-framework)传统IoC容器的实现
- Java Beans作为IoC容器
- 特性
- 依赖查找
- 生命周期管理
- 配置元信息
- 事件
- 自定义
- 资源管理
- 持久化
- 规范
- JavaBeans:https://www.oracle.com/technetwork/java/javase/tech/index-jsp-138795.html
- BeanContext:https://docs.oracle.com/javase/8/docs/technotes/guides/beans/spec/beancontext.html依赖查找VS依赖注入
- 优劣对比
构造器注入VS Setter注入
在选择构造器注入(Constructor Injection)和Setter注入(Setter Injection)之间,需要根据具体的情况来决定。 构造器注入是通过类的构造器来注入依赖对象,通常在创建对象的过程中完成注入。这种方式可以保证对象在创建完成后,其依赖关系已经完全被注入,使得对象在使用时具备完整的状态。构造器注入也可以使对象的依赖关系更加明确,减少了对于Setter方法的依赖。 Setter注入是通过类的Setter方法来注入依赖对象,通常在对象创建后通过调用Setter方法来完成注入。这种方式可以在对象创建后动态地修改其依赖关系,灵活性更高。Setter注入也可以支持可选的依赖,即某些依赖对象可以不注入而使用默认值。 选择构造器注入还是Setter注入,可以考虑以下几个方面:
- 对象的依赖关系是否必须要在创建时注入,还是可以在对象创建后进行注入。如果依赖关系必须在创建时注入,那么构造器注入是更合适的选择;如果依赖关系可以在创建后动态修改,那么可以考虑使用Setter注入。
- 对象的依赖关系是否是必需的,还是可选的。如果某些依赖是必需的,而且没有合适的默认值,那么构造器注入可以确保这些依赖在创建时被注入;如果某些依赖是可选的,可以使用Setter注入,并提供默认值。
- 对象的可测试性。构造器注入可以使得对象的依赖关系更加明确,方便进行单元测试,因为可以通过构造器传入模拟对象。而Setter注入在单元测试中可能需要通过反射等方式来设置依赖对象。
综上所述,选择构造器注入还是Setter注入取决于具体的情况。如果依赖关系必须在创建时注入,而且是必需的,那么构造器注入是更合适的选择;如果依赖关系可以在创建后动态修改,或者某些依赖是可选的,那么可以考虑使用Setter注入。同时,还需要考虑对象的可测试性和代码的可读性等因素。
面试题
1. 什么是IOC?
IOC,全称为Inversion of Control(控制反转),是一种软件设计原则和编程思想。它是面向对象编程(OOP)中的一种设计模式,用于解耦对象间的依赖关系。 在传统的程序设计中,对象之间的依赖关系由对象自身负责管理。例如,一个类在创建其他类的对象时,需要直接调用其他类的构造函数或方法来获取所需的对象。这种方式使得类之间紧密耦合,难以进行单元测试、代码复用和替换等操作。 而IOC是一种反转了对象创建和依赖关系管理的控制方式。在IOC中,对象的创建和依赖关系的管理由一个容器(Container)来负责。容器负责创建对象,并将依赖的对象注入到需要的地方。对象只需要定义自己所需的依赖,而不需要关心如何创建和管理这些依赖。这样可以实现对象间的解耦,提高代码的可维护性和可扩展性。 IOC的核心思想是通过依赖注入(Dependency Injection)来实现对象之间的解耦。依赖注入是指将需要的依赖对象通过构造器、Setter方法或其他方式注入到对象中,而不是由对象自己创建或获取依赖对象。依赖注入可以通过配置文件、注解或自动扫描等方式来实现。 IOC的好处包括:
- 解耦:IOC通过将对象的创建和依赖关系的管理交给容器来处理,使得对象之间的关系更加松散,减少了耦合。
- 可测试性:由于对象的依赖关系由容器注入,可以方便地进行单元测试,通过替换依赖对象来进行测试或模拟。
- 可扩展性:在IOC中,可以通过配置文件或注解等方式来管理对象的依赖关系,使得系统更加灵活,能够方便地进行功能扩展或替换。
常见的IOC框架包括Spring Framework、Google Guice等。这些框架提供了依赖注入的功能,可以帮助开发者实现IOC,并提供了其他的功能和工具来支持应用程序的开发。 总结来说,IOC(控制反转)是一种软件设计原则和编程思想,通过将对象的依赖关系的管理交给容器来处理,实现对象间的解耦。IOC的核心是依赖注入,将对象的依赖通过构造器、Setter方法或其他方式注入到对象中。IOC可以提高代码的可维护性、可测试性和可扩展性。
2. 依赖查找和依赖注入的区别
依赖查找(Dependency Lookup)和依赖注入(Dependency Injection)是两种不同的依赖关系管理方式。 依赖查找是指通过容器或上下文来查找所需的依赖对象。在依赖查找中,对象自己负责获取它所依赖的对象,通常是通过容器提供的方法或API来获取。例如,通过容器的getBean()方法来获取所需的对象。 依赖注入是指将依赖对象通过构造器、Setter方法或其他方式注入到对象中。在依赖注入中,对象不需要关心如何获取依赖对象,而是通过注入的方式来获取。注入可以通过配置文件、注解或自动扫描等方式实现。 区别如下:
- 控制方式不同:依赖查找是由对象自己控制获取依赖对象,而依赖注入是由容器控制将依赖对象注入到对象中。
- 依赖关系的表达方式不同:依赖查找需要在对象中显式调用容器提供的方法来获取依赖对象,而依赖注入是通过构造器、Setter方法或其他方式将依赖对象注入到对象中。
- 对象的可测试性不同:依赖查找在单元测试中可能需要使用模拟对象等技术来替代真实的依赖对象,而依赖注入可以通过注入不同的依赖对象来进行单元测试。
- 对象的依赖关系管理方式不同:依赖查找需要在每个对象中显式获取依赖对象,而依赖注入将依赖对象的管理交给容器来处理,对象只需要定义自己所需的依赖,不需要关心如何获取和管理这些依赖。
综上所述,依赖查找和依赖注入是两种不同的依赖关系管理方式。依赖查找由对象自己负责获取依赖对象,而依赖注入是由容器负责将依赖对象注入到对象中。选择依赖查找还是依赖注入取决于具体的需求和设计风格。依赖注入通常被认为是更推荐的一种方式,因为它能够实现对象间的解耦,提高代码的可维护性和可测试性。
3. Spring作为IOC容器有什么优势
Spring作为一个流行的IOC(控制反转)容器,具有以下优势:
- 松耦合:Spring实现了依赖注入(DI)机制,通过将对象的依赖关系交由容器管理,实现了对象间的松耦合。这使得代码更容易理解、维护和扩展,提高了系统的灵活性。
- 可测试性:Spring的依赖注入机制使得对象的依赖关系通过构造器、Setter方法或其他方式注入,而不是直接在对象内部创建或获取依赖对象。这样,在进行单元测试时,可以更方便地替换依赖对象,进行集成测试和模块测试。
- 面向接口编程:Spring鼓励面向接口编程,通过接口来定义具体类的依赖关系。这种编程方式使得代码更具扩展性和可替换性,有利于实现多态和依赖倒置原则。
- AOP支持:Spring提供了AOP(面向切面编程)的支持,通过配置和代理技术,可以实现横切关注点(例如事务管理、日志记录等)的集中管理和复用。这样,可以将业务逻辑与横切关注点分离,提高代码的重用性和可维护性。
- 生命周期管理:Spring容器管理对象的生命周期,可以在对象创建、初始化和销毁时执行相应的操作。例如,通过配置初始化方法和销毁方法,可以在对象创建和销毁时执行特定的逻辑。
- 配置灵活性:Spring采用基于XML、注解或Java配置的方式来进行配置,使得配置更加灵活和可扩展。可以根据具体需求选择合适的配置方式,方便地切换和修改配置,而不需要修改代码。
- 集成丰富:Spring提供了丰富的集成支持,可以与各种开源和商业框架进行集成,如Hibernate、MyBatis、Spring MVC等。这些集成支持简化了开发过程,提高了开发效率和系统的整合能力。
总的来说,Spring作为一个IOC容器,通过依赖注入、面向接口编程、AOP支持、生命周期管理和灵活的配置等特性,提供了优秀的开发框架和工具,使得开发者能够更加专注于业务逻辑的实现,提高了系统的可维护性、可测试性和可扩展性。