一、关于对象的生命周期

1、什么是对象的生命周期:指的是⼀个对象创建、存活、消亡的⼀个完整过程

2、为什么要学习对象的⽣命周期:由Spring负责对象的创建、存活、销毁,了解⽣命周期,有利于我们使⽤好Spring为我们创建的对象

3、生命周期的三个阶段:创建阶段、初始化阶段、销毁阶段

(1)、创建阶段

创建阶段以Spring工厂何时创建对象作为区分分为以下两种:

17.PNG

比如:在实体类中创建构造方法并输出一句话,(1)使用scope="singleton" 则直接在工厂创建的同时创建对象,

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

直接输出构造方法中的输出内容 (2)使用scope="prototype" 则Spring工厂会在获取对象的同时创建对象 (3)使用scope="singelton" 并在bean标签中加入 lazy-init="true"的话效果就会变成(2)的在获取对象时创建对象

(2)、初始化阶段

初始化阶段在创建阶段之后,主要用于资源的初始化;初始化分为继承接口实现和用bean标签引入方法实现。

细节如图,注意注入一定是在初始化之前,创建对象之后:

18.PNG

(3)、销毁阶段

销毁阶段调用销毁方法目的就是为了释放资源,同样也可以用继承接口实现和用bean标签引入方法实现。

19.PNG

测试代码实例如下:

package com.jin.life;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

/**
 * @author jinyunlong
 * @date 2021/11/29 15:46
 * @profession ICBC锅炉房保安
 */
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;
    }

    //public class Product{
    public Product() {
        System.out.println("Product.Product");
    }

    //这个就是初始化方法 : 做一些初始化操作
    //Spring会进行调用
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Product.afterPropertiesSet");
    }

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

    //销毁方法:销毁操作(资源释放的操作)
    @Override
    public void destroy() throws Exception {
        System.out.println("Product.destory");
    }

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

    <bean id ="product" class="com.jin.life.Product" init-method="myInit" destroy-method="myDestroy">
        <property name="name" value="jinjinjin"></property>
    </bean>
    //用于测试:生命周期
    @org.junit.Test
    public void test12(){
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Product product = (Product) ctx.getBean("product");
//        System.out.println("product.getName() = " + product.getName());
        ctx.close();
    }

全量代码运行结果,可见执行顺序为 (1)创建对象(scope="singleton"为Spring工厂创建同时创建对象)->(2)注入->(3)InitializingBean接口初始化->(4)定义⼀个普通的初始化方法,bean标签引用初始化->(5)DisposableBean接口销毁->(6)定义⼀个普通的销毁方法,bean标签引用销毁:

20.PNG

对象生命周期的流程概念图(少个注入):

21.PNG

二、配置文件参数化

终于要用到.properties 文件了,可以理解为该文件为Spring配置文件更细化拆分的一个文件,因为Spring配置文件里面内容非常的多,不便于维护,所以会把一些值以key-value的方式拿出来先在.properties文件中配置,然后再通过key放回Spring配置文件中进行注入:

把Spring配置⽂件中需要经常修改的字符串信息,转移到⼀个更⼩的配置⽂件中
1. Spring的配置⽂件中存在需要经常修改的字符串?
 存在 以数据库连接相关的参数 代表
2. 经常变化字符串,在Spring的配置⽂件中,直接修改
 不利于项⽬维护(修改)
3. 转移到⼀个⼩的配置⽂件(.properties)
 利于维护(修改)

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

以注入数据库参数为例,db.properties配置:

jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/vueblog
jdbc.username = root
jdbc.password = xxxx

Spring配置文件配置:

<!--    Spring配置文件与小配置文件的整合-->

    <context:property-placeholder location="classpath:/db.properties" />
    <bean id="conn" class="com.jin.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>

注意:(1) 需要加标签使Spring的配置文件与小配置文件行整合 (2) 在Spring配置文件中通过${key}获取小配置文件(key)中对应的值

    //用于测试:配置文件参数化
    @org.junit.Test
    public void test13() throws SQLException {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext1.xml");
        Connection conn = (Connection) ctx.getBean("conn");
        System.out.println("conn = " + conn);
        ctx.close();
    }

测试成功返回连接数据库对象:

22.PNG

所以我们天天写的.properties文件,本质就是IOC,只不过是在Spring配置文件的基础上,又细了一层。

三、自定义类型转换器

作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成了对象中成员变量对应类
型的数据,进而完成了注入。比如我们在类中定义对象类型为Integer,配置文件中value值写的是String型,这时候Spring就会通过自己内置的类型转换器进行转换后注入:

23.PNG

但是有的类Spring没有内置自定义转换器,比如String转Date(其实也有,但仅限于yyyy/MM/dd这种格式,下面在细节里说),这时候就要自定义(自己写)类型转换器了:

分两步: 1、创建类先继承Converter接口 2、在Spring配置文件中进行配置 (1)、创建步骤1的类(简单)对象 (2)、将(1) 注册到类型转换器上,根据1类上的泛型和covert()方法,即可实现自定义类型转换器,描述的比较抽象,日后估计自己都看不明白了,直接贴示例代码了:

package com.jin.converter;

import org.springframework.core.convert.converter.Converter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author jinyunlong
 * @date 2021/11/30 17:22
 * @profession ICBC锅炉房保安
 */
public class MyDateConverter implements Converter<String, Date> {

    private String pattern;

    public String getPattern() {
        return pattern;
    }

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

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

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

         */
    @Override
    public Date convert(String source) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        Date date = null;
        try {
            date = sdf.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}

<?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.xsd">

    <!--    Spring创建MyDateConverter类型对象-->
    <bean id="myDateConverter" class="com.jin.converter.MyDateConverter">
        <property name="pattern" value="yyyy-MM-dd" />
    </bean>

    <!--    ConversionServiceFactoryBean-->
    <!--    用于注册类型转换器-->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="myDateConverter" />
            </set>
        </property>
    </bean>

<!--    Spring配置文件与小配置文件的整合-->

    <bean id="person" class="com.jin.converter.Person">
        <property name="name" value="kingking" />
        <property name="birthday" value="2012-12-12" />
    </bean>

</beans>
    //用于测试:自定义类型转换器
    @org.junit.Test
    public void test14() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext2.xml");
        com.jin.converter.Person person = (com.jin.converter.Person) ctx.getBean("person");
        System.out.println("person = " + person.getBirthday());
    }

运行结果,成功获取Date型bir,(不注册自定义类型转换器直接调用测试方法报错)

24.PNG

几个细节:

1、在上面,MyDateConverter类中日期的格式,就是通过依赖注入(set注入)的方式,由配置文件完成赋值。

2、该类的作用分三步,参照代码注释

3、ConversionSeviceFactoryBean 定义 id属性 值必须是 conversionService,否则会报找不到自定义类型转换器错误(类型转换失败)

4、Spring框架内置日期类型的转换器,格式为2012/12/12,不支持2012-12-12,所以需要自定义类型转换器

四、后置处理Bean

说白了就是对对象的再加工(BeanPostProcessor作⽤:对Spring工厂所创建的对象,进行再加工)

1.PNG

Object postProcessBeforeInitiallization(Object bean String beanName)
作⽤:Spring创建完对象,并进⾏注⼊后,可以运⾏Before⽅法进⾏加⼯
获得Spring创建好的对象 :通过⽅法的参数
最终通过返回值交给Spring框架
Object postProcessAfterInitiallization(Object bean String beanName)
作⽤:Spring执⾏完对象的初始化操作后,可以运⾏After⽅法进⾏加⼯
获得Spring创建好的对象 :通过⽅法的参数
最终通过返回值交给Spring框架

实战中:
很少处理Spring的初始化操作:没有必要区分Before After。只需要实现其中的⼀个After
⽅法即可
注意:
 postProcessBeforeInitiallization
 return bean对象

代码实例:

package com.jin.beanpost;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * @author jinyunlong
 * @date 2021/12/1 10:51
 * @profession ICBC锅炉房保安
 */
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 Category){
            Category category = (Category) bean;
            category.setName("passpassrootroot");

        }
        return bean;
    }
}

实体类不贴了,注意before方法可以不用,但是一定要写,默认就是不处理放bean直接过了。

    <bean id="category" class="com.jin.beanpost.Category">
        <property name="id" value="2346" />
        <property name="name" value="pwdpwd" />
    </bean>

    <bean id="myBeanPostProcessor" class="com.jin.beanpost.MyBeanPostProcessor" />
    //用于测试:后置处理bean
    @org.junit.Test
    public void test15() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext3.xml");
//        MyBeanPostProcessor myBeanPostProcessor = (MyBeanPostProcessor) ctx.getBean("myBeanPostProcessor");
        Category category = (Category) ctx.getBean("category");
        System.out.println("category.getName() = " + category.getName());
//        Category categoryAfter = (Category) myBeanPostProcessor.postProcessAfterInitialization(category,"category");
//        System.out.println("categoryAfter.getName() = " + categoryAfter.getName());
    }

有一点要注意是BeanPostProcessor会对Spring工厂中所有创建的对象进行加工,所以在要加条件判断(after方法中)。


标题:Spring(3)
作者:jyl
地址:http://www.jinyunlong.xyz/articles/2021/11/29/1638183860757.html