写点什么

面试被问 spring ioc,这样说让面试官眼前一亮 (1)

  • 2021 年 12 月 29 日
  • 本文字数:3719 字

    阅读完需:约 12 分钟

面试被问spring ioc,这样说让面试官眼前一亮(1)

首先,我们看一下 spring 官方对 IOC 容器的解释(spring.io):


1.1. Introduction to the Spring IoC Container and Beans

This chapter covers the Spring Framework implementation of the Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.

1.1. spring IOC 容器

本章覆盖 spring IOC 架构实现的简单原则。IOC 也称为 依赖注入(DI).具体依赖通过 3 种方法来定义,分别是 构造器参数,工厂方法参数或者是静态配置文件属性的方式(xml)。然后再创建 bean 的时候,将定义的依赖注入到 bean 实体中。这个过程就是依赖注入(IOC).


我们从以下几个方面加深对 spring IOC 容器的理解:


  • 依赖注入方式

  • spring bean 概览

  • 定制一个 bean

  • 容器扩展点

  • 注解的容器配置

  • JSR-330 标准注解

  • Environment 抽象

  • ApplicationContext 的扩展功能

  • BeanFactory

spring 容器核心抽象


这幅图就反映了 spring 容器的高层抽象,你的业务实体(POJOS)和一些定制化的元数据(xml 方式,java 方式或者是 properties 方式)通过 spring 容器进行组装,然后 spring 容器产生一个完全体的 bean,随后通过 容器 来管理这些 bean 的生命周期。


我们引出第一个问题,我们的配置元数据(Configuration Metadata)可以通过哪些方式加载:

依赖加载方式

As the preceding diagram shows, the Spring IoC container consumes a form of configuration metadata. This configuration metadata represents how you, as an application developer, tell the Spring container to instantiate, configure, and assemble the objects in your application.

spring IOC 容器会消费配置元数据。配置元数据的作用就是,你作为开发者,通过元数据告诉 spring 容器,我需要你怎样实例化,配置,集成管理我应用的对象。


spring 官方文档告诉我们,配置元数据的作用类似于做菜的菜谱,而我们的 pojo 业务实体 就像是原材料,而 spring IOC 容器的功能则是厨师,我们通过把菜谱和原材料告诉厨师,它就可以帮我们做出符合我们要求的菜肴(唯一有区别的一点,spring ioc 容器会帮我们管理 bean 的整个生命周期,而厨师却不会帮我们送菜和收拾盘子。。)


  • XML 配置方式(基础配置方式)

  • 注解 配置方式(始于 spring 2.5 版本)

  • Java annotation 配置方式(始于 spring 3.0 版本)


传统标准的 xml 结构的元数据配置方式:


<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://www.springframework.org/schema/beans        https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean>
<bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean>
<!-- more bean definitions go here -->
</beans>
复制代码


当我们定义完我们的 bean 配置文件后,我们就可以“命令”容器去按我们的“菜谱”去实例化了:


ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");


有朋友可能会有疑问,这些所谓的 xml 文件,spring 是如何加载的呢,这个问题其实就涉及到 spring 的另一个主题了 spring resource 抽象,这是一个全新的主题,在后续的文章中继续给大家分享。


当我们让 spring IOC 容器帮我们初始化完了 bean 实例,如何去使用它们呢,很简单,只需要:


// 取回配置的实例PetStoreService service = context.getBean("petStore", PetStoreService.class);
// 使用配置的实例List<String> userList = service.getUsernameList();
复制代码


然而大多数情况下,在我们的应用开发中,都使用注解方式(@Autowired)增加了取实例的透明性。

spring bean 概览

我们所定义的静态配置元数据对象,实际上在程序运行时,都会以 BeanDefinition 对象的形式存在。


通常意义上,BeanDefinition 都包含如下 4 个部分的关键信息:


  • 包限定类名(A package-qualified class name)

  • bean 的行为属性定义(Bean behavioral configuration elements):如 scope,lifecycle 等

  • bean 的引用名(References to other beans that are needed for the bean to do its work)

  • 其他的配置信息(Other configuration settings to set in the newly created object ):如数据库连接池的最大连接数等


通常,有 3 种实例化 bean 的方式可供我们选择:

通过构造器实例化
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
复制代码
通过静态工厂方法实例化
<bean id="clientService"    class="examples.ClientService"    factory-method="createInstance"/>
复制代码


public class ClientService {    private static ClientService clientService = new ClientService();    private ClientService() {}
public static ClientService createInstance() { return clientService; }}
复制代码
通过实例化工厂方法
<!-- the factory bean, which contains a method called createInstance() --><bean id="serviceLocator" class="examples.DefaultServiceLocator">    <!-- inject any dependencies required by this locator bean --></bean>
<!-- the bean to be created via the factory bean --><bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
复制代码


public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() { return clientService; }}
复制代码


第三种方法的 FactoryBean 通常用于定制复杂 bean 对象,这个我们专门叙述


定义好 bean 之后,我们可以抛出以下问题:


  • 我们的 bean 是如何被 spring IOC 容器加载的

  • 是在什么阶段加载的


依赖注入(DI)有两种主要的方式:


  • 构造器注入方式(Constructor-based Dependency Injection)

  • setter 方法注入方式(Setter-based Dependency Injection)


The container then injects those dependencies when it creates the bean

bean 的依赖是在 spring 容器 创建 bean 的阶段被注入进去的。

同学们可以想一想如何解决循环依赖的问题,这个也是面试的必考点。

构造器注入方式
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) { // ... }}
复制代码


<beans>    <bean id="beanOne" class="x.y.ThingOne">        <constructor-arg ref="beanTwo"/>        <constructor-arg ref="beanThree"/>    </bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/></beans>
复制代码
setter 方法注入方式
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; }
// business logic that actually uses the injected MovieFinder is omitted...}
复制代码


TIPS : 依赖反转 IOC 的含义:

Code is cleaner with the DI principle, and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies and does not know the location or class of the dependencies. As a result, your classes become easier to test, particularly when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.

在依赖注入模式下的代码风格是非常简洁的,在为对象注入依赖的时候更关注解耦的方面。事实上,一个对象不会感知到它的依赖存在,也不知道依赖的位置和依赖的类(作者:实际上都是由 spring 容器 来统一关系,相当于中介功能)。结果就是,你的类可以更方便的测试。

容器扩展点

TO BE CONTINUE..

用户头像

还未添加个人签名 2018.11.08 加入

还未添加个人简介

评论

发布
暂无评论
面试被问spring ioc,这样说让面试官眼前一亮(1)