IOC容器启动过程

Srping框架IOC容器启动过程


近期面试问到了IOC容器的启动过程,作为新手的自己并没有很好的回答出来,也不是自己没有研究过源码,并且自己研究源码时的思路自认为还是比较清晰的,就是面试时不知道该如何回答,启动过程中的一个步骤该用语言怎么描述,并且前后的逻辑还要贯通,所以,看源码自己理清楚和给别人讲清楚还是两回事,所以在这里还是想要用自己的语言把IOC容器的启动过程用写的方式罗列出来,并且附上一定的源码截图,然后再锻炼用嘴的方式说出来。
IOC容器,对于了解Spring框架的开发人员来讲,都应该会知道它是什么东西的,简单的理解就是帮我们管理了一系列的Bean对象,当然这里的Bean对象包括我们自己定义的,采用Spring配置或者注解的方式注册到容器中的,也包括Spring自身会创建的一系列的Bean对象。
这里我们就通过Spring的AnnotationConfigApplicationContext这个类进行入手,在说启动流程之前,先简单的给一个这个类的使用案例,这个类相信用过Spring框架的开发者都应该会了解,通过注解的方式将配置文件里的Bean注册到IOC容器中,它的使用方式一般也就是像这样了:

//BeansContextConfig为我们自定义的包含@Configuration注解的配置类
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(BeansContextConfig.class);
//我们一般通过下面的方式进行Bean对象的获取
Person person1 = (Person) acac.getBean("person");
Person person2 = acac.getBean(Person.class);

下面就开始从源码着手,对IOC容器启动过程的一个简单的认识:
首先我们可以进入AnnotationConfigApplicationContext的构造方法里,可以看到以下的代码块:

public AnnotationConfigApplicationContext(Class... annotatedClasses) {
	this();
	register(annotatedClasses);
	refresh();
}

这里我们可以一行一行的进行分析:
1、我们先看第一行this()调用自身的无参构造方法,我们可以到这个构造方法里看一下:

public AnnotationConfigApplicationContext() {
	this.reader = new AnnotatedBeanDefinitionReader(this);
	this.scanner = new ClassPathBeanDefinitionScanner(this);
}

可以看到这个构造方法只有两行代码,字面意思,AnnotatedBeanDefinitionReader(注解的Bean定义读取器),ClassPathBeanDefinitionScanner(类路径的Bean定义扫描器),看到这里,猜的话感觉像是对Bean定义信息的注册,字面意思都是对Bean定义的获取,我们先不管有没有猜对,接着看源码,依然是一行一行的追踪:
(1)进入到AnnotatedBeanDefinitionReader

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
	this(registry, getOrCreateEnvironment(registry));
}

可以看到一个参数是把我们创建的AnnotationConfigApplicationContext给穿进来了,AnnotationConfigApplicationContext也是BeanDefinitionRegistry的一个实现类,第二个参数是获取或者创建环境变量信息,进去也可以看到是将AnnotationConfigApplicationContext环境变量里的环境变量获取到,如果没有创建一个,并作为第二个参数传入this构造方法里,不过不是我们的主线,所以继续往下走,

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	Assert.notNull(environment, "Environment must not be null");
	this.registry = registry;
	this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
	AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

倒数第二行不知道做什么的,可以先过掉,不是我们的主线,可以看到最下面的一行,

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

看方法名就感觉这个方法跟我们的主线有点关系,字面意思注册注解配置的处理器,可能还是不好理解,我们继续往下走,

public static Set registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, Object source) {
	DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
	if (beanFactory != null) {
		if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
			beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
		}
		if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
			beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
		}
	}
	Set beanDefs = new LinkedHashSet(4);
		。
		。
		。
	return beanDefs;
}

最终会走到这个方法里,代码比较多,所以就展示了一部分,我们可以看到这个方法里有很多的判断语句,不过可以一目了然的看到,基本都是判断我们创建的AnnotationConfigApplicationContext中是否包含相应后置处理器的Bean定义信息,如果没有的话,会在这个方法里的beanDefs中添加一个在registerPostProcessor方法里返回的后置处理器的Bean定义信息,最后将Bean定义信息的列表返回,我们再返回上一级

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
	registerAnnotationConfigProcessors(registry, null);
}

这里似乎并没有地方接收返回的Bean定义信息的列表,或许通过注解的方式启动容器,不需要接收吧,不过先不管这个,因为我们可以看到在registerPostProcessor方法里传入了我们创建的AnnotationConfigApplicationContext,我们先进来看看:

private static BeanDefinitionHolder registerPostProcessor(
			BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
	definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	registry.registerBeanDefinition(beanName, definition);
	return new BeanDefinitionHolder(definition, beanName);
}

可以看到第二行确实是将Bean定义信息给注册到了AnnotationConfigApplicationContext上下文中,所以这一步到这里也就结束了,这一步主要就是将容器内部的后置处理器的Bean定义信息给注册到我们的上下文,也就是IOC容器中。
(2)进入到ClassPathBeanDefinitionScanner

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
	this(registry, true);
}

在这里同样是把我们创建的AnnotationConfigApplicationContext穿进来,并传了一个true,接着往下走,最终会走到这里:

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, ResourceLoader resourceLoader) {
	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	this.registry = registry;
	if (useDefaultFilters) {
		registerDefaultFilters();
	}
	setEnvironment(environment);
	setResourceLoader(resourceLoader);
}

在这里我们可以看到,刚才默认传递进来的true是以useDefaultFilters参数的形式传递过来的,所以必然会走到if判断里,下面的两行,一行是设置环境变量,一行是设置资源加载器,不是我们的主线,所以直接到registerDefaultFilters注册默认的过滤器方法里看一下:

protected void registerDefaultFilters() {
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
		logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
	}
	try {
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class) ClassUtils.forName("javax.inject.Named", cl)), false));
		logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}

在这里我们可以看到第一行代码:

this.includeFilters.add(new AnnotationTypeFilter(Component.class));

往过滤器里添加了一个注解类型的过滤器,并且注解的类型是@Component,我们可以稍微的看一下注释:

/**
 * Register the default filter for {@link Component @Component}.
 * 

This will implicitly register all annotations that have the * {@link Component @Component} meta-annotation including the * {@link Repository @Repository}, {@link Service @Service}, and * {@link Controller @Controller} stereotype annotations. *

Also supports Java EE 6's {@link javax.annotation.ManagedBean} and * JSR-330's {@link javax.inject.Named} annotations, if available. * */

注册默认的过滤器,如@Component,@Repository,@Service,@Controller,在JavaEE 6版本以后加了@ManagedBean和JSR-330规范的@javax.inject.Named注解等
后面两个可以不用管,最重要的就是前面的四个,因为@Controller,@Service,@Repository注解上包含了@Component注解,所以这些注解都是可以被容器扫描到的,程序走完下面的两个try catch也就结束了。这一步的操作,主要就是向容器中添加注解类型的扫描器,所以该步骤也可以称为指定类路径下的Bean扫描策略。
2.走完上面的两个步骤,this()构造方法也就走完了,我们接着看register(annotatedClasses);方法的执行:

public void register(Class... annotatedClasses) {
	for (Class annotatedClass : annotatedClasses) {
		registerBean(annotatedClass);
	}
}

一直走到这里,我们可以看到,它循环遍历了我们传入的配置类,也就是我们自定义的标注有@Configuration注解的配置类,可以有多个,我们进入registerBean(annotatedClass);方法里,最终走到这里:

public void registerBean(Class annotatedClass, String name, Class... qualifiers) {
	AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
	if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
		return;
	}
	ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
	abd.setScope(scopeMetadata.getScopeName());
	String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
	AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
	if (qualifiers != null) {
		for (Class qualifier : qualifiers) {
			if (Primary.class == qualifier) {
				abd.setPrimary(true);
			}
			else if (Lazy.class == qualifier) {
				abd.setLazyInit(true);
			}
			else {
				abd.addQualifier(new AutowireCandidateQualifier(qualifier));
			}
		}
	}
	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
	definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

这个方法,看似也没多少东西,开始是根据我们传入的配置类创建一个注解的Bean定义信息,然后获取该Bean定义信息的元数据信息,也就是方法和属性相关的成员,接着调用该方法:

AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

对配置类中的元注解进行一系列的操作,例如懒加载,如果包含懒加载的注解,就将该配置类设置为懒加载的,接着判断是否包含这些注解,但是我们可以看到前面qualifiers参数默认传递的是null,所以这里可以暂时可以认为是不会走到if里面的,然后看方法名可以发现最关键的一个就是最下面的一个方法的调用:

BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);

将上面创建的Bean定义信息注册到传递进来的AnnotationConfigApplicationContext中,也就是IOC容器中,此时这一步也就结束了。这一步简单的概括就是将我们自定义的配置类的Bean定义信息注册到IOC容器中。
3、接着我们看refresh();方法:

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

我们可以看到这个方法里调用了很多的方法,并且每个方法再往下都会涉及到IOC容器的各种功能,我们可以一个方法一个方法的进行追踪,但是这里我们就不再往下追踪了,这个方法执行完成后IOC容器也就启动成功了。往后再继续发表其他的博客进行每个方法的解析,一个博客少点内容可以方便博客的阅读,这里的步骤也可以简单的理解为容器的心脏或者核心方法,因为就是这个方法带动了整个IOC的的运转。
4、总结:
IOC容器的启动过程:
1、将容器内部的后置处理器的Bean定义信息给注册到IOC容器中
2、向容器中添加注解类型的扫描器,指定类路径下的Bean扫描策略
3、将我们自定义的配置类的Bean定义信息注册到IOC容器中
4、执行容器的核心方法refresh(),带动Spring的运转
可以看到前三步都是在为第四步做前置的准备,真正的启动IOC容器的还是refresh方法,包括SpringBoot 也是采用这个方法来带动的

你可能感兴趣的:(IOC容器启动过程,Spring,Spring源码分析)