目录
一、什么是复杂对象
二、创建复杂对象的3种方式
2.1 实现FactoryBean接口
2.1.1 普通的创建方式
2.1.1 依赖注入的方式
2.1.3 FactoryBean的工作原理
2.2 实例工厂
2.3 静态工厂
一、什么是复杂对象
书接上回,我们已经分析了Spring是怎么去创建对象的了。那什么是简单对象什么又是复杂对象呢?那简单对象又要怎么去创建呢?
- 简单对象:能够直接通过new构造方法创建的对象,称为是简单对象
- 复杂对象:不能通过直接new构造方法创建的对象,称为是复杂对象,例如数据库连接操作的中的Connection对象等
二、创建复杂对象的3种方式
2.1 实现FactoryBean接口
2.1.1 普通的创建方式
1)这里用数据库连接的操作代码为例,首先需要创建一个类用于返回数据库的连接对象,这个类实现了Spring中的FactoryBean的接口(实现这个类当然就需要去重写该类中的方法)
- 首先返回什么什么对象就在泛型中写该类的对象
- 第一个方法用于书写创建复杂对象的代码
- 第二个方法用于返回这个复杂对象的class对象
- 第三个方法是告知Spring创建的对象是否是单例的
public class ConnectionFactoryBean implements FactoryBean<Connection> {@Overridepublic Connection getObject() throws Exception {// 用于创建复杂对象的代码// 创建数据库的连接对象Class.forName("com.mysql.jdbc.Driver");Connection conn =DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","123456");// 创建好了对象之后将这个对象返回return conn;}@Overridepublic Class<?> getObjectType() {// 用于返回创建的复杂对象的class对象return Connection.class;}@Overridepublic boolean isSingleton() {return false;}
}
2) 通过Spring的配置文件获取复杂对象。这里需要注意的是,这里通过bean标签获取到的对象和之前写过的配置文件有所不同了,之前这个class属性中对应的是哪个类获取到的就是哪个类的对象,但是在实现了FactoryBean接口的类中这里获取到的对象是创建的复杂对象(后面详说)
<bean id="connection" class="com.gl.demo.factory.ConnectionFactoryBean"/>
3) 接下来进行测试,这里如果成功打印则说明拿到的确实是Connection对象而不是ConnectionFactoryBean对象,通过测试可以发现获取到的对象就是Connection对象
@Test
public void test5() {ApplicationContext ctx =new ClassPathXmlApplicationContext("applicationContext.xml");Connection conn = (Connection) ctx.getBean("connection");System.out.println("conn = "+conn);
}
4)如果只是想获取ConnectionFactoryBean对象呢?这也是可以解决的,在getBean方法的参数中加上&符号即可,此时获取到的就是ConnectionFactoryBean对象了
public void test6() {ApplicationContext ctx =new ClassPathXmlApplicationContext("applicationContext.xml");ConnectionFactoryBean conn = (ConnectionFactoryBean) ctx.getBean("&connection");System.out.println("conn = "+conn);
}
2.1.1 依赖注入的方式
通过上述方式成功的获取到了复杂对象,但是这里细心一点就会发现,如果我的数据库更换了,或者这里的密码也更换了呢?那就需要去修改代码,那是不是就耦合了?耦合了怎么解决呢?是不是就可以利用前面所说的依赖注入解决啊!接下来修改代码
public class ConnectionFactoryBean implements FactoryBean<Connection> {private String driveClassName;private String url;private String username;private String password;public void setDriveClassName(String driveClassName) {this.driveClassName = driveClassName;}public void setUrl(String url) {this.url = url;}public void setUsername(String username) {this.username = username;}public void setPassword(String password) {this.password = password;}@Overridepublic Connection getObject() throws Exception {// 用于创建复杂对象的代码// 创建数据库的连接对象Class.forName(driveClassName);Connection conn =DriverManager.getConnection(url,username,password);// 创建好了对象之后将这个对象返回return conn;}@Overridepublic Class<?> getObjectType() {// 用于返回创建的复杂对象的class对象return Connection.class;}@Overridepublic boolean isSingleton() {return false;}
}
<bean id="connection" class="com.gl.demo.factory.ConnectionFactoryBean"><property name="driveClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false"/><property name="username" value="root"/><property name="password" value="123456"/>
</bean>
2.1.3 FactoryBean的工作原理
其实这里的核心就是一个接口回调。当我们通过getBean方法去获取这个连接对象的时候,Spring首先会通过instanceof方法去判断是不是实现了FactoryBean类,如果是的话Spring就会调用getObject方法返回连接对象,这也就是前面所说的为什么返回的不是ConnectionFactoryBean对象而是Connection对象。
2.2 实例工厂
这里可能就会有疑问了,为什么有了创建复杂对象的方式还要学这个实例工厂的方式呢?这是因为在写项目的过程中可能这个连接对象已经创建好了,我们只要去使用就行了。好,那我直接将这个代码实现FactoryBean接口不就可以了吗?其实这里你也不确定能不能拿到源码文件,如果是class文件呢?这也就是学习实例工厂的原因
1)假设这里已经有了连接对象的class文件,当然这里是有耦合的,但是为了演示简洁就不在写配置文件了,大家知道即可
public class ConnectionFactory {public Connection getConnection() {Connection conn = null;try {Class.forName("com.mysql.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","123456");} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}return conn;}
}
2)有了这个类之后是不是就需要获取到这个类对象,然后再通过这个类对象调用getConnection方法。但是如果通过getBean方法获取再调用就显得麻烦,这里就可以通过使用实例工厂进行整合。这里引出两个标签属性一个是factory-bean表示整合哪个类,另一个factory-method表示类中的哪个方法。
<bean id="connFactory" class="com.gl.demo.factory.ConnectionFactory"/>
<bean id="conn" factory-bean="connFactory" factory-method="getConnection"/>
2.3 静态工厂
这里的静态工厂其实与实例工厂是差不多的,只是方法是静态的,所以和实例工厂只有细微的区别
public class StaticConnectionFactory {public static Connection getConnection() {Connection conn = null;try {Class.forName("com.mysql.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","123456");} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}return conn;}
}
静态的方法可以通过类直接调用,那这个配置文件的写法也就明确了,直接在这个类对象中写上factory-method即可
<bean id="conn1" class="com.gl.demo.factory.StaticConnectionFactory" factory-method="getConnection"/>