五、Spring创建复杂对象和创建对象的次数

一、什么是复杂对象?

复杂对象:指的是不能直接通过new 构造方法创建的对象,那么反过来 简单对象 就是 可以直接通过new 构造方法创建对象 那么这类复杂对象有哪些呢? 有比如我们比较熟悉的Mybatis中的SqlSessionFactory 和Connection对象 简单对象的创建比较好理解,Spring也应该容易做到,那么复杂对象呢?如果复杂对象Spring 也能做到,那么Spring可就牛逼了

五、Spring创建复杂对象和创建对象的次数
文章图片

二、Spring工厂创建复杂对象的三种方式
2.1 实现FactoryBean接口
当我们实现了FactoryBean接口,那么我们就必须重写其三种方法,下面为其对应的三种 方法 1.public Object getObject() 该方法用于书写创建复杂对象的代码 并 把复杂对象作为方法的返回值 返回 *如果声明了T的类型,那么就会直接返回对应类型的对象 你知道为什么我们需要重写这个方法吗?这是因为每个复杂对象创建的代码未必一样,所以 需要我们自己去重写来创建 2.public Class getObjectType() 该方法用于返回复杂对象的Class对象 3.public boolean isSingleton() 返回true表示 该对象只需创建一次 返回false表示 每次调用都需要创建一个新的复杂对象

五、Spring创建复杂对象和创建对象的次数
文章图片

2.2 以创建Connection对象为例,下面展示其实现步骤
第一步:引入mysql相关依赖 mysql mysql-connector-java 5.1.48

第二步:新建一个类实现FactoryBean接口,并重写其三个方法 public class ConnectionBeanFactory implements FactoryBean { @Override public Connection getObject() throws Exception { Class.forName("com.mysql.jdbc.Driver"); //注意:如果是mysql8.0以上的,那么还需要在连接url处添加上对应的时区参数 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "admin"); return conn; }@Override public Class getObjectType() { return Connection.class; }@Override public boolean isSingleton() { return false; } }

第三步:配置Spring的配置文件 注意:如果Class中指定的类型 是FactoryBean的实现类,那么通过id值获取到的是 调用ConnectionBeanFactory方法中重写的getObject()方法所创建的对象

问题:如果我就是想创建这个ConnectionBeanFactory 的对象,该如何创建呢?
答案:我们只需要在代码层面加上"&"即可,示例如下
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml"); ConnectionBeanFactory conn = (ConnectionBeanFactory) ctx.getBean("&conn"); System.out.println("conn = " + conn);

2.3 关于isSingleton()方法,如果返回true,则只会创建一个对象,即我们所谓的单例,如果为false,则每次调用getBean()方法都会创建一个对象,为多例;
根据这个特点,决定是返回true(SqlSessionFactory)还是false(Connection)
为什么Connection对象是单例的?这是因为每获取一次Connection对象,那么就相当于 开启了一个事务,显然让这个对象共享,无异导致事务提交的混乱,完全打乱了事务的隔离特性

2.4 如果涉及到mysql高版本的连接创建时,需要指定不使用SSL证书来解决warn问题,配置如下
url="jdbc:mysql://localhost:3306/suns?userSSL=false"

备注:如果涉及到8.x版本的mysql,还要设置对应的时区,该配置可自行百度
2.5 四要素的代码优化
看到上面在代码层面将四要素都写死在代码上面了,这其实也是一种耦合的情况,所以我们可以使用Spring配置文件对其进行属性注入,改写如下
2.5.1 代码层面定义定义对应的成员变量,并且提供相应的setter和getter方法
public class ConnectionBeanFactory implements FactoryBean { //定义对应的四要素 private String driverClassName; private String url; private String username; private String password; public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; }public void setUrl(String url) { this.url = url; }public void setUsername(String username) { this.username = username; }public void setPassword(String password) { this.password = password; }public String getDriverClassName() { return driverClassName; }public String getUrl() { return url; }public String getUsername() { return username; }public String getPassword() { return password; }@Override public Connection getObject() throws Exception { Class.forName(driverClassName); //注意:如果是mysql8.0以上的,那么还需要在连接url处添加上对应的时区参数 Connection conn = DriverManager.getConnection(url, username, password); System.out.println("Creating Connection Bean......."); return conn; }@Override public Class getObjectType() { return Connection.class; }@Override public boolean isSingleton() { return true; } }

2.5.2 在Spring的配置文件中实现属性注入

2.5.3 依赖注入的体会(DI)
通过上面的配置,将Spring的属性注入的精髓利用得十分透彻,其好处在于很好地解耦合 如果哪天我觉得配置的信息不适合我用,那么我就就可以直接修改配置文件以达到效果

三、FactoryBean的实现原理(简易版)
在java编程体系中,许多开源框架都使用到了反射和接口回调,故江湖称:接口加反射,什么都能做。
需要思考下面的问题
1>为什么Spring规定FactoryBean接口 实现 并且通过getObject()方法才能获取对应的复杂对象? 2>为什么ctx.getBean("conn")获得的是复杂对象 Connection 而不是其本身ConnectionFactoryBean?

Spring内部运行流程
五、Spring创建复杂对象和创建对象的次数
文章图片

1>通过ctx.getBean("conn")获取ConnectionFactoryBean对象,然后通过instanceOf() 判断出其是否是FactoryBean接口的实现类 2>如果是FactoryBean接口的实现类的话,Spring按照规定,调用其重写的getObject()方法 来创建Connection对象 3>最终将Connection对象返回

四、FactoryBean总结
Spring中用于创建复杂对象的一种方式,也是Spring原生提供的,是其整合其他框架中 会大量使用FactoryBean

【五、Spring创建复杂对象和创建对象的次数】五、实例工厂
5.1 为什么需要实例工厂?
1>在没有Spring的基础上(即没有了FactoryBean接口),我们需要一个工厂来生成复杂对象, 如果哪天我们不用Spring了,也能有这个工厂来生产对象 2> 属于遗留的工厂类,Spring也能对其进行整合

    推荐阅读