Spring工厂创建复杂对象

本文阅读 8 分钟
首页 代码,Java 正文

img

  • 复杂对象:指的是不能直接通过new构造方法创建的对象
  • 比如Connection,SqlSessionFactory

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对象 img

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总结

img

3.1控制简单对象的创建次数

我们刚刚演示了创建Spring复杂对象,通过isSingleton方法,我们可以控制复杂对象的创建次数,但是,简单对象我们要怎么控制它的创建次数呢 我们可以使用score属性来控制创建次数

<bean id="accountscope" class="com.zyh.scope.Account" scope="singleton"></bean>

img

3.2控制复杂对象的创建次数

实现FacaoryBean接口,并且实现里面的方法 其中,isSingleton()方法中,返回true,则只创建一次对象,返回false,则每次都会创建新对象 如果没有实现isSingleton方法,那么还是通过scope属性来进行对象创建次数的控制

3.3控制对象创建次数的原因

优点:节省不必要的内存浪费 有的对象只需要创建一次,我们就不希望它每次都创建新对象,所以应该进行控制

  • 什么样的对象只被创建一次
    - SqlSessionFactory - DAO - Service
  • 什么样的对象每次都要创建新对象
    - Connection - SqlSession - Session
其实就是说,如果这个对象是大家共用的,线程安全的,我们就可以让它只创建一次,如果是线程不安全的,那么每一次都要创建新对象
本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://zengyihong.blog.csdn.net/article/details/124223418
-- 展开阅读全文 --
安全面试之XSS(跨站脚本攻击)
« 上一篇 07-24

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复