【Spring IOC】Spring Bean生命周期、配置文件参数化、自定义类型转换器、后置处理Bean

对象的生命周期

什么是对象的⽣命周期?

  • ⼀个对象 创建、存活、消亡 的⼀个完整过程。

为什么要学习对象的⽣命周期?

  • Spring 负责对象的 创建、存活、销毁,了解⽣命周期,有利于我们使用好 Spring 为我们创建的对象。

⽣命周期的 3 个阶段:

  • 创建阶段 —> 初始化阶段 —> 销毁阶段

创建阶段

Spring 工厂何时创建对象?

  • scope="prototype":Spring 工厂在获取对象 ctx.getBean("xxx") 的同时,创建对象。
  • scope="singleton"Spring 工厂创建的同时,创建对象。
    • 通过配置 <bean lazy-init="true"/> 懒加载也可以实现工厂获取对象的同时,创建对象

初始化阶段

什么时候?

  • Spring 工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作。

初始化方法提供:由程序员根据需求,提供初始化方法,最终完成初始化操作。

初始化方法调用:Spring 工厂进行调用

提供初始化方法的两种方式:

  • InitializingBean接口:
public class Product implements InitializingBean {
	// 程序员根据需求实现的方法
	// 由 Spring工厂 调用,完成初始化操作
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Product.afterPropertiesSet");
    }
}
<bean id="product" class="com.hey.life.Product" />
  • 对象中提供一个普通的初始化方法,配置文件种配置 init-method
public class Product {
    public void myInit() {
        System.out.println("Product.myInit");
    }
}
<bean id="product" class="com.yusael.life.Product" init-method="myInit"/>

与 FactoryBean接口 类似,如果不使用接口就可以在配置文件中配置方法。


【注意】

如果⼀个对象既实现 InitializingBean 同时⼜提供的 普通的初始化方法,执行顺序?

  • 执行 InitializingBean执行 普通初始化方法。

注入⼀定发⽣在初始化操作的前面。正如接口的方法名那样 afterPropertiesSet,在属性设置之后

初始化操作到底是什么?

  • 资源的初始化:数据库、IO、网络、…

销毁阶段

销毁阶段,即:Spring 销毁对象前,会调用对象的销毁方法,完成销毁操作

Spring 什么时候销毁所创建的对象?

  • 在工厂关闭的时候,关闭之前会销毁工厂创建的对象。

销毁方法提供:程序员根据业务需求,定义销毁方法,完成销毁操作

销毁方法调用:Spring 工厂进行调用

开发流程与初始化操作一样,提供销毁方法的两种方式:

  • DisposableBean 接口:
public class Product implements DisposableBean {
    // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作
    @Override
    public void destroy() throws Exception {
        System.out.println("Product.destroy");
    }
}

对象中提供一个普通的销毁方法,配置文件种配置 destroy-method

public class Product {
	// 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作
    public void myDestory() {
        System.out.println("Product.myDestory");
    }
}
<bean id="product" class="com.yusael.life.Product" destroy-method="myDestory"/>

【注意】

  • 销毁方法的操作只适用于 scope="singleton",初始化操作则都适用。

销毁操作到底是什么?

  • 资源的释放:io.close()connection.close()、…

对象的生命周期总结

public class Product implements InitializingBean, DisposableBean {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("Product.setName");
        this.name = name;
    }

    Product() { // 创建
        System.out.println("Product.Product");
    }

    // 程序员根据需求实现的方法, 完成初始化操作
    public void myInit() {
        System.out.println("Product.myInit");
    }

    // 程序员根据需求实现的方法, 完成初始化操作
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Product.afterPropertiesSet");
    }

    public void myDestory() {
        System.out.println("Product.myDestory");
    }

    // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作
    @Override
    public void destroy() throws Exception {
        System.out.println("Product.destroy");
    }
}
<bean id="product" class="com.yusael.life.Product" init-method="myInit" destroy-method="myDestory">
	<property name="name" value="yusael"/>
</bean>

在这里插入图片描述

配置文件参数化

配置文件参数化:把 Spring 配置文件中需要经常修改的字符串信息,转移到⼀个更小的配置文件中。

Spring 的配置文件中是否存在需要经常修改的字符串?

  • 存在:例如与数据库连接相关的参数

在这里插入图片描述

  • 经常变化得字符串,在 Spring 的配置文件中直接修改不利于项目维护(修改)
  • 将之转移到⼀个小的配置文件(.properties)利于维护(修改)
  • 优点:利于 Spring 配置文件的维护(修改)

开发步骤

  • 提供⼀个小的配置文件(.properities)
    名字:没有要求
    放置位置:没有要求
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/spring?useSSL=false
jdbc.username = root
jdbc.password = 1234
  • 整合到 Spring 的配置文件中:
<!--Spring的配置文件与⼩配置文件进行整合-->
<!--resources 下的文件在整个程序编译完后会被放到 输出目录得 classes 目录下,
		也就是 classpath路径下,
		src.main.java 中的文件也会整合到这个目录之下-->
<context:property-placeholder location="classpath:db.properties"/>
  • 使用时,可以直接在 Spring 配置文件中通过 ${key} 获取小配置文件中对应的值:
<bean id="conn" class="com.yusael.factorybean.ConnectionFactoryBean">
	<property name="driverClassName" value="${jdbc.driverClassName}"/>
	<property name="url" value="${jdbc.url}"/>
	<property name="username" value="${jdbc.username}"/>
	<property name="password" value="${jdbc.password}"/>
</bean>

自定义类型转换器

类型转换器:Spring 通过 类型转换器 把 配置文件 中 字符串 类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入

在这里插入图片描述

自定义一个类型转换器

  • 对于一些常见的数据类型,Spring有自带的类型转换器,可以转换字符串
  • 但是对于一些特殊的数据类型,Spring 内部没有提供特定类型转换器
  • 而程序员在应用的过程中还需要使用,那么就需要程序员⾃⼰定义类型转换器。

实现步骤:

  1. 实现 Converter 接口
public class MyDateConverter implements Converter<String, Date> {
    /*
     convert方法作用: String ---> Date
     SimpleDateFormat sdf = new SimpleDateFormat();
     sdf.parset(String) ---> Date

     参数:
     source : 代表的是配置文件中, 日期字符串 <value>2020-10-11</value>
     return : 当把转换好的 Date 作为 convert 方法的返回值后,
             Spring ⾃动的为birthday属性进行注入(赋值)
    */
    @Override
    public Date convert(String source) {
        Date date = null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            date = sdf.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}
  1. 配置文件中,先创建 MyDateConverter 对象,再注册类型转换器
<!--创建 MyDateConverter 对象-->
<bean id="myDateConverter" class="com.yusael.converter.MyDateConverter"/>
<!--用于注册类型转换器-->
<!--这里的 id 和 class 是固定的,通过注入将转换器对象注入,即通过这个工厂类来生产转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <ref bean="myDateConverter"/>
        </set>
    </property>
</bean>
<!-- 测试对象,注入一个日期属性 -->
<bean id="good" class="com.yusael.converter.Good">
    <property name="name" value="zhenyu"/>
    <property name="birthday" value="2012-12-12"/>
</bean>

这样就可以将配置文件中的字符串信息转换成日期类型的数据

细节分析

在上面的例子中,转换日期所使用的模板字符串是直接写在代码中的,这样耦合性很大,我们也可以将模板字符串再配置文件中进行配置,通过注入的方式,由配置文件赋值

public class MyDateConverter implements Converter<String, Date> {
	// 提取出来,作为其中的一个属性
    private String pattern;

    @Override
    public Date convert(String source) {
        Date date = null;
        try {
        	// 卸载代码中耦合较大
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            date = sdf.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    public String getPattern() {
        return pattern;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }

}

<!-- 配置文件完成对日期格式的赋值 -->
<bean id="myDateConverter" class="com.yusael.converter.MyDateConverter">
    <property name="pattern" value="yyyy-MM-dd"/>
</bean>

Spring 框架其实内置了日期类型的转换器:只是日期格式必须是 2020/05/01,但是我们平时并不会这么写,所以还是要自己实现一个

<bean id="good" class="com.yusael.converter.Good">
	<property name="name" value="zhenyu"/>
	<property name="birthday" value="2012/12/12"/>
</bean>

后置处理Bean

后置处理BeanBeanPostProcessor 作用:对 Spring 工厂所创建的对象,进行再加工。(AOP 的底层实现)

BeanPostProcessor 实际是个接口,其中有两个方法需要实现:

  1. Spring 创建完对象,并进行注入后,可以运行 Before ⽅法进行加工;
// 通过方法的参数获得 Spring 创建好的对象,最终通过返回值交给 Spring 框架。
public Object postProcessBeforeInitialization(Object bean, String beanName) {
    return bean;
}
  1. Spring 执行完对象的初始化操作后,可以运行 After ⽅法进行加工;
// 通过方法的参数获得 Spring 创建好的对象,最终通过返回值交给 Spring 框架。
public Object postProcessAfterInitialization(Object bean, String beanName) {
    return bean;
}

执行顺序:
如果有属性需要注入,那就要在调用构造方法之后,先执行注入过后再去处理
在这里插入图片描述

尽管有两个可实现的方法,但是在实际应用中:很少处理 Spring 的初始化操作,没有必要区分 Before,After。

  • 只需要实现其中一个,建议是 After 方法即可。

开发步骤

  1. 类 实现 BeanPostProcessor 接口
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Category category = (Category) bean;
        category.setName("yusael");
        return category;
    }
}
  1. Spring 配置文件中进行配置
<bean id="myBeanPostProcessor" class="com.yusael.beanpost.MyBeanPostProcessor"/>

【注意】
BeanPostProcessor 会对 Spring 工厂 创建的所有对象进行加工。

  • 如果工厂创建了多个不同的对象,要注意区别传入的对象
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	// 先判断是不是需要加工处理的 Bean
    if (bean instanceof  Category) {
        Category category = (Category) bean;
        category.setName("yusael");
        return category;
    }
    return bean;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值