Spring容器以及Bean的实例化
最近在看Spring源码相关的内容,结合鲁班学院的子路老师,记录一下自己的理解,以及在源码阅读中的一些问题。版本基于5.2.5
1 Spring容器
1.1 容器是什么?
The
org.springframework.context.ApplicationContext
interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.
这句话的大概意思有两个:
- Spring IOC容器就是一个
org.springframework.context.ApplicationContext
的实例化对象 - 容器负责了实例化,配置以及装配一个bean
从两个角度去考虑Spring 容器
- 从代码层次来看:Spring容器就是一个实现了
ApplicationContext
接口的对象(如ClassPathXmlApplicationContext
; - 从功能上来看: Spring 容器是 Spring 框架的核心,是用来管理对象的。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。
具体可以参考图如下
1.2 容器是如何工作的?
Spring容器通过我们提交的pojo类以及配置元数据产生一个充分配置的可以使用的系统
这里说的配置元数据,实际上我们就是我们提供的XML配置文件,或者通过注解方式提供的一些配置信息
2 Spring Bean
2.1 如何实例化一个Bean
从官网上看,有三种方法
- 构造方法
- 通过静态工厂
- 通过实例化工厂
从源码的角度来分析,直接定位到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
这个方法中,具体定位步骤不再演示了,通过形如下面这段代码:
package com.bloom.spring;
import com.bloom.spring.bean.MyBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
*
*/
public class Test1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MyBean myBean = context.getBean("myBean", MyBean.class);
System.out.println(myBean.getMyName() + "---" + myBean.getMyAge());
/*MyBean myBean = context.getBean("myBean", MyBean.class);
System.out.println(myBean.getMyName() + "---" + myBean.getMyAge());
myBean.myHi();
System.out.println(context.getBean(ISomeService.class));
*/
}
}
直接main方法运行,然后在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
这个方法的入口打一个断点,如图:
分析createBeanInstance方法的代码
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
// 确保BeanDefinition中的beanClass熟悉已经完成解析
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
// 通过BeanDefinition中的Supplier实例化这个bean
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// 通过FactoryMethod来实例化这个bean
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
// 下面这段代码都是在通过构造函数实例化这个Bean,分两种情况,一种是通过默认的无参构造,一种 是通过推断出来的构造函数
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
// 通过见到的构造方法来实例化Bean
return instantiateBean(beanName, mbd);
}
关注实例化对象的几个方法
1 通过BeanDefinition
中的instanceSupplier
直接获取一个实例化的对象。这个instanceSupplier
属性我本身不是特别理解,在xml中的标签以及注解的方式都没有找到方式配置这个属性。后来在org.springframework.context.support.GenericApplicationContext
这个类中找到了以下两个方法
这种通过supplier来实例化Bean在日常上基本不能见到,在这里也就不探究。可能是Spring提供的一种方便外部扩展的方法手段
2 通过不同的创建bean的手段,来分别验证对象的实例化方法
-
通过
@compent
,@Service
等注解的方式测试代码
package com.bloom.spring.bean; import org.springframework.stereotype.Component; /** * @program: bloom * @description: * @author: hao.yu * @create: 2020-04-16 16:23 **/ @Component public class Service { } package com.bloom.spring; import com.bloom.spring.bean.Service; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; /** * @program: bloom * @description: * @author: hao.yu * @create: 2020-04-16 16:17 **/ @ComponentScan public class Test2 { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Test2.class); ac.refresh(); System.out.println(ac.getBean(Service.class.getName())); } }
观察debug:
可以发现,代码执行到最后一行,同时我们看代码上面的注释可以知道,当没有进行特殊的处理的时候,默认会使用无参构造函数进行对象的实例化。
- 通过普通XML的方式(同
@compent
注解,这里就不赘诉了) - 通过
@Configuration
注解的方式
@Configuration
@ComponentScan("com.bloom")
public class Config {
@Bean
public Service service(){
return new Service();
}
}
package com.bloom.spring.bean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @program: bloom
* @description:
* @author: hao.yu
* @create: 2020-04-16 17:00
**/
public class Test3 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(Config.class);
System.out.println(ac.getBean("service"));
}
}
同样,断点也进入最后一行
- 通过
@Bean
的方式
断点结果
可以发现,通过@Bean
方法创建对象时,Spring底层是通过factoryMethod
的方法进行实例化对象的。Spring会在我们需要实例化的这个对象对应的BeanDefinition
中记录factoryBeanName
是什么(在上面的例子中factoryBeanName就是config),同时会记录这个factoryBean中创建对象的factoryMethodName
是什么,最后通过factoryBeanName
获取一个Bean然后反射调用factoryMethod
实例化一个对象。
需要注意的是
- 这里所说的通过静态工厂方式通过
factoryBeanName
获取一个Bean,注意,这个Bean,不是一个FactoryBean
。也就是说不是一个实现了org.springframework.beans.factory.FactoryBean
接口的Bean。至于什么是FactoryBean
我们在后面的文章会认真分析 - 提到了一个概念
BeanDefinition
,它就是Spring对自己所管理的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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myBean" class="com.bloom.spring.bean.MyBean">
<property name="myAge" value="100"/>
<property name="myName" value="张三丰"/>
</bean>
<bean id="heBean" class="com.bloom.spring.bean.HeBean">
<property name="heName" value="张翠山"/>
</bean>
<bean id="companyBean" class="com.bloom.spring.factory.CompanyFactoryBean">
<property name="companyInfo" value="拉勾,中关村,500"/>
</bean>
<bean id="myService" class="com.bloom.spring.factory.ServiceFactory" factory-method="getSomeService">
</bean>
</beans>
package com.bloom.spring.factory;
/**
* @program: bloom
* @description:
* @author: hao.yu
* @create: 2020-04-16 17:09
**/
public class ServiceFactory {
public static ISomeService getSomeService() {
return new SomeServiceImpl();
}
}
package com.bloom.spring.factory;
/**
* @program: bloom
* @description:
* @author: hao.yu
* @create: 2020-04-16 17:10
**/
public class SomeServiceImpl implements ISomeService {
public SomeServiceImpl() {
System.out.println("执行无参构造器");
}
@Override
public void doSome() {
System.out.println("执行doSome()方法");
}
}
package com.bloom.spring.factory;
/**
* @program: bloom
* @description:
* @author: hao.yu
* @create: 2020-04-16 17:09
**/
public interface ISomeService {
void doSome();
}
断点如下:
可以发现,这种情况也进入了instantiateUsingFactoryMethod
方法中。通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理。Spring的调用逻辑大概是:
- 通过
bean
标签中的class属性得到一个Class对象 - 过Class对象获取到对应的方法名称的Method对象
- 最后反射调用
Method.invoke(null,args)
因为是静态方法,方法在执行时,不需要一个对象。
- 通过实例工厂方法的方式(
@Bean
方式执行的流程是一样的)
实例化总结
-
象实例化,只是得到一个对象,还不是一个完全的Spring中的Bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的声明周期;
-
Spring官网上指明了,在Spring中实例化一个对象有三种方式:
a. 构造函数
b. 实例工厂方法
c. 静态工厂方法
-
总结
Spring通过解析我们的配置元数据,以及我们提供的类对象得到一个Beanfinition对象。通过这个对象可以实例化出一个java bean对象。主要流程如图: