【Spring IOC】Spring工厂创建对象

参考自B站UP主视频《孙哥说Spring5》

什么是复杂对象?

在这里插入图片描述

Spring 工厂创建复杂对象的三种方式

(1)FactoryBean接口

开发步骤

实现FactoryBean接口

  • 实现 getObjectgetObjectTypeisSingleton 方法
  • getObject():用于书写创建复杂对象时的代码。
  • getObjectType():返回创建的复杂对象的类型。
  • isSingleton:用于决定是否单例。
public class ConnectionFactoryBean implements FactoryBean<Connection> {
    // 用于书写创建复杂对象时的代码
    @Override
    public Connection getObject() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring", "root", "1234");
        return conn;
    }

    // 返回创建的复杂对象的类型
    @Override
    public Class<Connection> getObjectType() {
        return Connection.class;
    }

    // 是否单例
    @Override
    public boolean isSingleton() {
        return false; // 每一次都创建新的复杂对象
        // return true; // 只创建一次这种类型的复杂对象
    }
}

Spring 配置文件的配置

  • 如果 class 中指定的类型是 FactoryBean 接⼝的实现类,那么通过 id值 获得的是这个类所创建的复杂对象。
  • 比如下面 class 指定的是 ConnectionFactoryBean,获得的是 Connection 对象。
<!--class 指定了 ConnectionFactoryBean, 获得的是该类创建的复杂对象 Connection -->
<bean id="conn" class="com.yusael.factorybean.ConnectionFactoryBean"/>

此处通过这个id获得到的是getObject()方法返回的conn


如果就想获得 FactoryBean 类型的对象,需要加个 &ctx.getBean("&conn")

ConnectionFactoryBean cfb = (ConnectionFactoryBean) ctx.getBean("&conn");

isSingleton 方法返回 true 只会创建⼀个复杂对象,返回 false 每⼀次都会创建新的对象;

  • 需要根据这个对象的特点 ,决定是返回 true(例如:SqlSessionFactory) 还是 false(例如:Connection);

上面的代码中,直接将字符串信息写在代码中,这样耦合性很大,可以改为依赖注入的方式

ConnectionFactoryBean 中依赖的 4 个字符串信息 ,通过配置⽂件进行注⼊。

@Getter@Setter // 提供 get set 方法
public class ConnectionFactoryBean implements FactoryBean<Connection> {
	// 将依赖的字符串信息变为成员变量, 利用配置文件进行注入。
    private String driverClassName; // com.mysql.jdbc.Driver
    private String url; // jdbc:mysql://localhost:3306/spring
    private String username; // root
    private String password; // 1234
    @Override
    public Connection getObject() throws Exception {
        Class.forName(driverClassName);
        Connection conn = DriverManager.getConnection(url, username, password);
        return conn;
    }
    @Override
    public Class<Connection> getObjectType() {
        return Connection.class;
    }
    @Override
    public boolean isSingleton() {
  		return false;
    }
}

配置文件中添加注入信息

<!--体会依赖注入, 好处: 解耦合, 今后要修改连接数据库的信息只需要修改配置文件, 无需改动代码-->
<bean id="conn" class="com.yusael.factorybean.ConnectionFactoryBean">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/spring?useSSL=false"/>
    <property name="username" value="root"/>
    <property name="password" value="1234"/>
</bean>

FactoryBean 实现原理

原理:接口回调

问题:

  • 为什么 Spring 规定 FactoryBean 接⼝实现 getObject()
  • 为什么 ctx.getBean("conn") 获得的是复杂对象 Connection ⽽非 ConnectionFactoryBean

Spring 内部运行流程:

  • 配置文件中通过 id 可以获得 ConnectionFactoryBean 类的对象 ,进而通过 instanceof 判断出是 FactoryBean 接⼝的实现类;
  • Spring 按照规定 getObject() —> Connection
  • 返回 Connection

在这里插入图片描述

总结

Spring 中⽤于创建复杂对象的⼀种方式,也是 Spring 原⽣提供的,后续 Spring 整合其他框架时会⼤量应⽤ FactoryBean 方式。

(2)实例工厂

为什么要用实力工厂的方式创建对象?

  • 避免 Spring 框架的侵⼊,实现FactoryBean接口就是Spring框架的侵入
  • 整合遗留系统,如果通过实现 FactoryBean接口 可能会出现冲突、又或者只能拿到字节码文件而不能拿到源代码文件

实现步骤

先写一个工厂类

public class ConnectionFactory {
    public Connection getConnection() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring?useSSL=false", "root", "1234");
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
}

然后在配置文件中注入

<!--实例工厂-->
<!-- 先创建出工厂实例 -->
<bean id="connFactory" class="com.yusael.factorybean.ConnectionFactory"/>

<!-- 通过工厂实例里的方法创建复杂对象 -->
<!-- 如果要获得 conn 这个实例,可以通过工厂 connFactory ,调用 getConnection 方法获得 -->
<bean id="conn" factory-bean="connFactory" factory-method="getConnection"/>

(3)静态工厂

和实例工厂的差别就是:获取对象的方法是个静态方法

// 实例工厂获得实例的方式
new ConnectionFactory().getConnection();
// 静态工厂获得实例的方式
StaticConnectionFactory.getConnection();

实现步骤

先写一个静态工厂,StaticConnectionFactory 类

public class StaticFactoryBean {
	// 静态方法
    public static Connection getConnection() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring?useSSL=false", "root", "1234");
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
}

配置文件

<!--静态工厂不需要实例,直接调用方法就可以获得-->
<bean id="conn" class="com.yusael.factorybean.StaticFactoryBean" factory-method="getConnection"/>

总结

在这里插入图片描述

  • 对于复杂对象的创建,一般推荐使用 FactoryBean接口 来创建对象

控制 Spring 工厂创建对象的次数

在前面的创建方式中,要么是单例创建,要么是每次都创建一个新的实例

但是上述的方式创建的是复杂对象,那如果是简单对象的创建呢?

该如何控制简单对象的创建次数呢?

对于简单对象,如何控制创建次数?

配置文件中配置,Spring中默认singleton

  • sigleton:只会创建⼀次简单对象,默认值;
  • prototype:每⼀次都会创建新的对象;
<!--控制简单对象创建次数-->
<bean id="scope" scope="singleton" class="com.yusael.scope.Scope"/>

对于FactoryBean接口的实现类,可以通过isSingleton方法控制

  • 如果是实例工厂或者静态工厂,没有 isSingleton ⽅法,与简单对象一样通过 scope 控制。

问题来了:为什么我们要控制对象的创建次数呢?每次都创建一个实例好像也没有什么问题啊。

好处就是节省不必要的内存浪费

对于一些重量级的、可以被共用的、线程安全的对象,没必要每次使用的时候都创建一个实例

  • 特别是一些创建过程比较复杂的对象,每一次创建会带来内存的消耗
  • 例如SqlSessionFactory很占内存、DAO层Service层

相反的,对于一些不能被共用的,线程不安全的对象,可以每次创建的时候都创建一个新的实例

  • 例如:ConnectionSqlSession | SessionStrust2中的Action

拓展:SpringMVC中的Controller也是默认是单例的,但是单例模式下容易出现的问题就是controller中定义很多的属性,那么单例肯定会出现竞争访问,不同用户共享数据变量是不安全的。因此尽量不要在Controller中定义成员变量,否则就使用 prototype 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值