-
Spring对象的生命周期、配置文件参数化、自定义类型转换器、后置处理Bean
2020-09-21 10:35:30Spring对象的生命周期、配置文件参数化、自定义类型转换器、后置处理Bean1.对象的生命周期什么是对象的生命周期?生命周期的三个阶段:创建阶段初始化阶段销毁阶段测试2.配置文件参数化3.自定义类型转换器开发步骤:...Spring对象的生命周期、配置文件参数化、自定义类型转换器、后置处理Bean
1.对象的生命周期
1.1 什么是对象的生命周期?
-
对象 创建-存活-消亡 整个过程。
-
以前由GC回收,现在由Spring管理,有助于使用好Spring为我们创建的对象。
1.2 生命周期的三个阶段:
-
创建阶段 —> 初始化阶段 —> 销毁阶段
1.2.1 创建阶段
- Spring工厂何时创建对象?
scope="singleton"
:工厂创建的同时,对象创建。可以添加lazy-init属性为true,实现prototype的效果
scope="prototype"
:获取对象的时候创建 getBean()
1.2.2 初始化阶段
Spring创建完对象之后,调用对象的初始化方法,完成对应的初始化操作
-
提供:程序猿根据需求,提供初始化方法,最终完成初始化操作
-
调用:Spring工厂进行调用
方式一
:实现initializingBean接口
方式二
:写一个普通方法,在bean标签中添加属性 init-method=“xxx()” -
细节分析:
- 如果既实现了接口,又提供了普通的初始化方法?
先执行initializing的方法,再执行普通的初始化方法
- 初始化和注入谁先进行?
先进行注入,再执行初始化方法
- 什么叫初始化操作?
资源的初始化:数据库,IO,网络。。。
1.2.3 销毁阶段
调用销毁方法,完成销毁操作
- 销毁方法:程序猿根据需求定义销毁方法,完成销毁操作
- 何时调用:Spring工厂完成调用
- Spring什么时候销毁对象?
工厂关闭后,context.close()
方式一
:DisposableBean
方式二
:定义普通销毁方法,bean标签中设置属性destroy-method- 细节:
1.销毁方法的操作只适用于scope=“singleton”
2.什么叫销毁操作?
主要指的是 资源的释放操作:IO、Connection
1.3 测试
测试bean
package zyc.stu.Spring5_45_53.Life; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; /** * @author zhuZiGe_ * @create 2020-09-10-18:01 */ public class Product implements InitializingBean, DisposableBean { private String name; public String getName() { return name; } public void setName(String name) { System.out.println("setName"); this.name = name; } public Product() { System.out.println("Product.Constructor"); } //初始化方法:执行一些初始化操作,会被spring调用 public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet()"); } //普通方法,bean标签内调用也可以成为初始化方法 public void myInit(){ System.out.println("Product.myInit"); } //销毁方法:销毁操作(资源释放) public void destroy() throws Exception { System.out.println("destroy()"); } //普通方法:bean标签内调用也可以成为销毁方法 public void myDestroy(){ System.out.println("Product.myDestroy"); } }
<bean id="product" class="zyc.stu.Spring5_45_53.Life.Product" scope="prototype" init-method="myInit" destroy-method="myDestroy"> <property name="name"> <value>zyc</value> </property> </bean>
测试
@Test public void test2(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml"); //Product product = (Product) context.getBean("product"); context.close(); }
1.4 总结:从上到下依次进行
2.配置文件参数化
把 Spring 配置文件中需要经常修改的字符串信息,转移到⼀个更小的配置文件中。
2.1 为什么要参数化
- Spring 的配置文件中是否存在需要经常修改的字符串?
- 存在:以数据库连接相关的参数…
- 经常变化字符串,在 Spring 的配置文件中,直接修改不利于项目维护(修改)
- 转移到⼀个小的配置文件(.properties)利于维护(修改)
优点:利于 Spring 配置文件的维护(修改)
2.2 配置文件参数的开发步骤
2.2.1 准备配置文件
提供⼀个小的配置文件(.properities)
jdbc.driverClassName = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://localhost:3306/springdemo?useSSL=false jdbc.username = root jdbc.password = root
2.2.2 整合
Spring 的配置文件与小配置文件进行整合:
<!--resources 下的文件在整个程序编译完后会被放到 classpath 目录下,src.main.java中的文件也是--> <context:property-placeholder location="classpath:/db.properties"/>
2.2.3 取值
在 Spring 配置文件中通过 ${key} 获取小配置文件中对应的值:
<bean id="conn" class="com.yusael.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>
3.自定义类型转换器
类型转换器:将xml中的字符串类型转换为其他类型
自定义:当Spring内部没有提供特定的类型转换器时,可以根据需求自定义类型转换器3.1 开发步骤:
- 新建转换器类,实现Converter<s,t>接口 s:原始类型 v:转换后的类型(以后Spring在注入v类型的时候就知道如何处理了)
- Spring配置文件配置
- Spring配置文件注册:告知Spring它是一个类型转换器
3.2 细节:
ConversionServiceFactoryBean内部有个set<?> Converters集合属性,所以配置文件中,property赋值时采用set子标签,内部再引用第二步配置好的自定义的bean.
创建测试bean Person:
package zyc.stu.Spring5_54_61.Converter; import java.io.Serializable; import java.util.Date; /** * @author zhuZiGe_ * @create 2020-09-11-10:43 */ public class Person implements Serializable { 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; } public Person(String name, Date birthday) { this.name = name; this.birthday = birthday; } public Person() { } }
创建Convertor:实现接口
package zyc.stu.Spring5_54_61.Converter; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * @author zhuZiGe_ * @create 2020-09-11-10:53 */ public class MyDateConverter implements Converter<String,Date> { private String format; public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } /** * convert方法作用:String-->Date * SimpleDateFormat sdf = new SimpleDateFormat(); * sdf.parse(string) * @param s 代表的是配置文件中的value对应的值 * @return date 自动注入到到对应property */ public Date convert(String s) { Date date = null; try { SimpleDateFormat sdf= new SimpleDateFormat(this.format); date = sdf.parse(s); } catch (ParseException e) { e.printStackTrace(); } return date; } }
配置
步骤:-
将自定义的类型转换器纳入Spring管理
-
注册类型转换器
ConversionServiceFactoryBean的id值只能为conversionService自定义的类型转换器都要纳入ConversionServiceFactoryBean中
<!-- 自定义类型转换器 --> <bean id="myDateConverter" class="zyc.stu.Spring5_54_61.Converter.MyDateConverter"> <property name="format" value="yyyy-MM-dd"/> </bean> <!-- 注册 --> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="myDateConverter"/> </set> </property> </bean> <bean id="person" class="zyc.stu.Spring5_54_61.Converter.Person"> <property name="name" value="zyc"/> <property name="birthday" value="2020-9-11"/> </bean>
测试
@Test public void test2(){ ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext1.xml"); Person person = (Person) context.getBean("person"); System.out.println(person.getBirthday()); }
3.3 思考
- 日期格式作为参数交给spring管理,依赖注入解耦
- 注册时,bean的id属性必须为conversionService,不然spring找不到
- Spring框架内置了日期类型转换器 格式为
2020/9/11
4.后置处理bean
BeanPostProcessor作用:对Spring工厂创建的对象进行再加工。
4.1 Spring创建对象步骤
实现BeanPostProcessor接口前:
-
构造方法创建bean-->InitializingBean-->init-method=""
实现BeanPostProcessor接口后:
-
构造方法创建bean-->postProcessBeforeInitialization()-->InitializingBean-->init-method=""-->postProcessAfterInitialization()
4.2 两个重写的方法
postProcessBeforeInitialization(Object,String):
加工bean,对象作为第一个参数,id作为第二个参数
postProcessAfterInitialization(Object,String):
再次加工bean,对象作为第一个参数,id作为第二个参数4.3 实战建议
基本上不写初始化操作,对于可能出现的加工操作就没必要区分before和after了,只要实现其中一个after即可,before仅充当一个搬运工的角色
-
before可以什么都不干,但必须返还bean给Spring
4.4 细节
BeanPostProcessor会对Spring工厂中(仅限于注册的xml文件中的bean)所有的对象进行加工,如果只处理某个对象,别忘了类型判断。
测试bean
package zyc.stu.Spring5_54_61.beanPostProcess; /** * @author zhuZiGe_ * @create 2020-09-11-15:37 */ public class Category { private int id; private String name; @Override public String toString() { return "Category{" + "id=" + id + ", name='" + name + '\'' + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Category() { } public Category(int id, String name) { this.id = id; this.name = name; } }
后置处理bean
- before一般只做传递bean
- after中要做类型判断:
在同一个Spring的配置文件中存在很多其他的bean,如果没有将这些bean的scope属性设置为prototype,则在创建工厂时就会报错,类型转换异常;就算设为了prototype,当你不小心获取这些bean的时候也会报同样的错误。因为这些bean都会经过Processor的处理
package zyc.stu.Spring5_54_61.beanPostProcess; import org.springframework.beans.BeansException; /** * @author zhuZiGe_ * @create 2020-09-11-15:40 */ public class BeanPostProcessor implements org.springframework.beans.factory.config.BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof Category){ Category cat = (Category) bean; cat.setName("hahah"); } return bean; } }
配置
<bean id="category" class="zyc.stu.Spring5_54_61.beanPostProcess.Category"> <property name="id" value="12"/> <property name="name" value="嘻嘻嘻"/> </bean> <bean id="myBeanPostProcessor" class="zyc.stu.Spring5_54_61.beanPostProcess.BeanPostProcessor"/>
测试
@Test public void test3(){ ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext1.xml"); Category category = (Category) context.getBean("category"); System.out.println(category); }
-
-
Spring 学习 对象的⽣命周期、配置⽂件参数化、后置处理Bean
2020-06-15 17:09:29细节3、后置处理Bean 1、对象的⽣命周期 1、什么是对象的⽣命周期 指的是⼀个对象创建、存活、消亡的⼀个完整过程 2、 为什么要学习对象的⽣命周期 由Spring负责对象的创建、存活、销毁,了解⽣命周期,有利于我们...Spring 学习 对象的⽣命周期
1、对象的⽣命周期
1、什么是对象的⽣命周期
指的是⼀个对象创建、存活、消亡的⼀个完整过程
2、 为什么要学习对象的⽣命周期
由Spring负责对象的创建、存活、销毁,了解⽣命周期,有利于我们使⽤好Spring为我们创建的对象
⽣命周期的3个阶段
1、创建阶段
Spring⼯⼚何时创建对象
scope=“singleton”
Spring⼯⼚创建的同时,对象的创建
注意:设置scope=singleton 这种情况下 也需要在获取对象的同时,创建对象scope=“prototype”
Spring⼯⼚会在获取对象的同时,创建对象ctx.getBean("")2、、初始化阶段
Spring⼯⼚在创建完对象后,调⽤对象的初始化⽅法,完成对应的初始化操作
- 初始化⽅法提供:程序员根据需求,提供初始化⽅法,最终完成初始化操作
- 初始化⽅法调⽤:Spring⼯⼚进⾏调⽤
InitializingBean接⼝
//程序员根据需求,实现的⽅法,完成初始化操作
public void
afterProperitesSet(){
}对象中提供⼀个普通的⽅法
public void myInit(){
}
细节分析
1、如果⼀个对象即实现InitializingBean 同时⼜提供的 普通的初始化⽅法 顺序- InitializingBean
- 普通初始化⽅法
2、注⼊⼀定发⽣在初始化操作的前⾯
3、什么叫做初始化操作
资源的初始化:数据库 IO ⽹络3、销毁阶段
Spring销毁对象前,会调⽤对象的销毁⽅法,完成销毁操作
- Spring什么时候销毁所创建的对象? ctx.close();
- 销毁⽅法:程序员根据⾃⼰的需求,定义销毁⽅法,完成销毁操作
调⽤:Spring⼯⼚完成调⽤
DisposableBean
public void destroy()throws
Exception{}
定义⼀个普通的销毁⽅法
public void myDestroy()throws
Exception{
}
细节分析
1.销毁⽅法的操作只适⽤于 scope=“singleton”
2. 什么叫做销毁操作
主要指的就是 资源的释放操作 io.close() 、connection.close();2、配置⽂件参数化
把Spring配置⽂件中需要经常修改的字符串信息,转移到⼀个更⼩的配置⽂件中
- Spring的配置⽂件中存在需要经常修改的字符串?
存在 以数据库连接相关的参数 代表 - 经常变化字符串,在Spring的配置⽂件中,直接修改
不利于项⽬维护(修改) - 转移到⼀个⼩的配置⽂件(.properties)
利于维护(修改)
配置⽂件参数化:利于Spring配置⽂件的维护(修改)
1、配置⽂件参数的开发步骤
提供⼀个⼩的配置⽂件(.properities)
名字:随便 放置位置:随便 jdbc.driverClassName =com.mysql.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/suns? useSSL=false jdbc.username = root jdbc.password = 123456
Spring的配置⽂件与⼩配置⽂件进⾏整合
applicationContext.xml <context:property-placeholder location="classpath:/db.properties"/>
在Spring配置⽂件中通过${key}获取⼩配置⽂件中
对应的值
3、⾃定义类型转换器
1、类型转换器
作⽤:Spring通过类型转换器把配置⽂件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进⽽完成了注⼊
2、⾃定义类型转换器
原因:当Spring内部没有提供特定类型转换器时,⽽程序员在应⽤的过程中还需要使⽤,那么就需要程序员⾃⼰定义类型转换器
类 implements Converter接⼝
public class MyDateConverter implements Converter<String, Date> { @Override public Date convert(String source) { Date date = null; try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); date = sdf.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; }
convert⽅法作⽤:String —> Date
SimpleDateFormat sdf = newSimpleDateFormat();
sdf.parset(String) —> Date
param:source 代表的是配置⽂件中 ⽇期字符串 2020-10-11
return : 当把转换好的Date作为convert⽅法的返回值后,Spring⾃动的为birthday属性进⾏注⼊(赋值)在Spring的配置⽂件中进⾏配置
MyDateConverter对象创建出来
<bean id="myDateConverter" class="xxxx.MyDateConverter"/>
类型转换器的注册
⽬的:告知Spring框架,我们所创建的MyDateConverter是⼀个类型转换器 <!--⽤于注册类型转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFa ctoryBean"> <property name="converters"> <set> <ref bean="myDateConverter"/> </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; } public Date convert(String source) { Date date = null; try { SimpleDateFormat sdf = new SimpleDateFormat(pattern); date = sdf.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; } }
<!--Spring创建MyDateConverter类型对象--> <bean id="myDateConverter" class="com.baizhiedu.converter.MyDateConverter"> <property name="pattern" value="yyyy-MM-dd"/> </bean>
ConversionSeviceFactoryBean 定义 id属性 值必须conversionService
Spring框架内置⽇期类型的转换器
⽇期格式:2020/05/01 (不⽀持 :2020-05-01)3、后置处理Bean
BeanPostProcessor作⽤:对Spring⼯⼚所创建的对象,进⾏再加⼯。
AOP底层实现:
注意:BeanPostProcessor接⼝xxxx(){
}
后置处理Bean的运⾏原理分析
`程序员实现BeanPostProcessor规定接⼝中的⽅法:
Object postProcessBeforeInitiallization(Object bean String beanName)
作⽤:Spring创建完对象,并进⾏注⼊后,可以运⾏Before⽅法进⾏加⼯获得Spring创建好的对象 :通过⽅法的参数最终通过返回值交给Spring框架实战中:
很少处理Spring的初始化操作:没有必要区分Before After。只需要实现其中的⼀个After⽅法即可
注意:
postProcessBeforeInitiallization
return bean对象BeanPostProcessor的开发步骤
1、 实现 BeanPostProcessor接⼝public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } //修改了categroy 的name 值 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Categroy categroy =(Categroy) bean; categroy.setName("xiaowb"); return categroy; } }
Spring的配置⽂件中进⾏配置
<bean id="myBeanPostProcessor" class="xxx.MyBeanPostProcessor"/>
BeanPostProcessor细节
BeanPostProcessor会对Spring⼯⼚中所有创建的对象进⾏加⼯。
-
【Spring IOC】Spring Bean生命周期、配置文件参数化、自定义类型转换器、后置处理Bean
2020-08-29 20:21:03什么是对象的⽣命周期? ⼀个对象 创建、存活、消亡 的⼀个完整过程。 为什么要学习对象的⽣命周期? 由 Spring 负责对象的 创建、存活、销毁,了解⽣命周期,有利于我们使用好 Spring 为我们创建的对象。 ⽣命...对象的生命周期
什么是对象的⽣命周期?
- ⼀个对象 创建、存活、消亡 的⼀个完整过程。
为什么要学习对象的⽣命周期?
- 由 Spring 负责对象的 创建、存活、销毁,了解⽣命周期,有利于我们使用好 Spring 为我们创建的对象。
⽣命周期的 3 个阶段:
- 创建阶段 —> 初始化阶段 —> 销毁阶段
创建阶段
Spring 工厂何时创建对象?
scope="prototype"
:Spring 工厂在获取对象ctx.getBean("xxx")
的同时,创建对象。scope="singleton"
:Spring 工厂创建的同时,创建对象。- 通过配置
<bean lazy-init="true"/>
懒加载也可以实现工厂获取对象的同时,创建对象。
- 通过配置
初始化阶段
什么时候?
- Spring 工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作。
初始化方法提供:由程序员根据需求,提供初始化方法,最终完成初始化操作。
初始化方法调用:Spring 工厂进行调用。
提供初始化方法的两种方式:
InitializingBean
接口:
public class Product implements InitializingBean { // 程序员根据需求实现的方法 // 由 Spring工厂 调用,完成初始化操作 @Override public void afterPropertiesSet() throws Exception { System.out.println("Product.afterPropertiesSet"); } }
<bean id="product" class="com.hey.life.Product" />
- 对象中提供一个普通的初始化方法,配置文件种配置
init-method
:
public class Product { public void myInit() { System.out.println("Product.myInit"); } }
<bean id="product" class="com.yusael.life.Product" init-method="myInit"/>
与 FactoryBean接口 类似,如果不使用接口就可以在配置文件中配置方法。
【注意】
如果⼀个对象既实现
InitializingBean
同时⼜提供的 普通的初始化方法,执行顺序?- 先执行
InitializingBean
,再执行 普通初始化方法。
注入⼀定发⽣在初始化操作的前面。正如接口的方法名那样
afterPropertiesSet
,在属性设置之后初始化操作到底是什么?
- 资源的初始化:数据库、IO、网络、…
销毁阶段
销毁阶段,即:Spring 销毁对象前,会调用对象的销毁方法,完成销毁操作。
Spring 什么时候销毁所创建的对象?
- 在工厂关闭的时候,关闭之前会销毁工厂创建的对象。
销毁方法提供:程序员根据业务需求,定义销毁方法,完成销毁操作
销毁方法调用:Spring 工厂进行调用。
开发流程与初始化操作一样,提供销毁方法的两种方式:
DisposableBean
接口:
public class Product implements DisposableBean { // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作 @Override public void destroy() throws Exception { System.out.println("Product.destroy"); } }
对象中提供一个普通的销毁方法,配置文件种配置
destroy-method
:public class Product { // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作 public void myDestory() { System.out.println("Product.myDestory"); } }
<bean id="product" class="com.yusael.life.Product" destroy-method="myDestory"/>
【注意】
- 销毁方法的操作只适用于
scope="singleton"
,初始化操作则都适用。
销毁操作到底是什么?
- 资源的释放:
io.close()
、connection.close()
、…
对象的生命周期总结
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; } Product() { // 创建 System.out.println("Product.Product"); } // 程序员根据需求实现的方法, 完成初始化操作 public void myInit() { System.out.println("Product.myInit"); } // 程序员根据需求实现的方法, 完成初始化操作 @Override public void afterPropertiesSet() throws Exception { System.out.println("Product.afterPropertiesSet"); } public void myDestory() { System.out.println("Product.myDestory"); } // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作 @Override public void destroy() throws Exception { System.out.println("Product.destroy"); } }
<bean id="product" class="com.yusael.life.Product" init-method="myInit" destroy-method="myDestory"> <property name="name" value="yusael"/> </bean>
配置文件参数化
配置文件参数化:把 Spring 配置文件中需要经常修改的字符串信息,转移到⼀个更小的配置文件中。
Spring 的配置文件中是否存在需要经常修改的字符串?
- 存在:例如与数据库连接相关的参数等
- 经常变化得字符串,在 Spring 的配置文件中直接修改不利于项目维护(修改)
- 将之转移到⼀个小的配置文件(
.properties
)利于维护(修改) - 优点:利于 Spring 配置文件的维护(修改)
开发步骤
- 提供⼀个小的配置文件(.properities)
名字:没有要求
放置位置:没有要求
jdbc.driverClassName = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://localhost:3306/spring?useSSL=false jdbc.username = root jdbc.password = 1234
- 整合到 Spring 的配置文件中:
<!--Spring的配置文件与⼩配置文件进行整合--> <!--resources 下的文件在整个程序编译完后会被放到 输出目录得 classes 目录下, 也就是 classpath路径下, src.main.java 中的文件也会整合到这个目录之下--> <context:property-placeholder location="classpath:db.properties"/>
- 使用时,可以直接在 Spring 配置文件中通过
${key}
获取小配置文件中对应的值:
<bean id="conn" class="com.yusael.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>
自定义类型转换器
类型转换器:Spring 通过 类型转换器 把 配置文件 中 字符串 类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入。
自定义一个类型转换器
- 对于一些常见的数据类型,Spring有自带的类型转换器,可以转换字符串
- 但是对于一些特殊的数据类型,Spring 内部没有提供特定类型转换器
- 而程序员在应用的过程中还需要使用,那么就需要程序员⾃⼰定义类型转换器。
实现步骤:
- 实现 Converter 接口
public class MyDateConverter implements Converter<String, Date> { /* convert方法作用: String ---> Date SimpleDateFormat sdf = new SimpleDateFormat(); sdf.parset(String) ---> Date 参数: source : 代表的是配置文件中, 日期字符串 <value>2020-10-11</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) { e.printStackTrace(); } return date; } }
- 配置文件中,先创建
MyDateConverter
对象,再注册类型转换器
<!--创建 MyDateConverter 对象--> <bean id="myDateConverter" class="com.yusael.converter.MyDateConverter"/> <!--用于注册类型转换器--> <!--这里的 id 和 class 是固定的,通过注入将转换器对象注入,即通过这个工厂类来生产转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="myDateConverter"/> </set> </property> </bean> <!-- 测试对象,注入一个日期属性 --> <bean id="good" class="com.yusael.converter.Good"> <property name="name" value="zhenyu"/> <property name="birthday" value="2012-12-12"/> </bean>
这样就可以将配置文件中的字符串信息转换成日期类型的数据
细节分析
在上面的例子中,转换日期所使用的模板字符串是直接写在代码中的,这样耦合性很大,我们也可以将模板字符串再配置文件中进行配置,通过注入的方式,由配置文件赋值
public class MyDateConverter implements Converter<String, Date> { // 提取出来,作为其中的一个属性 private String pattern; @Override public Date convert(String source) { Date date = null; try { // 卸载代码中耦合较大 SimpleDateFormat sdf = new SimpleDateFormat(pattern); date = sdf.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; } public String getPattern() { return pattern; } public void setPattern(String pattern) { this.pattern = pattern; } }
<!-- 配置文件完成对日期格式的赋值 --> <bean id="myDateConverter" class="com.yusael.converter.MyDateConverter"> <property name="pattern" value="yyyy-MM-dd"/> </bean>
Spring 框架其实内置了日期类型的转换器:只是日期格式必须是
2020/05/01
,但是我们平时并不会这么写,所以还是要自己实现一个<bean id="good" class="com.yusael.converter.Good"> <property name="name" value="zhenyu"/> <property name="birthday" value="2012/12/12"/> </bean>
后置处理Bean
后置处理Bean
BeanPostProcessor
作用:对 Spring 工厂所创建的对象,进行再加工。(AOP 的底层实现)BeanPostProcessor 实际是个接口,其中有两个方法需要实现:
- Spring 创建完对象,并进行注入后,可以运行
Before
⽅法进行加工;
// 通过方法的参数获得 Spring 创建好的对象,最终通过返回值交给 Spring 框架。 public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; }
- Spring 执行完对象的初始化操作后,可以运行
After
⽅法进行加工;
// 通过方法的参数获得 Spring 创建好的对象,最终通过返回值交给 Spring 框架。 public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; }
执行顺序:
如果有属性需要注入,那就要在调用构造方法之后,先执行注入过后再去处理
尽管有两个可实现的方法,但是在实际应用中:很少处理 Spring 的初始化操作,没有必要区分 Before,After。
- 只需要实现其中一个,建议是 After 方法即可。
开发步骤
- 类 实现
BeanPostProcessor
接口
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return null; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Category category = (Category) bean; category.setName("yusael"); return category; } }
- Spring 配置文件中进行配置
<bean id="myBeanPostProcessor" class="com.yusael.beanpost.MyBeanPostProcessor"/>
【注意】
BeanPostProcessor
会对Spring 工厂
创建的所有对象进行加工。- 如果工厂创建了多个不同的对象,要注意区别传入的对象
@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 先判断是不是需要加工处理的 Bean if (bean instanceof Category) { Category category = (Category) bean; category.setName("yusael"); return category; } return bean; }
-
【Spring5.x】对象的生命周期、配置文件参数化、自定义类型转换器、后置处理Bean
2020-05-25 00:39:28什么是对象的⽣命周期? ⼀个对象 创建、存活、消亡 的⼀个完整过程。 为什么要学习对象的⽣命周期? 由 Spring 负责对象的 创建、存活、销毁,了解⽣命周期,有利于我们使用好 Spring 为我们创建的对象。 ⽣命...工厂高级特性
更多内容请查看笔记目录:【Spring 5.x】学习笔记汇总
对象的生命周期
什么是对象的⽣命周期?
- ⼀个对象 创建、存活、消亡 的⼀个完整过程。
为什么要学习对象的⽣命周期?
- 由 Spring 负责对象的 创建、存活、销毁,了解⽣命周期,有利于我们使用好 Spring 为我们创建的对象。
⽣命周期的 3 个阶段:
- 创建阶段 —> 初始化阶段 —> 销毁阶段
创建阶段
Spring 工厂何时创建对象?
scope="prototype"
:Spring 工厂在获取对象ctx.getBean("xxx")
的同时,创建对象。scope="singleton"
:Spring 工厂创建的同时,创建对象。
通过配置<bean lazy-init="true"/>
也可以实现工厂获取对象的同时,创建对象。
初始化阶段
什么时候?Spring 工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作。
初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作。
初始化方法调用:Spring 工厂进行调用。提供初始化方法的两种方式:
InitializingBean
接口:
public class Product implements InitializingBean { //程序员根据需求实现的方法, 完成初始化操作 @Override public void afterPropertiesSet() throws Exception { System.out.println("Product.afterPropertiesSet"); } }
- 对象中提供一个普通的初始化方法,配置文件种配置
init-method
:
public class Product { public void myInit() { System.out.println("Product.myInit"); } }
<bean id="product" class="com.yusael.life.Product" init-method="myInit"/>
初始化操作的细节分析:
-
如果⼀个对象既实现
InitializingBean
同时⼜提供的 普通的初始化方法,执行顺序?
先执行InitializingBean
,再执行 普通初始化方法。 -
注入⼀定发⽣在初始化操作的前面。
-
初始化操作到底是什么?
资源的初始化:数据库、IO、网络、…
销毁阶段
Spring 销毁对象前,会调用对象的销毁方法,完成销毁操作。
Spring 什么时候销毁所创建的对象?ctx.close();
销毁方法提供:程序员根据业务需求,定义销毁方法,完成销毁操作
销毁方法调用:Spring 工厂进行调用。开发流程与初始化操作一样,提供销毁方法的两种方式:
DisposableBean
接口:
public class Product implements DisposableBean { // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作 @Override public void destroy() throws Exception { System.out.println("Product.destroy"); } }
- 对象中提供一个普通的销毁方法,配置文件种配置
destroy-method
:
public class Product { // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作 public void myDestory() { System.out.println("Product.myDestory"); } }
<bean id="product" class="com.yusael.life.Product" destroy-method="myDestory"/>
销毁阶段细节分析:
- 销毁方法的操作只适用于
scope="singleton"
,初始化操作都适用。 - 销毁操作到底是什么?
资源的释放:io.close()
、connection.close()
、…
对象的生命周期总结
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; } Product() { // 创建 System.out.println("Product.Product"); } // 程序员根据需求实现的方法, 完成初始化操作 public void myInit() { System.out.println("Product.myInit"); } // 程序员根据需求实现的方法, 完成初始化操作 @Override public void afterPropertiesSet() throws Exception { System.out.println("Product.afterPropertiesSet"); } public void myDestory() { System.out.println("Product.myDestory"); } // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作 @Override public void destroy() throws Exception { System.out.println("Product.destroy"); } }
<bean id="product" class="com.yusael.life.Product" init-method="myInit" destroy-method="myDestory"> <property name="name" value="yusael"/> </bean>
配置文件参数化
配置文件参数化:把 Spring 配置文件中需要经常修改的字符串信息,转移到⼀个更小的配置文件中。
- Spring 的配置文件中是否存在需要经常修改的字符串?
存在:以数据库连接相关的参数… - 经常变化字符串,在 Spring 的配置文件中,直接修改不利于项目维护(修改)
- 转移到⼀个小的配置文件(.properties)利于维护(修改)
优点:利于 Spring 配置文件的维护(修改)
配置文件参数的开发步骤
- 提供⼀个小的配置文件(.properities)
名字:没有要求
放置位置:没有要求
jdbc.driverClassName = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://localhost:3306/spring?useSSL=false jdbc.username = root jdbc.password = 1234
- Spring 的配置文件与小配置文件进行整合:
<!--Spring的配置文件与⼩配置文件进行整合--> <!--resources 下的文件在整个程序编译完后会被放到 classpath 目录下,src.main.java中的文件也是--> <context:property-placeholder location="classpath:/db.properties"/>
在 Spring 配置文件中通过
${key}
获取小配置文件中对应的值:<bean id="conn" class="com.yusael.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>
自定义类型转换器
类型转换器
作用:Spring 通过 类型转换器 把 配置文件 中 字符串 类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入。
自定义类型转换器
产生原因:当 Spring 内部没有提供特定类型转换器时,而程序员在应用的过程中还需要使用,那么
就需要程序员⾃⼰定义类型转换器。[开发步骤]:
- 类 implements Converter 接口
public class MyDateConverter implements Converter<String, Date> { /* convert方法作用: String ---> Date SimpleDateFormat sdf = new SimpleDateFormat(); sdf.parset(String) ---> Date 参数: source : 代表的是配置文件中, 日期字符串 <value>2020-10-11</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) { e.printStackTrace(); } return date; } }
- 在 Spring 的配置文件中进行配置;
先创建MyDateConverter
对象,再注册类型转换器;
<!--创建 MyDateConverter 对象--> <bean id="myDateConverter" class="com.yusael.converter.MyDateConverter"/> <!--用于注册类型转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="myDateConverter"/> </set> </property> </bean> <bean id="good" class="com.yusael.converter.Good"> <property name="name" value="zhenyu"/> <property name="birthday" value="2012-12-12"/> </bean>
自定义类型转换器细节
MyDateConverter
中的日期的格式,通过 依赖注入 的方式,由配置文件完成赋值。
public class MyDateConverter implements Converter<String, Date> { private String pattern; @Override public Date convert(String source) { Date date = null; try { SimpleDateFormat sdf = new SimpleDateFormat(pattern); date = sdf.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; } public String getPattern() { return pattern; } public void setPattern(String pattern) { this.pattern = pattern; } }
<!-- 配置文件完成对日期格式的赋值 --> <bean id="myDateConverter" class="com.yusael.converter.MyDateConverter"> <property name="pattern" value="yyyy-MM-dd"/> </bean>
ConversionSeviceFactoryBean
定义 id属性,值必须是conversionService
;
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="myDateConverter"/> </set> </property> </bean>
- Spring 框架其实内置了日期类型的转换器:日期格式必须是
2020/05/01
。
<bean id="good" class="com.yusael.converter.Good"> <property name="name" value="zhenyu"/> <property name="birthday" value="2012/12/12"/> </bean>
后置处理 Bean
BeanPostProcessor 作用:对 Spring 工厂所创建的对象,进行再加工。(AOP 的底层实现)
后置处理 Bean 原理分析
程序员实现BeanPostProcessor
接口中规定的两个方法:public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; }
作用:Spring 创建完对象,并进行注入后,可以运行
Before
⽅法进行加工;- 通过方法的参数获得 Spring 创建好的对象,最终通过返回值交给 Spring 框架。
public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; }
作⽤:Spring 执行完对象的初始化操作后,可以运行
After
⽅法进行加工;- 通过方法的参数获得 Spring 创建好的对象,最终通过返回值交给 Spring 框架。
实战中:很少处理 Spring 的初始化操作,没有必要区分
Before
,After
。只需要实现其中一个,建议是After
方法即可。BeanPostProcessor 开发步骤
- 类 实现
BeanPostProcessor
接口
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return null; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Category category = (Category) bean; category.setName("yusael"); return category; } }
- Spring 配置文件中进行配置
<bean id="myBeanPostProcessor" class="com.yusael.beanpost.MyBeanPostProcessor"/>
细节:BeanPostProcessor 会对 Spring 工厂创建的所有对象进行加工。如果工厂创建了多个不同的对象,要注意区别传入的对象:
@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof Category) { Category category = (Category) bean; category.setName("yusael"); return category; } return bean; }
-
前置++ 和 后置++
2020-06-18 23:29:28这里也很好解释了为什么后置++在感觉上是在语句执行完后才进行递增,其实在执行过程中只是对临时值进行处理,原有值已经递增了。 注意: 为什么在前置++时要实现返回引用,而后置++时却不用引用返回。 . -
spring容器启动过程
2020-10-27 03:01:581.什么是BeanFactoryPostProcessor 叫做BeanFactory的后置处理器,和Bean的后置处理器对比理解。 BeanPostProcessor是用来对Bean进行处理的, BeanFactoryPostProcessor是用来对BeanFactory进行处理的。 2.... -
spring1.0初探之容器初始化过程解析
2020-10-29 18:06:10上一篇我们说到了spring...无论是spring1.0还是spring5.0,其核心思想都离不开容器的初始化,bean的注入,后置处理起的使用等等功能,那么spring后面的版本提供了很多复杂的功能,这对我们阅读源码有一定的影响,而sp -
spirng源码阅读日记3--spring aop
2018-06-05 14:55:18这里主要以xml配置为例解析aop代理的具体生成过程。aop是对目标对象的某个...其实就是在创建bean时,初始化完成后,进行后置处理得到的。(AbstractAutowireCapableBeanFactory 579行 exposedObject = initializeBea... -
Java设计模式之建造者模式
2018-11-28 19:24:00概论 什么是建造者模式呢?将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。...第四步:后置处理,例如记录操作日志 最后核心的算法设计在run方法中。如下代码所示: 1 pu... -
UML和模式应用(架构师必备).part01.rar
2010-04-02 14:11:231.4 什么是面向对象分析和设计 1.5 简短示例 1.6 什么是UML 1.7 可视建模的优点 1.8 历史 1.9 参考资料 第2章 迭代、进化和敏捷 2.1 什么是UP?其他方法能否对其进行补充 2.2 什么是迭代和进化式开发 2.3 ... -
UML和模式应用 面向对象分析与设计导论
2010-02-07 15:04:491.5.3 组织中的角色是什么 1.5.4 谁该干什么?他们之间如何协作 1.6 面向对象的分析与设计的例子 1.6.l 定义用况 1.6.2 定义概念模型 1.6.3 定义协作图 1.6.4 定义设计类图 1.6.5 掷骰子游戏... -
JAVA面试题最全集
2010-03-13 13:09:1087.UNIX中QT是什么意思? 88.在软件开发生命周期中的哪个阶段开始测试? 89.dotnet与J2EE的比较? 90.什么是ActiveX? 91.Java中IDL是什么? 92.ISO9000和CMM是什么?IS09000和CMM(软件能力成熟度模型)认证是国际上... -
C++面向对象高效编程第2版 (中文版、带书签) 作者: (美)Kayshav Dattatri
2018-10-31 22:38:46第1章 什么是面向对象编程 1 1.1 背景 1 1.1.1 面向过程编程示例 2 1.1.2 银行账户的表示 3 1.1.3 银行账户的安全 4 1.1.4 用面向对象编程解决问题 5 1.2 理解对象模型 7 1.3 术语 8 1.4 ... -
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是Spring ...
-
C语言入门经典(第4版)--源代码及课后练习答案
2013-02-02 17:18:552.2 什么是变量 21 2.3 存储数值的变量 21 2.3.1 整数变量 21 2.3.2 变量的命名 25 2.3.3 变量的使用 26 2.3.4 变量的初始化 28 2.3.5 算术语句 28 2.4 变量与内存 34 2.5 整数变量类型 35 2.5.1 无符号的... -
C语言入门经典(第4版)--详细书签版
2013-02-02 17:16:502.2 什么是变量 21 2.3 存储数值的变量 21 2.3.1 整数变量 21 2.3.2 变量的命名 25 2.3.3 变量的使用 26 2.3.4 变量的初始化 28 2.3.5 算术语句 28 2.4 变量与内存 34 2.5 整数变量类型 35 2.5.1 无符号的... -
《从新手到高手——C++全方位学习》.pdf【第一部分】
2011-06-23 16:12:1315.5.4 句柄是什么 294 15.5.5 显示图片 295 15.5.6 动画 299 15.5.7 键盘控制人物移动 303 15.5.8 迷宫墙壁 307 15.5.9 走迷宫 313 15.5.10 用链表记录行走路线 316 第16章 多态性 322 16.1 为什么要使用... -
C# 3.0与.NET 3.5高级编程
2013-05-15 14:12:538.1.1 XAML是什么 139 8.1.2 用C#模拟XAML实现的内容 141 8.1.3 XAML的种类 143 8.2 XAML基础 143 8.2.1 命名空间 144 8.2.2 代码后置文件 146 8.2.3 使用简单的属性和类型转换器 149 8.2.4 属性语法与属性元素语法 ... -
深入实践C++模板编程.温宇杰(带详细书签).pdf
2018-04-11 16:01:54同时,这也是一本有趣的计算机图书,作者用生动的语言、精巧的构思以及详尽的示例,定会使读者在学习模板编程的过程中体会到发现新风景的愉悦。对于C++语言进阶读者,强烈推荐本书。 —— 广州三星通信研究院 首席... -
传智播客扫地僧视频讲义源码
2019-04-03 21:54:2214_成员函数和友元函数完成一元运算符重载(后置) 15_友元函数实现左移右移操作符重载(函数返回值当左值需返回引用)_传智扫地僧 16_友元函数实现操作符重载知识总结 17_重载等号操作符_传智扫地僧 18_数组类小案例_...
-
88
-
CS8316双节锂电池7.4V供电内置升压防破音恒定单声道25W音频功放IC
-
Java面向对象重温【单例设计模式】
-
项目经理成长之路
-
MySQL 管理利器 mysql-utilities
-
【布道者】Linux极速入门
-
关于下拉框无法设置只读readonly
-
NFS 网络文件系统
-
SecureCRT 连接 GNS3/Linux 的安全精密工具
-
2021-02-26
-
C++代码规范和Doxygen根据注释自动生成手册
-
china_regional_data.sql
-
shell实例-跨目录执行,彩色输出,临时文件,行遍历文件
-
access应用的3个开发实例
-
shell 脚本关键字&符号
-
go定时任务
-
JMETER 性能测试基础课程
-
2021-02-26
-
DTS结构及其编译方法.pdf
-
微信读书APP协议阅读 2021-2-26