目录
一、对象的生命周期
1、什么是对象的生命周期
2、为什么要学习对象的生命周期
3、生命周期的三个阶段
(1)创建阶段
(2)初始化阶段
1、InitializingBean 接口
2、对象中提供一个普通的方法
3、细节分析
(3)销毁阶段
1、DisposableBean
2、定义一个普通的销毁方法
3、细节
二、配置文件的参数化
1、配置文件参数化的开发步骤
三、自定义类型转换器
1、类型转换器编辑
2、自定义类型转换器
实现 converter 接口
在 Spring 配置文件中进行配置
MyDateConveter 对象创建出来
3、细节
一、对象的生命周期
1、什么是对象的生命周期
对象的生命周期,指的是一个对象创建、存活、消亡的一个完整过程
2、为什么要学习对象的生命周期
由 Spring 来负责对象的创建、存活、销毁、了解生命周期更加有利于我们使用好 Spring 为我们创建的对象
原来,我们想创建一个对象,就自己 new 一个对象,这个对象在一直有人引用它的时候,就一直存活在虚拟机的内存当中,直到被虚拟机垃圾回收为止
现在,我们对象的创建、存活、消亡的过程是由 Spring 来控制的,所以如果我们能够了解到这个对象的生命周期,实际上是更加有利于我们利用好 Spring 为我们创建的对象
3、生命周期的三个阶段
1、创建阶段
2、初始化阶段
3、销毁阶段
(1)创建阶段
创建阶段 指的是 Spring 工厂何时帮我们创建对象
scope = singleton 的时候,Spring 会在工厂创建的同时,完成对象的创建
注意: 如果想设置 scope = singleton 的情况下也需要在获取对象的同时创建对象,要在 <bean 中加一个属性:
<bean lazy-init = "true"/>
scope = prototype 的时候,Spring 会在获取对象的时候创建对象
(2)初始化阶段
初始化阶段指的是 spring 工厂在创建完对象之后,调用对象的初始化方法,完成对应的初始化操作
1、初始化方法的提供:由程序员根据需求来提供初始化方法,最终完成初始化操作
2、初始化方法调用:这个方法由 Spring 工厂进行调用
1、InitializingBean 接口
这个接口当中为我们规定了一个方法:afterPropertiesSet( );
我们想要完成的初始化操作的代码,就可以写在这个方法当中,最终会由 Spring 来调用
//这个就是初始化方法:做一些初始化操作
//最终 Spring 会进行调用
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Product.afterPropertiesSet");
}
2、对象中提供一个普通的方法
public void myInit(){
}
这个方法是由 Spring 来进行调用的,那么 Spring 怎么认识这个方法呢?
我们可以通过配置文件来告诉 Spring 这个方法
<bean id = "product" class = "xxx.Product" init-method = "myInit" />
init-method 这个属性的作用就是把你写好了的初始化方法的名字,在这里通过这段配置来进行配置,这样 Spring 在读到了配置文件之后,就能发现这个属性,进而知道这个配置文件当中所描绘的这个方法名就是 myInit,最终,Spring 才能对它进行调用
3、细节分析
1、如果一个对象,既实现了 InitializingBean ,同时又提供了普通的初始化方法
两个初始化操作都会执行,但是先执行接口的初始化操作,再执行普通的初始化方法
2、Spring 在创建完对象之后,先进行注入操作,再调用初始化方法
3、什么叫做初始化操作
所谓的初始化操作,主要指的就是对于资源的初始化:数据库资源,IO 资源,网络资源.....
(3)销毁阶段
销毁阶段指的是 Spring 销毁对象之前,会调用对象的销毁方法,完成销毁操作
1、Spring 什么时候销毁所创建的对象?
在工厂关闭的时候,ctx.close( );
2、程序员根据自己的需求,定义销毁方法,完成销毁操作,由 Spring 工厂来完成调用
1、DisposableBean
//销毁方法: 销毁操作(资源释放的操作)
@Override
public void destroy() throws Exception {
System.out.println("Product.destroy");
}
注意: 对象的销毁必须得发生在工厂关闭的时候
这个时候发生了报错,是因为 close 是定义在 ClasPathXmlApplicationContext 这个子类当中,而此时我们声明的是 父接口 ApplicationContext
按照多态性原则,只能调用父接口规定的方法,但是父接口中没有规定 close( );
此时我们只需要让 ApplicationContext 变成 ClasPathXmlApplicationContext 即可
2、定义一个普通的销毁方法
public void myDestory(){
System.out.println("Product.myDestory");
}
显然,如果想要 Spring 来帮我们调用的话,我们得在配置文件中告诉 Spring
<bean id="product" class="life.Product" init-method="myInit" destroy-method="myDestory"></bean>
这样,整个销毁方法就定义完了
3、细节
1、销毁方法的操作,只适用于 scope = "singleton"
2、什么叫做销毁操作?
销毁操作主要指的就是一些资源的释放操作,比如 io.close(),connection.close()
二、配置文件的参数化
把 Spring 配置文件中经常需要修改的字符串信息,转移到一个更小的配置文件中,就叫配置文件的参数化
1、Spring 的配置文件中,是否存在需要经常修改的字符串?
存在,例如:数据库连接相关的参数
2、经常变化的字符串在 Spring 的配置文件中直接修改,不利于项目维护(修改)
3、转移到一个小的 配置文件(.properties),利于维护(修改)
配置文件参数化:利于 Spring 参数文件的修改
1、配置文件参数化的开发步骤
在我们曾经写过的代码中,有一个叫做 FactoryBean 接口,这个接口里面实现了 ConnectionFactoryBean,在它的设置过程当中,因为我们分析它需要连接相关的四个参数,于是就为它进行了依赖注入,所以我们就把这四个参数最终通过 Spring 的配置文件进行了赋值
1、提供一个小的配置文件(.properties)
名字以及放置位置都可以随便
下面,我们就要把 Spring 配置文件中经常需要修改的信息从中提取出来,放到 .properties 的文件当中
jdbc.driverClassName = com.mysql.jdbc.Driver
jrbc.url = jdbc:mysql//localhost:3306/java?useSSL=false
jdbc.username = root
jdbc,password = 123456
2、把 Spring 的配置文件与小配置文件进行整合
一旦我们转移出去之后,那么作为 Spring 来讲,它的配置文件这块的内容,我们就不能写了
但是后续在这些位置当中,我们还要使用我们转移走的那些内容
所以我们还需要再把小的配置文件中的内容读回来
既然要读回来,就涉及到了 Spring 的配置文件和小的配置文件的整合
我们只需要让 Spring 知道这个小的配置文件叫什么名字,放到了哪里,就可以了
所以我们只需要在 Spring 配置文件中使用一个标签来说明小配置文件的名字和位置即可
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
3、在 Spring 配置文件中通过 ${ key } 的形式获取小配置文件中对应的值
三、自定义类型转换器
1、类型转换器
我们之前说过,在文件当中写的内容,都是 String 类型,但是我们却成功地把 "1" 赋值给了 id 属性,而 Product 的 id 属性,是 Integer 类型的
为什么我们此时能把 String 类型的数据赋值给 Integer 类型的数据呢?
它之所以能把 字符串 赋值给 Integer ,其实是因为在这个过程中,Spring 为我们进行了类型转换
那么这个类型转换就是 类型转换器 来完成的
在我们把这个数据注入给 Integer 的属性之前,Spring 就会有一个叫做 Converter 的类型转换器介入了,Spring 会通过 Converter 这个类型转换器,把 String 先转换成 int 之后,再给 id 赋值
作为 Converter 来讲,在 Spring 定义的时候,把它声明成了接口,因为接口就意味着多种实现,同样,在类型转换的过程中,也会涉及到多种类型的转换
作用:Spring 通过类型转换器,把配置文件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入
2、自定义类型转换器
我们先再写一个 Person类,然后有 String 和 Date 两种类型的数据:
然后在一个新的配置文件中对其进行注入
接着运行测试代码
此时,发现了报错
观察报错信息,意思是不能把 String 类型的数据转换成 Date 类
这是因为,我们不能直接把 String 存入 Date 类型的数据中,而 Spring 恰好没有提供对应的类型转换器,所以就需要我们自定义类型转换器
当 Spring 内部没有提供特点类型转换器的时候,而程序员在应用的过程中, 还需要使用,那么就需要程序员自己定义类型转换器了
实现 converter 接口
/*
convert 方法作用: String ---> Date
SimpleDateFormat.parse("") --> Date
sdf.parset(String) ---> Date
param:source 代表的是配置文件中 日期字符串 <value>2020-10-11</value>
return : 当把转换好的 Date 作为 convert 方法的返回值后,Spring 就会自动的为 birthday 进行注入(赋值)
*/
public class myDateConverter implements Converter<String,Date> {
@Override
public Date convert(String sourse) {
Date date = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
date = sdf.parse(sourse);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return date;
}
}
在 Spring 配置文件中进行配置
MyDateConveter 对象创建出来
<!--Spring 创建 MyDateConverter 类型对象-->
<bean id="myDateConverter" class="Converter.myDateConverter"></bean>
类型转换器的注册
目的:告诉 Spring 框架,我们所创建的 MyDateConverter 是一个类型转换器
<!-- ConversionServiceFactoryBean 实现注册功能-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="myDateConverter"></ref>
</set>
</property>
</bean>
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 sourse) {
Date date = null;
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
try {
date = sdf.parse(sourse);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return date;
}
}
<!--Spring 创建 MyDateConverter 类型对象-->
<bean id="myDateConverter" class="Converter.myDateConverter">
<property name="pattern" value="yyyy-MM-dd"></property>
</bean>
2、ConversionServiceFactoryBean 在定义 id 属性的时候,id 必须为 conversionService
3、Spring 框架内置日期类型的转换器
日期格式:2023/09/12(不支持 2023-09-12)