一、什么是复杂对象?
复杂对象:指的是不能直接通过new 构造方法创建的对象,那么反过来 简单对象 就是
可以直接通过new 构造方法创建对象
那么这类复杂对象有哪些呢? 有比如我们比较熟悉的Mybatis中的SqlSessionFactory
和Connection对象
简单对象的创建比较好理解,Spring也应该容易做到,那么复杂对象呢?如果复杂对象Spring
也能做到,那么Spring可就牛逼了
文章图片
二、Spring工厂创建复杂对象的三种方式
2.1 实现FactoryBean接口
当我们实现了FactoryBean接口,那么我们就必须重写其三种方法,下面为其对应的三种
方法
1.public Object getObject()
该方法用于书写创建复杂对象的代码 并 把复杂对象作为方法的返回值 返回
*如果声明了T的类型,那么就会直接返回对应类型的对象
你知道为什么我们需要重写这个方法吗?这是因为每个复杂对象创建的代码未必一样,所以
需要我们自己去重写来创建
2.public Class getObjectType()
该方法用于返回复杂对象的Class对象
3.public boolean isSingleton()
返回true表示 该对象只需创建一次
返回false表示 每次调用都需要创建一个新的复杂对象
文章图片
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内部运行流程
文章图片
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也能对其进行整合