Spring源码解析之零配置
前言
1.spring在配置时代,都是基于xml配置的,启动一个spring容器,通过ClassPathXmlApplicationContext类的refresh方法,然后解析bean标签,完成bean的注册,解析自定义标签<context:component-scan base-package=cn.com.dq.xml></context:component-scan>对注解的支撑,完成bean的注册
2.spring在零配置时代,基于注解,如何启动容器?如何完成对注解的支撑?
AnnotationConfigApplicationContext
AnnotationConfigApplicationContext是基于注解启动spring容器的类,它有2个构造方法,1.参数是包路径,2.参数是一个类
构造方法参数是包路径
public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); }
this方法
public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
this方法是他的无参构造器,在无参构造器中,干了2件事情,1.new了一个AnnotatedBeanDefinitionReader,2.new 了一个ClassPathBeanDefinitionScanner
- AnnotatedBeanDefinitionReader看着似曾相识,我们在xml启动容器的时候,要做xml解析,创建了一个XmlBeanDefinitionReader
- ClassPathBeanDefinitionScanner也是看着似曾相识,我们在xml启动容器时候,xml自定义标签解析<context:component-scan base-package=cn.com.dq.xml></context:component-scan>标签时候,创建了一个扫描器就是ClassPathBeanDefinitionScanner
scan方法
public void scan(String... basePackages) { Assert.notEmpty(basePackages, At least one base package must be specified); this.scanner.scan(basePackages); } 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); }
看到上面的代码又是似曾相识,我们又会联想到xml的方式
我们看xml方式下component-scan扫描的源码
public BeanDefinition parse(Element element, ParserContext parserContext) { String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // Actually scan for bean definitions and register them. ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }
我们可以看到doScan是同一个方法,因为他俩的扫描器是同一个类
我们在看xml方式下组件注册registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
最终调到的是AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
而基于注解的方式小组件注册AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);跟下去调的是
registerAnnotationConfigProcessors(registry, null);发现又是同一个方法,只是基于注解的第二个参数null而已
refresh方法
发现与xml方式的又是同一个方法,因为AnnotationConfigApplicationContext与ClassPathXmlApplicationContext的父类是AbstractApplicationContext,refresh方法是AbstractApplicationContext里面的方法
构造方法参数是类
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); register(annotatedClasses); refresh(); }
他与构造器参数是包路径唯一不同的是register方法,我们来看register方法
register
void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } abd.setInstanceSupplier(instanceSupplier); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); if (qualifiers != null) { for (Class<? extends Annotation> qualifier : qualifiers) { if (Primary.class == qualifier) { abd.setPrimary(true); } else if (Lazy.class == qualifier) { abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } for (BeanDefinitionCustomizer customizer : definitionCustomizers) { customizer.customize(abd); } BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
1.传入的参数class封装成beanDefinition
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
2.判断是否存在@Condition注解,如果有,会判断该类上面是否有@Configuration,@Component,@ComponentScan,@Import,@ImportResource,如果都没有,然后判断方法上是否有@Bean注解
对于有其中任何一个注解,代码就不会往下面执行,为什么?因为@Condition是会进行条件判断。springboot中会大量运用该注解,我们这里是没有这个注解的,代码继续往下
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } if (phase == null) { if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } public static boolean isConfigurationCandidate(AnnotationMetadata metadata) { return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata)); } public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) { return metadata.isAnnotated(Configuration.class.getName()); } public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) { // Do not consider an interface or an annotation... if (metadata.isInterface()) { return false; } // Any of the typical annotations found? for (String indicator : candidateIndicators) { if (metadata.isAnnotated(indicator)) { return true; } } // Finally, let's look for @Bean methods... try { return metadata.hasAnnotatedMethods(Bean.class.getName()); } catch (Throwable ex) { if (logger.isDebugEnabled()) { logger.debug(Failed to introspect @Bean methods on class [ + metadata.getClassName() + ]: + ex); } return false; } }
3.接着就是对beanDefinition的scope属性赋值,接着执行了这行代码
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);,点进去看
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { //对@Lazy注解支持 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); } //对@DependsOn注解支持 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)); } }
发现和xml的又一样,就是对于一些注解的支撑,然后填充beanDefinition中的属性,包含了@Lazy,@DependsOn等注解的支撑。
4.接着就是这行代码BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);看方法名是注册bean
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); //完成BeanDefinition的注册 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); //建立别名和 id的映射,这样就可以根据别名获取到id // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
上述代码又回到了xml注册bean的方法,构造方法参数是类是,是将指定的类注册到spring容器中,并实例化。
总结
基于xml和基于注解bean的注册的相同与不同
不同点:
1.基于xml创建的解析器是XmlBeanDefinitionReader,而基于注解的是AnnotatedBeanDefinitionReader
2.基于xml他是解析component-scan标签创建扫描器,而基于注解的是直接new的扫描器
相同点:
1.他们创建的扫描器都是同一个,扫描注解,注册组件流程是一模一样。