1.复杂对象
- 复杂对象:指的是不能直接通过new构造方法创建的对象
- 比如Connection,SqlSessionFactory
2.Spring工厂创建复杂对象的3种方式
2.1FactoryBean接口
public class MyFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
把创建复杂对象的代码写在这里,并且把复杂对象作为方法的返回值返回
return null;
}
@Override
public Class<?> getObjectType() {
返回所创建的复杂对象的Class对象
return null;
}
@Override
public boolean isSingleton() {
如果我们只创建一个这种类型的对象,就返回true
如果每一个都要创建新的对象,就返回false
return true;
}
}
接下来,我们看看要怎么通过代码来把它实现出来
2.1.1步骤
- 实现FactoryBean接口
public class ConnectFactoryBean implements FactoryBean<Connection> {
@Override
public Connection getObject() throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode?serverTimezone=Asia/Shanghai", "root", "123456");
return conn;
}
@Override
public Class<?> getObjectType() {
return Connection.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
- 配置文件的配置
<bean id="conn" class="com.zyh.factorybean.ConnectFactoryBean"></bean>
接下来我们讨论一下其中的一下细节
我们先来回顾一下,之前创建简单对象的时候的情形
<bean id="user" class="com.zyh.basic.Person"></bean>
@Test
public void test2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//第一个参数是配置文件的id 第二个参数写的是我们想要创建的对象类型 这样就不用强转了
Person person = ctx.getBean("person", Person.class);
System.out.println(person);
}
这个时候,ctx.getBean("person")方法获得的是Person对象
但是如果我们创建的是复杂对象的话,那么这里就出现不一样的地方了
<bean id="conn" class="com.zyh.factorybean.ConnectFactoryBean"></bean>
ctx.getBean("conn");
这个时候ctx.getBean("conn");获得的不是ConnectionFactoryBean这个类的对象
FactoryBean接口的实现类:ConnectionFactoryBean
ctx.getBean("conn")获得的是它所创建的复杂对象
如果Class中指定的类型是FactoryBean接口的实现类,那么通过id值获得的是这个类所创建的复杂对象
<bean id="connection" class="com.zyh.factorybean.ConnectFactoryBean"></bean>
接下来进行测试看看结果
/**
* 用来测试FactoryBean接口
*/
@Test
public void test11(){
ApplicationContext ctx=new ClassPathXmlApplicationContext("/applicationContext.xml");
Connection conn = (Connection) ctx.getBean("connection");
System.out.println(conn);
}
com.mysql.cj.jdbc.ConnectionImpl@1722011b
我们可以看出来打印出来的结果确实是Connection类型而不是ConnectFactoryBean
2.1.2细节分析
我们看到刚刚的测试结果,可以看到最后返回的类型是Connection类型,而不是实现FactoryBean接口的类,
- 如果我们想要获得FactoryBean类型的对象,就得通过ctx.getBean("&connection");这样获得的就是CounnectionFactoryBean
/**
* 用来测试FactoryBean接口
*/
@Test
public void test11() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
ConnectFactoryBean conn = (ConnectFactoryBean) ctx.getBean("&connection");
System.out.println(conn);
}
测试结果
com.zyh.factorybean.ConnectFactoryBean@762ef0ea
isSingleton方法
- 返回true,则只会创建一个复杂对象
- 返回false,则每一次都会创建新对象
- 我们要根据这个对象的特定来决定方法返回的是true(SqlSessionFactory)或false(Connection)
- 对于连接对象,它不可以共用每次都应该创建新对象
依赖注入问题
- 我们之前分析过,如果存在依赖问题,我们就可以把被依赖的设置成为成员变量,然后通过注入来赋值,我们刚刚写的程序里面,要和数据库进行连接,用户名,密码等都是重要信息,没有它们,我们就无法连接数据库,所以,我们可以把它们设置成为成员变量
package com.zyh.factorybean;
import org.springframework.beans.factory.FactoryBean;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* @author zengyihong
* @create 2022--04--17 0:24
*/
public class ConnectFactoryBean implements FactoryBean<Connection> {
private String driver;
private String url;
private String username;
private String password;
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = 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 getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public Connection getObject() throws Exception {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url,username,password );
return conn;
}
@Override
public Class<?> getObjectType() {
return Connection.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
然后我们可以通过Spring注入来为它们赋值
我们因为提供了set方法
所以我们可以用set注入
<bean id="connection" class="com.zyh.factorybean.ConnectFactoryBean">
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/bjpowernode?serverTimezone=Asia/Shanghai"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
2.1.3 FactoryBean实现原理
我们可能有这样的疑惑
- 为什么Spring规定FactoryBean接口实现,并且把实现复杂对象的创建写在getObject()方法里面
为什么通过ctx.getBean("connection")方法获得的是复杂对象Connection,而不是获得FactoryBean接口的实现类ConnectionFactoryBean
Spring内部运行过程
- 通过connection获得 ConnectionFactoryBean类的对象,然后通过instanceof 判断出是FactoryBean接口的实现类
- SSpring按照规定调用 getObject方法,获得Connection对象
- 然后返回Connection对象
2.1.4FactoryBean总结
Spring中用来创建复杂对象的一种方式,也是Spring原生提供的
2.2实例工厂
使用实例过程来创建复杂对象主要是为了解决下面的几个问题
- 避免Spring框架的侵入,我们使用FactoryBean的方法来创建复杂对象时,必须实现这个接口,如果Spring框架不再提供了,那我们之前的操作就全部白费了
- 为了整合遗留系统,我们去一家公司后,项目可能已经搭建好了基本模型,里面可能已经提供了具体方法来获得这个复杂对象,这个时候,就得通过实例工厂的方法来获得这个复杂对象,我们就得去解决配置文件的问题
public class ConnectionFactory {
private Connection conn=null;
public Connection getConnection(){
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode?serverTimezone=Asia/Shanghai","root","123456");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return conn;
}
}
我们要想获得Connection对象的话,应该先把ConnectionFactory对象创建出来,这样调用这个对象的getConnection方法就可以获得Connection对象了
<!--先把ConnectionFactory对象创建出来-->
<bean id="connFactory" class="com.zyh.factorybean.ConnectionFactory"></bean>
<!-- 然后我们要调用ConnectionFactory的getConnect方法,这样才能获得对象-->
<bean id="conn" factory-bean="connFactory" factory-method="getConnection"></bean>
/**
* 测试实例工厂
*/
@Test
public void test12(){
ApplicationContext ctx=new ClassPathXmlApplicationContext("/applicationContext.xml");
Connection conn = (Connection)ctx.getBean("conn");
System.out.println(conn);
}
测试结果
com.mysql.cj.jdbc.ConnectionImpl@1722011b
2.3静态工厂
public class StaticConnectionFactory {
public static Connection getConnection(){
Connection conn=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode?serverTimezone=Asia/Shanghai","root","123456");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return conn;
}
}
静态工厂和实例工厂的区别就是,当我们用静态工厂来调用方法的时候,不需要再创建对象来调用方法,可以直接用类.静态方法来定调用
<bean id="connStatic" class="com.zyh.factorybean.StaticConnectionFactory" factory-method="getConnection"></bean>
@Test
public void test13(){
ApplicationContext ctx=new ClassPathXmlApplicationContext("/applicationContext.xml");
Connection conn = (Connection)ctx.getBean("connStatic");
System.out.println(conn);
}
测试结果
com.mysql.cj.jdbc.ConnectionImpl@5b3f61ff
2.4总结
3.控制工程创建对象的次数
3.1控制简单对象的创建次数
我们刚刚演示了创建Spring复杂对象,通过isSingleton方法,我们可以控制复杂对象的创建次数,但是,简单对象我们要怎么控制它的创建次数呢
我们可以使用score属性来控制创建次数
<bean id="accountscope" class="com.zyh.scope.Account" scope="singleton"></bean>
作用域 | 描述 |
---|---|
singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
global-session | 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境 |
3.2控制复杂对象的创建次数
实现FacaoryBean接口,并且实现里面的方法
其中,isSingleton()方法中,返回true,则只创建一次对象,返回false,则每次都会创建新对象
如果没有实现isSingleton方法,那么还是通过scope属性来进行对象创建次数的控制
3.3控制对象创建次数的原因
优点:节省不必要的内存浪费
有的对象只需要创建一次,我们就不希望它每次都创建新对象,所以应该进行控制
什么样的对象只被创建一次
- SqlSessionFactory
- DAO
- Service
什么样的对象每次都要创建新对象
- Connection
- SqlSession
- Session
其实就是说,如果这个对象是大家共用的,线程安全的,我们就可以让它只创建一次,如果是线程不安全的,那么每一次都要创建新对象