Spring源码分析--注解方式启动容器

四、注解方式

前面我们了解了通过XML方式启动容器,这次我们看看注解是怎么启动容器的。

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
HelloService helloService = (HelloService) beanFactory.getBean("helloService");
helloService.sayHello();

我们进入AnnotationConfigApplicationContext构造方法中

public AnnotationConfigApplicationContext(Class... annotatedClasses) {

		this();
		
		register(annotatedClasses);
		refresh();// 启动容器
	}

首先会去调用自己的无参构造器this()

public AnnotationConfigApplicationContext() {
		// 注册spring 自带的bean  5个
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

跟踪进去我们可以看到注册了5个后置处理器,比较重要的有ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor,后置处理器会在bean初始化的时候调用,我们原先讲过了。

if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			// 注册 ConfigurationClassPostProcessor
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			// 注册 AutowiredAnnotationBeanPostProcessor
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

接下来,我们就会去注册我们传进来的配置类register(annotatedClasses),也就是把AppConfig这个bean注册。

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		// 根据beanName注册 (包括 id  name)
		String beanName = definitionHolder.getBeanName();
		// 注册beanDefiniton
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

refresh就是去启动容器,启动容器步骤和XML方式差不多,只是在创建ConfigurableListableBeanFactory有些区别。

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()

原先我们提过,注解方式和XML方式调用的refreshBeanFactory不同

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

		refreshBeanFactory();
		return getBeanFactory();
	}

注解启动是调用GenericApplicationContext.refreshBeanFactory方法

protected final void refreshBeanFactory() throws IllegalStateException {
		if (!this.refreshed.compareAndSet(false, true)) {
			throw new IllegalStateException(
					"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
		}
		this.beanFactory.setSerializationId(getId());
	}

在这里只是去给容器设置一个序列化过的id,那beanFactory是在什么时候创建的?原来在我们调用this()还会去调用父类的无参构造方法,在这里创建了beanFactory。

public GenericApplicationContext() {
		this.beanFactory = new DefaultListableBeanFactory();
	}

接下来我们看去执行BeanFactoryPostProcessor后置处理器

invokeBeanFactoryPostProcessors(beanFactory)

之前注册了ConfigurationClassPostProcessor那么就会在这里调用,去执行postProcessBeanDefinitionRegistry方法

private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection postProcessors, BeanDefinitionRegistry registry) {

		// ConfigurationClassPostProcessor
		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessBeanDefinitionRegistry(registry);
		}
	}

在postProcessBeanDefinitionRegistry内就会去解析配置类上的注解,我们先了解哪些类修饰的会交给Spring管理。

1、@Configuration+@Bean

用@Configuration注释类表明其主要目的是作为bean定义的源,@Configuration类允许通过调用同一类中的其他@Bean方法来定义bean之间的依赖关系。 

@Configuration 
public class AppConfig {
@Bean      
public MyService myService() {        
return new MyService();    
 }
 @Bean    
public UserService userService(){        
return new UserService(myService()); 
 }         
}

配置了@Configuration ,打印结果显示 UserService 依赖的对象myService和从容器中获取的对象 myService;
没有配置@Configuration,UserService依赖的对象myService 不是从容器中获取的,只是一个普通的对象。

因为@Configuration修饰的AppConfig是一个cglib的代理对象,所以@Configuration 保证了配置类的内部方法之间依赖调用时都从容器中获取bean

 

2、@ComponentScan+@Component, @Repository,@Service, @Controller 

这个用的比较多就不一一介绍了。

3、@Import

在下面例子中MyImportBeanDefinitionRegistrar:

如果实现了ImportSelector接口,实例化会并调用其selectImports方法,可以根据这个方法来规定bean只能根据类型来装配
如果实现了DeferredImportSelector,实例化并且调用其selectImports方法,会晚于前面一个的调用,
如果实现了ImportBeanDefinitionRegistrar接口,实例化并且调用其registerBeanDefinitions方法,可以修改注册bean的信息。
如果都没实现就只会实例化类

@Configuration 
@Import(value = MyImportBeanDefinitionRegistrar.class)
public class AppConfig {
 @Bean   
public User user(){      
return new User();   
} 
}

在@MapperScan注解中就使用了@import,所以可以扫描解析mapper文件

4、@Conditional

MyConditional会实现Condition接口中的matches方法,满足什么条件才能装配bean

@Configuration 
public class AppConfig {
 @Bean 
 @Conditional(value = MyConditional.class) 
public User user(){      
return new User();   
} 
}

5、@ImportResource

@Configuration
@ImportResource("spring.xml")
public class AppConfig {

}

负责引入xml配置,通过@ImportResource引入外部资源来注册实例化bean,参数指定就是配置类路径

 

首先进入会判断配置类是full还是lite类型,也就是有无被@Configuration标注

for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			// 第一次进 appConfig 没有设置配置类属性是full还是lite
			// registry.getBeanDefinition("appConfig").getAttribute("org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass")
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			// 设置配置类的属性是full还是lite
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

        // Return immediately if no @Configuration classes were found
		// 如果没有找到@Configuration类,则立即返回
		if (configCandidates.isEmpty()) {
			return;
		}

在ConfigurationClassUtils.checkConfigurationClassCandidate方法中会去判断配置类有哪些注解

// 判断是否有配置@Configuration
		if (isFullConfigurationCandidate(metadata)) {
			// 设置org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass为full
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		// 判断是否配置 @Component,@ComponentScan,@Import,@ImportResource 和方法配置了@Bean
		else if (isLiteConfigurationCandidate(metadata)) {
			// 设置org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass为lite
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}

接下来就会去解析配置类,后面解析过程在前面XML启动中讲述过。 

// 解析配置类  @ComponentScan (bean注册到容器) @Import @ImportResource @Bean(后面三个不会注册到bean容器)
parser.parse(candidates);
parser.validate();

在这里我们看@ComponentScan注解怎么具体扫描包把相应的bean注册的,首先会拿到所有的ComponentScans

Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				// @ComponentScan扫描bean,返回@Component修饰的BeanDefinitionHolder 集合,并且
				// 会将bean注册到容器
				Set scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

进入this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName())会去调用

scanner.doScan(StringUtils.toStringArray(basePackages))

也就是去扫描我们设置的包的路径

protected Set doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			// 找到@Component修饰的类的beanDefiniton集合
			Set 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);
					// 注册bean
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

调用findCandidateComponents方法去判断包下面哪些类被@Component修饰并添加进来,在后续注册进去

if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					// 注册bean
					registerBeanDefinition(definitionHolder, this.registry);
}

最后处理@Import注解的,如果是实现ImportSelector接口的类不会在此时注册到bean中,而在最开始的processConfigBeanDefinitions调用this.reader.loadBeanDefinitions(configClasses)注册,还有@ImportResource("spring.xml")也会把配置文件里的bean注册进去。

// 处理@Import   implements ImportSelector  并不会将bean注册到容器
processImports(configClass, sourceClass, getImports(sourceClass), true);

 

在这里就完成所有的bean的注册了,bean的实例化和XML方式启动一样。

你可能感兴趣的:(spring)