Spring工厂创建复杂对象

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用版 2核4GB 50GB
简介: 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

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

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
20天前
|
前端开发 Java Spring
Spring MVC 是如何对对象参数进行校验的
【6月更文挑战第4天】对象参数校验是使用 SpringMVC 时常用的功能,这篇文章尝试分析了,Spring 是如何实现这一功能的。
31 5
|
11天前
|
存储 XML Java
在 Java 中,Spring 框架提供了一种更加简单的方式来读取和存储对象
【6月更文挑战第18天】Java Spring 框架利用注解简化对象管理:@Component(及衍生注解@Service等)标注Bean类,自动注册到容器;@Autowired用于字段或方法,实现依赖注入,提升灵活性,减少XML配置。
17 2
|
19天前
|
JSON 前端开发 Java
Spring MVC 级联对象参数校验
【6月更文挑战第6天】在 Spring MVC 的使用过程中,我们会发现很多非常符合直觉的功能特性,但往往我们会习惯这种「被照顾得很好」的开发方式,依靠直觉去判断很多功能特性的用法。
19 1
|
1月前
|
存储 Java 对象存储
Spring 更简单的读取和存储对象
Spring 更简单的读取和存储对象
|
1月前
|
存储 XML Java
Spring框架学习 -- 读取和存储Bean对象
Spring框架学习 -- 读取和存储Bean对象
20 0
Spring框架学习 -- 读取和存储Bean对象
|
1月前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
44 0
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
1月前
|
存储 Java 数据库
Spring的使用-Bean对象的储存和获取/Bea对象的作用域与生命周期
Spring的使用-Bean对象的储存和获取/Bea对象的作用域与生命周期
|
1月前
|
SQL Java 数据库连接
15:MyBatis对象关系与映射结构-Java Spring
15:MyBatis对象关系与映射结构-Java Spring
48 4
|
1月前
|
Java Spring
Spring⼯⼚创建复杂对象
Spring⼯⼚创建复杂对象
38 11
|
1月前
|
安全 Java API
Spring工厂API与原理
Spring工厂API与原理
44 10