Spring IOC基于源码的理解(二)

一.知识了解

1.Beanfactory和Application,BeanFactory和FactoryBean

内容在Spring基本知识点(一)后半部分可见;

2.BeanDefinition

BeanDefinition是IOC容器体系非常重要的核心数据结构,Spring通过BeanDefinition来管理基于Spring的应用中的各种对象以及他们之间的相互依赖关系,实际就是POJO对象在IOC容器中的抽象。在DefaultListableBeanFactory中使用数据结构ConcurrentHashMap装载,示例如下

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable{
	private final Map beanDefinitionMap;
	public DefaultListableBeanFactory()
    {
		//删除了其他代码
        beanDefinitionMap = new ConcurrentHashMap();//通过Map持有载入的BeanDefition
    }
}

3.编程式使用IOC容器

方便理解IOC关键类的相互关系,修改上一篇的测试类代码

package RealUse;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;

public class Test {
	public static void main(String[] args) {
		//ApplicationContext ctx = new FileSystemXmlApplicationContext("bin/applicationContext.xml");
		ClassPathResource res = new ClassPathResource("applicationContext.xml");//1.创建IOC配置文件的抽象资源,包含了BeanDefinition的定义信息。资源定位。用的两个不同类路径:ClassPathResource;文件系统:FileSystemResource
		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();//2.创建一个BeanFactory的容器
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);//3.创建一个载入BeanDefinition的读取器,载入XML形式的BeanDefinition
		reader.loadBeanDefinitions(res);
		System.out.println("1.===返回getObject==="+factory.getBean("sample").getClass()+"=====");
		System.out.println("2.===返回本身==="+factory.getBean("&sample").getClass()+"======");
	}
}

4.总体步骤

IOC容器初始化(Resource定位、BeanDefinition的载入、IOC容器注册BeanDefinition)--->IOC容器的依赖注入

二.IOC容器的初始化

IOC容器的初始化由AbstractApplicationContext的refresh()启动,若已经有了容器存在那么需要把已有的容器销毁和关闭,保证refresh后使用的是新建立的IOC容器。看下具体代码:

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 准备上下文用于刷新
			prepareRefresh();
			// 创建BeanFactory,Bean定义的解析与注册
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// 为该上下文配置已经生成的BeanFactory
			prepareBeanFactory(beanFactory);
			try {
				// 1.设置BeanFactory的后置处理,加工的是BeanDefinition
				postProcessBeanFactory(beanFactory);
				invokeBeanFactoryPostProcessors(beanFactory);

				// 2.注册bean的后处理器。
				//!!!区别:工厂后处理器在获取后立即调用,而Bean后处理器在获取后注册到上下文持有的beanfactory中,供以后操作调用
				registerBeanPostProcessors(beanFactory);
				// 3.初始化信息源
				initMessageSource();
				// 4.初始化事件机制
				initApplicationEventMulticaster();
				// 5.初始其它特殊Bean
				onRefresh();
				//6.注册事件监听器;(观察者模式中的观察者角色)获取ApplicationListener类型的所有bean,即事件监听器
				registerListeners();
				// 7.Bean的真正实例化,初始化lazy-init!=true的singleton的Bean
				finishBeanFactoryInitialization(beanFactory);
				//发布容器事件,结束Refresh过程
				finishRefresh();
			} catch (BeansException ex) {
				destroyBeans();
				cancelRefresh(ex);
				throw ex;
			}
		}
	}

总而言之:创建一个Application上下文持有的一个BeanFactory对象,当这个Beanfactory对象创建完成后,Spring将配置文件的信息解析成为一个个的BeanDefinition对象并装入到容器的Bean定义注册(BeanDefinitionRegistry)中。

1.Resource定位

指的是BeanDefinition的资源定位,由ResourceLoader接口通过统一的Resource接口实现。

ResourceLoader接口:通过该类的实例可以获得一个Resource实例,仅仅包含一个方法 Resource getResource(String location)用于返回一个Resource实例。

package org.springframework.core.io;
// Referenced classes of package org.springframework.core.io:
//            Resource
public interface ResourceLoader
{
    public abstract Resource getResource(String s);

    public abstract ClassLoader getClassLoader();

    public static final String CLASSPATH_URL_PREFIX = "classpath:";
}

Resource接口是Spring资源访问的接口,Resource接口有大量的实现类。例如:

  • URLResource:访问网络资源
  • ClassPathResource:访问类加载路径里资源
  • FileSystemResource:访问文件系统里资源的实现类

当Spring应用需要进行资源访问的时候,并不是直接使用以上Resource的实现类,而是调用ResourceLoader实例的getResource方法来获得资源。ResourceLoader将会负责选择Resource的实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来。Spring会采用与ApplicationContext相同的策略访问资源。这就是设计模式中的策略模式(不同的策略拥有统一的接口,客户决定使用哪个策略类)

2.BeanDefinition的载入和解析

把用户定义好的Bean成IOC容器内部的数据结构,完成后IOC容器BeanDefinition中存在的还只是一些静态的配置信息。

解析是一个很复杂的过程,以XML文件对象为例,在读取器XmlBeanDefinitionReader中得到代表XML文件的Resource,然后按照Spring的Bean的定义规则解析XML的文档树。具体的解析在BeanDefinitionParserDelegate中完成,不再详述,打算自己写一个简单的XML解析代码。

3.向IOC容器注册BeanDefinition的过程,通过调用BeanDefinitionRegistry的实现来完成。

就是把解析得到的BeanDefinition设置到HashMap中。例如,DefaultListableBeanFactory实现了BeanDefinitionRegistry,该接口的实现完成了注册。

DefaultListableBeanFactory类中registerBeanDefinition如下

	public void registerBeanDefinition(String beanName,BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
		Assert.hasText(beanName, "'beanName' must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
		if (beanDefinition instanceof AbstractBeanDefinition)
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			} catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(
						beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
			//加上synchronized
		synchronized (beanDefinitionMap) {
			//检查是否有同名BeanDefinition存在
			Object oldBeanDefinition = beanDefinitionMap.get(beanName);
			if (oldBeanDefinition != null) {
				if (!allowBeanDefinitionOverriding)
					throw new BeanDefinitionStoreException(
							beanDefinition.getResourceDescription(), beanName,
							(new StringBuilder(
									"Cannot register bean definition ["))
									.append(beanDefinition)
									.append("] for bean '").append(beanName)
									.append("': There is already [")
									.append(oldBeanDefinition)
									.append("] bound.").toString());
				if (logger.isInfoEnabled())
					logger.info((new StringBuilder(
							"Overriding bean definition for bean '"))
							.append(beanName).append("': replacing [")
							.append(oldBeanDefinition).append("] with [")
							.append(beanDefinition).append("]").toString());
			} else {
				beanDefinitionNames.add(beanName);
				frozenBeanDefinitionNames = null;
			}
			beanDefinitionMap.put(beanName, beanDefinition);//BeanName为key值,beanDefinition是value值
			resetBeanDefinition(beanName);
		}
	}

三.IOC容器的依赖注入

一般发生在getBean的时候也可以在预实例化的时候,流程图如下

1.分析下getBean--->doGetBean的代码

	public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
	    return doGetBean(name, requiredType, args, false);
	}

	//这里是实际取得Bean的地方,也是触发依赖注入的地方
	protected <T> T doGetBean(
	            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
	            throws BeansException {

	    final String beanName = transformedBeanName(name);
	    Object bean;

	    // 先从缓存中获取Bean,处理那些已经被创建过的单件模式的Bean,对这种Bean的请求不需要重复创建
	    Object sharedInstance = getSingleton(beanName);
	    if (sharedInstance != null && args == null) {
	        if (logger.isDebugEnabled()) {
	            if (isSingletonCurrentlyInCreation(beanName)) {
	                logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
	                        "' that is not fully initialized yet - a consequence of a circular reference");
	            }
	            else {
	                logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
	            }
	        }
	        //FactoryBean相关处理,用来取得FactoryBean生产结果
	        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	    }

	    else {
	        // Fail if we're already creating this bean instance:
	        // We're assumably within a circular reference.
	        if (isPrototypeCurrentlyInCreation(beanName)) {
	            throw new BeanCurrentlyInCreationException(beanName);
	        }

	        // Check if bean definition exists in this factory.
	        //对BeanDefinition是否存在进行检查。检查能否在当前工厂取到想要的Bean,如果当前工厂找不到,就去双亲工厂去取
	        BeanFactory parentBeanFactory = getParentBeanFactory();
	        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
	            // Not found -> check parent.
	            String nameToLookup = originalBeanName(name);
	            if (args != null) {
	                // Delegation to parent with explicit args.
	                return (T) parentBeanFactory.getBean(nameToLookup, args);
	            }
	            else {
	                // No args -> delegate to standard getBean method.
	                return parentBeanFactory.getBean(nameToLookup, requiredType);
	            }
	        }

	        if (!typeCheckOnly) {
	            markBeanAsCreated(beanName);
	        }

	        try {
	            //根据Bean名字获取BeanDefinition
	            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
	            checkMergedBeanDefinition(mbd, beanName, args);

	            // Guarantee initialization of beans that the current bean depends on.
	            //保证这些Bean依赖的Bean全部被初始化,可能会触发递归调用,直到取到无任何依赖的Bean为止!
	            String[] dependsOn = mbd.getDependsOn();
	            if (dependsOn != null) {
	                for (String dependsOnBean : dependsOn) {
	                    if (isDependent(beanName, dependsOnBean)) {
	                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
	                                "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
	                    }
	                    registerDependentBean(dependsOnBean, beanName);
	                    getBean(dependsOnBean);
	                }
	            }

	            // Create bean instance.
	            if (mbd.isSingleton()) {
	                //通过createBean方法创建SingleTon Bean实例,有一个回调函数getObject,会在getSingleTon中调用ObjectFactory的createBean
	                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
	                    @Override
	                    public Object getObject() throws BeansException {
	                        try {
	                            return createBean(beanName, mbd, args);
	                        }
	                        catch (BeansException ex) {
	                            destroySingleton(beanName);
	                            throw ex;
	                        }
	                    }
	                });
	                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
	            }
	            //创建protoType的Bean
	            else if (mbd.isPrototype()) {
	                // It's a prototype -> create a new instance.
	                Object prototypeInstance = null;
	                try {
	                    beforePrototypeCreation(beanName);
	                    prototypeInstance = createBean(beanName, mbd, args);
	                }
	                finally {
	                    afterPrototypeCreation(beanName);
	                }
	                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
	            }

	            else {
	                String scopeName = mbd.getScope();
	                final Scope scope = this.scopes.get(scopeName);
	                if (scope == null) {
	                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
	                }
	                try {
	                    Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
	                        @Override
	                        public Object getObject() throws BeansException {
	                            beforePrototypeCreation(beanName);
	                            try {
	                                return createBean(beanName, mbd, args);
	                            }
	                            finally {
	                                afterPrototypeCreation(beanName);
	                            }
	                        }
	                    });
	                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
	                }
	                catch (IllegalStateException ex) {
	                    throw new BeanCreationException(beanName,
	                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
	                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
	                            ex);
	                }
	            }
	        }
	        catch (BeansException ex) {
	            cleanupAfterBeanCreationFailure(beanName);
	            throw ex;
	        }
	    }

	    // Check if required type matches the type of the actual bean instance.
	    //检查Bean的类型是不是需要的类型,如果是,就返回Bean,这个Bean已经是包含了依赖关系的Bean了。
	    if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
	        try {
	            return getTypeConverter().convertIfNecessary(bean, requiredType);
	        }
	        catch (TypeMismatchException ex) {
	            if (logger.isDebugEnabled()) {
	                logger.debug("Failed to convert bean '" + name + "' to required type [" +
	                        ClassUtils.getQualifiedName(requiredType) + "]", ex);
	            }
	            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
	        }
	    }
	    return (T) bean;
	}

2.有两种实例化Java对象的方法:

  • 通过BeanUtils,使用了JVM的反射功能
  • 通过CGLIB(一个常用的字节码生成器的类库)生成、
	public Object instantiate(RootBeanDefinition beanDefinition,String beanName, BeanFactory owner) {
		//1.若没有配置lookup-method采用JVM的反射功能
		if (beanDefinition.getMethodOverrides().isEmpty()) {
			//取得指定的构造器或者是生成对象的工厂方法来对Bean进行实例化
			Constructor constructorToUse = (Constructor) beanDefinition.resolvedConstructorOrFactoryMethod;
			if (constructorToUse == null) {
				final Class clazz = beanDefinition.getBeanClass();
				if (clazz.isInterface())
					throw new BeanInstantiationException(clazz,"Specified class is an interface");
				try {
					if (System.getSecurityManager() != null)
						constructorToUse = (Constructor) AccessController.doPrivileged(new PrivilegedExceptionAction() {

									public Constructor run() throws Exception {
										return clazz.getDeclaredConstructor(null);
									}

									public volatile Object run()throws Exception {
										return run();
									}

									final SimpleInstantiationStrategy this$0;
									private final Class val$clazz;

									{
										this$0 = SimpleInstantiationStrategy.this;
										clazz = class1;
										super();
									}
								});
					else
						constructorToUse = clazz.getDeclaredConstructor(null);//利用反射获取构造函数
					beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
				} catch (Exception ex) {
					throw new BeanInstantiationException(clazz,"No default constructor found", ex);
				}
			}
			return BeanUtils.instantiateClass(constructorToUse, new Object[0]);
		} else {//2.如果配置了lookup-method使用CGLIB来实例化对象
			return instantiateWithMethodInjection(beanDefinition, beanName,
					owner);
		}
	}

3.lookup-method的应用

如果一个singleton的调用者A依赖于一个prototype实例B,初始化singleton的时候会先实例化prototype,但是以后每次通过singleton调用prototype的时候,调用的永远是最开始的prototype。怎么解决呢?这种时候就要采用方法注入,即使用lookup-method的方法。Spring会采用运行时动态增强的方式CGLIB实现lookup-method。

如下实例,Singleton的A依赖prototype的B。

普通

被依赖的Aprototype

package Sample;

public class Aprototype {
	private double a=0d;
	public Aprototype(){
		a=Math.random();
	}
	public void getRandom(){
		System.out.println("Prototype输出:"+a);
	}
}

Bsingleton(依赖Aprototype)

package Sample;

public class Bsingleton {
	private Aprototype aprototype;
	public void setAprototype(Aprototype aprototype){
		this.aprototype=aprototype;
	}
	//public abstract Aprototype getAprototype();
	public void usePrototype(){
		aprototype.getRandom();
	}
}

测试类

public class Test {
	public static void main(String[] args) {
		ApplicationContext ctx = new FileSystemXmlApplicationContext("bin/applicationContext.xml");
		System.out.println("=====第一次调用Bsingleton======");
		Bsingleton b=(Bsingleton) ctx.getBean("bsingleton");
		b.usePrototype();

		System.out.println();

		System.out.println("=====第二次调用Bsingleton======");
		Bsingleton c=(Bsingleton) ctx.getBean("bsingleton");
		c.usePrototype();
	}
}

配置文件

<bean id="bsingleton" class="Sample.Bsingleton">
        <property name="aprototype" ref="aprototype" />
    </bean>
    <bean id="aprototype" class="Sample.Aprototype" scope="prototype"></bean>

结果:

=====第一次调用Bsingleton======

Prototype输出:0.6096473576637916

=====第二次调用Bsingleton======

Prototype输出:0.6096473576637916

分析:每次输出的结果一样,可见调用的始终是初始的prototype

采用lookup-method

Aprototype不变,测试类不变

Bsingleton添加抽象方法,修改为抽象类

package Sample;

public abstract class Bsingleton {
	//private Aprototype aprototype;
	//public void setAprototype(Aprototype aprototype){
		//this.aprototype=aprototype;
	//}
	public abstract Aprototype getAprototype();
	public void usePrototype(){
		getAprototype().getRandom();
	}
}

修改配置文件,改为lookup-method

<bean id="bsingleton" class="Sample.Bsingleton">
        <lookup-method name="getAprototype" bean="aprototype" />
    </bean>
    <bean id="aprototype" class="Sample.Aprototype" scope="prototype"></bean>

步骤:

  • 定义Bsingleton为抽象类,在Bsingleton中添加一个抽象的方法,该方法用于获取被依赖的Bean
  • 设置目标Bean为prototype
  • 在Bsingleton的Bean中添加<lookup-method
    name="XX" bean="BB" />,name是需要让Spring实现的方法,bean指定该方法实现的返回值。

结果:

=====第一次调用Bsingleton======

Prototype输出:0.7520596008690802

=====第二次调用Bsingleton======

Prototype输出:0.8769954010509483

分析:可以看到每次获取的值是不一样的,而不是始终是最初的prototype

文章参考资料:《Spring 技术内幕》

时间: 2024-08-29 11:26:57

Spring IOC基于源码的理解(二)的相关文章

Spring IOC 容器源码分析 - 创建单例 bean 的过程

1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去创建,而是从缓存中获取.如果某个 bean 还未实例化,这个时候就无法命中缓存.此时,就要根据 bean 的配置信息去创建这个 bean 了.相较于getBean(String)方法的实现逻辑,创建 bean 的方法createBean(String, RootBeanDefinition, Obj

Spring IOC 容器源码分析 - 余下的初始化工作

1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bean 做最后的初始化工作.相较于之前几篇文章所分析的源码,initializeBean 的源码相对比较简单,大家可以愉快的阅读.好了,其他的不多说了,我们直入主题吧. 2. 源码分析 本章我们来分析一下 initializeBean 方法的源码.在完成分析后,还是像往常一样,把方法的执行流程列出来.好了,看源码

Spring IOC 容器源码分析

前言: Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文并不能让你成为 Spring 专家,不过一定有助于大家理解 Spring 的很多概念,帮助大家排查应用中和 Spring 相关的一些问题. 阅读建议:读者至少需要知道怎么配置 Spring,了解 Spring 中的各种概念,少部分内容我还假设读者使用过 SpringMVC.本文要说的 IOC

Spring Developer Tools 源码分析:二、类路径监控

在 Spring Developer Tools 源码分析一中介绍了 devtools 提供的文件监控实现,在第二部分中,我们将会使用第一部分提供的目录监控功能,实现对开发环境中 classpath 的监控. 二.类路径监控 首先看一些这一部分可能涉及到的类图: 在图中,红色斜线左上部分是第一部分中介绍的文件目录监控的类,其中 FileSystemWatcher 会通过独立线程监控指定的目录,当目录内容发生变化时,通过对比快照可以获得所有监控目录变化的文件ChangedFiles,然后将变化通知

Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反射创建一个原始的 bean 对象,然后再向这个原始的 bean 对象中填充属性.对于填充属性这个过程,简单点来说,JavaBean 的每个属性通常都有 getter/setter 方法,我们可以直接调用 setter 方法将属性值设置进去.当然,这样做还是太简单了,填充属性的过程中还有许多事情要做.

Spring IOC 容器源码分析 - 创建原始 bean 对象

1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreateBean方法中的一个重要的调用,即createBeanInstance方法.在本篇文章中,你将看到三种不同的构造 bean 对象的方式.你也会了解到构造 bean 对象的两种策略.如果你对这些内容感兴趣,那么不妨继续往下读.我会在代码进行大量的注解,相信能帮助你理解代码逻辑.好了,其他的就不多说了

Spring IOC 容器源码分析 - getBean调用方法解析(三) -- 实例化 Bean 对象

1. createBeanInstance protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // 解析 bean ,将 bean 类名解析为 class 引用 Class<?> beanClass = resolveBeanClass(mbd, beanName); /* * 检测类的访问权限.默认情况下,对于非 public

Spring 源码学习(二) IOC容器启动过程

这一节主要是记录一下Spring Ioc 容器的启动过程. Spring 的 Ioc 容器是怎么被加载和使用的? web容器为它提供了宿主环境 ServlectContext,  Tomcat 启动时会读取web.xml. 并且实例化web.xml中配置的ContextLoaderListener ,下面看一下ContextLoaderListener的创建过程: 在实例化ContextLoaderListener 之后,通过接口回调执行ContextLoaderListener 类中的cont

Spring 源码学习(二)

容器概述 IoC也被称作依赖注入(DI).它是一个处理对象依赖项的过程,也就是将他们一起工作的其他的对象,只有通过构造参数.工厂方法参数或者(属性注入)通过构造参数实例化或通过工厂方法返回对象后再设置属性.当创建bean后,IoC容器再将这些依赖项注入进去.这个过程基本上是反转的,因此得名控制反转(IoC). 下图是 IoC 的高级别视图 IoC容器利用Java的POJO类和配置元数据来生成 完全配置和可执行 的系统或应用程序.而Bean在Spring中就是POJO,也可以认为Bean就是对象.