Spring07:对象的生命周期+类型转换器

1.Spring中对象的生命周期。

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

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

2.为什么要学习对象的生命周期?

原来我们开发需要一个对象时,自己new一个。这个对象如果一直有引用指向的话,它就会一直存活下去。知道没有引用指向它了,就会被垃圾回收器回收掉,消亡了。

所以在原来我们不怎么考虑对象的声明周期这个问题。因为我们需要时就new,不要时,GC又会自动帮我们回收。

但是现在对象的创建,存活,消亡是由Spring来控制了,所以需要考虑这个问题。所以如果了解好这个对象的生命周期,更有助于我们利用Spring为我们创建的对象。

1.2.Spring生命周期的三个阶段

1.2.1 创建对象:Spring工厂何时创建对象?

  1. scope=“singleton”:Spring工厂创建的同时,创建这个对象。
    在这里插入图片描述

  2. scope=“prototype”:Spring工厂在获取对象的同时,创建对象
    Spring工厂获取对象:context.getBean("account");

    ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
    Account account = (Account)context.getBean("account");
    
  3. 注意:如果一个对象是scope=“singleton”,但是我又不想让它随着工厂的创建而创建,也让它随着对象的获取才创建:懒初始化

    lazy-init="true"

    <bean id="connection" lazy-init="true" class="com.baizhiedu.basic.factorybean.ConnectionFactoryBean">
    

在这里插入图片描述

1.2.2.初始化阶段

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

2.初始化方法是谁提供和调用呢?

  • 程序员根据需求,提供初始化方法,Spring工厂来调用,最终完成初始化操作。

3.如何定义初始化方法?

  • 3.1.继承initializingBean接口,实现afterPropertiesSet()方法,之后Spring工厂就会调用afterPropertiesSet方法,来为对象进行初始化。
public class Product implements InitializingBean {
    
    

    /**
     * 这个就是初始化方法,Spring会调用这个方法为对象进行初始化。
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
    
    
        System.out.println("afterPropertiesSet: 正在初始化....");
    }
}

<bean id="product" class="com.baizhiedu.basic.init.Product"></bean>

@Test
public void test15(){
    
    
    ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
    Product product = (Product)context.getBean("product");
}

//结果
afterPropertiesSet: 正在初始化....
  • 3.2.如果不想耦合Spring的框架:在对象中提供一个普通方法,再在配置文件中配置init-method属性即可。

    也不用继承initializingBean接口,避免了Spring的侵入和耦合!

    public class Product{
          
          
        /**
         * 不用耦合Spring框架,避免Spring侵入。
         */
        public void myInit(){
          
          
            System.out.println("myInit: 正在初始化....");
        }
    }
    
    <!--测试初始化方法-->
    <bean id="product" init-method="myInit" class="com.baizhiedu.basic.init.Product"></bean>
    

4.初始化阶段的细节分析

  • 4.1.如果一个对象既实现了initializingBean接口,重写了afterPropertiesSet()方法;而且也有一个普通初始化方法,并且在配置文件中配置了该方法。那么结果会怎样?

    两个方法都执行:先执行initializingBean接口中的afterPropertiesSet()方法,在执行普通初始化方法。

在这里插入图片描述

  • 4.2.Spring创建对象后—>执行属性注入操作—>执行初始化操作。

1.2.3.销毁阶段

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

1.Spring什么时候销毁所创建的对象呢?
context.close():工厂关闭时,会调用销毁方法,销毁对象。
注意:context.close()是ApplicationContext工厂的子类中定义的方法。多态只能调用父类中定义的方法,所以这里不能用多态。

2.销毁方法:程序员根据自己的逻辑实现销毁方法,Spring最后来调用。

3.怎么调用销毁方法

  • 继承DisposableBean,重写destroy()方法
  • 或者自己写一个destroy方法,配置文件在进行destroy-method属性配置
  • 如果两个都有,那么先调用DisposableBean 接口中的destroy方法,后调用自己写的销毁方法myDestroy。
public class Product implements InitializingBean, DisposableBean {
    
    

    /**
     * 这个就是初始化方法,Spring会调用这个方法为对象进行初始化。
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
    
    
        System.out.println("afterPropertiesSet: 正在初始化....");
    }

    /**
     * 不用耦合Spring框架,避免Spring侵入。
     */
    public void myInit() throws Exception{
    
    
        System.out.println("myInit: 正在初始化....");
    }

    @Override
    public void destroy() throws Exception {
    
    
        System.out.println("destroy:正在销毁");
    }

    public void myDestroy() throws Exception{
    
    
        System.out.println("myDestroy:正在销毁");
    }
}

<bean id="product" init-method="myInit" destroy-method="myDestroy" class="com.baizhiedu.basic.init.Product"></bean>

4.细节分析:

  • 销毁方法的操作适合用于:scope="singleton"的bean类型。
  • 日常开发用的很少。

在这里插入图片描述

2.配置文件参数化

1.什么是配置文件参数化

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

2.Spring配置文件中存在哪些经常需要修改的字符串信息。

比如数据库连接参数:如果后期跟换数据库,或者修改密码用户名等。

在这里插入图片描述
3.Spring中存在这些经常需要修改的字符串好不好?

  • 好不好不体现在功能上,因为肯定是可以完成功能的。我们应该从后期维护的角度上来分析:

    一般而言,一个项目的Spring配置文件有几千行,后面如果需要修改这些字符串参数,那么定位到这一块较困难。

    而且一般这种数据库连接参数的修改是交给运维人员的,他可能看不懂这些Spring的配置文件,而且让运维人员直接修改Spring配置文件中的内容很容易出错,或者将别处不小心改错。

    所以如果将这些经常需要修改的字符串放在Spring配置文件中,不好,不利于维护修改。

    不好,经常改不利于后期维护。在一个庞大的Spring配置文件中经常改也容易出错。

4.将这些经常需要修改的字符串抽取出来放到一个小的.properties文件中

利于Spring配置文件的维护,修改。

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

将数据库连接参数字符串,转移到一个更小的配置文件中

在这里插入图片描述

1.创建一个.properties文件

文件名:随意
文件防止位置:随意

在这里插入图片描述

2.Spring配置文件和小配置文件整合

  • 引入context:命名空间:敲下<context:property-placeholder后Spring会自动帮我们引入context命名空间。

在这里插入图片描述

  • 整合(读取)小配置文件

    <!--Spring配置文件整合小配置文件-->
    <context:property-placeholder location="classpath:/db.properties"/>
    
  • Spring配置文件修改:通过${key}获取小配置文件中对应的值。

        <!--Spring配置文件整合小配置文件-->
        <context:property-placeholder location="classpath:/db.properties"/>
        <bean id="connection" lazy-init="true" class="com.baizhiedu.basic.factorybean.ConnectionFactoryBean">
            <property name="password" value="${jdbc.password}"/>
            <property name="user" value="${jdbc.user}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="driver" value="${jdbc.driverClassName}"/>
        </bean>
    

3.自定义类型转换器

3.1.疑问:

在这里插入图片描述
1.配置文件中的值都是字符串,属性类型是Integer。按理说类型不同,不能直接赋值,那么是怎么注入进去的呢?

Spring内置了一个类型转换器组件Converter,帮我们做了类型转换:

在这里插入图片描述
2.类型转换器:

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

3.Converter是接口,所以我们亦可以实现自己的类型转换器接口。

3.2.观察一种情景

1.观察一种情景:

  • Student类:有一个private Date birthday;属性

    public class Student {
          
          
    
        private String name;
        private Date birthday;
    
        public String getName() {
          
          
            return name;
        }
    
        public void setName(String name) {
          
          
            this.name = name;
        }
    
        public Date getBirthday() {
          
          
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
          
          
            this.birthday = birthday;
        }
    
        @Override
        public String toString() {
          
          
            return "Student{" +
                    "name='" + name + '\'' +
                    ", birthday=" + birthday +
                    '}';
        }
    }
    
  • 配置文件配置:注入birthday属性

     <bean id="student" class="com.baizhiedu.basic.converter.Student">
         <property name="name" value="txl"/>
         <property name="birthday" value="1999-09-19"/>
     </bean>
    
  • 注意:配置文件中birthdayvalue是字符串,而类中要求的是Date。那么能够正确注入进去吗?

  • 测试报错:org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'birthday';

  • 所以此时Spring内置的默认转换器已经不能满足我们的需求了,我们要自己定义一个类型转换器。

3.3.自定义类型转换器

当Spring内部没有提供特定的类型转换器,或者默认的类型转换器不能满足我们的需要,那么就需要我们自己定义类型转换器。

1.提供一个类型转换器:

public class MyDateConverter implements Converter<String, Date> {
    
    
    @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;
    }
}
  • 注意泛型Converter<String, Date>:String表示原始类型,Date表示要转换成的类型。

2.整合自定义类型转换器

  • 让Spring工厂创建类型转换器对象

    <bean id="myDateConverter" class="com.baizhiedu.basic.converter.MyDateConverter"></bean>
    
  • Spring提供了一个类来帮我们注册自定义类型转换器:ConversionServiceFactoryBean。那么也要先创建这个类:

    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"></bean>
    
  • 注册类型转换器:告知Spring框架,这个类MyDateConverter 是一个类型转换器.

    方式:通过将自定义类型转换器注入到ConversionServiceFactoryBean类中的converters属性。

    注意:converters属性是Set类型。

    <!--帮助定义类型转换器注册的对象-->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="myDateConverter"/>
            </set>
        </property>
    </bean>
    

3.理解

我们将这个自定义类型注册好之后,怎么起作用呢:

Spring在注入时发现配置文件中的字符串类型要注入到Date属性中,那么就会看看是不是有相应的类型转换器:Converter<String, Date>。

而且泛型必须是<String, Date>,找到了就回调其中的convert(String s)方法转换一下类型,再将返回值Date注入到属性中。

3.4.自定义类型转换器细节分析

1.MyDateConverter中的日期格式,通过依赖注入的方式,由配置文件完成赋值。

  • 解耦:将参数字符串抽取成一个属性,提供get/set方法,在配置文件中注入这个属性。

在这里插入图片描述

  • 好处:日后我想换一个日期格式的话,直接在配置文件中修改即可。这样在我的Converter类型转换器中,就不会耦合这个pattern信息。

2.注意:

  • 配置ConversionServiceFactoryBean类的bean的id只能是:conversionService。Spring对这个类的bean的id要求只能是conversionService
  • Spring已经集成了自身的日期类型转换器,但是日期格式要求:2020/02/02

类路径:

1.在Maven开发中,java文件夹和resources文件夹是同级的,但是在编译后这两个文件夹会合并成一个文件夹,所以resources文件夹下的文件,相当于在java根目录下。

在这里插入图片描述

这些配置文件和com包是平级的关系,我们将编译后的classes文件夹称为类路径。所以配置文件在类路径文件夹下:

在这里插入图片描述

例如Spring配置文件整合小配置文件:location="classpath:db.properties"

<!--Spring配置文件整合小配置文件-->
<context:property-placeholder location="classpath:/db.properties"/>

猜你喜欢

转载自blog.csdn.net/tttxxl/article/details/115351634