Spring-工厂高级特性
一、对象的生命周期
什么是对象的生命周期?
指的是一个对象的创建、存活、消亡的一个完整过程
为什么要学习对象的生命周期?
由Spring负责对象的创建、存活、销毁,了解生命周期,有利于我们使用好Spring为我们创建的对象
生命周期的3个阶段
创建阶段
- Spring工厂何时创建对象
scope="singleton"
- Spring工厂创建的同时,完成对象的创建
- 注意:设置
scope="singleton"
这种情况下 也需要在获取对象的同时创建对象, 通过标签<bean lazy-init="true"/>
scope="prototype"
- Spring工厂在获取对象的同时,创建对象
ctx.getBean("");
初始化阶段
- Spring工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作
- 初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作
- 初始化方法调用:Spring工厂进行调用
- 方式一:initializingBean接口
- afterPropertesSet()
/**
- 初始化方法:程序员根据需求,实现的方法,完成初始化操作
- @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Product afterPropertiesSet()...");
}
- afterPropertesSet()
- 方式二:对象中提供一个普通的方法
避免Spring框架的侵入 (implements initializingBean)更灵活//方法名任意,为了让Spring调用,在Spring配置文件中配置属性 init-method="" 自定义的初始化方法 public void myInit(){ } // <bean id="product" class="xxx.Product" init-method="myInit"/>
- 细节分析
- 如果一个对象既实现了InitializingBean 同时又提供了 普通的初始化方法。执行顺序是什么?
- 先执行 InitializingBean 再执行普通初始化方法
- 注入一定发生在初始化操作的前面
- 什么叫做初始化操作?
- 资源的初始化:数据库、IO、网络....(比较占用资源时间空间)
- 实际上我们需要做的初始化操作很少,就像Servlet中的 init 方法,我们几乎没有使用init方法
- 如果一个对象既实现了InitializingBean 同时又提供了 普通的初始化方法。执行顺序是什么?
销毁阶段
Spring销毁对象前,会调用对象的销毁方法,完成销毁操作
- Spring什么时候销毁所创建的对象?
- ctx.close();
- 销毁方法:程序员根据自己的需求,定义销毁方法,完成销毁操作
- 调用者:Spring工厂完成调用
- 方式一:DisposableBean接口
public void destroy throw Exception(){ }
- 方式二:定义一个普通的销毁方法
public void myDestroy throw Exception(){ } <bean id="" class="" init-method="" destroy-method="myDestroy">
- 细节分析
- 销毁方法的操作只适用于scope="singleton"(初始化方法都适用)
- 什么叫做销毁操作?
- 主要指的是 资源的释放操作 io.close() connection.close()
- 实际上我们需要做的销毁操作很少,就像Servlet中的 destroy 方法,我们几乎没有使用destroy方法
- 细节分析
二、配置文件参数化
把Spring配置文件中经常修改的字符串信息,转移到一个更小的配置文件中
- Spring的配置文件中存在需要经常修改的字符串信息吗?
存在 以数据库连接相关的参数为代表 - 经常变化的字符串,在Spring的配置文件中,直接修改
不利于项目维护(修改) - 转移到一个小的配置文件(properties)
配置文件参数化:利于Spring配置文件的维护(修改)
配置文件参数化的步骤
-
提供一个小的配置文件(.properties)
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=15737979065
-
Spring配置文件和properties小配置文件进行整合
<!--在Spring配置文件中添加此标签--> <context:property-placeholder location="classpath:/db.properties"/>
-
在Spring配置文件中通过 ${key}的形式获取 value值
<bean id="conn" class="com.mysite.factorybean.ConnectionFactoryBean"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
三、自定义类型转换器
类型转换器
作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入。
自定义类型转换器
产生原因:当Spring内部没有提供特定类型转化器时,而程序员在应用过程中还需要使用,那么就需要我们自己定义类型转换器
- 类 implements Converter接口
public class MyDateConverter implements Converter<String,Date> { /** * 将字符串类型转换成 日期类型 * String -> Date * SimpleDateFormat sdf = new SimpleDateFormat() * sdf.parse(String) -> Date * @param source 代表的是配置文件中 日期字符串 <value>2020-10-22</value> * @return 当把转换好的 Date作为convert方法的返回值后,Spring自动为birthday属性进行自动注入 */ @Override public Date convert(String source) { Date date = null; try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); date = sdf.parse(source); } catch (ParseException e) { throw new RuntimeException(e); } return date; } }
-
在Spring的配置文件中进行配置
-
MyDateConverter 对象创建出来
<!--Spring 创建MyDateConverter 类的对象--> <bean id="myDateConverter" class="xxx.MyDateConverter">
-
类型转换器的注册
目的:告诉Spring框架,我们所创建的MyDateConverter是一个类型转换器<!--通过ConversionServiceFactoryBean类 用于注册类型转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="myDateConverter"/> </set> </property> </bean>
-
细节:
- MyDateFormat中的日期格式,通过依赖注入的方式,由配置文件完成赋值。
定义成员变量private String pattern; 提供get set方法。在Spring配置文件中的MyDateFormat类中进行属性的配置 - 在创建ConversionServiceFactoryBean的对象时,id值是固定的,不能改变,一定是 conversionService
- Spring框架内置了日期类型的转换器
日期格式:2020/10/22(支持的格式)
四、后置处理Bean
BeanPostProcessor作用:对Spring工厂所创建的对象,进行再加工(AOP底层大量使用)
后置处理bean的原理分析
实现BeanPostProcessor规定接口中的方法:
-
Object postProcessorBeforeInitiallization(Object bean,String beanName)
作用:Spring创建完对象,注入后,初始化前,可以运行Before方法进行加工 -
Object postProcessorAfterInitiallization(Object bean,String beanName)
作用:Spring对象的初始化操作后,可以运行After方法进行加工
实战中,很少做初始化的操作,很少处理Spring的初始化操作,没有必要区分Befor After。只需要实现其中的一个After方法即可
注意:
postProcessorBeforeInitallization
return bean对象
BeanPostProcessor的开发步骤
- 类 实现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 { Category category = (Category) bean; category.setName("lisi"); return bean; } }
- Spring的配置文件中进行配置
<bean id="myBeanPostProcessor" class="xxx.MyBeanPostProcessor"/>
- BeanPostProcessor细节
BeanPostProcessor会对Spring工厂中所有创建的对象进行加工
使用 instanceof 来判断 bean 的类型,对bean 进行加工,否则会报ClassCastException@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization()... bean = "+bean.getClass()); //如果 bean 传入的 Category类型强转 if (bean instanceof Category) { Category category = (Category) bean; category.setName("lisi"); } //如果 bean 传入的 Person类型强转 if (bean instanceof Person) { Person person = (Person) bean; person.setName("lisi"); } return bean; }
可以在实现BeanPostProcessor接口后 不做加工,但是必须要做判断,因为BeanPostProcessor 会扫描Spring工厂(Spring配置文件)中所有对象