第一章:注入
一:什么是注入
(Injection)注入就是通过Spring的工厂类和spring的配置文件,对spring所创建的对象进行赋值,为成员变量进行赋值
二:为什么注入
为什么需要Spring工厂创建对象的时候给对象进行赋值呢?
1:通过配置文件进行注入也可以实现解耦合的这样的作用,直接写死在代码当中会引发耦合效应
2:通过编码的方式为成员边量进行赋值会出现耦合,注入的方式会避免这样的耦合的作用
三:如何注入
<bean id = "person" class = "com.pactera.spring.Person">
<!--property标签是复制标签,name属性是对应bean类中的属性。-->
<property name = "id">
<value>10</value>
</property>
<property name = "name">
<value>xjr</value>
</property>
</bean>
1:提供GS方法
为成员变量提供setget方法,这是注入的第一步
2:Spring的配置文件
四:注入好处
解耦合
五:Spring注入的思想
1:Spring获取到配置文件路径之后,调用demo4j这个第三方组件来对xml配置文件进行解析,根据解析的内容创建对应的对象
2:对象创建之后,根据property标签中的配置的属性的值,根据对象的set和get方法进行赋值操作。
总结:Spring会通过底层属性对应的set方法进行赋值才完成对应的赋值操作,这种方式我们也称为set注入
1:Set注入详解
1:针对于不同类型的成员变量,在标签中进行注入的方式也不一样,不过都得嵌套到property标签当中
2:JDK内置类型:jdk自定义的数据类型,或者叫引用类型
3:用户自定义类型:用户自定义的类型
1):JDK内置类型
1、string+8中基本类型。
<property name = "id">
<value>10</value>
</property>
<property name = "name">
<value>xjr</value>
</property>
2、数组
<property name="emails">
<!--这个是为了定义数组-->
<list>
<value>suns@pactera.com</value>
<value>cuilei@pactera.com</value>
<value>kaixin@pactera.com</value>
</list>
</property>
3、set集合
<set>
<!--在这里能用value标签是因为在Set<String>属于八中基本类型和String的范畴。-->
<value>138111111111</value>
<!--ref这个标签是给自定义类的注入进行使用的。-->
<ref bean=""/>
<!--set集合当中还可以套用set集合-->
<set>
</set>
</set>
4、list集合
<property name="address">
<list >
<!--这是list集合的写法,list集合是有序的,list集合是可以重复的。-->
<value>zpark</value>
<value>shangdi</value>
<value>xierqi</value>
<set></set>
<ref></ref>
<list></list>
</list>
</property>
5、map集合赋值方式
<property name="qqs">
<!--这里是map的写法。-->
<map>
<entry>
<key><value>suns</value></key>
<value>43222222222222</value>
</entry>
<entry>
<key><value>xiexei</value></key>
<value>43222222222222</value>
</entry>
<!-- <entry>
当key或者value是对象的时候
<key><value>zhang</value></key>
<ref bean="">43222222222222</ref>
<key><ref bean="">zhang</ref></key>
<ref bean="">43222222222222</ref>
</entry>-->
</map>
</property>
6、properties类型的赋值
更加复杂的jdk类型比如说:date类型,需要成蹊苑自定义类型转换器进行,这是在工厂的高级部分进行讲解。
2):用户自定义类型
A:第一种方式
1:第一步还是为成员变量提供get和set方法
2:第二步在配置文件中进行注入
补充说明:
1:通过这样一种编译时耦合与接口+配置文件中定义注入类型的这样的形式,最大程度上降低了代码的耦合型
2:这种写法代码冗余,而且会重复创建某个对象。写一个bean标签就会创建一个对象,而且注入的这个对象不可能被复用只能在这个对象中被引用,被注入的对象多次创建就会浪费虚拟机内存的资源
/*
* @Description:给自定义引用类型赋值。
* @Author: DaShu
* @Date: 2021/5/28 16:02
* result:说明配置文件当中这样写可以实现自定义类型的注入。
*/
//<bean id="userService" class = "com.pactera.spring.UserServiceImpl">
// <property name="userDao">
// <!--这个dao对象只使用一次,并且不会被其他对象使用。-->
// <bean class = "com.pactera.spring.UserDaoImpl"/>
// </property>
//</bean>
//<bean id="userService" class = "com.pactera.spring.UserServiceImpl">
// <property name="userDao">
// <ref bean="userDao"/>
// </property>
//</bean>
//<bean id="userDao" class="com.pactera.spring.UserDaoImpl"></bean>
//<ref local = "">这个标签从spring4就已经废除了,这个只能引用本配置文件中的bean
@Test
public void test11() throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
UserService userService = (UserService)ctx.getBean("userService");
Class clazz = userService.getClass();
Field userDao = clazz.getDeclaredField("userDao");
userDao.setAccessible(true);
//Object o = userDao.get(userService);
//System.out.println(o.getClass().getTypeName());//com.pactera.spring.UserDaoImpl
System.out.println(userDao.getGenericType());
System.out.println(userDao.getName());
System.out.println(userDao.get(userService).getClass().hashCode() == UserDaoImpl.class.hashCode());//true
}
<bean id="userService" class = "com.pactera.spring.UserServiceImpl">
<property name="userDao">
<!--这个dao对象只使用一次,并且不会被其他对象使用。-->
<bean class = "com.pactera.spring.UserDaoImpl"/>
</property>
</bean>
B:第二种方式
Ref 是引用的意思
3):Set注入简化写法
A:基于属性的简化
执行test方法的快捷键是 ctrl+shift+f10
这是jdk类型的注入的形式。
<property name = "name">
<value = "suns">
</property>
该类型的简化写法
<property name = "name" value = "suns"/>
用value属性代替value标签。
注意:通过value属性只能简化八中基本类型和String类型。
用户自定义类型:
<property name = "userDao">
<ref bean = "userDao">
</property>
<property name= "userDao" ref = "userDao"/>
注意:这种方式只能替换ref属性的形式。
B:基于p空间简化属性
适用于jdk的类型的简化:**补充说明:**1) alt+enter可以导入这个命名空间P的配置,在这个配置当中,有了这个命名空间之后,Property标签就没用了。
4):构造注入
注入:通过Spring的配置文件,为成员变量赋值
Set注入:Spring通过Set方法,通过配置文件,为成员变量赋值
构造赋值:Spring通过构造方法,通过配置文件,为成员变量赋值
A:开发步骤
1:提供有参构造方法
2: 提供spring的配置文件进行配置
<bean id = "customer" class="com.pactera.spring.constructor.Customer">
<!--一个这样的标签对应一个这样的参数。并且顺序还是对应的。-->
<constructor-arg index="0">
<value>suns</value>
</constructor-arg>
<constructor-arg index="1">
<value>102</value>
</constructor-arg>
</bean>
/*
* @Description:用于测试构造工厂
* @Author: DaShu
* @Date: 2021/5/31 11:21
*/
@Test
public void test13(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
Customer customer = (Customer) ctx.getBean("customer");
System.out.println(customer.toString());//Customer{name='suns', age=102}
}
B:构造方法的重载
第一种方式:
1)、 参数的个数不同时通过constructor-arg标签的个数进行区分
2)、参数相同时,通过在这个标签中引入type标签就可以解决
5):注入的总结
Set注入 vs 构造注入,未来这两种我们到底是使用什么注入
1:未来实战:使用set注入会更多,构造注入会更多,有重载,更麻烦,
2:Spring底层他自己的一些对象的配置,也是使用大量的set的注入,构造注入用的比较少。
第二章:反转控制和依赖注入
一:什么叫做控制反转
控制:控制值得是对于对象创建、程序变量赋值的控制权,控制和注入的关系不是并列的关系,控制翻转依赖注入的,注入是控制翻转中的一个环节,例如:直接在代码中完成对于成员边量的赋值,这提高了代码的耦合,这样的控制权是在代码中的,提高了耦合性,将成员变量的赋值权交给Spring工厂和他的配置文件,这样就通过注入的方式,控制权是在Spring上边,解开了耦合性。
控制反转也就是说:对于对象的创建、对象的成员变量的赋值的权利翻转到spring的工厂和配置文件当中,可以起到解耦合的作用,控制反转的好处解耦合的作用。
底层实现:工厂设计模式,反射+配置文件
代码编写中最好的降低耦合性的方式:
反射+配置文件+耦合月接口,可以把代码的耦合性降低早最低。
二:什么是依赖注入
通过Spring的工厂和配置文件为对象的成员变量赋值,依赖:我需要你的时候,你就是我的依赖:比如说:Service层中的Dao层的对象,就可以说Service层依赖dao层。Dao层是Service层的依赖。我依赖谁,就可以把他作为成员变量通过,spring的配置文件,创建他的对象,给我注入。好处:解耦合,通过提供set,get方法,最终通过Spring的配置文件完成对象的创建和注入
第三章:Spring创建复杂对象
一:复杂对象概念
什么是复杂对象?什么是简单对象?
1:简单对象,直接通过new 构造方法创建的对象
2:复杂对象,不能通过直接new构造方法的创建的对象
补充说明:
1:这些复杂对象大多都是Spring集成的一些其他优秀的框架中的核心类的对象,不能直接new,我们也希望Spring能够创建这些类的对象,保证Spring工厂什么对象都可以创建
2:Spring容器就是Spring工厂
二:常见的复杂对象
链接对象和SqlSessionFactory对象。
三:FactoryBean
1:概述
这种方式的本质在一个实现了BeanFactory这样的接口的Bean中,书写创建该复杂对象的代码,这样通过Bean对应id获取到的Bean是复杂对象的Bean而不是这个Bean的对象,Spring非常重要的一种机制,Spring原生提供的帮我们创建复杂对象的方式。
2:开发步骤
1:实现FactoryBean接口,在该类的getBean()方法代码中创建该类对象
2:在Spring的核心配置文件中配置这个类
3:通过这个工厂对象中的getBean方法获取该复杂对象
3:FactoryBean的Method
getObject()获取该复杂对象,调用这个方法获取到复杂对象,然后在调用Spring工厂对象进行getBean的时候将这个复杂对象进行返回
获取该复杂对象的Class对象,此对象也是Spring工厂进行调用
isSingleTon()这个方法返回的事TRUE或者FALSE如果是TRUE的话,表示的事单例设计模式,每次getBean的时候,获取的都是这一个对象,不会多次创建,如果是FALSE的话,每次创建都会创建一个新的复杂对象,返回的也是新的复杂对象
补充说明:
1:他这个复杂对象的创建每个跟每个都不一样,所以,spring是给定了接口,将对象创建的核心代码让你来写
四:Spring配置文件中的配置
配置文件当中这样写理论上Spring为我们创建的ConnectionFactoryBean的对象,但是我们使用getBean方法进行获取时候,获取的不再是这个类的对象,而是我们希望获取的复杂的对象
五:FactoryBean开发细节
1)、如果这个类型是FactoryBean类型的实现,我们获取的是复杂对象,如果我们就想获取这个FactoryBean类型的对象怎么办?getBean的时候,在id前面加上一个&符号,类似于getBean(“&conn”)这样的话就获取的是FactoryBean类型的对象,isSingleTon()方法返回TRUE的时候,只会创建一个对象,返回FALSE的时候,每次获取spring都会创建一个对象。
2)、Connection对象能够公用?
不能公用,链接对象一个很大的作用在于控制事务,假设一个链接开启了事务,假设两个用户都获取到了conn这个对象,你使用这个对象提交了事务,就影响到了我,那就不行。SqlSessionFactory这个对象是线程安全的,重量级的,之创建一次,应该创建一次,应该设置成TRUE。
3、获取Connnection对象的时候的一些细节?
4)、我们对于依赖注入的体会
通过依赖注入的思想,将写死的内容变成成员变量,然后交由Spring进行依赖注入。获得解耦合的好处。
把Connection依赖的四个字符串信息,进行了依赖注入,好处就是解耦合。
5)、为什么pring要规定一个factoryBean接口要我们实现,并且将创建复杂对象的代码写在getObject()方法当中(FactoryBean实现原理)?
接口回调:在java体系当中,接口+反射什么都能做,本质上是一个接口回调,在Spring的底层代码中,通过id值获取ConnectionFactoryBean类的对象,进而判断是不是 BeanFactory接口的实现类(InstanceOf),如果是的话,Spring按照接口约定,调用getObject方法获取Connection对象,返回Connection对象
6)、为什么说Spring创建的复杂对象都是懒加载的?
Spring对于复杂对象的创建是懒加载的,使用FactoryBean创建复杂对象,在spring容器进行创建的时候只会先创建实现了FactoryBean这个类的对象,当getBean的时候才会有Spring的接口回调,获取复杂对象并进行返回,所以这是懒加载的
6、Factorybean总结
spring当中创建复杂对象的一种方式,也是spring原生提供的,后续我们讲整合其他框架时会大量应用BeanFactory这个接口的形式
六:实例工厂
1:为什么使用实例工厂
避免Spring框架的侵入,使用Spring框架进行复杂框架开发的时候,如果使用beanFactory进行开发的,必须使用Spring提供的BeanFactory接口,这样日后离开Spring的话,那么久没有什么意义了
应用实例工厂创建对象,主要是为了整合遗留系统,遗留系统中一些复杂对象的创建已经写好了,而且提供的是class文件不是java文件,这种情况处理的话,就是使用BeanFactory已经没办法了
class ConnectionFactory{
public Connection getConnection(){
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/student", "root", "root");
return connection;
}
}
<bean id="connFactory" class="com.pactera.spring.factorybean.ConnectionFactroy"></bean>
<bean id = "conn" factory-bean="connFactory" factory-method="getConnection"/>
七:静态工厂
实例工厂需要先创建实例工厂的对象,静态工厂当中直接类名.方法名进行调用就可以了,二者都是为了避免Spring的侵入,为了整合遗留的资源,以上就是Spring创建复杂对象的三种方式
1:配置文件上有些区别
<!--静态工厂不需要创建对象,直接类名调用就完事了,用的比较少。使用的时候通过getBean("conn")获取的就是这个复杂对象。-->
<bean id = "conn" class="com.pactera.spring.factorybean.StaticConnectionFactory" factory-method="getConnection"/>
2:Spring工厂创建对象的总结
第五章:控制Spring工厂创建对象的次数
一:控制简单对象的创建次数
<!--控制这个类的创建的次数,这个参数默认是SingleTon-->
<bean id="account" class = "com.pactera.spring.scope.Account" scope="singleton"/>
/*
* @Description:测试spring只创建一个对象。
* @Author: DaShu
* @Date: 2021/5/31 14:31
*/
@Test
public void test18(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
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 = "com.pactera.spring.life.Product" init-method ="myInit"/>
/*
* @Description:测试--afterPropertiesSet方法执行了 spring的初始化方法
* @Author: DaShu
* @Date: 2021/5/31 14:31
* result:
*/
@Test
public void test20(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
System.out.println("-----------------------工厂对象已经加载完毕------------------------");
Product product = (Product) ctx.getBean("product");
//Product.Product
//afterPropertiesSet方法执行了。
//myInit方法执行了。
}
如果一个对象上上述两种方式都采用了,那么会怎么样?
先执行实现接口执行回调的方法,在执行普通的初始化方法。
Spring创建完对象之后会进行DI注入和初始化那么spring是先进行注入还是先进行初始化呢
Spring创建对象之后会先进行注入,注入完成之后在进行初始化。也就是先为成员边变量赋值,在进行初始化,所以,初始化方法叫做afterpropertyset,初始化方法经常用作资源的加载或者资源的初始化。
//Product.Product --构造方法创建对象
//Product.setName --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("/applicationContext.xml");
Product product = ctx.getBean("product", Product.class);
((ClassPathXmlApplicationContext)ctx).close();
//Product.Product
//Product.setName
//afterPropertiesSet方法执行了。
//myInit方法执行了。
//2021-06-02 15:11:03 DEBUG ClassPathXmlApplicationContext:987 - Closing org.springframework.context.support.ClassPathXmlApplicationContext@5e4c8041, started on Wed Jun 02 15:11:02 CST 2021
//Product.destroy
//Product.MyDestroy
}
<!---->
<bean id = "product" class = "com.pactera.spring.life.Product" 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:db.properties"/>
<bean id="conn" class = "com.pactera.spring.factorybean.ConnectionFactoryBean">
<property name="driverClassName" value="${jdbc.DriverManager}"/>
<property name="url" value="${jdbc.url}"/>
<property name="userName" value="jdbc.username"/>
<property name="password" value="jdbc.password"/>
</bean>
第八章:自定义类型转换器
一:什么是类型转换器
在之前的注入的时候,我们都忽略了一个细节,那就是在配置文件当中所写的内容都是字符串类型。那么如何给Integer类型进行赋值呢?当然是可以的。Spring是如何实现类型转换呢?spring实现这个类型转换的原因spring当中的类型转换器起的功效,他这个字符串类型转换器的底层也就是个Integer.parInt(“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("applicationContext2.xml");
Object person = ctx.getBean("person");
System.out.println(person);
// Failed to instantiate [java.util.Date]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException
//Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'birthday';
// nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type
// 'java.util.Date' for property 'birthday': no matching editors or conversion strategy found
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="person" class = "com.pactera.spring.converter.Person">
<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="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="person" class = "com.pactera.spring.converter.Person">
<property name="name" value = "suns"/>
<property name="birthday" value="2021-04-01"/>
</bean>
<bean id="myDateConvert" class = "com.pactera.spring.converter.MyDateConvert"/>
<!--这个类的作用是用于注册类型转换器-->
<bean id = "conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!--调用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("applicationContext2.xml");
Object person = ctx.getBean("person");
System.out.println(person);
//自定义类型转换器注册之后:com.pactera.spring.converter.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="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="person" class = "com.pactera.spring.converter.Person">
<property name="name" value = "suns"/>
<property name="birthday" value="2021-04-01"/>
</bean>
<bean id="myDateConvert" class = "com.pactera.spring.converter.MyDateConvert">
<property name="pattern" value = "yyyy-MM-dd"/>
</bean>
<!--这个类的作用是用于注册类型转换器-->
<bean id = "conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!--调用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的默认的就不行用了。