前面我们了解了通过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管理。
用@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
这个用的比较多就不一一介绍了。
在下面例子中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文件
MyConditional会实现Condition接口中的matches方法,满足什么条件才能装配bean
@Configuration
public class AppConfig {
@Bean
@Conditional(value = MyConditional.class)
public User user(){
return new User();
}
}
@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方式启动一样。