Spring的IOC浅析

上篇写了Spring的AOP,文章链接Spring的AOP浅析
这一次,我们聊聊Spring的另一大核心功能,IOC。
IOC翻译成中文,意思是:控制反转。我们从字面意思上分析一下这个词语。
1.控制
看下面的例子:A类依赖B类。

public class A {
    public void func(){
		B b = new B();
		b.func();
	}
}

如上:我们在A中想要使用B的时候,A需要自己去new一个B出来。此时,A就和B强耦合在一起,B有变化,A就要发生变化。这明显不符合程序设计的开闭原则。
有了IOC后,A需要B的时候,怎么办呢?我们可以直接将B进行注入。方式如下:

public class A {
	@Autowired
	private B b;
    public void func(){
		b = new B();
		b.func();
	}
}

此时,B依赖就是由IOC容器负责实例化,然后注入到A中。
控制讲的就是:一个类如何获取它所需要的依赖。从自己new一个B到由IOC根据需要注入B。此时,对B的控制就发生了反转。
维基百科是这样定义“控制反转”的。
2004年,Martin Flower提出了 "哪些方面的控制被反转了?"这个问题,他得出的结论是:依赖对象的获得被反转了。关于这个结论,他为控制反转起了一个很好听的名字“依赖注入”。许多非凡的应用都是由两个或者多个类通过彼此的合作来实现业务逻辑的,这使得每个对象都需要与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要考自身实现,那么如你所见,这将导致代码高度耦合并且难以测试。
在有IOC之前,企业级POJO的王者是EJB。我曾经维护过一个EJB的项目,实现和Spring IOC相同的功能,要写一大堆接口。并且EJB还需要依赖实现了EJB协议的容器,比如:JBoss或者weblogic。运维难度比tomcat要高好多。所以,相比之下,IOC现在非常的流行。
说回IOC,上面我们聊了一下IOC能做的事。接下来,我们就得聊聊IOC是如何实现的?
从源码角度介绍一下IOC的结构体系
在这里插入图片描述
在实际使用中,BeanFactory和Application是两条线。其中:
BeanFactory<-----HierarchicalBeanFactory<------ConfigurableBeanFactory
这是一条线,代表了简单IOC容器,提供最基本的IOC功能。如:getBean。
ResourceLoader<-------ApplicationContext<------WebApplicationContext
这是一条线,代表了高级容器,除了继承了父接口的getBean功能,又通过继承MessageSource、ApplicationEventPublisher、ResourceLoader接口获得了高级功能。
我们以FileSystemXmlApplicationContext为例来说明一下IOC容器的启动过程。
该类的体系结构大致如下:
在这里插入图片描述
下面我们说一下IOC容器的启动以及Bean对象的生成过程。启动和生成是两个过程。IOC容器的启动是伴随着Web工程开始的,但是Bean对象的生成一般是第一次调用getBean对象时生成。
我们使用一下FileSystemXmlApplicationContext这个IOC容器。

@Test
    public void testFileSystem(){
        FileSystemXmlApplicationContext file = new FileSystemXmlApplicationContext("classpath:config/applicationContext.xml");
        file.getBean("testBean");
    }

这个IOC容器的启动入口在构造器中的refresh方法

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);
        if(refresh) {
            this.refresh();
        }
    }

进入构造器,我们看到里面有一个setConfigLocations方法。这个方法就是设置配置文件的位置,之后调用refresh方法启动IOC容器。
我们深入到refresh方法内部看一下,IOC容器启动的具体流程
在这里插入图片描述
上图中,我标示出来的方法,obtainFreshBeanFactory()。进去看一下。
最终加载xml文件中bean定义的方法在AbstractRefreshableApplicationContext类的refreshBeanFactory方法。
在这里插入图片描述
先是createBeanFactory,创建出一个DefaultListableBeanFactory,大家记住这个类。这个类是已经具备了IOC容器的基本功能,我们如果要扩展自己的IOC容器,可以从这个类下手。像XmlBeanFacotory或者ApplicationContext的实现,都是对这个类进行扩展。
我们继续往下看。
loadBeanDefinitions(beanFactory)方法开始读取Spring的配置文件。
接下来,我们进入AbstractXmlApplicationContext类,看一下loadBeanDefinitions(beanFactory)的方法实现。
在这里插入图片描述
首先先创建出一个XmlBeanDefinitionReader对象,这个对象是真正读取xml文件的类。然后继续看loadBeanDefinitions(beanDefinitionReader)里面的实现,最终流程来到了XmlBeanDefinitionReader对象的loadBeanDefinitions(EncodedResource encodedResource)方法。根据Spring配置文件资源对象初始化了一个InputStream流。之后根据InputStream流构建Document对象。然后就开始解析这个Document对象。
解析Document对象,是在DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法中。
在这里插入图片描述
我们进入parseDefaultElement方法中,可以看到我们熟悉的标签定义了
在这里插入图片描述
比如:import、alias、bean、beans。我们以bean标签为例,看一下Spring IOC是如何对bean标签进行解析的。进入processBeanDefinition方法中。
首先,我们看到一个BeanDefinitionHolder对象,这个对象很重要,它封装了Bean对象的一系列描述性信息,比如:bean的class名称,bean的名称、bean是否是lazy-init类型,bean的依赖注入模式,是否开启了bean依赖的检查等等。
并且BeanDefinitionHolder持有Spring BeanDefinition对象,BeanDefinition是Spring 的Bean对象表示,是IOC中的Bean的基础数据机构,后期真正的Bean对象会依据该数据结构生成。但此时,这个对象还不能使用。因为,Bean中的依赖注入还没有发生。目前,它只是定义好了,将来要生成的Bean是一个什么样子。真正开始创建Bean的时候是在第一次获取Bean对象的时候,也就是getBean方法的第一次调用。
当然,凡事无绝对。Spring在这个地方增加了一个开关,就是lazy-init属性。如果我们显示的将lazy-init设置为false。此时,在BeanDefinition初始化阶段。就会将该类需要的依赖进行注入,此时,在IOC容器启动的过程中,就会生成真正意义上的Bean对象。
lazy-init属性的处理是在refresh方法的finishBeanFactoryInitialization方法中。感兴趣的朋友可以去看一下。
我们继续看registerBeanDefinition方法,这个方法会将BeanDefinition对象注入到IOC容器中。进入DefaultListableBeanFactory类的registerBeanDefinition方法。
在这里插入图片描述
我们看到一个名称为beanDefinitionMap的ConcurrentHashMap对象,这个Map对象就是IOC容器。
到目前为止,我们已经看到了IOC容器的真正容貌。其实也没有啥,对吧?哈哈。开玩笑,通过上面的一系列分析,我们看到Spring的体系非常的庞大,其中很多设计,非常值得我们学习。
好了,继续看registerBeanDefinition方法的实现。
首先,会校验bean的名称是否为空。之后开始对beanDefinition进行校验。然后会根据bean的名称从beanDefinitionMap判断是否已经创建了这个BeanDefinition对象。
如果这个对象不为空,会检查是否设置allowBeanDefinitionOverriding属性。默认值是true。
之后,会将BeanDefinition放进beanDefinitionMap对象。至此,我们已经完成了Spring配置文件的解析,注册。此时,IOC容器已经准备完毕。等待着getBean方法调用。
下面,我们看一下getBean方法的实现。
继续用我们上面的那个例子。
getBean方法,实际就是将我们上面的beanDefinition对象根据bean的id从beanDefinitionMap中取出来,然后根据beanDefinition对象的定义生成真正的bean对象。
下面,我们看一下具体的实现。
点击getBean方法,首先进入AbstractApplicationContext类中的getBean方法。从这里也可以看出,IOC容器对于一些比较基础的功能,是由父类完成。子类在父类功能的基础上扩展高级功能。
在这里插入图片描述
getBean方法里首先获取一下BeanFactory对象,这个beanFactory对象是我们在FileSystemXmlApplicationContext的refresh方法中初始化进去的,是DefaultListableBeanFactory。接着执行getBean(String beanName)方法。方法入参是一个String类型。这个getBean(String beanName)方法是在DefaultListableBeanFactory的父类AbstractBeanFactory类中。
在这里插入图片描述
首先我们看到getObjectForBeanInstance方法,该方法是处理FactoryBean这种特殊的bena。如果我们的bean对象需要从FactoryBean中获取,走的就是getObjectForBeanInstance方法。我们可以进去看一下,从FactoryBean中获取bean对象的核心代码在FactoryBeanRegistrySupport类的doGetObjectFromFactoryBean方法中。在其中,我们可以看到我们熟悉的factory.getObject方法,在上一篇AOP中我们讲过,AOP的核心对象是ProxyFactoryBean。这个bean负责对target类进行增强,之后产生一个代理对象。当时,我们就提到了getObject方法,这个方法是获取代理对象的入口。忘记的朋友可以再去回顾一下Spring的AOP解析
我们回到AbstractBeanFactory类的doGetBean方法中,继续往下看。
根据beanName先从父容器中查找beanDefinition对象。找到直接返回,找不到,从子容器中查找。
子容器中查找的代码是:getMergedLocalBeanDefinition(beanName)方法。
最终获取beanDefinition对象的地方在DefaultListableListableBeanFactory类的getBeanDefintion方法,如下图:
在这里插入图片描述
是不是很眼熟?没错,我们在IOC容器启动时,把id和BeanDefinition的映射关系存储进了beanDefinitionMap中。现在,开始真正的使用bean对象了,我们从这个map中再通过id把BeanDefinition对象拿出来。
如果找不到,就直接报出bean找不到异常
拿到BeanDefiniton对象后,下面开始真正的创建bean。首先,找到当前bean的依赖对象有哪些,然后从IOC容器中找到这些对象的定义。
之后开始创建bean,这里需要判断单例bean还是多例bean。我们以单例bean对象为例:
看一下createBean(beanName,mbd,args)方法,如下:
在这里插入图片描述
这个方法里,首先是处理BeanDefinition对象。填充字节码信息等。之后,调用doCreateBean(beanName, mbdToUse, args)方法。我们进入该方法。该方法中,首先根据class生成bean对象。
在这里插入图片描述
生成bean对象的方式有三种。
1).根据工厂方法生成bean对象

if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

2).根据构造函数生成bean对象

// 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);
		}

3).根据默认构造函数生成bean对象

// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);

bean对象有了,下面开始对bean对象进行依赖注入以及执行bean的生命周期相关的一系列方法

// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//依赖注入
			populateBean(beanName, mbd, instanceWrapper);
			//实例化bean
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}

我们先看依赖注入的实现
可以看到依赖注入有两种方式。

int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		//根据名称注入
		//根据类型注入
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}

我们以根据名称注入的举例。
进入autowireByName方法。可以看到,其实很简单。就是根据属性的名称去IOC容器中查找相对应的bean对象。
然后调用registerDependentBean(propertyName, beanName);方法注册依赖的bean。

这个行代码日后还要研究,这行代码应该是解决Spring bean循环依赖问题的关键所在。
好了,我们继续看依赖注入。得到依赖注入的对象后,下面就是将这些对象设置到待实例化的对象中。设置到对象中的方式也很简单,就是调用属性的set方法,用反射的方式注入进去。
接下来,我们看bean的生命周期方法。这个在面试中被问到的机率相当之高,大家一定要仔细看这一段。

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
		//执行aware的方法
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
		//执行BeanPostProcessor的postProcessBeforeInitialization方法。我们熟知的ApplicationContextAware接口就是在这个步骤中,被IOC执行了setApplicationContext接口,将应用上下文进行注入
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
		//执行init-method方法。如果bean实现了InitializeBean接口的话,先执行该接口的afterPropertiesSet方法
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
		//执行BeanPostProcessor的postProcessAfterInitialization方法
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

aware的方法,Spring一共执行了三个。

private void invokeAwareMethods(String beanName, Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof BeanNameAware) {
				((BeanNameAware) bean).setBeanName(beanName);
			}
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}

invokeInitMethods的方法实现为:

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
			//执行afterPropertiesSet方法
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

		if (mbd != null && bean.getClass() != NullBean.class) {
			String initMethodName = mbd.getInitMethodName();
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
					//利用反射执行自定义的初始化方法
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

说完了bean创建的生命周期,下面我们看一下bean销毁的生命周期,bean销毁的生命周期入口是doClose方法

protected void doClose() {
		// Check whether an actual close attempt is necessary...
		if (this.active.get() && this.closed.compareAndSet(false, true)) {
			if (logger.isInfoEnabled()) {
				logger.info("Closing " + this);
			}

			LiveBeansView.unregisterApplicationContext(this);

			try {
				// Publish shutdown event.
				publishEvent(new ContextClosedEvent(this));
			}
			catch (Throwable ex) {
				logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
			}

			// Stop all Lifecycle beans, to avoid delays during individual destruction.
			if (this.lifecycleProcessor != null) {
				try {
					this.lifecycleProcessor.onClose();
				}
				catch (Throwable ex) {
					logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
				}
			}

			// Destroy all cached singletons in the context's BeanFactory.
			destroyBeans();

			// Close the state of this context itself.
			closeBeanFactory();

			// Let subclasses do some final clean-up if they wish...
			onClose();

			// Reset local application listeners to pre-refresh state.
			if (this.earlyApplicationListeners != null) {
				this.applicationListeners.clear();
				this.applicationListeners.addAll(this.earlyApplicationListeners);
			}

			// Switch to inactive.
			this.active.set(false);
		}
	}

最终的销毁方法实现在DisposableBeanAdapter类的destroy方法中

@Override
	public void destroy() {
		if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
			for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
				processor.postProcessBeforeDestruction(this.bean, this.beanName);
			}
		}
		//执行实现了DisposableBean接口的destroy方法
		if (this.invokeDisposableBean) {
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
			}
			try {
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((DisposableBean) this.bean).destroy();
						return null;
					}, this.acc);
				}
				else {
					((DisposableBean) this.bean).destroy();
				}
			}
			catch (Throwable ex) {
				String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
				if (logger.isDebugEnabled()) {
					logger.warn(msg, ex);
				}
				else {
					logger.warn(msg + ": " + ex);
				}
			}
		}

		if (this.destroyMethod != null) {
		//执行自定义的销毁方法,destroy-method方法
			invokeCustomDestroyMethod(this.destroyMethod);
		}
		else if (this.destroyMethodName != null) {
			Method methodToCall = determineDestroyMethod(this.destroyMethodName);
			if (methodToCall != null) {
				invokeCustomDestroyMethod(methodToCall);
			}
		}
	}

好了,以上就是对IOC的解析。Spring小白,如果有错误,欢迎大家指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章