spring5.0源码-容器源码阅读笔记

4 篇文章 0 订阅
4 篇文章 0 订阅

Spring 版本 分支5.0.x Spring git地址
代码运行说明

  • git clone git@github.com:spring-projects/spring-framework.git
    cd spring-framework

  • gradlew.bat build

读head first,讲述如何更好的去提高效率,通过交互来提高,来引导整个流程,我也仿制一下
1.Spring 是怎么用的
Spring使用非常广泛,从基础来讲,肯定是容器为基础,有了容器才有操作的空间,下面的代码是容器的基础加载
这段代码是容器的的基础使用

public class SpringIocTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
        EasyBean bean = context.getBean(EasyBean.class);
        bean.sayHello();
        context.close();
    }
}

<?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 http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
    <bean class="com.cn.smartqq.EasyBean"></bean>
</beans>
下面这个会失败
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean class="com.cn.smartqq.EasyBean"></bean>
</beans>

这里贴的XML代码里面包括了beans的定义,我非常奇怪,有一些的源码解读里面为什么不带这个能运行起来,这个下面会解析的
代码贴出来了,那么现在关注代码的运行

  • 首先将config.xml加载进来,进行初始化工作
  • 然后通过传入class获取bean
  • 剩下就是调用
  • 关闭容器

下面有两个问题,从实际出发,先看这2个问题

  • 当beans不声明的时候,无法加载成功,为什么?
  • 传入class就能获取bean,为什么?
    直接拿代码了,主要代码是这个类,注释里面写出了问题1的答案
public class DefaultDocumentLoader implements DocumentLoader {
	省略N多
	public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
		if (logger.isDebugEnabled()) {
			logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
		return builder.parse(inputSource);  这里是关键,但是这里又是工厂模式建立的,看下面的关键方法
	}
	
	protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
			throws ParserConfigurationException {

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(namespaceAware);

		if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
			factory.setValidating(true);
			if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) { 
			  最后就是进入了这里,感慨一句,代码是越来越方便维护,以前这里都是硬写的参数3,1之类的,写成true了,所以必须要有
			  写成true之后是必须读xlmns里面的内容的
				// Enforce namespace aware for XSD...
				factory.setNamespaceAware(true);
				try {
					factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
				}
				catch (IllegalArgumentException ex) {
					ParserConfigurationException pcex = new ParserConfigurationException(
							"Unable to validate using XSD: Your JAXP provider [" + factory +
							"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
							"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
					pcex.initCause(ex);
					throw pcex;
				}
			}
		}

		return factory;
	}
}

再来看问题2,首先个人猜了一下实现,可能放到一个hash表里面,可能是类似于concurrenthashmap,线程安全,然后通过反射的形势生成对象,因为spring这里不一定是jvm加载就生成所有对象,spring使用中也有单例,非单例的形式。所以肯定有生成对象的代码只是if条件不符合,忽略过去了(大概猜了个3成)
看代码如何getbean,但是跳入getbean就会发现,竟然不是ClassPathXmlApplicationContext这个类的实际获取,这里就看下代码的整个继承关系
在这里插入图片描述
右点击打开可能看的更清楚,我这里就不换图,因为后面的代码都是通过这里,getBean方法调用的是
AbstractApplicationContext这个类里面的内容,然后这里需要开始插入设计的好处了,非常明显,下面是2个代码的具体,其他省略

	设计非常清晰,我这个方法就是子类实现,那么因为这里除了xml的这个bean加载,还有其他的加载
	说到这里,我就要吐槽,前端设计真的还是比不上后端,就像腾讯去设计前端,改动也非常多,什么对继承开放,对修改关闭都不是很严格,更不用说更菜的设计了,不过前端也改变非常快
	@Override
	public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
	
	//---------------------------------------------------------------------
	// Implementation of BeanFactory interface
	//---------------------------------------------------------------------

	@Override
	public Object getBean(String name) throws BeansException {
		assertBeanFactoryActive();//这里只是一个监控是否已经关掉容器,assert断言就能说明了,命名很棒
		return getBeanFactory().getBean(name);
	}

然后又是追踪了,虽然设计很好,但是追踪起来一样吐血

AbstractRefreshableApplicationContext 这个类里面看到这段语句
	@Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		synchronized (this.beanFactoryMonitor) {//同步锁,虽然concurrent包有很多好用的锁工具,还是syn这个简洁明了
			if (this.beanFactory == null) {
				throw new IllegalStateException("BeanFactory not initialized or already closed - " +
						"call 'refresh' before accessing beans via the ApplicationContext");
			}
			return this.beanFactory;
		}
	}
	为什么看这个方法了,因为这个类里面只有这个方法去复制beanFactory,这里可能有问题说,有没有可能还是子类去实现
	set beanFactory这个动作,的确有可能,并且这个方法的type是protected的,那么最多子类去实现了,但是这里已经有
	default实现,那么我们先看看default实现
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();//看这步
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
	下面的跳转,其实普通是猜不错来的,但是这里还是继续下去,后面,再讲这里具体的实现,下面代码省略很多,文章篇幅会太长
	其实是到了DefaultListBeanFactory这个类里面,下面开始删代码,太长了
	public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
        NamedBeanHolder<T> namedBean = this.resolveNamedBean(requiredType, args);
	通过namedBean返回实例
    }
    NamedBeanHolder 这个结构是name,加实例,进入这个方法,这个方阿飞的
    private <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType, Object... args) throws BeansException {
  			省略
  			这句很关键,getbean
  			 return new NamedBeanHolder(beanName, this.getBean(beanName, requiredType, args));
  			省略
    }
    关键代码到了
    sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
                        public Object getObject() throws BeansException {
                            try {
                                return AbstractBeanFactory.this.createBean(beanName, mbd, args);
                            } catch (BeansException var2) {
                                AbstractBeanFactory.this.destroySingleton(beanName);
                                throw var2;
                            }
                 }
    });
    其实这个createBean里面会包含非常多的信息,有一个非常有趣的类叫做RootBeanDefinition这个就会包含我们
    最爱的scope,lazyinit信息之类的信息
    然后经过非常长的追踪到了
    Class<?> resolvedClass = ClassUtils.forName(className, classLoader); 
    这里就是获取bean了,其实最后,猜了个5成,直接用的hashmap,名字叫做commonClassCache,所以只有公共的才去获取的到
    所以不用管获取
    然后获取是反射,这段代码
    return clToUse != null?clToUse.loadClass(name):Class.forName(name); 

其实上面看不出啥了refreshBeanFactory这个方法你只能连蒙带猜的去看怎么实现,那么这个refreshBeanFactory又是什么时候去实现的?
是ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(“config.xml”);这里去实现的。
这段代码关键在于对于父类的构造方法的调用,其次是refresh();

public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent)
      throws BeansException {

   super(parent); //首先是父类的调用,但是请注意,我们这里传入的是null
   Assert.notNull(paths, "Path array must not be null");
   Assert.notNull(clazz, "Class argument must not be null");
   this.configResources = new Resource[paths.length];
   for (int i = 0; i < paths.length; i++) {
      this.configResources[i] = new ClassPathResource(paths[i], clazz);
   }
   refresh();
}

父类的构造函数一直向上追寻到AbstractApplicationContext这个类

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
   this();
   setParent(parent); 
}

这个this()里面去构造了一个支持ant location的的一个resourceload的解析器,这里只是发生的set操作
然后使用setParent,注意这里传入的null,所以里面的代码是没执行的,功能是将父的contentxt,merge到现有的context里面去,有兴趣的可以去看看
现在回过来看看refresh()这个方法

再来定几个疑问
1.怎么初始化xml文件
2.bean工厂初始化干了什么?

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses. 上下文对beanfactory的一些处理,但是现在调用是没有任何处理的
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context. 国际化用的,在beanfacoty里面判断是否包含messageSource字符串来判断是否开启国际化
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

prepareRefresh()这个没啥好说的就是准备
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
这里有一个写法,在AbstractApplicationContext里面去调用了一个abstract方法refreshBeanFactory,但是实现在于子类AbstractRefreshableApplicationContext中
这个方法我关注的是beanfactory的刷新/创建,并且读取xml文件读bean就隐藏在这里面

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   refreshBeanFactory();
   return getBeanFactory();
}
@Override
protected final void refreshBeanFactory() throws BeansException {
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      loadBeanDefinitions(beanFactory); //读取xml文件
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}
protected DefaultListableBeanFactory createBeanFactory() {
   return new DefaultListableBeanFactory(getInternalParentBeanFactory()); //这里面的不用管,现在的调用是空,那么只用考虑DefaultListableBeanFactory的实现
}

那么我们拿到的factory类就是DefaultListableBeanFactory实现
prepareBeanFactory(beanFactory); 这个方法也就是对factory进行参数设置
这里有一个写法也值得讲一下
在这里插入图片描述
AbstractAutowireCapableBeanFactory,ConfigurableListableBeanFacotry有相同的方法ignoreDependencyType同
注意了,这个方法再DefaultListableBeanFactory是没有实现的,而是直接调用的AbstractAutowireCapableBeanFactory里面的实现方法
loadBeanDefinitions(beanFactory) 就是读取xml文件,会校验xml填写的对不对,bean的一些数量信息都会有提现

-----------------------我是分割线-------------------------------

这里还要讲一下代码设计中的策略模式和工厂模式

Define a family of algorithms,encapsulate each one,and make them
interchangeable.(定义一组 算法,将每个算法都封装起来,并且使它们之间可以互换。)

就拿ClassPathXmlApplicationContext 这个类的继承图来说,他继承实现AbstractApplicationContext类,这个类里面将 resourceLoader作为一批算法,然后DefaultResourceLoader作为算法簇中的一个,context作为调用算法的类,这里写的非常灵活,用继承方式,将具体实现引入,而不是通过New的方式去引入
在这里插入图片描述

工厂模式

Define an interface for creating an object,but let subclasses decide
which class to instantiate.Factory Method lets a class defer
instantiation to subclasses.(定义一个用于创建对象的
接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。)

工厂模式然后会有抽象工厂模式,也就是更复杂的情况下的工厂模式,但是其根本定义还是这个
在这里插入图片描述在这里插入图片描述

来我们看下BeanFactory的一个部分继承图,其实这里根本没有实现,实现在其他的里面,在下一张图里面
在这里插入图片描述

这里,把具体实现扔到了这里在这里插入图片描述

那么这是工厂模式吗?看起来和工厂模式没有关系啊,

设计模式是告诉我们如何组织类和对象以解决某种问题
出自headFirst,这本书真推荐,比国内的设计模式讲的更清晰,我之前看的一些设计模式的书和资料,从心啊在看来都太死板了

他这里是使用反射的方式去生成类的,是传入参数,class,而不是传入具体类,请注意这个虚线号,不是继承,也不是实现,是一个构成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值