http://www.javashuo.com/article/p-uvudtich-bm.htmlhtml
上一篇文章中,咱们经过createApplicationContext方法建立了一个ApplicationContext的实例对象。本文将阅读一下在ApplicationContext在refresh以前的prepareContext中作了哪些事情。spring
咱们跟进prepareContext方法springboot
private void prepareContext( ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner ) { // 设置Environment context.setEnvironment(environment); // 后置处理 postProcessApplicationContext(context); // 调用ApplicationContextInitializer接口的实现 applyInitializers(context); // 发布ApplicationContext准备事件 listeners.contextPrepared(context); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 注册args参数为单例bean beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { // 注册banner为单例bean beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { // 设置beanFactory中是否容许重复bean覆盖 ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 加载main方法所在类 Set<Object> sources = getAllSources(); // 注册main方法所在类到beanFactory load(context, sources.toArray(new Object[0])); // 发布Context加载事件 listeners.contextLoaded(context); }
咱们看到prepareContext方法主要逻辑包含了三块内容app
1)基本的初始化,如设置Environment,调用ApplicationContextInitializer接口的实现类post
2)注册现有的对象为单例bean,如args、bannerthis
3)加载main方法所在的主类spa
很显然,加载main方法的主类是咱们关注的重点。咱们先看看getAllSources方法返回code
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); }
primarySources是在SpringApplication初始化的时候设置的,而sources默认是没有的。所在getAllSoures方法将返回main方法所在的主类。xml
下面,咱们跟进load方法,阅读一下加载main所在主类的逻辑htm
protected void load(ApplicationContext context, Object[] sources) { // 获取BeanDefinition加载器 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(); }
load方法中,先是得到了BeanDefinitionLoader,而后去加载资源。这里要注意!为何是BeanDefinitionLoader呢?
首先,咱们得知道Spring的bean资源来自各类地方,如xml、annotation等,那么这些bean在配置的时候也就是对bean进行定义,而这些定义映射到内存中的对象就是BeanDefinition的对象,Spring后续会根据BeanDefinition再获取具体的bean。
简单来讲就是:配置 --> BeanDefinition --> Bean 这样一个逻辑
因此,后续咱们会看到BeanDefinitionLoader会将主类加载成BeanDefinition,而后注册到ApplicationContext当中。
先跟进getBeanDefinitionRegistry方法,看看BeanDefinition会被注册到哪里去
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) { // 返回当前ApplicationContext if (context instanceof BeanDefinitionRegistry) { return (BeanDefinitionRegistry) context; } if (context instanceof AbstractApplicationContext) { return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory(); } throw new IllegalStateException("Could not locate BeanDefinitionRegistry"); }
咱们注意,springboot的AnnotationConfigServletWebServerApplicationContext这个ApplicationContext的实现类,随着继承链路向上走是继承自GenericApplicationContext的,而GenericApplicationContext实现了BeanDefinitionRegistry。
因此,getBeanDefinitionRegistry将最终返回强转过的ApplicationContext。也就是说BeanDefinition将被注册到ApplicationContext里面。
回到load方法中,咱们再跟进createBeanDefinitionLoader方法
protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) { return new BeanDefinitionLoader(registry, sources); }
再跟进构造方法
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) { this.sources = sources; // 注解方式的读取器 this.annotatedReader = new AnnotatedBeanDefinitionReader(registry); // xml方式的读取器 this.xmlReader = new XmlBeanDefinitionReader(registry); // 类路径下的扫描器 this.scanner = new ClassPathBeanDefinitionScanner(registry); // 扫描排除当前main方法的主类 this.scanner.addExcludeFilter(new ClassExcludeFilter(sources)); }
咱们看到加载器支持注解、xml两种方式。类路径下的扫描器排除了当前的主类
回到load方法
protected void load(ApplicationContext context, Object[] sources) { // 获取BeanDefinition加载器 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(); }
此时,咱们已经获取了BeanDefinitionLoader,下面调用该loader的load方法开始加载
跟进第二个load方法
public int load() { int count = 0; for (Object source : this.sources) { count += load(source); } return count; }
再跟进第三个load方法
private int load(Object source) { 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()); }
因为咱们的主类是一个class,因此进入第一个if分支的load方法
继续跟进
private int load(Class<?> source) { // 省略 if (isComponent(source)) { this.annotatedReader.register(source); return 1; } return 0; }
该方法先经过isComponent方法判断了主类是否是被@Component注解,若是是,那么调用注解方式的阅读器,注册该资源。
跟进isComponent方法,看看怎么判断的
private boolean isComponent(Class<?> type) { // 找到是否匹配@Component注解 if (AnnotationUtils.findAnnotation(type, Component.class) != null) { return true; } // 省略 }
其实就是找这个类是否有@Component注解,但请注意咱们一般都使用@SpringBootApplication这个注解,并无直接注解@Component。而@SpringBootApplication是一个组合注解,其中就组合了@Component
而AnnotationUtils.findAnnotation方法将会递归遍历注解,最终找到@Component。
isComponent判断为true之后,咱们再跟进annotationReader.register(source)阅读一下读取主类的过程
public void register(Class<?>... annotatedClasses) { for (Class<?> annotatedClass : annotatedClasses) { registerBean(annotatedClass); } }
继续跟进registerBean方法
public void registerBean(Class<?> annotatedClass) { doRegisterBean(annotatedClass, null, null, null); }
再跟进doRegisterBean方法,该方法比较长,咱们省略掉一些次要的部分
<T> void doRegisterBean( Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers ) { // 先包装成BeanDefinition AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); // 解析scope元数据 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); // 生成bean的名 String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); // 解析一些常见的注解元数据 AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 注册BeanDefinition到ApplicationContext BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
能够看到,doRegisterBean方法的主要逻辑就是包装并解析出一个BeanDefinition,而后调用registerBeanDefinition方法把BeanDefinition给注册到ApplicationContext中。
注册相关的本文就再也不继续展开了,后续的文章会跟进这些内容。
总的来讲,prepareContext方法主要就是为了加载并注册主类的BeanDefinition到ApplicationContext。这里注意!咱们一直都在说注册到ApplicationContext,但熟悉spring的都会知道不管是Bean仍是BeanDefinition都是注册到BeanFactory中的。但咱们一直没有严格区分它,后续的文章咱们将会把ApplicationContext和BeanFactory进行区分。