第五章:控制Spring工厂创建对象的次数
一:控制简单对象的创建次数
<!--控制这个类的创建的次数,这个参数默认是SingleTon-->
<bean id="account" class = "" scope="singleton"/>
/*
* @Description:测试spring只创建一个对象。
* @Author: DaShu
* @Date: 2021/5/31 14:31
*/
@Test
public void test18(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("/");
Account account = (Account)ctx.getBean("account");
Account account1 = (Account) ctx.getBean("account");
System.out.println(account == account1);
//当是singleton的时候,spring只会创建一次这个对象。
//当是prototype的时候,获取一次创建一次。
//spring当中不写这个属性的时候,默认就是singleton。
}
二:如何控制复杂对象的创建次数
FactoryBean接口当中的isSingleTon()重写的时候,return true即可。
实例工厂和静态工厂,还是以scope属性的方式进行控制。
三:为什么控制创建对象的次数
有些对象是可以大家公用的,可以公用的这些对象就创建一次就可以了,有些对象是不能公用的,不能公用的就一人创建一次,这样做就是节省内存的空间,节省不必要的内存浪费
什么样的对象只创建一次就行了
SqlSessionFactory这个mybatis工厂对象是一个重量级的对象,重量级的对象只创建一次就好了,Dao,的对象,Service只创建一次被大家公用就可以了
什么样的对象每次都创建新的呢
1:connection对象,设计到事务.
2:sqlSession对象,封装了连接对象。
3:Session对象。 Struct2当中Controller当中的actrion
总结:线程安全,可以公用,才只创建一次
第六章:对象的生命周期
一:什么是对象的生命周期
对象的生命周期指的是一个对象创建到销毁的完整的过程。
二:研究对象生命周期的意义
User user = new User();我们通过new的方式在java虚拟机当中创建了一个对象,只要有引用指向这个对象,对象就会一直存在于jvm内存当中,当虚拟机内存满了,或者整个进程结束了,那么这个对象就消亡了。
对象交由Spring进行创建和管理之后,对象的创建、存活(保存)、销毁都交由Spring进行管理,我们需要了解其中的原理,并且合理的进行利用。
三:声明周期三阶段
对象交由Spring创建之后,生命周期的三个阶段
1:创建阶段
Spring工厂创建对象,当对象的创建是scope = singleton的时候,spring工厂创建的同时,对象也就被创建了,当对象的创建是scope = prototype的时候,spring会在获取对象的同时创建对象。获取对象就是getBean()方法执行的时候
如果scope是 singleton但是我们就想在getBean()的时候获取对象,实现一种懒加载的情况,那么我们应该怎么做?添加一个lazy-init= true属性
总结:singleton情况默认是在工厂对象创建的时候就创建了,如果这个singleton类型想要做到懒加载的话,bean标签当中添加一个属性就好了,单例默认都不是懒加载,多例默认都是懒加载,如果想改变这个规则,可以添加一个属性。**
2:初始化阶段
初始化阶段:Spring工厂创建完对象之后会调用对象的初始化方法完成对应的初始化操作。
初始化方法是谁提供:是由程序员根据需求,提供初始化方法,完成初始化操作。
初始化方法调用:spring的工厂来调用初始化方法
初始化方法的定义:spring为我们提供了两种定义对象初始化方法的途径,第一种是类实现InitializingBean这个接口,在这个接口当中为我们定义了一个方法,afterPropertiesSet()方法。可以把我们对象的初始化代码写到这里边,当spring识别类实现了这个接口之后,就会调用这个方法(这个接口耦合了spring的接口,造成了一定的侵入)。第二种方式不需要我们实现任何接口,在对象中提供一个普通的方法,这个方法 public void myInit(){} 方法名可以任意取,spring识别这个方法通过配置文件来告诉他应该调用哪个。这两种方法可以同时使用,回调会最先执行,初始化方法第二执行
<bean id = "product" class = "" init-method ="myInit"/>
/*
* @Description:测试--afterPropertiesSet方法执行了 spring的初始化方法
* @Author: DaShu
* @Date: 2021/5/31 14:31
* result:
*/
@Test
public void test20(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("/");
System.out.println("-----------------------工厂对象已经加载完毕------------------------");
Product product = (Product) ctx.getBean("product");
//
//afterPropertiesSet方法执行了。
//myInit方法执行了。
}
如果一个对象上上述两种方式都采用了,那么会怎么样?
先执行实现接口执行回调的方法,在执行普通的初始化方法。
Spring创建完对象之后会进行DI注入和初始化那么spring是先进行注入还是先进行初始化呢
Spring创建对象之后会先进行注入,注入完成之后在进行初始化。也就是先为成员边变量赋值,在进行初始化,所以,初始化方法叫做afterpropertyset,初始化方法经常用作资源的加载或者资源的初始化。
// --构造方法创建对象
// --set方法进行注入
//afterPropertiesSet方法执行了。--接口方法进行初始话
//myInit方法执行了。--普通方法进行初始化。
什么叫做初始化操作?
对于数据的初始化: 数据库-- IO—网络。所谓的初始化操作大多是资源的初始化,大部分情况下都是为了系统资源的初始化,这些操作会耗费时间占用内存资源,所以我们一般在系统启动的时候进行操作。**
3: 销毁阶段
什么叫做销毁阶段?
Spring销毁对象前会调用spring的销毁方法完成销毁操作
Spring什么时候销毁他所创建的对象呢
Spring 销毁他创建的对象,是在工厂关闭的时候,在工厂关闭之前也就是调用close方法的时候,spring工厂会销毁他所创建的对象
销毁方法是谁定义的
销毁方法是程序员定义的,程序员是根据需求完成销毁操作
销毁方法谁来调用呢
Spring来调用,Spring工厂来完成调用
如何定义销毁方法
定义销毁方法也有两种方式,第一种方法是实现spring的DisposableBean接口,通过实现其中方法进行销毁
另外一种就是通过标签的方式指定方法的名称。自定义一个普通的销毁方法。所谓的销毁操作就是资源释放的操作
/*
* @Description:测试--destroy()方法
* @Author: DaShu
* @Date: 2021/5/31 14:31
* result:
*/
@Test
public void test21(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("/");
Product product = ctx.getBean("product", Product.class);
((ClassPathXmlApplicationContext)ctx).close();
//
//
//afterPropertiesSet方法执行了。
//myInit方法执行了。
//2021-06-02 15:11:03 DEBUG ClassPathXmlApplicationContext:987 - Closing @5e4c8041, started on Wed Jun 02 15:11:02 CST 2021
//
//
}
<!---->
<bean id = "product" class = "" init-method ="myInit" destroy-method="MyDestroy">
<property name="name" value="shit"/>
</bean>
销毁细节分析
销毁细节操作只适用于scope为singleton的作用,对于另外一种没有任何作用
什么叫做销毁操作
所谓的销毁操作值得一些资源的释放,比方说
io流的关闭,链接的关闭。这种销毁操作用的很少。
第七章:配置文件参数化
一:配置文件参数化
所谓的配置文件参数化就是把spring当中常用的常修改的字符串配置到配置文件当中,spring当中的配置文件是不是有哪些经常修改的字符串,为啥要转移到一个小的配置文件当中,这个小的配置文件是什么配置文件?
Spring的配置文件当中存在那些经常修改的字符串吗?
比方说阿里巴巴提供的连接池的配置当中需要提供四个属性,这四个属性是获取链接的必不可少的东西。驱动信息,账户和密码都有可能要变,所以需要写在配置文件当中,存在:以数据库连接相关的参数为代表。
字符串充斥在spring的配置文件当中是不是一个好事
这些东西跟spring的主配置文件放在一块就很累。开发不仅仅考虑一个功能实现的问题还需要考虑一个维护难度的问题
更小的配置文件用什么
我们更小的配置文件用的是properties文件,不用xml文件了。
信息转移走之后的配置应该怎么写
${}的含义表示的是这块的内容是需要使用运算得出的,不是el表达式的意思。value这块的值需要通过运算来获得,是需要通过key所对应的key进行填充,配置文件参数化:利于spring配置文件的维护,便于修改。
二:配置文件参数化的开发步骤
1:提供一个小的配置文件
必须是properteis类型的配置文件,使用这个这个配置文件,主要保证是properteis进行结尾就行,至于是什么开头,放在哪里spring并不介意
2:将小配置文件和Spring配置文件进行整:
<!-- spring配置文件和小配置文件的整合,spring会自动帮我们把context命名空间引入进来,这是spring专门为了整合小配置文件做的一个命名空间-->
<!--小配置文件放在resource文件下边,对于这个maven项目来讲,main下边有java,还有和他同级的resource文件夹,maven开发的过程中是这样分级的,但是
当编译过后,resource里边的文件会放到和com平级的地方看着仿佛是在java里边,编译会把两个目录合二为一,resource下边的文件相当于在java目录的根下边,
所以是和com是平级的就对了,在maven项目中target目录代表的事这些项目编译好的结果,class文件夹是存放编译好的文件,class文件夹下com和配置文件是
平级的。我们把class这个文件夹叫做类路径也就是我们的classpath,classpath同时也是spring当中配置文件的一个关键字,这个关键字代表的就是类路径,就是对应的class路径
这样我们就做好了spring配置文件和小配置文件的整合-->
<context:property-placeholder location="classpath:"/>
<bean id="conn" class = "">
<property name="driverClassName" value="${}"/>
<property name="url" value="${}"/>
<property name="userName" value=""/>
<property name="password" value=""/>
</bean>
第八章:自定义类型转换器
一:什么是类型转换器
在之前的注入的时候,我们都忽略了一个细节,那就是在配置文件当中所写的内容都是字符串类型。那么如何给Integer类型进行赋值呢?当然是可以的。Spring是如何实现类型转换呢?spring实现这个类型转换的原因spring当中的类型转换器起的功效,他这个字符串类型转换器的底层也就是个(“1”);这就是类型转换器最为核心的工作,就是数据类型的转换。
二:自定义类型转换器
就是我们对Spring当中的类型转换器不满意了,这是对于spring的框架的一种扩展;
1:Spring内置转换器不好使的情况*
/*
* @Target: 测试spring的内置转换器不好使的时候。
* @Author: DaShu
* @Date: 2021/6/8 21:35
* @Result: 某些格式的Date类型使用spring内置转换器就不好使了,需要自定义。
*/
@Test
public void test23(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("");
Object person = ctx.getBean("person");
System.out.println(person);
// Failed to instantiate []: Constructor threw exception; nested exception is
//Failed to convert property value of type '' to required type '' for property 'birthday';
// nested exception is : Cannot convert value of type '' to required type
// '' for property 'birthday': no matching editors or conversion strategy found
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="http:///2001/XMLSchema-instance" xmlns:util="/schema/util"
xmlns:context="/schema/context"
xsi:schemaLocation="/schema/beans /schema/beans/ /schema/util /schema/util/ /schema/context /schema/context/">
<bean id="person" class = "">
<property name="name" value = "suns"/>
<property name="birthday" value="2021-04-01"/>
</bean>
</beans>
package com.pactera.spring.converter;
import java.util.Date;
/**
* @Auther: DaShu
* @Date: 2021/6/8 21:33
* @Description:
*/
public class Person {
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;
}
}
对于日期格式来讲,spring并没有提供日期的转换器,因为地区和地区之间的格式都不一样,所以spring就没办法提供了,需要自己写,自己定制。
2:如何自定义类型转换器
需要实现converer接口,重写convert方法
用于解决类似于日期类型转换不过去的这样的情况。
这个代码写好之后需要交由spring进行创建对象,才能让Spring进行使用
/**
* @Auther: DaShu
* @Date: 2021/6/8 21:51
* @Description:
*/
public class MyDateConvert implements Converter<String, Date> {
/*
* @Target: String -> Date
* @Author: DaShu
* @Date: 2021/6/8 21:54
* @Result:
*/
@Override
public Date convert(String source) {
//这个参数Source,他就代表了的是配置文件中的日期字符串
//一旦converter进行了转换之后,怎么交给属性进行赋值呢?spring帮我们做了,返回值
//spring会自动使用进行赋值。本质上就是一个简单的接口回调的事
Date date = null;
try{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
date = sdf.parse(source);
return date;
}catch (Exception e){
e.printStackTrace();
}
return date;
}
}
3:Spring配置文件中进行注册
在Spring配置文件中进行配置即可
在Spring配置文件当中进行配置,第一将这个对象交由Spring进行创建,第二:类型转换器的注册:主要的目的及时告知Spring框架,我们所创建这个类是一个类型转换器,告诉了Spring之后,Spring就会把他当做Spring转换器对待,这样的话Spring就会在做日期类型转换的时候调用这个类当中个的方法,这就是注册的目的,注册也是在Spring的配置文件当中完成的。
注意:Spring配置文件当中bean标签的配置顺序和Spring的执行的顺序是没有关联的。所以,我们只需要在一个合适的位置进行配置就可以了。
注册的本质在于将这个咱们定义的这个格式转换器赋值给某个对象的属性。就注册到这里边了,这样到需要使用对应类型的类转换器的时候,Spring就会自动进行调用了。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="http:///2001/XMLSchema-instance" xmlns:util="/schema/util"
xmlns:context="/schema/context"
xsi:schemaLocation="/schema/beans /schema/beans/ /schema/util /schema/util/ /schema/context /schema/context/">
<bean id="person" class = "">
<property name="name" value = "suns"/>
<property name="birthday" value="2021-04-01"/>
</bean>
<bean id="myDateConvert" class = ""/>
<!--这个类的作用是用于注册类型转换器-->
<bean id = "conversionService" class="">
<!--调用set方法为属性进行赋值,这是spring定义的类中的属性,这个属性是set类型,需要使用set类型记性赋值-->
<property name="converters">
<set>
<ref bean = "myDateConvert"/>
</set>
</property>
</bean>
</beans>
/*
* @Target: 测试spring的内置转换器不好使的时候。
* @Author: DaShu
* @Date: 2021/6/8 21:35
* @Result: 某些格式的Date类型使用spring内置转换器就不好使了,需要自定义。
*/
@Test
public void test23(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("");
Object person = ctx.getBean("person");
System.out.println(person);
//自定义类型转换器注册之后:@e1de817 代码正常执行。
}
- 细节介绍
这个simpledateformat工具类应该依赖这个yyyy-MM-dd这个,这个可以配置到spring的配置文件当中进行注入。
/**
* @Auther: DaShu
* @Date: 2021/6/8 21:51
* @Description:
*/
public class MyDateConvert implements Converter<String, Date> {
private String pattern;
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
/*
* @Target: String -> Date
* @Author: DaShu
* @Date: 2021/6/8 21:54
* @Result:
*/
@Override
public Date convert(String source) {
//这个参数Source,他就代表了的是配置文件中的日期字符串
//一旦converter进行了转换之后,怎么交给属性进行赋值呢?spring帮我们做了,返回值
//spring会自动使用进行赋值。本质上就是一个简单的接口回调的事
Date date = null;
try{
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
date = sdf.parse(source);
return date;
}catch (Exception e){
e.printStackTrace();
}
return date;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="http:///2001/XMLSchema-instance" xmlns:util="/schema/util"
xmlns:context="/schema/context"
xsi:schemaLocation="/schema/beans /schema/beans/ /schema/util /schema/util/ /schema/context /schema/context/">
<bean id="person" class = "">
<property name="name" value = "suns"/>
<property name="birthday" value="2021-04-01"/>
</bean>
<bean id="myDateConvert" class = "">
<property name="pattern" value = "yyyy-MM-dd"/>
</bean>
<!--这个类的作用是用于注册类型转换器-->
<bean id = "conversionService" class="">
<!--调用set方法为属性进行赋值,这是spring定义的类中的属性,这个属性是set类型,需要使用set类型记性赋值-->
<property name="converters">
<set>
<ref bean = "myDateConvert"/>
</set>
</property>
</bean>
</beans>
配置文件当中定义注入了:
1:id属性我们唯一就行随便叫什么都行,但是需要大家注意的是:如果是ConversionServiceFactoryBean的话,他的id只能是conversionService大小写都得一样,如果他的id变了,就相当于没有注册进去,命名一定要慎重。
2:日期这种类型的话,Spring已经帮我们集成好了,默认的这种方式是2020/04/05这种形式,而我们自己写的这种形式的话,换了我们这种形式的话,Spring的默认的就不行用了。