Spring创建复杂对象、控制Spring工厂创建对象的次数
创建复杂对象
1. 什么是复杂对象
复杂对象:指的就是不能直接通过new构造方法创建的对象
- Connection
- SqlSessionFactory
- …
2. Spring工厂创建复杂对象的3种方式
2.1 FactoryBean接口
开发步骤
实现Factory接口
package com.company.factory;
import org.springframework.beans.factory.FactoryBean;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Collection;
public class ConnectionFactoryBean implements FactoryBean<Connection> {
// 用于书写创建复杂对象的代码 并 把复杂对象作为方法的 返回值 返回
@Override
public Connection getObject() throws Exception {
// 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&" +
"useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai";
Connection connection = DriverManager.getConnection(url, "root", "123456");
return connection;
}
// 返回所创建复杂对象的Class对象
@Override
public Class<?> getObjectType() {
return Collection.class;
}
// 返回true:对象只需要创建一次
// 返回false:每一次调用 都需要创建一个新的复杂对象
@Override
public boolean isSingleton() {
return false;
}
}
Spring配置文件的设置
# #如果Class中指定的类型是FactoryBean接口的实现类,那么通过id值获得的是这个类所创建的复杂对象Connection
<bean id="conn" class="com.company.factory.ConnectionFactoryBean"/>
Connection conn = (Connection) ctx.getBean("conn");
简单对象与复杂对象的比较
<bean id="user" class="com.xxx.User"/>
简单对象
ctx.getBean("user")获得的是User这个类的对象
<bean id="conn" class="com.compa ny.factorybean.ConnectionFactoryBean"/>
错误认知:
ctx.getBean("conn")获得的是ConntionFactoryBean这个类的对象
FactoryBean接口的实现类: ConntionFactoryBean
ctx.getBean("conn")获得的是 它所创建的复杂对象Connection
细节分析
1、如果就想获得FactoryBean类型的对象ctx.getBean("&conn")
- 获得就是ConnectionFactoryBean对象
ConnectionFactoryBean conn = (ConnectionFactoryBean) ctx.getBean("&conn");
2、isSingleton方法
- 返回true 只会创建一个复杂对象
- 返回false每一次都会创建新的对象
- 问题:根据这个对象的特点,决定是返回true (SqlSessionFactory) 还是false (Connection)
3、mysq|高版本连接创建时,需要制定SSL证书, 解决问题的方式
jdbc:mysql://localhost:3306/test?useSSL=false
4、依赖注入的体会(DI)
- 把ConnectionFactoryBean中依赖的4个字符串信息 ,进行配置文件的注入
- 好处:解耦合
<bean id="conn" class="com.company.factory.ConnectionFactoryBean">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
ConnectionFactoryBean
package com.company.factory;
import org.springframework.beans.factory.FactoryBean;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Collection;
public class ConnectionFactoryBean implements FactoryBean<Connection> {
private String driverClassName;
private String url;
private String username;
private String password;
set和get
// 用于书写创建复杂对象的代码 并 把复杂对象作为方法的 返回值 返回
@Override
public Connection getObject() throws Exception {
Class.forName(driverClassName);
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
// 返回所创建复杂对象的Class对象
@Override
public Class<?> getObjectType() {
return Collection.class;
}
// 返回true:对象只需要创建一次
// 返回false:每一次调用 都需要创建一个新的复杂对象
@Override
public boolean isSingleton() {
return false;
}
}
FactoryBean的实现原理[简易版]
接口回调
- 为什么Spring规定FactoryBean接口 实现并且getObject()?
- ctx. getBean(“conn”) 获得是复杂对象Connection 而没有获得ConnectionFactoryBean(&)
Spring内部运行流程
- 通过conn获得ConnectionFactoryBean类的对象
- 进而通过instanceof 判断出是FactoryBean接口的实现类
- Spring按照规定getObject() —> Connection返回Connection
FactoryBean总结
Spring中用于创建复杂对象的一种方式,也是Spring原生提供的,之后整合其他框架,大量应用FactoryBean
2.2 实例工厂
- 避免Spring框架的侵入(必须implements FactoryBean)
- 处理遗留问题(只有.class 没有.java)
ConnectionFactory
/**
* 此时为遗留系统(.class)
*/
public class ConnectionFactory {
Connection connection = null;
public Connection getConnection(){
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","abc8658539");
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
}
开发步骤
<bean id="connectionFactory" class="com.company.factory.ConnectionFactory"/>
<bean id="conn" factory-bean="connectionFactory" factory-method="getConnection"/>
2.3 静态工厂(方法为静态的)
开发步骤
<bean id="conn" class="com.company.factory.StaticConntionFactory" factory-method="getConnection"/>
3. Spring工厂对象创建总结
控制Spring工厂创建对象的次数
1、如何控制简单对象的创建次数
<bean id="account" scope="singleton|prototype" class="xxx.Account"/>
sigleton:只会创建一次简单对象默认值
prototype:每一次都会创建新的对象
2、如何控制复杂对象的创建次数
FactoryBean{
isSingleton(){
return true 只会创建一次
return false 每一次都会创建新的
}
}
若没有(如实例工厂与静态工厂)isSingleton方法,还是通过scope属性进行对象创建次数的控制
3.为什么要控制对象的创建次数?
好处:节省不必要的内存浪费
什么样的对象只创建一次?(可以共用 或者 线程安全)
- SqlSessionFactory
- DAO
- Service
什么样的对象每一 次都要创建新的?(不可以被共用 或者 线程不安全)
- Connection (控制了提交事务,每个事务的提交时间都不相同)
- SqlSession | Session (封装了提交事务)
- Struts2 Action
对象的生命周期
1、什么是对象的生命周期
指的是一个对象创建、存活、消亡的一个完整过程
2、为什么要学习对象的生命周期
由Spring负责对象的创建、 存活、销毁,了解生命周期,有利于我们使用好Spring为我们创建的对象
3、生命周期的3个阶段
1、创建阶段
Spring工厂何时创建对象
- scope=“singleton”
Spring工厂创建的同时,同时创建对象
注意:如果 在scope = “singleton” 这种情况下也想要在获取对象的同时创建对象,则增加<bean lazy-init="true"/>
- scope=“prototype”
Spring工厂会在获取对象的同时,创建对象:ctx.getBean("")
2、初始化阶段
Spring工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作
初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作
初始化方法调用: Spring工厂进行调用
- InitializingBean接口(Spring自己会进行调用,但是也是会造成Spring侵入)
//程序员根据需求,实现的方法,完成初始化操作
public void afterProperitesSet(){
}
- 对象中提供一个普通的方法(通知spring调用)
public void myInit(){
}
<bean id="product" class= "xxx.Product" init-method=" myInit" />
- 细节分析
-
如果一个对象既实现InitializingBean同时又提供的普通的初始化方法顺序
首先 InitializingBean,后 普通初始化方法
-
注入一定发生在初始化操作的前面(set)
-
什么叫做初始化操作
资源的初始化: 数据库、IO、网络 …
3、销毁阶段
Spring销毁对象前,会调用对象的销毁方法,完成销毁操作
1、Spring什么时候销毁所创建的对象?
ctx.close() ;
2、销毁方法:程序员根据自己的需求,定义销毁方法,完成销毁操作
调用:Spring工厂 完成调用
- DisposableBean
public void destroy() throws Exception {
System.out.println("Product.destroy");
}
- 定义一个普通的销毁方法
public void myDestory() throws Exception {
System.out.println("Product.myDestory");
}
<bean id="product" class="com.company.life.Product" init-method="myInit" destroy-method="myDestory"/>
- 细节分析
-
销毁方法的操作只适用于
scope="singleton"
-
什么叫做销毁操作:主要指的就是资源的释放操作
io.close()
和connection.close()
总结
Test
package com.company.life;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
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;
}
public Product() {
System.out.println("Product.Product");
}
// public Product(String name) {
// System.out.println("name = " + name);
// this.name = name;
// }
// 这个就是初始化方法:做一些初始化操作
// Spring进行调用
// implements InitializingBean
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Product.afterPropertiesSet");
}
public void myInit() {
System.out.println("Product.myInit");
}
// 销毁方法:销毁操作(进行资源释放)
@Override
public void destroy() throws Exception {
System.out.println("Product.destroy");
}
public void myDestory() throws Exception {
System.out.println("Product.myDestory");
}
}
/**
* 用于测试:测试生命周期
*
*/
@Test
public void test12(){
// Spring工厂创建 若 scope = "singleton" 则在此时创建对象
// ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
// 若 scope = "prototype" 则在此时获取对象时 创建对象
Product product = (Product) ctx.getBean("product");
// close方法ApplicationContext里面没有实现这个方法
ctx.close();
}
<!-- <bean id="product" scope="prototype" class="com.company.life.Product"/>-->
<!-- 此时虽然是singleton但是也是在获取对象时创建-->
<bean id="product" class="com.company.life.Product" init-method="myInit" destroy-method="myDestory">
<property name="name" value="张三"/>
<!-- <constructor-arg>-->
<!-- <value>张三</value>-->
<!-- </constructor-arg>-->
</bean>