[Spring]一文明白IOC容器和思想

Posted Philosophy7

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Spring]一文明白IOC容器和思想相关的知识,希望对你有一定的参考价值。

✅作者简介:大家好,我是Philosophy7?让我们一起共同进步吧!🏆
📃个人主页:Philosophy7的csdn博客
🔥系列专栏: 数据结构与算法
👑哲学语录: 承认自己的无知,乃是开启智慧的大门
💖如果觉得博主的文章还不错的话,请点赞👍+收藏⭐️+留言📝支持一下博>主哦🤞


文章目录

一、Spring简介

[Spring | Home](https://spring.io/)官网地址,由于Spring是国外网站,可能访问速度会有点慢。

Spring使创建Java企业应用程序变得容易。它提供了在企业环境中使用Java语言所需的一切,支持Groovy和Kotlin作为JVM上的替代语言,并根据应用程序的需要灵活地创建多种体系结构。从Spring Framework 5.1开始,Spring需要JDK 8+ (Java SE 8+),并提供对JDK 11 LTS的开箱即用支持。Java SE 8 update 60被建议作为Java 8的最低补丁版本,但通常建议使用最新的补丁版本。

Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。

Spring 框架是一个开源的 Java 平台,它最初是由 Rod Johnson 编写的,并且于 2003 年 6 月首 次在 Apache 2.0 许可下发布。 Spring 是轻量级的框架,其基础版本只有 2 MB 左右的大小。

Spring 框架的核心特性是可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应 用程序是需要扩展的。 Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO 编程模型来促进良好的编程实践。

1.1、SpringFramework创始人

Rod Johnson,Spring框架的创始人,同时也是SpringSource的联合创始人。Spring是面向切面编程(AOP)和控制反转(IoC)的容器框架。

1.2、Spring全家桶

[Spring | Projects](https://spring.io/projects)

在该链接可以看出Spring的生态包含哪些,在这我列举我们常用的Spring Framework、SpringBoot、SpringCloud、SpringSecurity、SpringAMQP等

1.3、SpringFramework

以Spring为基础框架,可以把Spring看作成地基,基本上所有的Spring项目都是以SpringFramework为基础的。

1) Feauture:

  • Core technologies: dependency injection, events, resources, i18n, validation, data binding, type conversion, SpEL, AOP.
  • Testing: mock objects, TestContext framework, Spring MVC Test, WebTestClient
  • Data Access: transactions, DAO support, JDBC, ORM, Marshalling XML.
  • Spring MVC and Spring WebFlux web frameworks.
  • Integration(集成): remoting, JMS, JCA, JMX, email, tasks, scheduling, cache.
  • Languages: Kotlin, Groovy, dynamic languages.

核心技术: 依赖注入(Dependency injection 简称DI)、事件、数据绑定、SPEL表达式、AOP面向切面等

测试: SpringMVC测试、WebTestClient等

数据访问: 事务、支持DAO、JDBC、ORM、XML

2) SpringFramework五大模块

功能模块功能介绍
Core Container核心容器,在 Spring 环境下使用任何功能都必须基于 IOC 容器。
AOP&AspectJs面向切面编程
Testings提供了对 junit 或 TestNG 测试框架的整合。
Data Access/Integration提供了对数据访问/集成的功能。
Spring MVC提供了面向Web应用程序的集成功能

二、IOC

Inversion of Control,反转控制。那么什么是控制反转呢,就是将对象的创建权改变了,创建对象的任务交给IOC容器进行管理。

//原始创建方式 --- 采用new的方式来创建对象
Object obj = new Object();

前言:

Container Overview

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans. The container gets its instructions on what objects to instantiate, configure, and assemble by reading configuration metadata. The configuration metadata is represented in XML, Java annotations, or Java code. It lets you express the objects that compose your application and the rich interdependencies between those objects.

Several implementations of the ApplicationContext interface are supplied with Spring. In stand-alone applications, it is common to create an instance of ClassPathXmlApplicationContext or FileSystemXmlApplicationContext. While XML has been the traditional format for defining configuration metadata, you can instruct the container to use Java annotations or code as the metadata format by providing a small amount of XML configuration to declaratively enable support for these additional metadata formats.

In most application scenarios, explicit user code is not required to instantiate one or more instances of a Spring IoC container. For example, in a web application scenario, a simple eight (or so) lines of boilerplate web descriptor XML in the web.xml file of the application typically suffices (see Convenient ApplicationContext Instantiation for Web Applications). If you use the Spring Tools for Eclipse (an Eclipse-powered development environment), you can easily create this boilerplate configuration with a few mouse clicks or keystrokes.

Bean Overview

A Spring IoC container manages one or more beans. These beans are created with the configuration metadata that you supply to the container (for example, in the form of XML <bean/> definitions).

Within the container itself, these bean definitions are represented as BeanDefinition objects, which contain (among other information) the following metadata:

  • A package-qualified class name: typically, the actual implementation class of the bean being defined.
  • Bean behavioral configuration elements, which state how the bean should behave in the container (scope, lifecycle callbacks, and so forth).
  • References to other beans that are needed for the bean to do its work. These references are also called collaborators or dependencies.
  • Other configuration settings to set in the newly created object — for example, the size limit of the pool or the number of connections to use in a bean that manages a connection pool.

以上内容来源于官网,为避免翻译难免有失偏颇,可自行翻译

2.1、思想:

IOC容器的引入,让我们的开发变的快捷、方便、高效,且IOC容器能够解耦合降低类与类之间的依赖关系。

①获取资源的传统方式:

传统的方式是组件主动的从容器中获取所需要的资源,在这样的 模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效 率。

②反转控制方式获取资源:

只需要将资源放入到容器之中,容器如何创建对象开发人员不需要关心,只需要明白如何从容器中获取这个资源即可。

③DI

DI(Dependency Injection) 依赖注入

DI 是 IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器 的资源注入。相对于IOC而言,这种表述更直接。

所以结论是:IOC 就是一种反转控制的思想, 而 DI 是对 IOC 的一种具体实现

2.2、IOC容器的实现

Spring 的 IOC 容器就是 IOC 思想的一个落地的产品实现。IOC 容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建 IOC 容器。Spring 提供了 IOC 容器的两种实现方式:

  • BeanFactory
    • 这是 IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。
  • ApplicationContext
    • BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。

ApplicationContext的实现类

类型名简介
ClassPathXmlApplicationContext通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
FileSystemXmlApplicationContext通过文件系统路径读取 XML 格式的配置文件创建 IOC 容 器对象
ConfigurableApplicationContextApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、 关闭和刷新上下文的能力。
WebApplicationContext专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对 象,并将对象引入存入 ServletContext 域中。

2.3、基于XML管理Bean

实操一:入门案例HelloWorld

1) 创建Maven项目

maven项目管理工具,来对项目进行管理

2) 改pom.xml

 <dependencies>
        <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
</dependencies>

3) 创建HelloWorld类

public class HelloWorld 
    //编写方法
    public void sayHello()
        System.out.println("Hello World");
    

4) 创建配置文件

resource包下创建applicationContext.xml,通常都是以该文件名命名

<?xml version="1.0" encoding="UTF-8"?>
<!--Spring约束 不用记 直接编写-->
<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 http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--
    通过Bean标签将HelloWorld实例交给Spring IOC容器管理
        id: bean的唯一表示
        class: 设置bean对应的全类名
-->
    <bean id="helloWorld" class="com.philosophy.spring.bean.HelloWorld"></bean>
</beans>

5) 测试

@Test
public void test() 
     //通过xml文件的方式来管理Bean
     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

     //2.获取Bean对象 getBean可传递Bean的ID 或 对应的Class类对象
     HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");

     //3.调用方法
     helloWorld.sayHello();

执行流程

1.创建组件类 --> 2.将组件配置到配置文件中 3.创建IOC容器对象 读取配置文件中的Bean 4.获取IOC中容器的对象 5.完成特定功能

实操二: 获取Bean

①方式一: 根据Bean的ID获取:

由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。 上个实验中我们使用的就是这种方式

//根据Bean的唯一标识来获取
HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");

②方式二: 根据类型获取

HelloWorld helloWorld = context.getBean(HelloWorld.class);

③方式三:根据ID和类型获取

HelloWorld bean = context.getBean("helloWorld", HelloWorld.class);

④注意:

当根据类型获取Bean时,要求IOC容器中指定的类型Bean只能有一个

当IOC容器中一共配置了两个相同的类型时

<bean id="helloWorld" class="com.philosophy.spring.bean.HelloWorld"></bean>
<bean id="helloWorldTwo" class="com.philosophy.spring.bean.HelloWorld"></bean>

根据类型时获取Bean就会抛出异常

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.philosophy.spring.bean.HelloWorld' available: expected single matching bean but found 2: helloWorld,helloWorldTwo

实操三: 依赖注入setter方式

1) 创建实体类Person

package com.philosophy.spring.bean;

public class Person 
    private Integer id; //ID
    private String name; //name名称
    private Integer age; //名称

    public Integer getId() 
        return id;
    

    public void setId(Integer id) 
        this.id = id;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public Integer getAge() 
        return age;
    

    public void setAge(Integer age) 
        this.age = age;
    

    @Override
    public String toString() 
        return "Person" +
                "id=" + id +
                ", name='" + name + '\\'' +
                ", age=" + age +
                '';
    

2) 配置Bean并实现属性赋值

<bean id="person" class="com.philosophy.spring.bean.Person">
        <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
        <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
        <!-- value属性:指定属性值 -->
        <property name="id" value="1001"/>
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
</bean>

3) 测试

@Test
public void testPerson() 
    //1.创建IOC容器
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

    //2.获取对象
    Person person = context.getBean(Person.class);

    System.out.println(person);

实操四: 依赖注入构造器方式

1) 编写有参构造

public Person(Integer id,String name,Integer age) 
        this.id = id;
        this.name = name;
        this.age = age;

2)配置Bean

<bean id="personTwo" class="com.philosophy.spring.bean.Person">
    <!--对照构造器中的参数顺序 一一赋值-->
    <constructor-arg value="1002"/>
    <constructor-arg value="李四"/>
    <constructor-arg value="33"/>
</bean>

3)测试

@Test
public void testPerson02() 
    //1.创建IOC容器
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

    //2.获取对象
    Person person = (Person) context.getBean("personTwo");

    System.out.println(person);

实操五: 特殊值处理

①、什么是字面量?

//声明一个变量
int a = 10;

声明一个变量a,初始化值为10,此时a代表的是一个变量名称,当我们引用a时获取的是其值10

若a是一个带引号的'a',那么它就不是一个变量,它就是这个字母的本身,这就是字面量

<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<property name="name" value="张三"/>

②、null值

<!--为name属性 赋值为Null-->
<property name="name">
	<null />
</property>

<property name="name" value="null"></property> <!--等同于该操作-->

③CDATA

<property name="expression">
    <!-- 解决方案二:使用CDATA节 -->
    <!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
    <!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
    <!-- 所以CDATA节中写什么符号都随意 -->
    <value><![CDATA[a < b]]></value>
</property>

实操六: 为引用类型赋值(级联赋值)

1) 创建身份证类IdCard

package com.philosophy.spring.bean;

/**
 * 身份证类
 */
public class IdCard 
    private String id; //身份证号

    public IdCard(String id) 
        this.id = id;
    

    public IdCard() 
    

    public String getId() 
        return id;
    

    public void setId(String id) 
        this.id = id;
    

2) 修改Person类

添加以下代码:

private IdCard idCard; //身份证号

public IdCard getIdCard() 
        return idCard;


public void setIdCard(IdCard idCard) 
        this.idCard = idCard;


@Override
    public String toString() 
        return "Person" +
                "id=" + id +
                ", name='" + name + '\\'' +
                ", age=" + age +
                ", idCard=" + idCard +
                '';
    

3) 配置Bean

 	<bean id="idCard" class="com.philosophy.spring.bean.IdCard">
        <property name="id" value="10086"/>
    </bean>

    <bean id="personThree" class="com.philosophy.spring.bean.Person">
        <property name="id" value="1003"/>
        <property name="name" value="鸡哥"/>
        <property name="age" value="23"/>
        <!--ref属性: 引用IOC容器中某个Bean的id 将所对应的Bean为属性赋值-->
        <property name="idCard" ref="idCard"/>
    </bean>

4) 测试

@Test
    public void testPersonAndIdCard() 
        //1.创建IOC容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //2.获取对象
        Person person = (Person) context.getBean("personThree");

        System.out.println(person);
    

以上是关于[Spring]一文明白IOC容器和思想的主要内容,如果未能解决你的问题,请参考以下文章

Spring03---IOC

Spring 与 IoC

Spring框架—— IOC容器和Bean的配置

Spring:笔记整理——IOC容器

spring IOC

好莱坞原则—Spring的IOC容器