Spring工厂高级特性-对象的生命周期,配置文件参数化,自定义类型转换器,后置处理Bean

本文详细介绍了Spring框架中对象从创建到销毁的整个生命周期,包括创建、初始化和销毁三个阶段的具体操作,以及如何通过配置实现对象的自定义初始化和销毁。

1. 对象的生命周期

1.1 什么是对象的生命周期

生命周期指的是一个对象创建、存活、消亡的一个完整过程

1.2 为什么要学习对象的生命周期

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

1.3 生命周期的3个阶段

(重点掌握创建阶段,初始化操作和销毁操作用的比较少)

1.3.1创建阶段

Spring工厂何时创建对象?

  • 如果对象只会被生产一次 scope=“singleton”
Spring会在工厂创建的同时完成对象的创建

注意:设置scope=singleton 这种情况下 也需要在获取对象的同时创建对象 可以通过懒初始化来实现“”

   <bean id="account" scope="singleton" class="com.tcgroup.scope.Account" lazy-init="true"/>
  • 如果对象会被多次创建 scope=“prototype”
Spring工厂会在获取对象的同时,创建对象
classPathXmlApplicationContext.getBean("");
1.3.2初始化阶段

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

1. 初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作
2. 初始化方法调用:Spring工厂进行调用
定义初始化方法

第一种形式:实现InitializingBean接口

这个接口是由Spring提供的(耦合了Spring框架),里面有一个afterPropertiesSet()方法,这个就是初始化方法,可以在里面做一些初始化操作

public class Account implements InitializingBean {
    public Account (){
        System.out.println("构造方法");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("调用初始化方法");
    }
}

第二种形式:对象中提供一个普通的方法

public class Account {
    public void myInit(){
        System.out.println("调用初始化方法");
    }
}

在配置文件中通过init-method属性声明这个方法作为初始化方法进行调用

<bean id="account" scope="singleton" class="com.tcgroup.scope.Account"  init-method="myInit"/>

细节分析

  1. 如果一个对象即实现InitializingBean 同时又提供的 普通的初始化方法顺序
1. InitializingBean 
2. 普通初始化方法

在这里插入图片描述

  1. 注入一定发生在初始化操作的前面,先执行注入 然后执行构造方法
    在这里插入图片描述

  2. 什么叫做初始化操作

资源的初始化:数据库 IO 网络 .....
1.3.3 销毁阶段

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

1. Spring什么时候销毁所创建的对象?
      classPathXmlApplicationContext.close();
      
2. 销毁方法:程序员根据自己的需求,定义销毁方法,完成销毁操作
      调用:Spring工厂完成调用
定义销毁方法

第一种形式:实现DisposableBean接口

//销毁方法(释放资源)
@Override
public void destroy() throws Exception {
    System.out.println("调用destroy销毁方法");
}

在这里插入图片描述
第二种形式:对象中提供一个普通的方法

public class Account {
    public void myDestroy(){
        System.out.println("调用myDestroy销毁方法");
    }
}

在配置文件中通过destroy-method属性声明这个myDestroy方法作为销毁方法进行调用

   <bean id="account" scope="singleton" class="com.tcgroup.scope.Account"  init-method="myInit" destroy-method="myDestroy">
       <property name="age" value="10"/>
   </bean>

在这里插入图片描述
细节分析

  • 如果一个对象即实现DisposableBean同时又提供的 普通的初始化方法顺序
2. DisposableBean
3. 普通初始化方法
  • 销毁方法的操作只适用于 scope=“singleton”
    如果scope=“prototype” 那么销毁方法将不起作用
    在这里插入图片描述
  • 什么叫做销毁操作
主要指的就是 资源的释放操作  io.close() 
 connection.close();

1.4 生命周期总结

在这里插入图片描述

2. 配置文件参数化

把Spring配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中

1. Spring的配置文件中存在需要经常修改的字符串?
   存在 以数据库连接相关的参数为代表
2. 经常变化字符串,在Spring的配置文件中,直接修改
   不利于项目维护(修改) 
3. 转移到一个小的配置文件(.properties)
   利于维护(修改)

配置文件参数化:利于Spring配置文件的维护(修改)

2.1 配置文件参数的开发步骤

  • 提供一个小的配置文件(.properities)
名字:随便(考虑可读性即可 这里取得名字是db.properties)
放置位置:随便
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.31.174:3306/?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC
jdbc.userName=root
jdbc.psaaword=123456
  • Spring的配置文件与小配置文件db.properties进行整合
    classpath表示根目录
  • 在Spring配置文件中通过${key}获取小配置文件中对应的值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context-4.2.xsd">

<!--  Spring的配置文件与小配置文件进行整合  -->
    <context:property-placeholder location="classpath:/db.properties"/>
<!-- 在Spring配置文件中通过${key}获取小配置文件中对应的值 -->
    <bean id="conn" class="com.tcgroup.factory.ConnectionFactoryBean">
        <property name="driver" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="userName" value="${jdbc.userName}"/>
        <property name="psaaword" value="${jdbc.psaaword}"/>
    </bean>
</beans>

3.类型转换器

3.1 类型转换器

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

在这里插入图片描述

3.2. 自定义类型转换器

3.2.1需要自定义类型转换器原因:

当Spring内部没有提供特定类型转换器时,而程序员在应用的过程中还需要使用,那么就需要程序员自己定义类型转换器

例如 我们定义了一个类,有String name和Date brithday两个属性

public class Biaozi {
    private String name;
    private Date brithday;

    public String getName() {
        return name;
    }

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

    public Date getBrithday() {
        return brithday;
    }

    public void setBrithday(Date brithday) {
        this.brithday = brithday;
    }

    @Override
    public String toString() {
        return "Biaozi{" +
                "name='" + name + '\'' +
                ", brithday=" + brithday +
                '}';
    }
}

接下来通过注入的方式给这个累的对象赋值,然后尝试输出
在这里插入图片描述
结果是报错,提示不能将String类型转换成Date类型

3.2.2自定义类型转换器
  • 自定义一个类 实现 Converter接口
    import org.springframework.core.convert.converter.Converter;
public class MyDateConverter implements Converter<String,Date> {

    /*
    convert方法作用:String --->  Date
                   SimpleDateFormat sdf = new SimpleDateFormat();
                   sdf.parset(String) ---> Date
    param:source 代表的是配置文件中 日期字符串 <value>2020-10-11</value>

    return : 当把转换好的Date作为convert方法的返回值后,Spring自动的为birthday属性进行注入(赋值)

  */
    @Override
    public Date convert(String s) {
        Date date=null;
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            date = simpleDateFormat.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}
  • 在Spring的配置文件中进行配置
<!--    MyDateConverter对象创建出来  -->
<bean id="myDateConverter" class="com.tcgroup.converter.MyDateConverter"/>
<!--    类型转换器的注册    -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
<!--      把刚刚注册的MyDateConverter对象,
添加到ConversionServiceFactoryBean的converters属性中去        -->
            <ref bean="myDateConverter"/>
        </set>
    </property>
</bean>

这样就解决了上面不能将String类型转换成Date类型的报错
在这里插入图片描述

3.2.3细节处理
  • MyDateConverter中的日期的格式,通过依赖注入的方式,由配置文件完成赋值。
public class MyDateConverter implements Converter<String,Date> {
    private String pattern;

    public String getPattern() {
        return pattern;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }
    @Override
    public Date convert(String s) {
        Date date=null;
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
            date = simpleDateFormat.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}
<!--    MyDateConverter对象创建出来  -->
    <bean id="myDateConverter" class="com.tcgroup.converter.MyDateConverter">
        <property name="pattern" value="yyyy-MM-dd"/>
    </bean>
<!--    类型转换器的注册    -->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
<!--      把刚刚注册的MyDateConverter对象,
添加到ConversionServiceFactoryBean的converters属性中去        -->
                <ref bean="myDateConverter"/>
            </set>
        </property>
    </bean>
  • **ConversionSeviceFactoryBean 定义 id属性 值必须 conversionService **
    (原本其他bean标签里面的ID值是随便取得 只要唯一即可, 但是class对象是ConversionSeviceFactoryBean,ID就必须是conversionService 且区分大小写)
  • Spring框架内置日期类型的转换器
日期格式:2020/05/01 (不支持 :2020-05-01)

4.后置处理Bean

注意:BeanPostProcessor是个接口,AOP编程的底层实现会体现其厉害之处
BeanPostProcessor作用:对Spring工厂所创建的对象,进行再加工。
后置处理Bean的运行原理分析
在这里插入图片描述
程序员实现BeanPostProcessor规定接口中的方法
(两个方法都是default修饰的,默认实现):

Object postProcessBeforeInitiallization(Object bean String beanName)
作用:Spring创建完对象,并进行注入后,可以运行Before方法进行加工
通过方法的参数获得Spring创建好的对象,进行加工,
最终通过返回值交给Spring框架

Object postProcessAfterInitiallization(Object bean String beanName)
作用:Spring执行完对象的初始化操作后,可以运行After方法进行加工
通过方法的参数获得Spring创建好的对象,进行加工
最终通过返回值交给Spring框架

实战中:
由于很少处理Spring的初始化操作:所以大部分市县没有必要区分Before After。只需要实现其中的一个After方法即可

注意
如果只操作了postProcessAfterInitiallizationr方法
postProcessBeforeInitiallization也还是需要return bean对象

4.1 开发步骤:

  • 创建一个实体类用于测试(实际开发不用)
 package com.tcgroup.entity;

public class Categroy {
    private Integer id;
    private String name;

    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;
    }

    @Override
    public String toString() {
        return "Categroy{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
  • 创建一个类实现 BeanPostProcessor接口
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Categroy categroy=(Categroy) bean;
        categroy.setName("xiongjianren");
        return categroy;
    }
}
  • Spring的配置文件中进行配置
<bean id="c" class="com.tcgroup.entity.Categroy">
    <property name="id" value="10"/>
    <property name="name" value="xiongerzi"/>
</bean>

<bean id="myBeanPostProcessor" class="com.tcgroup.beanpost.MyBeanPostProcessor"/>

在上面代码中 通过配置文件我们设置Categroy对象的name为xiongerzi, 但是我们通过后置处理bean吧名字设置成了xiongjianren,所以最终输出这个对象的name属性是xiongjianren。

4.2 BeanPostProcessor细节

BeanPostProcessor会对基于当前配置文件创建的Spring工厂中所有创建的对象进行加工(也就是当前XML文件里面生产出来的所有对象),而步骤处理里面涉及强制类型转换,如果这个工厂需要创建Categroy以外的其他对象, 强制类型转换时就会报错, 所以需要对后置处理步骤进行保护,对对象的类型进行判断。

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Categroy){
        Categroy categroy=(Categroy) bean;
        categroy.setName("xiongjianren");
        return categroy;}
        return bean;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值