目录
书接上回,我们已经分析了Spring是怎么去创建对象的了。那什么是简单对象什么又是复杂对象呢?那简单对象又要怎么去创建呢?
- 简单对象:能够直接通过new构造方法创建的对象,称为是简单对象
- 复杂对象:不能通过直接new构造方法创建的对象,称为是复杂对象,例如数据库连接操作的中的Connection对象等
1)这里用数据库连接的操作代码为例,首先需要创建一个类用于返回数据库的连接对象,这个类实现了Spring中的FactoryBean的接口(实现这个类当然就需要去重写该类中的方法)
- 首先返回什么什么对象就在泛型中写该类的对象
- 第一个方法用于书写创建复杂对象的代码
- 第二个方法用于返回这个复杂对象的class对象
- 第三个方法是告知Spring创建的对象是否是单例的
- public class ConnectionFactoryBean implements FactoryBean
{ - @Override
- public 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;
- }
- @Override
- public Class> getObjectType() {
- // 用于返回创建的复杂对象的class对象
- return Connection.class;
- }
- @Override
- public 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);
- }
通过上述方式成功的获取到了复杂对象,但是这里细心一点就会发现,如果我的数据库更换了,或者这里的密码也更换了呢?那就需要去修改代码,那是不是就耦合了?耦合了怎么解决呢?是不是就可以利用前面所说的依赖注入解决啊!接下来修改代码
- public class ConnectionFactoryBean implements FactoryBean
{ - 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;
- }
-
- @Override
- public Connection getObject() throws Exception {
- // 用于创建复杂对象的代码
- // 创建数据库的连接对象
- Class.forName(driveClassName);
- Connection conn =
- DriverManager.getConnection(url,username,password);
- // 创建好了对象之后将这个对象返回
- return conn;
- }
- @Override
- public Class> getObjectType() {
- // 用于返回创建的复杂对象的class对象
- return Connection.class;
- }
- @Override
- public 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>
其实这里的核心就是一个接口回调。当我们通过getBean方法去获取这个连接对象的时候,Spring首先会通过instanceof方法去判断是不是实现了FactoryBean类,如果是的话Spring就会调用getObject方法返回连接对象,这也就是前面所说的为什么返回的不是ConnectionFactoryBean对象而是Connection对象。
这里可能就会有疑问了,为什么有了创建复杂对象的方式还要学这个实例工厂的方式呢?这是因为在写项目的过程中可能这个连接对象已经创建好了,我们只要去使用就行了。好,那我直接将这个代码实现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"/>
这里的静态工厂其实与实例工厂是差不多的,只是方法是静态的,所以和实例工厂只有细微的区别
- 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"/>