目录
1.Spring中对象的生命周期。
1.1.为什么要学习对象的生命周期
1.什么是对象的生命周期:指的是一个对象的创建,存活,消亡的一个完整的过程。
2.为什么要学习对象的生命周期?
原来我们开发需要一个对象时,自己new一个。这个对象如果一直有引用指向的话,它就会一直存活下去。知道没有引用指向它了,就会被垃圾回收器回收掉,消亡了。
所以在原来我们不怎么考虑对象的声明周期这个问题。因为我们需要时就new,不要时,GC又会自动帮我们回收。
但是现在对象的创建,存活,消亡是由Spring来控制了,所以需要考虑这个问题。所以如果了解好这个对象的生命周期,更有助于我们利用Spring为我们创建的对象。
1.2.Spring生命周期的三个阶段
1.2.1 创建对象:Spring工厂何时创建对象?
-
scope=“singleton”:Spring工厂创建的同时,创建这个对象。
-
scope=“prototype”:Spring工厂在获取对象的同时,创建对象
Spring工厂获取对象:context.getBean("account");
ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml"); Account account = (Account)context.getBean("account");
-
注意:如果一个对象是scope=“singleton”,但是我又不想让它随着工厂的创建而创建,也让它随着对象的获取才创建:
懒初始化
lazy-init="true"
扫描二维码关注公众号,回复: 13306631 查看本文章<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>
-
注意:配置文件中
birthday
的value
是字符串,而类中要求的是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"/>