Spring06:Spring工厂创建复杂对象

1.什么是复杂对象

1.简单对象:可以直接通过new构造方法(反射也是调用构造方法)创建的对象。

2.复杂对象:不能直接通过new构造方法创建的对象。比如Connection,SqlSessionFactory对象,他们不能直接通过new构造方法创建,创建过程较复杂。

Connection:
	Class.forName("com.mysql.jdbc.Driver");
	Connection coon = DriverManager.getConnection();

SqlSessionFactory:
	InputStream in = Resources.getResourceAsStream();
	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);

3.作为Spring工厂,肯定要既可以帮我们创建简单对象,也可以创建复杂对象。那么一切对象Spring都可以帮我们解耦合。

2.Spring工厂创建复杂对象的3种方式

2.1.FactoryBean接口

Spring原生提供的帮我们创建复杂对象的方式。

2.1.1.开发步骤

  • 1.实现FatoryBean接口:要实现三个方法
public class MyFactoryBean implements FactoryBean {
    
    
  /**
   * 1.用于书写创建复杂对象的代码,并把复杂对象作为方法的返回值返回
   * @return
   * @throws Exception
   */
  @Override
  public Object getObject() throws Exception {
    
    
      return null;
  }

  /**
   * 2.返回所创建复杂对象的Class对象
   * @return
   */
  @Override
  public Class<?> getObjectType() {
    
    
      return null;
  }

  /**
   *3. 对象只需要使用一次,创建一次:true
   *  每一次调用都需要创建一个新的该对象:false
   * @return
   */
  @Override
  public boolean isSingleton() {
    
    
      return false;
  }
}
  • 2.配置Spring配置文件:
<bean id="connection" class="com.baizhiedu.basic.factorybean.ConnectionFactoryBean"></bean>
  • 3.注意:

    如果按照以前bean标签的理解,创建一个简单对象的。注册,创建一个我们自己实现的FactoryBean,即ConnectionFactoryBean对象,这样理解是错误的。

    如果在配置文件中配置了FactoryBean接口的实现类的< bean />标签,那么Spring针对这个bean标签的含义是直接创建一个复杂对象(不是class属性指定的FactoryBean接口的实现类,而是这个实现类要创建的复杂对象)放到IOC容器中。

    在这里插入图片描述

  • 注意看日志:初始化了7个配置在配置文件中的bean

在这里插入图片描述

2.1.2.示例:ConnectionFatoryBean< Connection >

  • 1.引入MySql驱动坐标

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
    
  • 2.实现FactoryBean接口:指定泛型为要创建的复杂对象

        public class ConnectionFactoryBean implements FactoryBean<Connection> {
          
          
          @Override
          public Connection getObject() throws Exception {
          
          
              return null;
          }
      
          @Override
          public Class<?> getObjectType() {
          
          
              return null;
          }
      
          @Override
          public boolean isSingleton() {
          
          
              return false;
          }
       }
    

    注意:isSingleton在FactoryBean接口中是一个default方法,默认返回true。所以可以不实现。

在这里插入图片描述

  • 3.具体方法实现
    MySQL高版本创建连接时,需要指定SSL证书,不然会报warning(不影响使用),解决警告:
    useSSL=false:不用SSL验证,mysql也就不会找ssl证书,也就不会给warning警告
       public class ConnectionFactoryBean implements FactoryBean<Connection> {
          
          
      
          /**
           * 用于书写创建复杂对象的代码,并把复杂对象作为方法的返回值返回
           * @return
           * @throws Exception
           */
          @Override
          public Connection getObject() throws Exception {
          
          
              Class.forName("com.mysql.jdbc.Driver");
              Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_db?useSSL=false", "root", "root");
              return  connection;
          }
      
          /**
           * 返回所创建复杂对象的Class对象
           * @return
           */
          @Override
          public Class<?> getObjectType() {
          
          
              return Connection.class;
          }
      
          /**
           * 对象只需要使用一次,创建一次:true
           * 每一次调用都需要创建一个新的该对象:false
           * @return
           */
          @Override
          public boolean isSingleton() {
          
          
              return false;
          }
       }
    

2.1.3.思想

思想:既然复杂对象我们都不确定该怎么来创建,或者说创建的方式各式各样。那么Spring就提供一个接口,让你自己来实现。

在这里插入图片描述

2.1.4.FactoryBean的细节分析

1.我就想获得ConnectionFactoryBean< Connection >这个对象,不想要Connection。那么怎么做呢?

用工厂对象获取bean对象时,参数中的id值要加一个 & 符号:
FactoryBean factoryBean = (FactoryBean)context.getBean("&connection");

在这里插入图片描述
2.isSingleton()方法:

  • 返回true:之后只创建一个复杂对象,再创建这个对象时直接拿已经创建好的。
    在这里插入图片描述

  • 返回false:每一次创建都会创建新的对象。
    在这里插入图片描述

  • 注意:

    如果这个对象能被大家所共用,那么只需要创建一个该对象即可,返回true。否则返回false。

  • 比如:
    1.Connection就是不能被大家共用的,每次使用都必须创建一个新的该对象。
    2.SqlSessionFactory是一个重量级资源,重量级资源一般都是线程安全的且占内存较大,所以可以只创建一个,大家共用。

3.依赖注入的体会:

把ConnectionFactoryBean中依赖的4个字符串信息,进行配置文件的注入。

这4个字符串在此处被写死了,存在耦合。将其作为成员变量,后续通过Spring注入进来,进行解耦。日后直接在配置文件中修改对应的配置即可。

在这里插入图片描述

  • 3.1.创建对象的属性和get/set方法

      	  private String driver;
          private String url;
          private String user;
          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 getUser() {
          
          
              return user;
          }
      
          public void setUser(String user) {
          
          
              this.user = user;
          }
      
          public String getPassword() {
          
          
              return password;
          }
      
          public void setPassword(String password) {
          
          
              this.password = password;
          }
    
  • 3.2.配置文件中配置bean和注入属性

    <bean id="connection" class="com.baizhiedu.basic.factorybean.ConnectionFactoryBean">
       <property name="password">
           <value>root</value>
       </property>
       <property name="user">
           <value>root</value>
       </property>
       <property name="url">
           <value>jdbc:mysql://localhost:3306/test_db?useSSL=false</value>
       </property>
       <property name="driver">
           <value>com.mysql.jdbc.Driver</value>
       </property>
   </bean>
  • 3.3.代码修改
      @Override
      public Connection getObject() throws Exception {
          
          
          Class.forName(driver);
          Connection connection = DriverManager.getConnection(url, user, password);
          return  connection;
      }
    

2.1.5.FactoryBean原理分析(简易)

最底层:反射+接口回调

1.为什么Spring规定FactoryBean接口让我们去实现,并且要将那个创建复杂对象的代码写在getObject()方法中?

2.为什么我们最后通过工厂的getBean方法获得的是复杂对象,而不是FactoryBean接口的实现类。

3.原因过程:

  • 当我们通过工厂的getBean去创建对象时

  • Spring会根句id=coon,找到配置文件中对应的bean标签。

  • 然后Spring会根据class属性指定的类名,判断这个类是不是FactoryBean的子类instanceof(FactoryBean)

    • 是:调用这个类中的getObject()返回,并返回复杂对象Connection
    • 不是:直接返回简单对象,ConnectionFactoryBean

在这里插入图片描述

2.1.6.FactoryBean小结

Spring中用于创建复杂对象的一种方式,也是Spring原生提供的,后续讲解Spring整合其他框架,大量应用FactoryBean。

2.2.实例工厂

1.为什么还需要实例工厂呢?

为了避免Spring框架的侵入。因为如果用Spring原生提供的FactoryBean来创建复杂对象的话,那么必须实现FactoryBean这个接口。如果日后没有Spring了的话,那么前面写的复杂对象的实现工厂就没有意义了。

2.Spring整合遗留的实例工厂

假设系统中已经有了一个创建Connection对象的工厂,并且该工厂要先创建工厂实例,在调用其中的工厂方法:这就是一个实例工厂。

ConnectionFactory connectionFactory =  new ConnectionFactory();
Connection  connection  = connectionFactory .getConnection();
public class ConnectionFactory {
    
    

    public Connection getConnection(){
    
    
        Connection connection = null;
        try {
    
    
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_db?useSSL=false", "root", "root");
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
        
        return connection;
    }
}
  • 配置文件:指定遗留的实例工厂作为factory-bean来创建Connection对象
      <bean id="coonFactory" class="com.baizhiedu.basic.factorybean.ConnectionFactory"></bean>
      <bean id="coon" factory-bean="coonFactory" factory-method="getConnection"></bean>
    

2.3.静态工厂

实例工厂创建复杂对象的方法是实例方法:

ConnectionFactory connectionFactory =  new ConnectionFactory();
Connection  connection  = connectionFactory .getConnection();

静态工厂创建复杂对象的方法是静态方法:没有创建工厂对象的过程

Connection  connection  = ConnectionFactory .getConnection();
public class StaticConnectionFactory {
    
    

    public static Connection getConnection(){
    
    
        Connection connection = null;
        try {
    
    
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_db?useSSL=false", "root", "root");
            System.out.println("实例工厂创建Connection。");
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }

        return connection;
    }
}

配置文件:因为是静态方法创建对象,所以不用实例工厂对象,即不用配置工厂的bean。

<bean id="coon1" class="com.baizhiedu.basic.factorybean.StaticConnectionFactory" factory-method="getConnection"></bean>

3.Spring工厂创建对象小结

在这里插入图片描述

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

使用实现FactoryBean接口创建复杂对象时,可以重写isSingleton方法来控制复杂对象的创建次数,那么怎么控制简单对象的创建次数呢?

1.控制简单对象的创建次数:scope属性

<bean id="account" class="com.baizhiedu.basic.scope.Account" scope="singleton"></bean>
  • scope=“singleton”:该对象只创建一次。
  • scope=“prototype”:该对象每次创建都会创建一个新的对象。
  • scope的默认值是singleton

2.复杂对象:通过实现FactoryBean接口实现isSingleton()方法来控制。

3.如果是实例工厂和静态工厂创建复杂对象,那么没有isSingleton()方法。我们也可以通过在配置文件中的scope属性来控制。

4.为什么要控制对象创建次数:节省内存空间

5.举例:

  • 什么样的对象只创建一次:线程安全的
    SqlSessionFactory, Dao(无状态的), Service(无状态的)
  • 什么样的对象,每一次创建都要创建新的:线程不安全的
    Connection,SqlSession | Session, Struts2 Action

猜你喜欢

转载自blog.csdn.net/tttxxl/article/details/115345160
今日推荐