zoukankan      html  css  js  c++  java
  • 【Spring源码这样读】-再次走近容器Spring IOC 二

    继上文的问题,我们本章,继续分析我们的容器到底在做了一些什么事情。

    上文我们说到我们的ClassPathResource到底做了什么,但是我们阅读源码之后发现只是做了一些初始化。那我们推测的获取配置文件,然后去配置文件里面获取内容的推测还能成立吗?不需要急,我们一步一步往下看就能看到结果。既然我们看了ClassPathResource,我们在来看看new XmlBeanFactory(classPathResource);到底做了什么。

    构造bean工厂类

    /**
     * Create a new XmlBeanFactory with the given resource,
     * which must be parsable using DOM.
     * @param resource the XML resource to load bean definitions from
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource) throws BeansException {
    	this(resource, null);
    }
    
    /**
     * Create a new XmlBeanFactory with the given input stream,
     * which must be parsable using DOM.
     * @param resource the XML resource to load bean definitions from
     * @param parentBeanFactory parent bean factory
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    	super(parentBeanFactory);
    	this.reader.loadBeanDefinitions(resource);
    }
    

    我们使用的就是第一个构造,但是第一个构造调用了第二个,第二个构造只有两步。我们继续往下看

    再来看XmlBeanFactory初始化的源码

    • super(parentBeanFactory):用来初始化父类
    • this.reader.loadBeanDefinitions(resource):spring初始化资源加载的真正实现

    我们先来看看“super(parentBeanFactory):用来初始化父类”这一步做了什么吧

    /**
     * 先是调用DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory)带参构造方法
     */
    public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    	super(parentBeanFactory);
    }
    
    /**
     * 调用父类AbstractAutowireCapableBeanFactory
     */
    public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    	this();
    	setParentBeanFactory(parentBeanFactory);
    }
    
    /**
     * 调用AbstractAutowireCapableBeanFactory()无参构造方法
     */
    public AbstractAutowireCapableBeanFactory() {
    	super();
    	ignoreDependencyInterface(BeanNameAware.class);
    	ignoreDependencyInterface(BeanFactoryAware.class);
    	ignoreDependencyInterface(BeanClassLoaderAware.class);
    }
    
    /** 
     * 在初始化XmlBeanFactory容器
     */
    public void setParentBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    	if (this.parentBeanFactory != null && this.parentBeanFactory != parentBeanFactory) {
    		throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory);
    	}
    	this.parentBeanFactory = parentBeanFactory;
    }
    

    到这里是不是比较明了的知道了这一步具体做了什么?其实看完super(parentBeanFactory)的源码发现就做了两件事:获取添加需要忽略指定接口实现类的自动装配和初始化。那我们的第二步具体做了什么呢?

    this.reader.loadBeanDefinitions(resource)分析

    之前我们说了我们应该是根据配置文件找到对应的类,那源码是这样吗?看看这句代码的实现就知道了。

    /**
     * 先是调用loadBeanDefinitions(Resource resource)
     */
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    	return loadBeanDefinitions(new EncodedResource(resource));
    }
    
    /**
     * 这个方法正是我们之前推测的实现
     * 从xml配置文件中加载bean
     */
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    	Assert.notNull(encodedResource, "EncodedResource must not be null");
    	if (logger.isTraceEnabled()) {
    		logger.trace("Loading XML bean definitions from " + encodedResource);
    	}
    
    	Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    	if (currentResources == null) {
    		currentResources = new HashSet<>(4);
    		this.resourcesCurrentlyBeingLoaded.set(currentResources);
    	}
    	if (!currentResources.add(encodedResource)) {
    		throw new BeanDefinitionStoreException(
    				"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    	}
    	try {
    		InputStream inputStream = encodedResource.getResource().getInputStream();
    		try {
    			InputSource inputSource = new InputSource(inputStream);
    			if (encodedResource.getEncoding() != null) {
    				inputSource.setEncoding(encodedResource.getEncoding());
    			}
    			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    		}
    		finally {
    			inputStream.close();
    		}
    	}
    	catch (IOException ex) {
    		throw new BeanDefinitionStoreException(
    				"IOException parsing XML document from " + encodedResource.getResource(), ex);
    	}
    	finally {
    		currentResources.remove(encodedResource);
    		if (currentResources.isEmpty()) {
    			this.resourcesCurrentlyBeingLoaded.remove();
    		}
    	}
    }
    

    当我们贴完代码之后,是不是可以明显的看到我们之前推测的实际证据?

    ClassPathResource和InputStream

    我们从上面的源码看到,我们定义的类在xml中,源码里面有InputStream。但是传入的只有一个ClassPathResource。那他是怎么封装进入ClassPathResource的呢?

    其实这里的关联比较简单。我们不妨来看看这些类的类结构图
    在这里插入图片描述

    到这里是不是就已经很明了了我们的InputStream是怎么将内容传递进入ClassPathResource

    真正获取bean

    获取bean才是我们这一段操作的最关键的地方,那bean又是怎么获取出来的呢?这里贴一下主代码吧

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
    		@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    
    	final String beanName = transformedBeanName(name);
    	Object bean;
    
    	// Eagerly check singleton cache for manually registered singletons.
    	Object sharedInstance = getSingleton(beanName);
    	if (sharedInstance != null && args == null) {
    		if (logger.isTraceEnabled()) {
    			if (isSingletonCurrentlyInCreation(beanName)) {
    				logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
    						"' that is not fully initialized yet - a consequence of a circular reference");
    			}
    			else {
    				logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
    			}
    		}
    		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.
    		BeanFactory parentBeanFactory = getParentBeanFactory();
    		if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    			// Not found -> check parent.
    			String nameToLookup = originalBeanName(name);
    			if (parentBeanFactory instanceof AbstractBeanFactory) {
    				return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
    						nameToLookup, requiredType, args, typeCheckOnly);
    			}
    			else if (args != null) {
    				// Delegation to parent with explicit args.
    				return (T) parentBeanFactory.getBean(nameToLookup, args);
    			}
    			else if (requiredType != null) {
    				// No args -> delegate to standard getBean method.
    				return parentBeanFactory.getBean(nameToLookup, requiredType);
    			}
    			else {
    				return (T) parentBeanFactory.getBean(nameToLookup);
    			}
    		}
    
    		if (!typeCheckOnly) {
    			markBeanAsCreated(beanName);
    		}
    
    		try {
    			final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    			checkMergedBeanDefinition(mbd, beanName, args);
    
    			// Guarantee initialization of beans that the current bean depends on.
    			String[] dependsOn = mbd.getDependsOn();
    			if (dependsOn != null) {
    				for (String dep : dependsOn) {
    					if (isDependent(beanName, dep)) {
    						throw new BeanCreationException(mbd.getResourceDescription(), beanName,
    								"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
    					}
    					registerDependentBean(dep, beanName);
    					try {
    						getBean(dep);
    					}
    					catch (NoSuchBeanDefinitionException ex) {
    						throw new BeanCreationException(mbd.getResourceDescription(), beanName,
    								"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
    					}
    				}
    			}
    
    			// Create bean instance.
    			if (mbd.isSingleton()) {
    				sharedInstance = getSingleton(beanName, () -> {
    					try {
    						return createBean(beanName, mbd, args);
    					}
    					catch (BeansException ex) {
    						// Explicitly remove instance from singleton cache: It might have been put there
    						// eagerly by the creation process, to allow for circular reference resolution.
    						// Also remove any beans that received a temporary reference to the bean.
    						destroySingleton(beanName);
    						throw ex;
    					}
    				});
    				bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    			}
    
    			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, () -> {
    						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.
    	if (requiredType != null && !requiredType.isInstance(bean)) {
    		try {
    			T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
    			if (convertedBean == null) {
    				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
    			}
    			return convertedBean;
    		}
    		catch (TypeMismatchException ex) {
    			if (logger.isTraceEnabled()) {
    				logger.trace("Failed to convert bean '" + name + "' to required type '" +
    						ClassUtils.getQualifiedName(requiredType) + "'", ex);
    			}
    			throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
    		}
    	}
    	return (T) bean;
    }
    

    仅从代码量上就能看出来bean的加载经历了一个相当复杂的过程,其中涉及各种各样的考虑。

    我们将上面的内容大致总结一下:

    • 转换对应beanName。
    • 尝试从缓存中加载单例。
    • bean的实例化。
    • 原型模式的依赖检查。
    • 检测parentBeanFactory。
    • 将存储XML配置文件的GernericBeanDefinition转换为RootBeanDefinition。
    • 寻找依赖。
    • 针对不同的scope进行bean的创建。
    • 类型转换。(强转)

    其实读到这里,我们不难看出,大致跟我们之前的推测出入并不是很大,只是源码的开发人员们对代码的逻辑做的更为严谨,并且做了很多的必要的校验。

  • 相关阅读:
    Centos7安装Typecho详细教程
    Liunx 安装 Nessus
    攻防世界 web进阶练习 NewsCenter
    针对Linux 文件完整性监控的实现
    ParrotSec 中文社区 QQ群认证 Openssl解密
    中转Webshell 绕过安全狗(二)
    中转Webshell 绕过安全狗(一)
    Kali Linux Web渗透测试手册(第二版)
    JavaScript指定断点操作
    年轻程序员如何快速成长
  • 原文地址:https://www.cnblogs.com/xlecho/p/14667983.html
Copyright © 2011-2022 走看看