目录
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