spring源码笔记01-BeanFactory

314 阅读2分钟

一切从BeanFactory开始

spring的helloWorld

<bean id="helloBean" class="com.HelloWorld"></bean>
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld obj = (HelloWorld) ac.getBean("helloBean");
obj.printHello();

几行代码完成了SpringBeanFactory加载和生成Bean的过程,在ClassPathXmlApplicationContext里面右击鼠标,点击Diagrams->show Diagrams(IDEA),就如下图所示

ClassPathXmlApplicationContext
注意图片的左上角,BeanFactory 是ClassPathXmlApplicationContext的最上层接口,BeanFactory定义了工厂的基本行为,比如

Object getBean(String name) throws BeansException;

前面的ac.getBean其实就是对BeanFactory接口的实现,所以说,ClassPathXmlApplicationContext就是一个BeanFactory,也就是一个IOC容器.

IOC容器的创建

helloWorld很简单,背后也蕴涵着spring的诸多内容。为此我们有两个疑问

  1. 容器初始化过程
  2. getBean的时候如何加载

从xml到Java Bean 第一步肯定是解析xml,通过debug来看下源码

  1. 构造器
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}
  1. 实际执行
        // parent=null,configLocations={"applicationContext.xml"},refresh=true 
		super(parent); 
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}

super(parent):设置好 Bean 资源加载器

  • super(parent)最终执行如下代码(在AbstractApplicationContext.class):
	/**
	 * Create a new AbstractApplicationContext with no parent.
	 */
	public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}
  • getResourcePatternResolver如下
	protected ResourcePatternResolver getResourcePatternResolver() {
		return new PathMatchingResourcePatternResolver(this);
	}
  • new PathMatchingResourcePatternResolver(this)如下
	public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
		this.resourceLoader = resourceLoader;
	}

所以this.resourceLoader是指向ClassPathXmlApplicationContext的实例.

  • 调试折回到 AbstractApplicationContext.class类的时候,处理了parent :
    setParent(parent);

setConfigLocations(configLocations):设置 Bean

	public void setConfigLocations(@Nullable String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}

这个图就这么一句核心this.configLocations[i] = resolvePath(locations[i]).trim()

  • this.configLocations[i] = resolvePath(locations[i]).trim()
	protected String resolvePath(String path) {
		return getEnvironment().resolveRequiredPlaceholders(path);
	}
  • getEnvironment()
	public ConfigurableEnvironment getEnvironment() {
		if (this.environment == null) {
			this.environment = createEnvironment();
		}
		return this.environment;
	}
	protected ConfigurableEnvironment createEnvironment() {
		return new StandardEnvironment();
	}
  • resolveRequiredPlaceholders(path)
	public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
		return this.propertyResolver.resolveRequiredPlaceholders(text);
	}
  • resolveRequiredPlaceholders(text)如下;参数text="application.xml"
	public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
		if (this.strictHelper == null) {
			this.strictHelper = createPlaceholderHelper(false);
		}
		return doResolvePlaceholders(text, this.strictHelper);
	}

第一次加载strictHelper是为null的,初始化它需要以下参数

	public static final String PLACEHOLDER_PREFIX = "${";

	public static final String PLACEHOLDER_SUFFIX = "}";

	public static final String VALUE_SEPARATOR = ":";
  • doResolvePlaceholders(text, this.strictHelper);
	private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
		return helper.replacePlaceholders(text, this::getPropertyAsRawString);
	}

由于我传递的参数是"application.mxl" ,并没有用到 "${" ,所以在解析占位符的代码中直接return了,并没有进行解析,也就是返回了"application.mxl"

protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {

		StringBuilder result = new StringBuilder(value);

		int startIndex = value.indexOf(this.placeholderPrefix);
		while (startIndex != -1) {
			...省略解析代码
			}
			else {
				startIndex = -1;
			}
		}

		return result.toString();
	}

结果就是 this.configLocations[0]="application.xml" ,

refresh()

	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.
				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.
				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();
			}
		}
	}

refresh 的作用类似于对 IOC 容器的重启, 在新建立好的容器中对容器进行初始化,对 BeanDefinition资源进行载入,由于篇幅内容较长,所以在下一篇讲解