Sping系列课程–工厂
第一章 引言
4. 工厂设计模式
4.1 什么是工厂设计模式
1. 概念:通过工厂类,创建对象
User user = new User();
UserDAO userDAO = new UserDAOImpl();
2. 好处: 解耦合
耦合:指的是代码间的强关联关系,一方的改变会影响到另一方
问题:不利于代码维护
UserService userService = new UserServiceImpl();
4.2 简单的工厂实现
package com.jujuxiaer.basic;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @author Jujuxiaer
* @date 2020-11-06 20:34
*/
public class BeanFactory {
private static Properties env = new Properties();
/* 将applicationContext.properties文件中的内容读取到Properties集合中,
然后这个IO操作属于系统级资源,避免重复性的打开IO,最后在程序启动的时候一次性的读取想要的内容
所以读这个配置文件时候,可以在静态代码块中进行
*/
static {
// 1. 获得IO输入流
InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
// 2. 文件内容封装到Properties集合中
try {
env.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/*
对象创建的三种方式:
1. 直接调用构造方法 创建对象 UserService userService = new UserServiceImpl();
2. 通过反射的方式 创建对象 解耦合
Class clazz = Class.forName("com.jujuxiaer.basic.UserServiceImpl");
UserService userService = (UserService) clazz.newInstance();
*/
public static UserService getUserService() {
// 使用new 的方式创建对象
// return new UserServiceImpl();
// 使用反射方式创建对象
UserService userService = null;
try {
// 但是在这里类的全限定类名字符串还是存在耦合,可通过配置文件的方式解决
// Class clazz = Class.forName("com.jujuxiaer.basic.UserServiceImpl");
Class clazz = Class.forName(env.getProperty("userService"));
userService = (UserService) clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return userService;
}
public static UserDAO getUserDAO() {
UserDAO userDAO = null;
try {
Class clazz = Class.forName(env.getProperty("userDAO"));
userDAO = (UserDAO) clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return userDAO;
}
}
4.3 通用工厂设计
-
问题
简单工厂会存在大量的代码冗余
-
通用工厂的代码
// 创建一切想要的对象
public class BeanFactory {
/**
* @param key 配置文件中的key,比如 userService userDAO
* @return
*/
public static Object getBean(String key){
Object ret = null;
try{
Class<?> clazz = Class.forName(env.getProperty(key));
ret = clazz.newInstance();
} catch (Exception e){
}
return ret;
}
}
4.4 通用工厂的使用方式
1. 定义类型(类)
2. 通过配置文件的配置来告知工厂(applicationContext.properties)
key = value
3. 通过工厂获得类的对象
Object ret = BeanFactory.getBean("key");
5. 总结
Spring本质: 工厂ApplicationContext(applicationContext.xml)
第二章 第一个Spring程序
1. 软件版本
1. JDK1.8+
2. Maven3.5
3. IDEA2020.2
4. SpringFramework 5.1.4
官方网址 www.spring.io
2. 环境搭建
-
Spring的Jar包
# 设置pom依赖 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.4.RELEASE</version> </dependency>
-
Spring的配置文件
1. 配置文件的放置位置: 任意位置,没有硬性要求 2. 配置文件的命名:没有硬性要求,建议我们叫applicationContext.xml 思考:日后应用Spring框架时,需要进行配置文件路径的设置
3. Spring的核心API
-
ApplicationContext
作用: Spring提供的ApplicationContext这个工厂,用于地对象的创建 好处:解耦合
-
ApplicationContext接口类型
接口:屏蔽实现的差异 非Web环境:ClassPathXmlApplicationContext(常用于Main函数或Junit单元测试中) Web环境:XmlWebApplicationContext
-
重量级资源
ApplicationContext工厂的对象会占用大量的内存。 不会频繁的创建对象:一个应用只会创建一个工厂对象。 ApplicationContext工厂:一定是线程安全的(也就是能被多线程并发访问)
-
4. 程序开发
1. 创建类型(类)
2. 配置文件的配置 applicationContext.xml
<!-- id属性: 名字(唯一)
class属性:配置全限定类名
-->
<bean id="person" class="com.jujuxiaer.basic.Person"/>
3. 通过工厂类,获取对象
ApplicationContext
|- ClassPathXmlApplicationContext
// 1. 获取Spring工厂
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
// 2. 通过工厂获取对象
Person person = (Person) ctx.getBean("person");
5. 细节分析
-
名词解释
Spring工厂创建的对象,叫做Bean或者组件(Component)
-
Spring工厂中的相关方法
// 1. 获取Spring工厂 ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml"); // 2. 通过工厂获取对象 Person person = (Person) ctx.getBean("person"); // 不需要进行强转 Person person = ctx.getBean("person", Person.class); // 此时在Spring工厂的配置当中,只能有一个Bean标签是Person类型, 不然的话会抛异常 // 抛的异常信息: expected single matching bean but found 2: person,person1 Person person = ctx.getBean(Person.class); // 返回的是 Bean标签中定义的bean的名字 数组 // ctx.getBeanDefinitionNames() 获取的是Spring工厂配置文件中所有Bean标签的id值 String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println("beanDefinitionName = " + beanDefinitionName); } System.out.println("person = " + person); // 根据类型获取Spring工厂配置文件中对应的Bean标签中id的值 String[] beanNamesForType = ctx.getBeanNamesForType(Person.class); for (String beanName : beanNamesForType) { System.out.println("beanName = " + beanName); } // 用于判断会否存在指定的id值的Bean if (ctx.containsBeanDefinition("11person")) { System.out.println("True = " + true); } else { System.out.println("False = " + false); } // 用于判断会否存在指定的id值的Bean, 暂时来看和上面的ctx.containsBeanDefinition("person")作用一样 if (ctx.containsBean("person")) { System.out.println("True = " + true); } else { System.out.println("False = " + false); }
-
配置文件中需要注意的细节
1. 只配置class属性 <bean class="com.jujuxiaer.basic.Person"/> a) 上述这种配置,有没有id值呢,也就是Spring会为我们赋上id值吗 回答: 有id值,值为 com.jujuxiaer.basic.Person#0 b) 上述这种配置方式,应用功能场景:如果这个bean只需使用一次,那么就可以省略id值 如果这个bean会使用多次或者被其他bean引用,则需要设置id值 2. name属性 作用: 用于在Spring配置文件中,为bean对象定义别名 相同: 1. ctx.getBean("id|name"); --> Object 2. <bean id="" class=""/> 等效 <bean name="" class=""/> 区别: 1. 别名可以定义多个,但是id属性只能有一个值 <bean id="person" name="p,p1" class="com.jujuxiaer.basic.Person"/> 多个别名之间以逗号分隔 2. XML的id属性的值,命名要求: 必须要以字母开头,后面可跟字母、数字、下划线、连字符,不能以特殊字符开头,比如"/person" name属性的值,命名没有要求,比如可以设置成"/person" name属性会应用在特殊命名的场景中: /person (spring+struct1) XML发展到了今天:id属性的限制,不存在了, 比如亦可以用"/person"命名 3. 代码 // 用于判断会否存在指定的id值的Bean,不能判断name属性值 if (ctx.containsBeanDefinition("p")) { System.out.println("True = " + true); } else { System.out.println("False = " + false); } // 用于判断会否存在指定的id值的Bean, 暂时来看和上面的ctx.containsBeanDefinition("person")作用一样 // 也可以判断name属性值 if (ctx.containsBean("p")) { System.out.println("True = " + true); } else { System.out.println("False = " + false); }
6. Spring工厂底层实现原理(简易版)
注意:Spring工厂是可以调用对象私有构造函数创建对象的
7. 思考
问题: 在未来的开发过程中,是不是所有的对象都交由Spring工厂来创建呢?
回答: 理论来说,是的。但是又特例:对于实体对象(entity)不会交给Spring创建,而是交由持久层创建。
第三章 Spring5.x与日志框架的整合
Spring与日志框架整合后,日志框架就能在控制台中,输出Spring框架运行过程中的一些重要的信息。
好处:便于了解Spring框架的运行过程,利于程序的调试。
-
Spring 如何整合日志框架
默认 Spring1.2.3都是与commons-logging.jar整合 Spring5.x默认整合的日志框架为logback log4j2 Spring5.x整合log4j 1. 引入log4j jar包 2. 引入log4j.properties配置文件
-
pom
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
log4j.properties
# resources文件夹根目录下 ### 配置根 log4j.rootLogger = debug,console ### 日志输出到控制台显示 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
-
第四章 注入(injection)
1. 什么是注入
通过Spring的工厂以及配置文件,为所创建对象的成员变量赋值
1.1 为什么需要注入
通过编码的方式为成员变量赋值,存在耦合
1.2 如何进行注入,开发步骤
-
类为成员变量提供get/set方法
-
配置Spring的配置文件
<bean id="person" class="com.jujuxiaer.basic.Person"> <property name="id"> <value>10</value> </property> <property name="name"> <value>juzhihua</value> </property> </bean>
1.3 注入的好处
解耦合
2. Spring注入的原理分析(简易版)
Spring底层通过调用对象属性对应的set方法,完成成员变量的赋值,这种方式我们称为set注入
第五章 set注入详解
针对不同类型的成员变量,在<property>标签中需要嵌入其他标签
1. JDK内置类型
1.1 String + 8中基本类型
<value>jujuxiaer</value>
1.2 数组
<property name="emails">
<list>
<value>11@qq.com</value>
<value>22@qq.com</value>
</list>
</property>
1.3 set集合
对应的成员变量为Set tels; 因为Set的泛型此时是String,所有标签中嵌套的为标签,如果Set泛型为其他的类型,则标签中嵌套的为其他标签
<property name="tels">
<set>
<value>1341111111</value>
<value>1342222222</value>
</set>
</property>
1.4 list集合
<property name="addresses">
<list>
<value>湖北省武汉市</value>
<value>上海市黄浦区</value>
</list>
</property>
<list>
<ref bean
<set
</list>
1.5 map集合
注意: map -- entry -- 键有特定的标签 <key></key>
值根据对应的类型选择对应类型的标签,比如<ref bean <set <list 等等
<property name="qqs">
<map>
<entry>
<key><value>jujuxiaer</value></key>
<value>1225449109</value>
</entry>
</map>
</property>
1.6 Properties
Properties类型:特殊的Map,该map的键为String,值也为String
<property name="p">
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
</props>
</property>
1.7 复杂JDK类型(Date)
需要程序员自定义类型转换器,处理。
2. 用户自定义类型
2.1 第一种方式
-
为成员变量提供get/set方法
-
配置文件中进行注入(赋值)
<bean id="userService" class="com.jujuxiaer.basic.UserServiceImpl"> <property name="userDAO"> <bean class="com.jujuxiaer.basic.UserDAOImpl"/> </property> </bean>
2.2 第二种方式
-
第一种方式存在的问题
1. 配置文件代码冗余 2. 被注入的对象(userDAO),多次创建,浪费(JVM)内存资源
-
为成员变量提供get/set方法
-
配置文件中进行注入(赋值)
<bean id="userDAO" class="com.jujuxiaer.basic.UserDAOImpl"/> <bean id="userService" class="com.jujuxiaer.basic.UserServiceImpl"> <property name="userDAO"> <ref bean="userDAO"/> </property> </bean> # Spring4.x 废弃了 <ref local=""/> 基本等效 <ref bean=""/>
3. set注入的简化方式
3.1 基于属性简化
# JDK类型注入
<property name="name">
<value>jujuxiaer</value>
</property>
<property name="name" value="jujuxiaer"/>
# 注意:value属性,只能简化8种基本类型+String 注入标签
# 用户自定义类型
<bean id="userDAO" class="com.jujuxiaer.basic.UserDAOImpl"/>
<bean id="userService" class="com.jujuxiaer.basic.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
</property>
</bean>
# 用户自定义类型,简化后
<bean id="userDAO" class="com.jujuxiaer.basic.UserDAOImpl"/>
<bean id="userService" class="com.jujuxiaer.basic.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
3.2 基于p命名空间简化
# JDK类型注入
<bean id="person" class="com.jujuxiaer.basic.Person">
<property name="name">
<value>jujuxiaer</value>
</property>
</bean>
<bean id="person" class="com.jujuxiaer.basic.Person" p:name="jujuxiaer"/>
# 注意:value属性,只能简化8种基本类型+String 注入标签
# 用户自定义类型
<bean id="userDAO" class="com.jujuxiaer.basic.UserDAOImpl"/>
<bean id="userService" class="com.jujuxiaer.basic.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
</property>
</bean>
# 用户自定义类型,简化后
<bean id="userDAO" class="com.jujuxiaer.basic.UserDAOImpl"/>
<bean id="userService" class="com.jujuxiaer.basic.UserServiceImpl" p:userDAO-ref="userDAO"/>
第六章 构造注入
注入:通过Spring的配置文件,为成员变量赋值
set注入: Spring通过调用set方法,通过配置文件为成员变量赋值
构造注入: Spring通过调用构造方法,通过配置文件为成员变量赋值
1. 开发步骤
-
提供有参的构造方法
public class Customer implements Serializable { private String name; private int age; public Customer(String name, int age) { this.name = name; this.age = age; } }
-
Spring的配置文件
<bean id="customer" class="com.jujuxiaer.basic.constructer.Customer"> <constructor-arg> <value>jujuxiaer</value> </constructor-arg> <constructor-arg> <value>102</value> </constructor-arg> </bean>
2. 构造方法重载
2.1 参数个数不同时
通过控制<constructor-arg>标签数量进行区分
2.2 参数个数相同时
通过在标签引入type属性来进行类型的区分<constructor-arg type="">
<bean id="customer" class="com.jujuxiaer.basic.constructer.Customer">
<constructor-arg type="int">
<value>23</value>
</constructor-arg>
</bean>
3. 注入总结
未来实战中使用set注入,还是构造注入?
回答:set注入会更多
原因:1. 构造注入麻烦,有重载
2. Spring框架底层,大量应用了set注入
第七章 反转控制 与 依赖注入
1. 反转(转移)控制(Inverse of Control,简称IOC)
控制: 对于成员变量的赋值的控制权
反转控制:把对于成员变量赋值的控制权,从代码中反转(转移)到了Spring工厂和配置文件中完成
好处: 解耦合
底层实现:工厂设计模式
2. 依赖注入(Dependency Injection,简称DI)
注入:通过Spring工厂和配置文件,为对象(bean/组件)的成员变量赋值
依赖注入:一个类需要另一个类时,就意味着依赖,一旦出现依赖,就可以把另一个类作为本类的成员变量,最终通过Spring配置文件进行注入(赋值)。
第八章 Spring工厂创建复杂对象
1. 什么是复杂对象
复杂对象:指的是不能直接通过new构造方法创建的对象
Connection
SqlSessionFactory
2. Spring工厂创建复杂对象的3中方式
2.1 FactoryBean接口
-
开发步骤
-
实现FactoryBean接口
-
-
Spring配置文件的配置
# 如果Class属性中指定的类型是FactoryBean接口的实现类,那么通过id值获得的是这个类所创建的复杂对象,比如Connection <bean id="conn" class="com.jujuxiaer.factorybean.ConnectionFactoryBean"/>
-
细节
-
如果 ,ctx.getBean("&conn"); 获得的就是ConnectionFactoryBean对象
-
isSingleton方法
返回true,只会创建一个复杂对象
返回false,每一次都会创建新的对象
问题:根据这个对象的特点,决定是返回true(SqlSessionFactory),还是false(Connection)
-
mysql高版本在连接创建时,需要指定SSL证书,解决问题的方式
url = "jdbc:mysql://localhost:3306/suns?useSSL=false"
-
依赖注入的体会(DI)
把ConnectionFactoryBean中依赖的四个字符串信息,进行配置文件的注入 好处:解耦合 <bean id="conn" class="com.jujuxiaer.factorybean.ConnectionFactoryBean"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/suns?useSSL=false"/> <property name="user" value="root"/> <property name="password" value="123456"/> </bean>
-
-
FactoryBean的实现原理(简易版)
接口回调 1. 为什么Spring规定FactoryBean接口 实现 并且getObject()? 2. ctx.getBean("conn");获得的是复杂对象Connection,而不是获得ConnectionFactoryBean Spring内部运行流程: 1. 通过conn获得ConnectionFactoryBean类对象,进而通过instanceod判断出是FactoryBean接口的实现类 2. Spring按照规定getObejct() --> Connection 3. 返回Connection
-
Factory总结
Spring中用于创建复杂对象的一种方式,也是Spring原生提供的,后续我们在讲解Spring整合其他框架时,会大量应用FactoryBean
2.2 实例工厂
1. 避免Spring框架的侵入
2. 整合遗留系统
-
开发步骤
<bean id="connFactory" class="com.jujuxiaer.factorybean.ConnectionFactory"/> <bean id="conn" factory-bean="connFactory" factory-method="getConnection"/>
2.3 静态工厂
-
开发步骤
<bean id="conn" class="com.jujuxiaer.factorybean.StaticConnectionFactory" factory-method="getConnection"/>
3. Spring工厂创建对象总结
第九章 控制Spring工厂创建对象次数
1. 如何控制简单对象的创建次数
<bean id="account" class="com.jujuxiaer.scope.Account" scope="singleton|prototype"/>
singleton: 只会创建一次简单对象
prototype: 每一次都会创建简单对象
默认为singleton
2. 如何控制复杂对象的创建次数
FactoryBean {
isSingleton() {
return true 只会创建一次
return false 每一次都会创建新的
}
}
如果没有isSingleton方法,还是通过制定scope属性,进行对象创建次数的控制。
3. 为什么要控制对象的创建次数
好处:节省不必要的内存浪费
-
什么样的对象只创建一次
1. SqlSessionFactoryBean 2. DAO 3. Service
-
什么样的对象,每一次都要创建新的呢?
1. Connection(因为要控制事务,不能被大家共用) 2. SqlSession(里面封装了Connection) | Session 3. Struct2 Action
第十章 对象的生命周期
1. 什么是对象的生命周期
指的是一个对象创建、存活、消亡的一个完整过程。
2. 为什么要学习对象的生命周期
由Spring来负责对象的创建、存活、销毁,了解生命周期后,有利于我们使用好Spring为我们创建的对象。
3. 生命周期的3个阶段
-
创建阶段
Spring工厂何时创建
-
scope=“singleton”
Spring工厂创建的同时,对象创建 注意: 设置scope="singleton" 这种情况下,也需要在获取对象的同时,创建对象 <bean id="product" class="com.jujuxiaer.life.Product" scope="singleton" lazy-init="true"/>
-
scope=“prototype”
Spring工厂会在获取对象的同时,创建对象 ctx.getBean("")
-
-
初始化阶段
Spring工厂在创建完成对象后,调用对象的初始化方法,完成对应的初始化操作 1. 初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作 2. 初始化方法调用:Spring工厂进行调用
-
InitializingBean接口
public class Product implements InitializingBean { public Product() { System.out.println("Product.Product"); } /** * 完成对象的初始化操作 * @throws Exception */ public void afterPropertiesSet() throws Exception { System.out.println("Product.afterPropertiesSet"); } }
-
对象中提供一个普通的方法
public void myInit() { } // <bean id="product" class="xxx.Product" init-method="myInit"/>
-
细节分析
-
如果一个对象既实现了InitializingBean,又提供了普通的初始化方法,执行顺序是怎样呢?
1. InitializingBean 2. 普通的初始化方法
-
注入一定发生在初始化操作的前面
-
什么叫做初始化操作
资源的初始化:数据库 IO 网络 ......
-
-
-
销毁阶段
Spring销毁对象前,会调用对象的销毁方法,完成销毁操作 1. Spring什么时候销毁所创建的对象? ctx.close(); 2. 销毁方法:程序员根据自己的需求,定义销毁方法,完成销毁操作 调用:Spring工厂完成调用
- DisposableBean
- 定义一个普通的销毁方法
public class Product implements InitializingBean, DisposableBean { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("Product.setName"); } public Product() { System.out.println("Product.Product"); } // 完成对象的初始化操作 public void afterPropertiesSet() throws Exception { System.out.println("Product.afterPropertiesSet"); } public void myInit() { System.out.println("Product.myInit"); } public void destroy() throws Exception { System.out.println("Product.destroy"); } public void myDestroy() throws Exception { System.out.println("Product.myDestroy"); } } <bean id="product" class="com.jujuxiaer.life.Product" init-method="myInit" destroy-method="myDestroy"> <property name="name" value="productName"/> </bean> @Test public void test17() { // 1. 获取Spring工厂 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml"); ctx.close(); }
- 细节分析
-
销毁方法的操作只适用于scope=“singleton”
-
什么叫做销毁操作
主要指的是 资源的释放操作,比如 io.close(); connection.close();
第十一章 配置文件参数化
把Spring配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中
1. Spring的配置文件中存在经常修改的字符串?
存在,以数据库连接相关的参数 代表
2. 经常变化字符串,在Spring配置文件中,直接修改
不利于项目维护(修改)
3. 转移到一个小的配置文件(.properties)
利于维护(修改)
配置文件参数化:利于Spring配置文件的维护(修改)
1. 配置文件参数化的开发步骤
-
提供一个小的配置文件(.properties)
# 文件名称:随意;放置位置:随意 jdbc.driverClassName = com.mysql.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/suns?useSSL=false jdbc.user = root jdbc.password = 123456
-
Spring的配置文件和小配置文件的整合
<!-- 在applicationContext.xml中加入以下配置 --> <context:property-placeholder location="classpath:/db.properties"/>
-
在Spring配置文件中通过${key}获取小配置文件中对应的值
第十二章 自定义类型转换器
1. 类型转换器
作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入
2. 自定义类型转换器
需要自定义类型转换器的原因:
当Spring内部没有提供特定类型转换器时,而程序员在应用的过程中还需要使用个,那么就需要程序员自己定义类型转换器
-
类实现Convert接口
/** * @author Jujuxiaer * @date 2020-11-08 14:25 * Converter<String, Date>中String是待转换的原始数据类型,Date是转换后的数据类型 */ public class MyDateConverter implements Converter<String, Date> { /* convert作用: String --> Date param: source 代表的是配置文件中 日期字符串 <value>2020-11-07</value> return: 当把转换好的Date作为convert方法返回值后,Spring自动为birthday属性进行注入(赋值) */ @Override public Date convert(String s) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = null; try { date = sdf.parse(s); } catch (ParseException e) { e.printStackTrace(); } return date; } }
-
在Spring的配置文件中进行配置
-
MyDateConverter对象创建出来
<!--Spring创建MyDateConverter类型对象--> <bean id="myDateConverter" class="com.jujuxiaer.converter.MyDateConverter"/>
-
类型转换器的注册
<!--用于注册类型转换器 id必须为conversionService--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <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; } /* convert作用: String --> Date param: source 代表的是配置文件中 日期字符串 <value>2020-11-07</value> return: 当把转换好的Date作为convert方法返回值后,Spring自动为birthday属性进行注入(赋值) */ @Override public Date convert(String s) { SimpleDateFormat sdf = new SimpleDateFormat(pattern); Date date = null; try { date = sdf.parse(s); } catch (ParseException e) { e.printStackTrace(); } return date; } }
<!--Spring创建MyDateConverter类型对象--> <bean id="myDateConverter" class="com.jujuxiaer.converter.MyDateConverter"> <property name="pattern" value="yyyy-MM-dd"/> </bean>
-
ConversionServiceFactoryBean定义id属性,必须为conversionService
-
Spring框架内置了日期类型转换器
内置的日期类型转换器支持的日期格式:2010/11/08 而不支持,比如2020-11-08 日期格式
第十三章 后置处理Bean
BeanPostProcessor作用:对Spring工厂所创建的对象,进行再加工
AOP底层实现:
注意: BeanPostProcesser接口
xxx (){
}
-
后置处理Bean de 运行原理分析
程序员源实现BeanPostProcesser规定接口中的方法:
Object postProcessBeforeInitialization(Object bean, String beanName)
作用:Spring创建完对象,并进行注入后,可以运行Before方法进行加工
获得创建好的对象:通过方法的参数
最终通过返回值交给Spring框架
Object postProcessAfterInitialization(Object bean, String beanName)
作用:Spring执行完对象的初始化操作后,可以运行After方法进行加工
获得创建好的对象:通过方法的参数
最终通过返回值交给Spring框架
实战中:
很少处理Spring的初始化操作(InitializingBean),没有必要区分Before After。只需要实现其中一个After方法即可。但注意postProcessBeforeInitialization方法也需要将bean对象返回。
-
BeanPostProcesser开发步骤
-
类 实现BeanPostProcesser接口
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("xiaowangba"); return bean; } }
-
Spring的配置文件进行配置
<bean id="myBeanPostProcessor" class="com.jujuxiaer.beanpost.MyBeanPostProcessor"/>
-
BeanPostProcessor细节
BeanPostProcessor会对Spring工厂中所有创建的对象进行加工
-