Spring 6(二)【IOC(2)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Spring 6(二)【IOC

Spring 6(二)【IOC(1)https://developer.aliyun.com/article/1532215

1.5、特殊类型属性的注入

我们的对象类型还可能是其它类型,比如数组、集合类型。这些特殊类型属性的注入无非就是配置文件的写法变化罢了。

1.5.1、数组类型

我们给上面的 Student 类添加一个 Int[] 类型的 hobbies 属性,记得添加 setter (没有 setter 方法就无法注入)和 getter 方法。

<bean id="student6" class="com.lyh.study.bean.Student">
    <property name="id" value="1006"/>
    <property name="name" value="狄如燕"/>
    <property name="age" value="19"/>
    <property name="sex" value="女"/>
    <property name="address" ref="address1"/>
    <property name="hobbies">
        <array>
            <value>唱歌</value>
            <value>舞剑</value>
        </array>
    </property>
</bean>

1.5.2、集合类型

(1)List 类型

我们给上面的 Student 类添加一个 List 类型的 students 属性并添加 setter 和 getter 方法。

<bean id="student7" class="com.lyh.study.bean.Student">
        <property name="id" value="1006"/>
        <property name="name" value="狄如燕"/>
        <property name="age" value="19"/>
        <property name="sex" value="女"/>
        <property name="address" ref="address1"/>
        <property name="students">
            <list>
                <ref bean="student4"/>
                <ref bean="student5"/>
                <ref bean="student6"/>
            </list>
        </property>
    </bean>

注意:如果是 Set 集合,只需要将其中的list标签改为set标签即可。

(2)Map 类型

我们给上面的 Student 类添加一个 Map 类型的 studentMap 属性并添加 setter 和 getter 方法。

<bean id="student8" class="com.lyh.study.bean.Student">
        <property name="id" value="1006"/>
        <property name="name" value="狄如燕"/>
        <property name="age" value="19"/>
        <property name="sex" value="女"/>
        <property name="address" ref="address1"/>
        <property name="studentMap">
            <map>
                <entry>
                   <key>
                       <value>1001</value>
                   </key>
                    <!-- 如果value是基本类型,直接使用<value>标签即可 -->
                    <ref bean="student1"/>
                </entry>
                <entry>
                    <key>
                        <value>1002</value>
                    </key>
                    <ref bean="student2"/>
                </entry>
            </map>
        </property>
    </bean>

1.5.3、通过  标签实现集合类型属性的注入

要使用 util 标签就必须先引入它,在配置文件头部需要加入以下内容:

使用 util 标签实现 Map 集合类型注入:

    <bean id="student8" class="com.lyh.study.bean.Student">
        <property name="id" value="1008"/>
        <property name="name" value="狄如燕"/>
        <property name="age" value="19"/>
        <property name="sex" value="女"/>
        <property name="address" ref="address1"/>
        <property name="studentMap" ref="map"/>
    </bean>
 
    <util:map id="map">
        <entry>
            <key>
                <value>1001</value>
            </key>
            <ref bean="student1"/>
        </entry>
        <entry>
            <key>
                <value>1002</value>
            </key>
            <ref bean="student2"/>
        </entry>
    </util:map>

可以看到,效果和上面直接通过  标签注入是一样的,这个  标签就相当于一个 Map 对象。

1.6、P 命名空间注入

命名空间是啥东西,其实就是我们 Spring 配置文件的头部那些带有 xlms: xxx 的部分(比如 xmls:util 就是 util 命名空间),所以 P 命名空间注入其实就是加这么一行:

有啥用呢?其实也就是用来简化配置文件的代码量:

<bean id="student10" class="com.lyh.study.bean.Student" p:id="1010" p:name="武则天" p:age="58" p:address-ref="address1" p:studentMap-ref="map"/>

可以看到,引入 p命名空间后,我们的属性直接变成了 bean 标签的一个属性值 p:属性名,引用类型的属性也可以通过 p:属性名-ref 的方式来引用。

1.7、引入外部属性文件

我们经常需要把一些常用但是又经常需要修改的类写入到 Spring 配置文件(比如 MySQL 工具类),而这些 Bean 的属性值的修改就需要通过外部属性文件来进行注入了,这样更加灵活,方便维护。

(1)需求:

       把一些特定的固定值,放到一个特定的外部文件中去(比如 db.properties),在 Spring 配置文件中进行引入,这样我们要进行修改时,只需要修改外部文件,而不需要修改代码。

(2)导入依赖:

 <!-- MySQL驱动 -->
<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>

(3)创建外部属性文件 db.properties

jdbc.user=root
jdbc.password=Yan1029.
jdbc.url=jdbc:mysql://localhost:3306/flink?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver

1.7.1、引入属性文件

(1)引入 context 名称空间:

(2)引入外部属性文件(db.properties)

<context:property-placeholder location="db.properties"/>

(3)配置连接池对应的 Bean

这个 Bean druid已经帮我们实现了,相当于一个工具类,我们都不用自己实现,只需要让 Spring 帮我管理即可。

<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="${jdbc.url}"/>
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="username" value="${jdbc.user}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

(4)测试:

    @Test
    public void testDataSource() throws SQLException {
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        DataSource druidDataSource = ac.getBean("druidDataSource", DataSource.class);
        Connection connection = druidDataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }

(5)运行结果:

com.mysql.cj.jdbc.ConnectionImpl@5b56b654

1.8、Bean 的作用域

在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围:

取值 含义 创建对象的时机
singleton(默认) 在IOC容器中,这个bean的对象始终为单实例 IOC容器初始化时
prototype 这个bean在IOC容器中有多个实例 获取bean时

也就是我上一节说的,单例对象和多例对象的区别。

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

取值 含义
request 在一个请求范围内有效
session 在一个会话范围内有效

我是应该用不上了。

这里说一下单例模式(singleton)和非单例模式(prototype)的区别:

  • 单例模式下,每次 getBean 都会返回同一个对象(在内存中的地址相同,可以用 == 进行测试)
  • 非单例模式下,每次 getBena 都会创建一个新的对象,尽管我们在配置文件中设置的它们的属性是一样的,但是它们指向的是不同的内存地址。

1.9、Bean 的生命周期

  1. bean对象创建(调用无参构造器)
  2. 给bean对象设置属性(调用我们自己写的 setter 方法)
  3. bean的后置处理器(初始化之前)
  4. bean对象初始化(需在配置bean时指定初始化方法)
  5. bean的后置处理器(初始化之后)
  6. bean对象就绪可以使用
  7. bean对象销毁(需在配置bean时指定销毁方法)
  8. IOC容器关闭

1.9.1、初始化和销毁方法

上面的 Bean 对象的初始化方法和销毁方法是我们自己实现的,然后通过给  标签添加属性来实现:

<!-- 使用init-method属性指定初始化方法 -->
<!-- 使用destroy-method属性指定销毁方法 -->
<bean class="com.lyh.study.bean.User" scope="prototype" init-method="initMethod" destroy-method="destroyMethod">
    <property name="id" value="1001"></property>
    <property name="name" value="朱重八"></property>
    <property name="age" value="18"></property>
    <property name="sex" value="男"></property>
</bean>

1.8.2、后置处理器

bean 的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现 BeanPostProcessor 接口,且配置到IOC容器中。

需要注意的是:bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行  

package com.lyh.study;
    
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
 
public class MyBeanProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 初始化之前的处理代码
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 初始化之后的处理代码
        return bean;
    }
}

在 IOC 容器内配置后置处理器(放进去就行了):

<!-- bean的后置处理器要放入IOC容器才能生效 -->
<bean id="myBeanProcessor" class="com.lyh.study.MyBeanProcessor"/>

1.10、FactoryBean

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

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

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
 
    @Nullable
    T getObject() throws Exception;
 
    @Nullable
    Class<?> getObjectType();
 
    default boolean isSingleton() {
        return true;
    }
}

(1)实现 FactoryBean 接口:

package com.lyh.study;
 
import com.lyh.study.bean.Student;
import org.springframework.beans.factory.FactoryBean;
 
public class StudentFactoryBean implements FactoryBean<Student> {
 
    @Override
    public Student getObject() throws Exception {
        return new Student();
    }
 
    @Override
    public Class<?> getObjectType() {
        return Student.class;
    }
}

(2)添加到 IOC 容器:

<bean id="studentFactoryBean" class="com.lyh.study.StudentFactoryBean"/>

(3)测试:

    @Test
    public void testBeanFactory(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        // 转换出来的不是 StudentBeanFactory 对象,而是 Student 对象
        Student studentBeanFactory = (Student) ac.getBean("studentFactoryBean");
        System.out.println(studentBeanFactory);
    }

(4)运行结果:

Student{id=null, name='null', age=null, sex='null'}

当 Spring 容器遇到一个实现了 FactoryBean 接口的 Bean 时,它不会直接实例化这个 Bean,而是会调用该 Bean 的 getObject() 方法来获取对象。这样,我们就可以在 getObject() 方法中编写自定义的对象创建逻辑,从而实现与第三方框架的整合。

Spring 6(二)【IOC(3)https://developer.aliyun.com/article/1532219

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
23天前
|
XML Java 数据格式
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
24 1
|
1月前
|
XML Java API
IoC 之 Spring 统一资源加载策略
IoC 之 Spring 统一资源加载策略
34 2
|
23天前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
26 0
|
19天前
|
XML Java 数据库连接
Spring6(二):IoC容器(2)
Spring6(二):IoC容器
20 2
|
19天前
|
XML Java 数据格式
Spring6(二):IoC容器(3)
Spring6(二):IoC容器(3)
16 1
|
19天前
|
XML Java 数据格式
Spring6(二):IoC容器(1)
Spring6(二):IoC容器(1)
31 1
|
1月前
|
存储 Java 测试技术
Java Spring IoC&DI :探索Java Spring中控制反转和依赖注入的威力,增强灵活性和可维护性
Java Spring IoC&DI :探索Java Spring中控制反转和依赖注入的威力,增强灵活性和可维护性
18 1
|
1月前
|
XML Java API
IoC 之 Spring 统一资源加载策略【Spring源码】
IoC 之 Spring 统一资源加载策略【Spring源码】
28 2
|
1月前
|
存储 Java Spring
Spring IOC 源码分析之深入理解 IOC
Spring IOC 源码分析之深入理解 IOC
45 2
|
1月前
|
XML Java 数据格式
Spring--两大核心之一--IOC
Spring--两大核心之一--IOC