1.复杂对象
1.1 什么是复杂对象
复杂对象:指的就是不能直接通过new构造方法创建的对象
Connection
SqlSessionFactory
Spring工厂创建复杂对象
2. Spring工厂创建复杂对象的3种方式
2.1 FactoryBean接口
2.1.1 开发步骤
- 实现FactoryBean接口
FactoryBean接口有三个方法需要被实现- Object getObject() :
用于书写创建复杂对象的代码,并把复杂对象作为方法的返回值返回 - Class getObjectType() :
用于返回所创建对象的Class对象 - boolean isSingleton()
返回 true 只会创建一个复杂对象
返回 false 每一次都会创建新的对象
- Object getObject() :
public class ConnectionFactoryBean implements FactoryBean<Connection> {
@Override
public Connection getObject() throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://192.168.31.174:3306/?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC","root","123456");
return connection;
}
@Override
public Class<?> getObjectType() {
return Connection.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
- Spring配置文件的配置
# 如果Class中指定的类型 是FactoryBean接口的实现类,那么通过id值获得的是这个类所创建的复杂对象 Connection
<bean id="conn" class="com.tcgroup.factory.ConnectionFactoryBean"/>
2.1.2 细节
如何获得FactoryBean类型的对象
通过我们上面的配置, 由于ConnectionFactoryBean实现了FactoryBean接口,所以通过ID“conn”获得的是ConnectionFactoryBean生产的Connection对象,
- 如果就想获得FactoryBean类型的对象 可以在ID“conn”前面加一个&
- classPathXmlApplicationContext.getBean("&conn")获得就是ConnectionFactoryBean对象
isSingleton方法
-返回 true 只会创建一个复杂对象
- 返回 false 每一次都会创建新的对象
不是系统自己识别ConnectionFactoryBean是不是单例模式 , 而是需要自己手动去根据这个对象的特点 ,决定是返回true (SqlSessionFactory) 还是 false (Connection),如果你在isSingleton方法里面写的是返回true只会创建一个复杂对象,不管调用几次getBean, 返回的都是同一个对象,适合做复用
依赖注入的思想对上面代码进行优化
public class ConnectionFactoryBean implements FactoryBean<Connection> {
private String url;
private String userName;
private String psaaword;
private String driver;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPsaaword() {
return psaaword;
}
public void setPsaaword(String psaaword) {
this.psaaword = psaaword;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
@Override
public Connection getObject() throws Exception {
Class.forName(driver);
Connection connection = DriverManager.getConnection(url,userName,psaaword);
return connection;
}
@Override
public Class<?> getObjectType() {
return Connection.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
<bean id="conn" class="com.tcgroup.factory.ConnectionFactoryBean">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.31.174:3306/?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC"/>
<property name="userName" value="root"/>
<property name="psaaword" value="123456"/>
</bean>
FactoryBean的实现原理[简易版]
接口回调
- 为什么Spring规定FactoryBean接口 实现 并且 getObject()?
- ctx.getBean(“conn”) 获得是复杂对象 Connection 而没有 获得 ConnectionFactoryBean(&)
Spring内部运行流程
- 通过conn获得 ConnectionFactoryBean类的对象 ,进而通过instanceof 判断出是FactoryBean接口的实现类
- Spring按照规定 getObject() —> Connection
- 返回Connection
2.2 实例工厂
1. 避免Spring框架的侵入
2. 整合遗留系统
开发步骤
假设我们有之前遗留下来的一个ConnectionFactory,提供了getConnection方法可以帮我们拿到一个Connection对象
public class ConnectionFactory {
public Connection getConnection(){
Connection conn=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://192.168.31.174:3306/?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC","root","123456");
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
}
那么接下来我们可以通过配置文件来获取这个Class文件并且用factory-method指定调用他的getConnection方法
第一行先创建ConnectionFactory实例, 然后第二行调用这个实例的getConnection方法
<bean id="connFactory" class="com.tcgroup.factory.ConnectionFactory"> </bean>
<bean id="connn" factory-bean="connFactory" factory-method="getConnection"/>
2.3 静态工厂
静态工厂和上面差不多 从之前来说,区别在于实例工厂需要先new一个ConnectionFactory对象,然后通过对象来调用getConnection方法,获取Connection对象,而静态方法可以直接通过类名.方法名来调用方法,而从Spring的配置文件上也是有区别的
public class StaticConnectionFactory {
public static Connection getConnection(){
Connection conn=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://192.168.31.174:3306/?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC","root","123456");
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
}
实例工厂需要先把实例创建出来, 然后再去引用,而静态工厂可以直接引用不需要创建实例
<bean id="connnn" class="com.tcgroup.factory.StaticConnectionFactory" factory-method="getConnection"/>
3.工厂创建对象总结:
4.控制Spring工厂创建对象的次数
4.1 如何控制简单对象的创建次数
通过applicationContext.xml文件配置scope属性为singleton可以控制Spring工厂创建这个简单对象的时候只创建一次
sigleton:只会创建一次简单对象 不写scope属性默认是sigleton
prototype:每一次都会创建新的对象
<bean id="account" scope="singleton" class="com.tcgroup.scope.Account"/>
4.2. 如何控制复杂对象的创建次数
上面已经介绍过,FactoryBean接口有一个 boolean isSingleton()方法需要被实现
返回 true 只会创建一个复杂对象
返回 false 每一次都会创建新的对象
FactoryBean{
isSingleton(){
return true 只会创建一次
return false 每一次都会创建新的
}
}
如没有isSingleton方法 还是通过scope属性 进行对象创建次数的控制
4.3 为什么要控制对象的创建次数?
好处:节省不必要的内存浪费,有些可以复用的对象没有必要每次都重新创建
- 什么样的对象只创建一次?
可以共用的,线程安全的
1. SqlSessionFactory
2. DAO
3. Service
- 什么样的对象 每一次都要创建新的?
不能共用的,线程不安全的
1. Connection
2. SqlSession | Session
3. Struts2 Action