Spring工厂创建复杂对象

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

    其实就是说,如果这个对象是大家共用的,线程安全的,我们就可以让它只创建一次,如果是线程不安全的,那么每一次都要创建新对象

猜你喜欢

转载自blog.csdn.net/qq_52797170/article/details/124223418
今日推荐