[Spring] 实例化Bean的三种方法

Spring框架的核心是IOC容器,它是个管理对象的工厂,常说的两个专业名词“控制反转”和“依赖注入”,就是它最重要的两大特征:

  1. 控制反转:创建对象的权利交给IOC容器,程序员负责声明要创建的对象
  2. 依赖注入:对象之间的依赖关系由IOC容器负责实现,程序员负责声明这种依赖关系,依赖关系就是指对象内部有哪些属性

Spring容器中的对象叫做Bean,本文要讲的实例化Bean方法,说的就是“控制反转”这个特性,程序员怎么声明Bean,容器怎么创建Bean

现在Spring已经发展到Spring Boot阶段了,大量使用注解配置,应用起来更加高阶,程序也更加“智能”。

但是我个人感觉,可能还是XML配置更容器理解原理特性,因此,以下程序都采用XML配置书写。

方法一:构造器

有一个对象类定义,该对象和其它对象没有依赖关系:

package com.comeheart.spring.entity;

public class User {
}

Spring标准XML配置文件中,程序员需要用<bean/>节点声明要创建的对象:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.comeheart.entity.User"></bean>

</beans>

其中,id属性声明对象在容器中的唯一标识符,class属性声明对象的全路径类名。

Spring容器读取到XML声明后,利用java反射机制调用User类的构造器,实例化该对象。以下源码有省略:

package org.springframework.beans;

public abstract class BeanUtils {

    public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
        Assert.notNull(ctor, "Constructor must not be null");
        // 省略代码
        return ctor.newInstance(argsWithDefaultValues);
    }
    
}

方法二:静态工厂方法

改造一下User类定义,首先禁用构造器实例化,然后提供静态工厂方法返回对象实例:

package com.comeheart.spring.entity;

public class User {

    private static User user = new User();

    private User() {
    }

    public static User createInstance() {
        return user;
    }

}

SpringXML声明如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.comeheart.spring.entity.User" factory-method="createInstance"></bean>

</beans>

class属性和factory-method属性结合,表示使用User类的静态工厂方法createInstance()创建该对象。有两点需要注意:

  1. factory-method方法返回值必须是class属性声明的类型
  2. factory-method方法本身必须属于class属性声明的类型

为什么factory-method方法一定要属于class类型?

因为Spring<bean/>定义里面,没有提供类似factory-class这样的声明,也就是不能指定工厂类,所以,默认的,factory-method属性声明的方法就属于class属性声明的类了。

咋一看,静态工厂方法不如构造器简洁,但是它有好处,程序员可以在IOC容器实例化对象的过程中,更自由的操作对象。

Spring容器读取XML配置后,判断对象声明中有factory-method属性和class属性,就会调用该类的工厂方法,获取对象实例:

package org.springframework.beans.factory.support;

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        // 省略前后代码
        // mbd.getFactoryMethodName() 就对应着 factory-method 声明
        if (mbd.getFactoryMethodName() != null) {
    	    return instantiateUsingFactoryMethod(beanName, mbd, args);
        }
    }    

}

方法三:对象工厂方法

User类定义回归到原来的样子:

package com.comeheart.spring.entity;

public class User {
}

新增一个Locator类定义,表示工厂对象,其中的对象工厂方法创建User类实例:

public class Locator {

    private static User user = new User();

    public User createUserInstance() {
        return user;
    }

}

此时,Locator内部需要有一个实实在在的User对象,这种“需要”就叫做“依赖”,Locator类依赖User类。

这种依赖关系如果交给Spring容器去处理,就是“依赖注入”的概念。

在这里,我并没有这么做,依赖关系由Locator自己管理,自己去new User()对象。

说回来,SpringXML配置文件声明如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="locator" class="com.comeheart.spring.entity.Locator"></bean>

    <bean id="user" factory-bean="locator" factory-method="createUserInstance"></bean>

</beans>

IOC容器读取XML配置后,会先使用构造器方法创建Locator对象,然后调用Locator对象的createUserInstance()工厂方法获取User对象。

静态工厂方法、对象工厂方法,Spring实现的两者代码逻辑都是一样的。

都会先判断factory-method逻辑,这和上一节代码相同:

package org.springframework.beans.factory.support;

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        // 省略前后代码
        // mbd.getFactoryMethodName() 就对应着 factory-method 声明
        if (mbd.getFactoryMethodName() != null) {
    	    return instantiateUsingFactoryMethod(beanName, mbd, args);
        }
    }    

}

追踪其中的instantiateUsingFactoryMethod(...)逻辑,可以得到:

package org.springframework.beans.factory.support;

class ConstructorResolver {

    public BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
    
        Object factoryBean;
        Class<?> factoryClass;
        boolean isStatic;

        String factoryBeanName = mbd.getFactoryBeanName();
        if (factoryBeanName != null) {
            if (factoryBeanName.equals(beanName)) {
                throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName, "factory-bean reference points back to the same bean definition");
            }
            factoryBean = this.beanFactory.getBean(factoryBeanName);
            if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
                throw new ImplicitlyAppearedSingletonException();
            }
            factoryClass = factoryBean.getClass();
            isStatic = false;
        } else {
            // It's a static factory method on the bean class.
            if (!mbd.hasBeanClass()) {
                throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName, "bean definition declares neither a bean class nor a factory-bean reference");
            }
            factoryBean = null;
            factoryClass = mbd.getBeanClass();
            isStatic = true;
		}
    }

}

这段代码中,有两个方法域变量factoryBeanfactoryClass

  1. factoryBean是对象工厂方法需要的
  2. factoryClass是静态工厂方法需要的

通过factoryBeanName做一个判断,就能确定该对象的实例化方式,是静态工厂方法,还是对象工厂方法。

容器启动与应用

容器加载配置启动成功以后,就可以使用容器提供的接口,获取各种实例化对象信息:

public class App {

    public static void main(String[] args) {
        // Spring容器启动
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        // 获取对象等使用方法
        User user = context.getBean("user", User.class);
        Class<?> clazz = context.getType("user");
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值