【SpringFramework】Spring IoC-基于XML的实现

简介: 本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。

一、初识IoC

IoC (Inversion of Control),即控制反转,是一种设计思想,是一个重要的面向对象编程的法则,能够知道开发者设计出松耦合、可扩展的程序。

在Spring中,通过IoC容器来管理所有Java对象的实例化和初始化,控制对象间的依赖关系。
由IoC容器管理的Java对象成为Spring Bean,它与new出来的对象本质上没有区别。
IoC容器是Spring框架的核心组件之一,贯穿了Spring从诞生到成长的整个过程。

控制反转,反转的是什么?(从程序员到第三方容器)

  • 将对象的创建权交出去,由第三方容器负责。
  • 将对象与对象间关系的维护权交出去,由第三方容器负责。

DI(Dependency Injection),即依赖注入,是控制反转的一种实现。

二、初识DI

依赖注入是控制反转设计思想的一种实现。在Spring创建对象的过程中,将对象的依赖属性通过配置传递给对象(注入依赖)。

依赖注入常见的2中实现方式:

  1. setter注入
  2. 构造器注入

IoC容器:用于存放和管理所有的Spring Bean。
DI:用于管理Spring Bean之间的依赖关系。

IoC容器在Spring中实现了两种方式:

  1. BeanFactory:IoC容器的基本实现,是Spring内部使用的接口,面向Spring本身,不提供给开发者使用。
  2. ApplicationContext:BeanFactory的子接口,在BeanFactory的基础上添加了更多高级特性,面向开发者,几乎所有的Spring应用都使用ApplicationContext。

ApplicationContext接口的实现类:

  1. ClassPathXmlApplicationContext:从类路径加载配置文件,创建IoC容器。
  2. FileSystemXmlApplicationContext:从文件系统加载配置文件,创建IoC容器。
  3. AnnotationConfigApplicationContext:从注解创建IoC容器。
  4. WebApplicationContext:在Web应用中创建IoC容器。
  5. ConfigurableApplicationContext:ApplicationContext的子接口,包含refresh()和close()等方法,让其具有启动、关闭和刷新上下文的能力。

三、基于XML管理Bean

引入依赖

<dependencies>
    <!--spring基础依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.39</version>
    </dependency>
    <!--junit5-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.8.2</version>
        <scope>test</scope>
    </dependency>
    <!--log4j2-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.23.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.23.1</version>
    </dependency>
</dependencies>

(1)获取Bean

正如在Aspringhelloworld中,我们使用了三种方式获取了bean,
但是在xml中如果配置了两个class属性一样id不同的bean,通过getBean(Class)方法获取bean时,会抛出异常,因为此时获取的bean是ambiguous,即不唯一的。

接口也可以作为getBean(Class)参数获取bean,但是要注意该接口只能有一个实现类。

根据类型获取bean,在满足唯一性的前提下,只要[对象 instanceof 指定类型类或接口] 返回为true,就可以匹配。

(2)依赖注入——setter注入

<bean id="student" class="com.sheeprunner.spring.b.two.Student">
    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <!--DI-->
    <property name="id" value="1" />
    <property name="name" value="张飞" />
    <property name="age" value="31" />
    <property name="sex" value="男" />
</bean>

(3)依赖注入——构造器注入

<bean id="student3" class="com.sheeprunner.spring.b.three.Student">
    <!--这种写法一定要按照构造器参数的顺序来写-->
<!--        <constructor-arg value="2" />-->
<!--        <constructor-arg value="李四" />-->
<!--        <constructor-arg value="23" />-->
<!--        <constructor-arg value="女" />-->
    <!--constructor-arg标签还有两个属性可以进一步描述构造器参数:
            - index属性:指定参数所在位置的索引(从0开始)
            - name属性:指定参数名
         index和name属性二选一即可
    -->
    <constructor-arg index="0" name="id" value="2" />
    <constructor-arg index="1" name="name" value="关羽" />
    <constructor-arg index="2" name="age" value="33" />
    <constructor-arg index="3" name="sex" value="男" />
</bean>

(4)特殊值注入

<bean id="student4" class="com.sheeprunner.spring.b.three.Student">
    <!--字面量赋值-->
    <property name="id" value="3" />
    <!--null值-->
    <property name="age">
        <null/>
    </property>
    <!--xml实体字符-->
    <property name="sex" value="a&lt;b" />
    <!--CDATA节-->
    <property name="name">
        <value><![CDATA[<34]]></value>
    </property>
</bean>

(5)引用类型注入(对象类型赋值)

<!--five-1:外部bean引用-->
<bean id="clazz5" class="com.sheeprunner.spring.b.five.Clazz">
    <property name="clazzId" value="11" />
    <property name="clazzName" value="一年级一班" />
</bean>
<bean id="student5" class="com.sheeprunner.spring.b.five.Student">
    <constructor-arg value="1"/>
    <constructor-arg value="孙尚香"/>
    <constructor-arg value="18"/>
    <constructor-arg value="女"/>
    <property name="clazz" ref="clazz5"/>
</bean>
<!--five-2:内部bean引用-->
<bean id="student5_2" class="com.sheeprunner.spring.b.five.Student">
    <property name="id" value="2"/>
    <property name="name" value="刘备"/>
    <property name="age" value="35"/>
    <property name="sex" value="男"/>
    <property name="clazz">
        <bean id="clazzInner" class="com.sheeprunner.spring.b.five.Clazz">
            <property name="clazzName" value="六年级三班" />
            <property name="clazzId" value="63"/>
        </bean>
    </property>
</bean>
<!--five-3:级联属性赋值-->
<bean id="student5_3" class="com.sheeprunner.spring.b.five.Student">
    <property name="id" value="3"/>
    <property name="name" value="刘禅"/>
    <property name="age" value="16"/>
    <property name="sex" value="男"/>
    <property name="clazz" ref="clazz5"/>
    <property name="clazz.clazzId" value="-1"/>
    <property name="clazz.clazzName" value="小班一班"/>
</bean>

(6)数组赋值

<bean id="clazz6" class="com.sheeprunner.spring.b.six.Clazz">
        <property name="clazzId" value="-1"/>
        <property name="clazzName" value="小一班" />
    </bean>
    <bean id="student6" class="com.sheeprunner.spring.b.six.Student">
        <property name="id" value="3"/>
        <property name="name" value="曹植"/>
        <property name="age" value="16"/>
        <property name="sex" value="男"/>
        <property name="clazz" ref="clazz6"/>
        <property name="hobbies">
            <array>
                <value>打游戏</value>
                <value>看书</value>
                <value>看电视电影</value>
                <value>听音乐</value>
            </array>
        </property>
    </bean>

(7)集合赋值

<!--
    死循环
    <bean id="student7_1" class="com.sheeprunner.spring.b.seven.Student">
        <property name="id" value="1"/>
        <property name="name" value="张郃"/>
        <property name="age" value="18"/>
        <property name="sex" value="男"/>
        <property name="clazz" ref="clazz7"/>
        <property name="hobbies">
            <array>
                <value>读书</value>
                <value>写字</value>
                <value>练武</value>
            </array>
        </property>
    </bean>
    <bean id="student7_2" class="com.sheeprunner.spring.b.seven.Student">
        <property name="id" value="2"/>
        <property name="name" value="关平"/>
        <property name="age" value="18"/>
        <property name="sex" value="男"/>
        <property name="clazz" ref="clazz7"/>
        <property name="hobbies">
            <array>
                <value>读书</value>
                <value>写字</value>
                <value>练武</value>
            </array>
        </property>
    </bean>-->
    <!--list-->
    <bean id="clazz7" class="com.sheeprunner.spring.b.seven.Clazz" >
        <property name="clazzId" value="-1"/>
        <property name="clazzName" value="小一班"/>
        <property name="studentList">
            <list>
                <ref bean="student5"/>
                <ref bean="student5_2"/>
                <ref bean="student5_3"/>
            </list>
        </property>
    </bean>
    <bean id="teacher7_1" class="com.sheeprunner.spring.b.seven.Teacher">
        <property name="teacherId" value="101"/>
        <property name="teacherName" value="苍井空"/>
    </bean>
    <bean id="teacher7_2" class="com.sheeprunner.spring.b.seven.Teacher">
        <property name="teacherId" value="102"/>
        <property name="teacherName" value="小泽玛利亚"/>
    </bean>
    <bean id="student7_1" class="com.sheeprunner.spring.b.seven.Student">
        <property name="id" value="1"/>
        <property name="name" value="张郃"/>
        <property name="age" value="18"/>
        <property name="sex" value="男"/>
        <property name="clazz" ref="clazz7"/>
        <property name="hobbies">
            <array>
                <value>读书</value>
                <value>写字</value>
                <value>练武</value>
            </array>
        </property>
        <property name="teacherMap">
            <map>
                <entry>
                    <key>
                        <value>101</value>
                    </key>
                    <ref bean="teacher7_1"/>
                </entry>
<!--                <entry key="102" value-ref="teacher7_2" />-->
                <entry>
                    <key>
                        <value>102</value>
                    </key>
                    <ref bean="teacher7_2"/>
                </entry>
            </map>
        </property>
    </bean>
    <!--引用集合类型  需要先引入util命名空间-->
        xmlns:util="http://www.springframework.org/schema/util"
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
    <!--list集合类型的bean-->
    <util:list id="students_util">
        <ref bean="student5"/>
        <ref bean="student5_2"/>
        <ref bean="student5_3"/>
    </util:list>
    <bean id="clazz_util" class="com.sheeprunner.spring.b.seven.Clazz">
        <property name="clazzId" value="13" />
        <property name="clazzName" value="一年级三班" />
        <property name="studentList" ref="students_util" />
    </bean>
    <!--map集合类型的bean-->
    <util:map id="teachers_util">
        <entry key="101" value-ref="teacher7_1" />
        <entry key="102" value-ref="teacher7_2" />
    </util:map>
    <bean id="student_util" class="com.sheeprunner.spring.b.seven.Student">
        <property name="id" value="18"/>
        <property name="name" value="孔明"/>
        <property name="age" value="56"/>
        <property name="sex" value="男神" />
        <property name="clazz" ref="clazz_util"/>
        <property name="hobbies">
            <array>
                <value>兵法</value>
                <value>奇谋</value>
                <value>百科</value>
            </array>
        </property>
        <property name="teacherMap" ref="teachers_util" />
    </bean>

(8)p命名空间

<!--引入p命名空间-->
 xmlns:p="http://www.springframework.org/schema/p"
<!--
    util和p仅做一下了解
    xml配置注入备案方式需要熟悉其操作及原理
    后面上面这些配置应该都很少会用,因为这些配置都是可以通过注解来配置的,而注解更加方便
-->
<bean id="student8" class="com.sheeprunner.spring.b.seven.Student"
    p:id="19" p:name="庞统" p:age="54" p:sex="男" p:hobbies="" p:clazz-ref="clazz_util" p:teacherMap-ref="teachers_util"/>

(9)引入外部配置文件

  • 数据源配置
  1. 引入依赖
<!--mysql连接驱动、druid数据源-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.30</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.15</version>
</dependency>
  1. spring配置文件
<!--引入context命名空间-->
        xmlns:context="http://www.springframework.org/schema/context"
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
    <!--引入外部资源文件-->
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!--将连接参数注入DruidDataSource的bean-->
    <bean id="druidDateSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--
            1. 注意引入资源路径和文件名,属性名不要写错
            2. 创建druid数据源的时候,driverClassName不是必须的,内部会推断出来
            3. ${}中的属性一定要与jdbc.properties文件中一致
        -->
<!--        <property name="driverClassName" value="${driver}" />-->
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
    </bean>

(10)bean的作用域

在Spring的xml配置文件中,bean标签的scope属性用于配置bean的作用域范围。

  1. singleton:单例模式,默认值,在Spring IoC容器中,只会在初始化时创建一个bean实例,所有对bean的引用都指向同一个对象。
  2. prototype:多例模式,每次对bean的请求都会创建一个新的bean实例,每次对bean的引用都指向一个新对象。

当在WebApplicationContext环境下还会有另外几个作用域(但不常用)

  1. request:每次HTTP请求都会创建一个新的bean实例,该实例仅在当前HTTP请求中有效。
  2. session:每次HTTP会话都会创建一个新的bean实例,该实例仅在当前HTTP会话中有效。
<!--scope属性不配置,默认singleton-->
    <bean id="user10" class="com.sheeprunner.spring.b.ten.User" scope="singleton">
        <property name="id" value="1"/>
        <property name="username" value="曹操"/>
        <property name="password" value="54321"/>
        <property name="age" value="66"/>
    </bean>
    <bean id="user10_2" class="com.sheeprunner.spring.b.ten.User" scope="prototype">
        <property name="id" value="2"/>
        <property name="username" value="孙权"/>
        <property name="password" value="12345"/>
        <property name="age" value="36"/>
    </bean>

(11)bean的生命周期

  1. 具体的生命周期过程
  1. bean对象创建(调用无参构造器)
  2. 为bean对象设置属性
  3. bean的后置处理器(初始化之前)
  4. bean对象初始化(需在配置bean时指定初始化方法)
  5. bean的后置处理器(初始化之后)
  6. bean对象就绪可以使用
  7. bean对象销毁(需在配置bean时指定销毁方法)
  8. IOC容器关闭
<bean id="user11" class="com.sheeprunner.spring.b.eleven.User" scope="singleton"
          init-method="initMethod" destroy-method="destroyMethod" >
        <property name="id" value="3"/>
        <property name="username" value="刘备"/>
        <property name="password" value="11111"/>
        <property name="age" value="40"/>
    </bean>
    <!-- bean的后置处理器要放入IOC容器才能生效 -->
    <bean id="myBeanProcessor" class="com.sheeprunner.spring.b.eleven.MyBeanProcessor"/>

为什么scope为prototype时不走destroy-method?

在Spring框架中,scope属性定义了Bean的作用域。当一个Bean被定义为prototype作用域时,意味着每次请求该Bean时都会创建一个新的实例。因此,对于prototype作用域的Bean,Spring容器不会管理其生命周期的结束阶段,具体原因如下:

  • 生命周期管理
  • singleton作用域的Bean由Spring容器管理其完整的生命周期,包括初始化和销毁。
  • prototype作用域的Bean由Spring容器创建,但一旦创建完毕并返回给客户端,Spring容器就不再管理该Bean的生命周期。
  • 销毁方法调用
  • 对于singleton作用域的Bean,Spring容器会在应用关闭时调用其destroy-method或实现DisposableBean接口的destroy方法。
  • 对于prototype作用域的Bean,Spring容器不会调用其destroy-methodDisposableBean接口的destroy方法,因为这些Bean的生命周期由客户端代码管理。
  • 内存管理
  • prototype作用域的Bean通常由客户端代码负责释放资源,例如关闭数据库连接、释放文件句柄等。
  • 如果Spring容器尝试管理prototype作用域Bean的销毁过程,可能会导致资源管理不一致或内存泄漏。

(12)FactoryBean

FactoryBean是Spring提供的一种整合第三方框架的常用机制。
和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。
通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

FactoryBean 是 Spring 框架中的一个特殊接口,用于自定义 Bean 的创建逻辑。它允许你在 Spring 容器中以更灵活的方式创建复杂的对象。以下是 FactoryBean 的主要作用和特点:

主要作用

  1. 自定义对象创建
  • FactoryBean 可以用来创建复杂的对象,特别是那些不能通过简单的构造函数或工厂方法创建的对象。
  • 例如,创建一个需要多个步骤初始化的对象,或者需要根据某些条件动态创建不同类型的对象。
  1. 控制对象的生命周期
  • FactoryBean 可以在对象创建前后执行一些额外的操作,如初始化资源、设置属性等。
  • 这使得你可以更精细地控制对象的生命周期。
  1. 集成第三方库
  • 当你需要在 Spring 应用中集成第三方库时,FactoryBean 可以帮助你将这些库的对象无缝集成到 Spring 容器中。
  • 例如,创建一个数据库连接池对象,或者初始化一个复杂的第三方服务客户端。

主要方法

FactoryBean 接口定义了以下几个主要方法:

  1. Object getObject()
  • 返回由 FactoryBean 创建的对象实例。
  • 这个方法是 FactoryBean 的核心,用于实际创建对象。
  1. Class<?> getObjectType()
  • 返回 getObject() 方法返回的对象的类型。
  • 如果对象类型在运行时才能确定,可以返回 null
  1. boolean isSingleton()
  • 指示 FactoryBean 创建的对象是否是单例的。
  • 返回 true 表示创建的对象是单例的,返回 false 表示每次请求都会创建一个新的对象。

(13)自动装配(基于xml)

自动装配:根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值

<!--byType是根据类型去自动匹配,如果只有一个bean配置,不配置id也可以-->
<!--    <bean class="com.sheeprunner.spring.b.thirteen.dao.UserDaoImpl" />-->
<!--    <bean class="com.sheeprunner.spring.b.thirteen.service.UserServiceImpl" autowire="byType"/>-->
<!--    <bean class="com.sheeprunner.spring.b.thirteen.controller.UserController" autowire="byType" />-->
<!--byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的bean进行赋值-->
<bean id="userDao1" class="com.sheeprunner.spring.b.thirteen.dao.UserDaoImpl" />
<bean id="userService1" class="com.sheeprunner.spring.b.thirteen.service.UserServiceImpl" autowire="byName"/>
<bean id="userController1" class="com.sheeprunner.spring.b.thirteen.controller.UserController" autowire="byName" />
public class UserController {
    private UserService userService1;
    public void addNewUser()
    {
        userService1.addUser();
    }
    public void setUserService1(UserService userService1) {
        this.userService1 = userService1;
    }
}
public class UserServiceImpl implements UserService {
    private UserDao userDao1;
    @Override
    public void addUser() {
        userDao1.save();
    }
    public void setUserDao1(UserDao userDao1) {
        this.userDao1 = userDao1;
    }
}
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("成功添加了一个用户!");
    }
}
目录
相关文章
|
11天前
|
XML Java 数据格式
【SpringFramework】Spring初体验
Spring是一款由Rod Johnson创立的主流Java EE轻量级开源框架,它旨在简化Java企业级项目开发,提供一站式轻量级解决方案,取代复杂的EJB。Spring的核心功能包括IoC(控制反转)和AOP(面向切面编程),并支持非侵入式开发、组件化和容器管理。这篇文章简要描述相关知识点和初始springframework。
100 60
【SpringFramework】Spring初体验
|
1天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
33 21
|
7天前
|
XML Java 数据格式
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
本文介绍了在使用Spring框架时,如何通过创建`applicationContext.xml`配置文件来管理对象。首先,在resources目录下新建XML配置文件,并通过IDEA自动生成部分配置。为完善配置,特别是添加AOP支持,可以通过IDEA的Live Templates功能自定义XML模板。具体步骤包括:连续按两次Shift搜索Live Templates,配置模板内容,输入特定前缀(如spring)并按Tab键即可快速生成完整的Spring配置文件。这样可以大大提高开发效率,减少重复工作。
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
|
7天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
6天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
12天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
49 6
|
27天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
46 2
|
3月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
248 2
|
7天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
14天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
63 14