介绍
前面一章介绍了创建ApplicationContext,这章将会介绍ApplicationContext的一些处理工作。
源码
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
// 处理ApplicationContext
postProcessApplicationContext(context);
// 初始化器对context进行初始化处理
applyInitializers(context);
// 发送事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 注册bean到spring容器中
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加载启动类类,如果被@Component注解修饰,加到bean定义的map中
load(context, sources.toArray(new Object[0]));
// 发送事件
listeners.contextLoaded(context);
}
这边整体的逻辑就是,对ApplicationContext进行一些准备工作。我们看下初始化器的初始化工作的代码。
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
// 拿到SpringApplication初始化时候创建的初始化器
for (ApplicationContextInitializer initializer : getInitializers()) {
// 判断泛型是否匹配
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
// 执行初始化
initializer.initialize(context);
}
}
初始化器初始化比较简单,就是执行初始化的方法,对于初始化器怎么来的,在第一章时候说了,从META-INF/spring.factories中获取的。
这边挑一个初始化器说下:ContextIdApplicationContextInitializer
public class ContextIdApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
private int order = Ordered.LOWEST_PRECEDENCE - 10;
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ContextId contextId = getContextId(applicationContext);
applicationContext.setId(contextId.getId());
applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(),
contextId);
}
private ContextId getContextId(ConfigurableApplicationContext applicationContext) {
ApplicationContext parent = applicationContext.getParent();
if (parent != null && parent.containsBean(ContextId.class.getName())) {
return parent.getBean(ContextId.class).createChildId();
}
return new ContextId(getApplicationId(applicationContext.getEnvironment()));
}
// 从配置中获取spring.application.name值
private String getApplicationId(ConfigurableEnvironment environment) {
String name = environment.getProperty("spring.application.name");
return StringUtils.hasText(name) ? name : "application";
}
/**
* The ID of a context.
*/
class ContextId {
private final AtomicLong children = new AtomicLong(0);
private final String id;
ContextId(String id) {
this.id = id;
}
ContextId createChildId() {
return new ContextId(this.id + "-" + this.children.incrementAndGet());
}
String getId() {
return this.id;
}
}
}
ContextIdApplicationContextInitializer的作用就是从environment中获取spring.application.name的值,如果有就设置成applicationContext的id,如果没有就默认application。
比如我们在配置文件application.yml中设置spring.application.name=spring-boot,那么这个程序的ApplicationContext的id就是spring-boot。
接着看主流程:context.getBeanFactory().registerSingleton则是把springApplicationArguments和springBootBanner加到spring容器中,这里把对象加到容器中,不是加到bean定义的map中。加到bean定义的map中的话还是得生成对象,加到容器中的,这些后面会说到,现在只是有这样的概念就好。
接下来说load方法:
public Set<Object> getAllSources() {
Set<Object> allSources = new LinkedHashSet<>();
if (!CollectionUtils.isEmpty(this.primarySources)) {
allSources.addAll(this.primarySources);
}
if (!CollectionUtils.isEmpty(this.sources)) {
allSources.addAll(this.sources);
}
return Collections.unmodifiableSet(allSources);
}
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug(
"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
// 创建一个BeanDefinitionLoader解析器
BeanDefinitionLoader loader = createBeanDefinitionLoader(
getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// 解析
loader.load();
}
loader方法主要是创建一个BeanDefinitionLoader,然后对于sources解析。这个sources就是我们的启动的Application类的class。
接下来看BeanDefinitionLoader的解析方法:
class BeanDefinitionLoader {
private final Object[] sources;
private final AnnotatedBeanDefinitionReader annotatedReader;
private final XmlBeanDefinitionReader xmlReader;
private BeanDefinitionReader groovyReader;
private final ClassPathBeanDefinitionScanner scanner;
private ResourceLoader resourceLoader;
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
Assert.notNull(registry, "Registry must not be null");
Assert.notEmpty(sources, "Sources must not be empty");
this.sources = sources;
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
this.xmlReader = new XmlBeanDefinitionReader(registry);
if (isGroovyPresent()) {
this.groovyReader = new GroovyBeanDefinitionReader(registry);
}
this.scanner = new ClassPathBeanDefinitionScanner(registry);
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
this.annotatedReader.setBeanNameGenerator(beanNameGenerator);
this.xmlReader.setBeanNameGenerator(beanNameGenerator);
this.scanner.setBeanNameGenerator(beanNameGenerator);
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
this.xmlReader.setResourceLoader(resourceLoader);
this.scanner.setResourceLoader(resourceLoader);
}
public void setEnvironment(ConfigurableEnvironment environment) {
this.annotatedReader.setEnvironment(environment);
this.xmlReader.setEnvironment(environment);
this.scanner.setEnvironment(environment);
}
public int load() {
int count = 0;
for (Object source : this.sources) {
// 解析
count += load(source);
}
return count;
}
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
// 根据类型调用不同的解析方式
return load((Class<?>) source);
}
if (source instanceof Resource) {
return load((Resource) source);
}
if (source instanceof Package) {
return load((Package) source);
}
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
private int load(Class<?> source) {
// Groovy是否存在
if (isGroovyPresent()
&& GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
GroovyBeanDefinitionSource.class);
load(loader);
}
// 如果被@Component修饰
if (isComponent(source)) {
// 注册到容器的bean定义的beanDefinitionMap的map中
this.annotatedReader.register(source);
return 1;
}
return 0;
}
/**
* 省略其他代码
*/
}
BeanDefinitionLoader主要负责对于bean的解析,并且加入到bean定义beanDefinitionMap中去。由于我们的启动类的对象是class,所以直接调用load(Class<?> soruce)的方法。这个方法主要去判断class是否有@Component修饰或者注解的注解有没有@Component修饰,如果有就加入到beanDefinitionMap中。
像我们的启动类一般都是加入@SpringBootApplication注解。而@SpringBootApplication注解又有@SpringBootConfiguration注解,@SpringBootConfiguration注解又有@Configuration注解,@Configuration注解是有@Component注解。这样我们的启动类就加入到spring的beanDefinitionMap中去了。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration
至此load的逻辑就结束了。BeanDefinitionLoader还有其他很多的方法,但是暂时先不看,用到时候再慢慢看,看多了也不清楚做什么的。
再向下prepareContext的逻辑就是发送事件了,就不再讲了。
总结
prepareContext主要就是准备ApplicationContext,对ApplicationContext进行一些初始化处理,然后把启动类加入到BeanDefinition中去,后面解析用。