Spring官网学习(二)IOC和Bean的实例化

本章节主要是学习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 application
Spring 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后端讨论群。
在这里插入图片描述

原创文章 19 获赞 396 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_36526036/article/details/105960505