本章节主要是学习Spring官网的1.1、1.2和1.3节内容,有兴趣的同学可以跟着官网一起学习。
文章目录
1、Spring容器和IOC
1.1、Spring容器是什么?
在官网1.2节中对Spring容器的描述如下:
The
org.springframework.context.ApplicationContext
interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.
这段话的意思主要是:
- Spring容器是
org.springframework.context.ApplicationContext
接口的一个实例化对象 - Spring容器主要负责实例化、配置和装配Spring的Bean对象。
1.2、Spring Bean是什么?
在官网中对Spring Bean描述如下:
A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your applicationSpring Bean定义的大致意思如下:
- Spring Bean必须是由Spring容器实例化、装配和以其他方式管理的对象。
- 通俗的理解就是,JAVA对象经由Spring容器实例化、装配就可以变成一个Spring Bean,否则只能是一个普通的JAVA对象。
1.3、IOC是什么?
在官网1.1节中对IOC有详细的描述,大概意思如下:
IOC(控制反转)也称为依赖注入,IOC容器负责Spring Bean的创建、装配、生命周期的管理以及Bean与Bean之间的关系。在这整个过程当中,IOC容器完成了一个完整Bean的实例化,包括其依赖的Bean的实例化,那么这个依赖的Bean实例化是由IOC容器完成,而不是由开发人员去实例化的,这个过程就是IOC(控制反转)了,就是将依赖的的Bean的实例化的交由IOC容器管理和注入。
2、Spring容器如何实例化一个Bean
Spring官网给出了一个Spring容器工作图:
从上面的图片中可以看出,Spring容器通过POJO对象和配置的元数据信息可以产生一个充分配置的可以使用的系统。
这里所说的POJO对象就是JAVA对象,配置原数据信息就是开发人员自己配置的一些Bean的信息,比如Bean是单例还是原型?是按类型加载Bean还是按名称加载Bean等等…,目前可以用xml或者注解的方式配置元数据信息。
2.1、Bean的实例化
Spring官网提供了三种实例化Bean方式,分别如下:
- 构造方法实例化Bean
- 静态工厂方法实例化Bean
- 实例工厂方法实例化Bean
俗话说,不是手撸的代码是得不到它的真传的,所以现在把官网所给的Demo撸一遍,跟着Spring源码看一看每一种Bean实例化过程的内在世界。
2.2、构造方法实例化Bean
2.2.1、Demo搭建
Demo结构如图所示:
- TestInstantiationBean类的main方法内容如上图所示,这个类是入口。
- MyServiceBean.xml的内容如下所示:
<?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="myServiceBean" class="com.spring.spring.MyServiceBean"/>
</beans>
- MyServiceBean类的内容如下:
public class MyServiceBean {
public MyServiceBean() {
}
}
2.2.2、源码分析
用Debug方式启动main方法,进入到getBean()方法,在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
方法处打一个断点,跟着源码看一下构造方法实例化Bean的具体实现,该方法的具体内容如下,省略和这次分析无关的代码,只分析相关的代码:
/**
* 创建实例对象的函数
**/
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 调用无参构造方法实例化对象
return instantiateBean(beanName, mbd);
}
/*
* 获取对象的class属性,然后利用反射获取对象的构造方法。根据构造方法实例化对象
**/
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { //申明对象变量
Object beanInstance;
//获取当前的BeanFactory
final BeanFactory parent = this;
//这一步就是利用反射获取构造方法,根据构造方法实例化对象
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
//将实例化的对象放到BeanWrapper中
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
//返回已经实例化的对象
return bw;
}
}
/**
* 通过反射获取构造方法,通过构造方法实例Bean对象。
**/
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
//通过RootBeanDefinition获取对象class
final Class<?> clazz = bd.getBeanClass();
//根据class利用反射获取到构造方法
constructorToUse = clazz.getDeclaredConstructor();
//将构造方法赋值给RootBeanDefinition的一个属性中去
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
}
//通过构造方法实例化对象,至此整个过程结束
return BeanUtils.instantiateClass(constructorToUse);
}
}
总结:
通过构造方法实例化Bean,可以分为三步
- 先根据xml文件中的bean标签的class属性获取Class对象
- 然后利用反射技术获取到Class对象的构造方法
- 最后根据构造方法实例化Bean对象
2.3、静态工厂方法实例化Bean
- MyServiceBean.xml的内容更改如下:
<?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="myServiceBean" class="com.spring.spring.MyServiceBean" factory-method="createInstance"/>
</beans>
- MyServiceBean类的内容更改如下:
public class MyServiceBean {
private static MyServiceBean myServiceBean = new MyServiceBean();
public static MyServiceBean createInstance() {
return myServiceBean;
}
}
然后用Ddbug启用Main方法,进入到getBean()方法,在
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
方法处打一个断点,静态方法实例化的源码过程如下,省略和这次分析无关的代码,只分析相关的代码:
/**
* 通过静态方法实例化Bean对象
**/
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
//获取xml配置文件中的factory-method属性,本次配置名为createInstance
//调用instantiateUsingFactoryMethod()方法实例化Bean
if (mbd.getFactoryMethodName() != null) {
//调用类的静态方法实例化Bean
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
}
/**
*最终实例化方法
**/
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
//反射调用静态方法,获取实例化对象
//此时的factoryBean没有实例化,为Null,因为factoryMethod为静态方法
Object result = factoryMethod.invoke(factoryBean, args);
return result;
}
instantiateUsingFactoryMethod()方法过程可以分为以下三步:
- 通过xml配置文件的bean标签的class属性获取Class对象
- 通过xml配置文件的bean标签的 factory-method属性获取静态方法名称
- 然后通过反射调用方法 invoke(factoryBean, args),获取一个实例化的Bean对象
2.4、实例工厂方法实例化Bean
- MyServiceBean.xml的内容更改如下:
<?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">
<!-- 实例化MyServiceBean -->
<bean id="myServiceBean" class="com.spring.spring.MyServiceBean">
</bean>
<!-- 工厂实例的MyServiceBean对象调用createInstance()方法实例化一个Bean -->
<bean id="clientService"
factory-bean="myServiceBean"
factory-method="createInstance"/>
- MyServiceBean类的内容更改如下:
public class MyServiceBean {
private static MyServiceBean myServiceBean = new MyServiceBean();
public MyServiceBean createInstance() {
return myServiceBean;
}
}
工厂实例方法可以看做是是前面两个方法的结合,
- 先调用无参构造方法实例化myServiceBean
- 然后用反射调用factoryMethod.invoke(factoryBean, args)方法,不同于静态工厂方法的是,此时的
factoryBean已经实例化了
。
2.5、注解实例化Bean
2.5.1、@compent、@Service、@Configuration注解实例化Bean
-此时的MyServiceBean类的内容更改如下,注解可以更换以上三种的任意一种
@Component
public class MyServiceBean {
}
- 测试类TestInstantiationBean.java方法如下:
public class TestInstantiationBean {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyServiceBean.class);
MyServiceBean myServiceBean = applicationContext.getBean(MyServiceBean.class);
System.out.println(myServiceBean);
}
}
然后用Ddbug启用Main方法,进入到getBean()方法,在
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
方法处打一个断点,可以发现其最终也是通过无参构造函数实例化Bean的,和构造方法实例化Bean的过程完全一致,有兴趣的同学可以自行Debug验证。
2.5.2、@Bean注解实例化Bean
-此时的MyServiceBean类的内容更改如下,注解可以更换以上三种的任意一种
@Configuration
public class MyServiceBean {
@Bean
public MyServiceBean createMyServiceBean(){
return new MyServiceBean();
}
}
- 测试类TestInstantiationBean.java内容不变。
然后用Ddbug启用Main方法,进入到getBean()方法,在
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
方法处打一个断点,可以发现其和实例方法实例化Bean的过程完全一致
,有兴趣的同学可以自行Debug验证。
3、总结
本章主要介绍的是官网的1.1、1.2、1.3节中的内容,涉及Spring的IOC容器概念和Bean的实例化源码解析过程。但是,请注意,这个时候只是进行了Bean的实例化,此时的Bean还不是一个完整的Spring的Bean,因为它还没有走过一个完整的Spring的生命周期。
3.1、 实例化Bean的方式
- 构造方法实例化Bean,其中注解@compent、@Service、@Configuration都是通过构造方式实例化Bean。
- 静态工厂方法实例化Bean,此时的factoryBean并没有实例化,为空,因为是静态方法,可以直接调用。
- 实例工厂方法实例化Bean,此时的factoryBean实例化了,因为需要实例化对象去调用方法。其中@Bean注解实例化Bean和 实例工厂方法实例化Bean的过程一致。
4、面试
1、Spring容器是什么?
答:Spring容器是ApplicationContext接口的一个实例化对象,主要负责实例化、配置和装配Spring的Bean对象。
2、Spring Bean和java对象有什么区别?
答:Spring bean需要经过Spring容器创建、装配并走过一个完整的Spring Bean的生命周期。java对象不一定是Spring bean,但是Spring bean一定是java对象。
3、Spring bean的实例化方式有几种?
答:三种。构造方法、静态方法和实例方法。
欢迎各位关注我的JAVAERS公众号,陪你一起学习,一起成长,一起分享JAVA路上的诗和远方。在公众号里面都是JAVA这个世界的朋友,公众号每天会有技术类文章,面经干货,也有进阶架构的电子书籍,如Spring实战、SpringBoot实战、高性能MySQL、深入理解JVM、RabbitMQ实战、Redis设计与实现等等一些高质量书籍,关注公众号即可领取哦。 欢迎大家加入JAVA后端讨论群。