Spring 源码系列之 AnnotationConfigApplicationContext 是如何工作的

这篇文章对 spring源码的分析仅仅相当于 “hello,world” 的程度。


Spring 的 ApplicationContext 是非常重要的,之前总是从大的层面来看,比如整体的架构;现在尝试从更微小的层面来研究,两者相结合,对 Spring 的了解会更深刻。


		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.scan("org.springframework.context.annotation6");
		context.refresh();

		context.getBean(uncapitalize(ConfigForScanning.class.getSimpleName()));
		context.getBean("testBean"); // contributed by ConfigForScanning
		context.getBean(uncapitalize(ComponentForScanning.class.getSimpleName()));
		context.getBean(uncapitalize(Jsr330NamedForScanning.class.getSimpleName()));
		Map<String, Object> beans = context.getBeansWithAnnotation(Configuration.class);
		assertEquals(1, beans.size());

来自 Spring 源码中的单元测试 


初始化


调用 AnnotationConfigApplicationContext 构造函数时,执行以下代码:

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

官方文档给出解释,AnnotationConfigApplicationContext 需要调用 register() 来进行填充,并手动调用 refresh() 进行刷新。如此看来,应该有构造函数在指定了需要填充的内容后,自动完成填充以及刷新吧。果然,AnnotationConfigApplicationContext 还有着如下两个构造函数:

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

	public AnnotationConfigApplicationContext(String... basePackages) {
		this();
		scan(basePackages);
		refresh();
	}

分析以上,可以发现,最终都会调用无参的构造函数,那么看来,AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner 非常重要了。


扫描


context.scan("org.springframework.context.annotation6") 调用的方法如下:

	public void scan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		this.scanner.scan(basePackages);
	}

此处的 scanner 就是上面的 ClassPathBeanDefinitionScanner

	public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		doScan(basePackages);

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

第一个 scan() 并不会真正的去处理扫描逻辑,而是由 doScan() 所做的;注意到了嘛?doScan() 的访问控制修饰符变为了 protected ,如果需要拓展,Spring 建议我们重写该方法,Spring 在很多处都采用了这种暗示。这对于我们来说将非常方便。

上面方法的执行大概分为:

  1. 查找候选bean定义

    这里有个非常重要的类:BeanDefiniition (bean 定义),bean 定义描述了一个 bean 实例,属性值,构造函数参数值以及能够为具体实现该 bean 而提供的更多的信息。在这个阶段,仅仅是通过扫描给定的包,而获得的一个非常初始化的 bean定义,后续阶段,都是基于这些数据,进行再次处理,丰富了这个 bean 定义。

  2. 处理候选的bean定义

    2.1 设置 bean 定义的域的元数据

    元数据为描述数据的数据

    这一步根据 bean 定义解析出 bean 的域元数据。实际上就是为了将 bean 定义中的 @Scope 注解(如果存在的话)转换为 ScopeMetadata ,重新存入 bean 定义中。

    	@Bean
    	@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
    	public TestBean testBean() {
    		return new TestBean();
    	}
    

    最初的候选 bean定义中,仅仅存储了注解的信息,但实际上这些注解信息的具体作用,则需要一步一步解析。

    如何解析则 ScopeMetadataResolver 接口的实现类 AnnotationScopeMetadataResolver 来解析:

    	@Override
    	public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
    		ScopeMetadata metadata = new ScopeMetadata();
    		if (definition instanceof AnnotatedBeanDefinition) {
    			AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
    			AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
    					annDef.getMetadata(), this.scopeAnnotationType);
    			if (attributes != null) {
    				metadata.setScopeName(attributes.getString("value"));
    				ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
    				if (proxyMode == ScopedProxyMode.DEFAULT) {
    					proxyMode = this.defaultProxyMode;
    				}
    				metadata.setScopedProxyMode(proxyMode);
    			}
    		}
    		return metadata;
    	}
    

    ScopeMetadataResolver 接口被定义为策略接口(策略模式上线了)。查看该接口的实现类,发现还有一个 Jsr330ScopeMetadataResolver 实现类,该实现类是处理基于 javax.inject.Singletonjavax.inject.Scope 的域注解的(不同于 spring 的规范,其实是一种东西)。


    2.2 获取 bean 的名称

    bean 名称的解析由 BeanNameGenerator 接口的实现类 AnnotationBeanNameGenerator 来完成(该接口依然为策略接口)。bean 名称要么是根据我们所定义注解的 value 值取得,要么就是使用默认的 bean 名称,即通过 Class 转换,例如:“mypackage.MyJdbcDao" -> "myJdbcDao”。

    2.3 预处理 bean 定义

    即为 bean定义是否能够延迟初始化附上默认值等

    2.4 处理 bean 定义的通用注解

    AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
    		if (lazy != null) {
    			abd.setLazyInit(lazy.getBoolean("value"));
    		}
    		else if (abd.getMetadata() != metadata) {
    			lazy = attributesFor(abd.getMetadata(), Lazy.class);
    			if (lazy != null) {
    				abd.setLazyInit(lazy.getBoolean("value"));
    			}
    		}
    
    		if (metadata.isAnnotated(Primary.class.getName())) {
    			abd.setPrimary(true);
    		}
    		AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
    		if (dependsOn != null) {
    			abd.setDependsOn(dependsOn.getStringArray("value"));
    		}
    
    		AnnotationAttributes role = attributesFor(metadata, Role.class);
    		if (role != null) {
    			abd.setRole(role.getNumber("value").intValue());
    		}
    		AnnotationAttributes description = attributesFor(metadata, Description.class);
    		if (description != null) {
    			abd.setDescription(description.getString("value"));
    		}
    

    根据 bean 定义中的通用注解,重新为 bean 定义赋值。

    2.5 bean 定义的域代理模式处理

    2.6 注册 Bean 定义


刷新


	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 为刷新准备此上下文
			prepareRefresh();

			// 告诉子类刷新内部 bean 工厂
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 准备 bean 工厂以供在此上下文中使用
			prepareBeanFactory(beanFactory);

			try {
				// 允许在上下文子类中对 bean 工厂进行后期处理
				postProcessBeanFactory(beanFactory);

				// 调用上下文中注册为bean的工厂处理器。
				invokeBeanFactoryPostProcessors(beanFactory);

				// 注册拦截bean创建的bean处理器。
				registerBeanPostProcessors(beanFactory);

				// 为此上下文初始化消息源。
				initMessageSource();

				// 为此上下文初始化事件多播程序。
				initApplicationEventMulticaster();

				// 初始化特定上下文子类中的其他特殊bean。
				onRefresh();

				// 检查侦听器bean并注册它们
				registerListeners();

				// 实例化所有剩余的(非惰性初始化)单例。
				finishBeanFactoryInitialization(beanFactory);

				// 最后一步:发布相应的事件。
				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();
			}
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值