每篇一句
年少有为,资产25亿
相关阅读
【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(一)
【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(二)
【小家Spring】AbstractBeanFactory#getBean()、doGetBean完成Bean的初始化、实例化,以及BeanPostProcessor后置处理器源码级详细分析
【小家Spring】AbstractAutowireCapableBeanFactory#populateBean实现Bean的依赖注入(属性赋值)和initializeBean对Bean的初始化
【小家Spring】Spring解析@Configuration注解的处理器:ConfigurationClassPostProcessor(ConfigurationClassParser)
【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析
前言
在分析Spring IOC容器启动流程的时候,在加载Bean定义信息BeanDefinition
的时候,用到了两个非常关键的类:AnnotatedBeanDefinitionReader
和ClassPathBeanDefinitionScanner
。它俩完成对Bean信息的加载。
因此为了更加顺畅的去理解Bean的加载的一个过程,本文主要介绍Spring的这两员大将的一个初始化过程,以及它俩扮演的重要角色
环境准备
因为我们只需要了解Bean的加载,所以只需要启动一个容器就行,并不需要web环境,因此本文用一个相对简单的环境,来进行讲解,如下:
@ComponentScan(value = "com.fsx", excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
//排除掉web容器的配置文件,否则会重复扫描
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {AppConfig.class}),
})
@Configuration
public class RootConfig {
@Bean
public Parent parent() {
return new Parent();
}
}
public static void main(String[] args) {
// 备注:此处只能用RootConfig,而不能AppConfig(启动报错),因为它要web容器支持,比如Tomcat
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
System.out.println(applicationContext.getBean(Parent.class)); //com.fsx.bean.Parent@639c2c1d
}
环境准备好了,启动之后也能正常打印出Bean的信息。因此接下来,就是要去分析源码,看看这两大工具起的作用
IOC容器加载Bean定义信息分析
AnnotationConfigApplicationContext
(spring-context包下)的继承图谱如下:
需要注意的是,我们在Tomcat等web环境下的容器类为:AnnotationConfigWebApplicationContext,它在spring-web包下
Spring容器里通过BeanDefinition
对象来表示Bean,BeanDefinition
描述了Bean的配置信息。
而BeanDefinitionRegistry接口提供了向容器注册,删除,获取BeanDefinition对象的方法。
简单来说,BeanDefinitionRegistry
可以用来管理BeanDefinition
,所以理解AnnotationConfigApplicationContext
很关键,它是spring加载bean,管理bean的最重要的类。
源码跟踪:
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
// 把该配置类(们)注册进来
register(annotatedClasses);
// 容器启动核心方法
refresh();
}
this()如下:
//AnnotatedBeanDefinitionReader是一个读取注解的Bean读取器,这里将this传了进去。
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
//从上面这个构造函数可以顺便提一句:如果你仅仅是这样ApplicationContext applicationContext = new AnnotationConfigApplicationContext()
// 容器是不会启动的(也就是不会执行refresh()的),这时候需要自己之后再手动启动容器
进而再看看register()
方法:
public void register(Class<?>... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.reader.register(annotatedClasses);
}
public void register(Class<?>... annotatedClasses) {
for (Class<?> annotatedClass : annotatedClasses) {
registerBean(annotatedClass);
}
}
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);
//abd.getMetadata() 元数据包括注解信息、是否内部类、类Class基本信息等等
// 此处由conditionEvaluator#shouldSkip去过滤,此Class是否是配置类。
// 大体逻辑为:必须有@Configuration修饰。然后解析一些Condition注解,看是否排除~
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(instanceSupplier);
// 解析Scope
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
// 得到Bean的名称 一般为首字母小写(此处为AnnotationBeanNameGenerator)
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
// 设定一些注解默认值,如lazy、Primary等等
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
// 解析qualifiers,若有此注解 则primary都成为true了
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);
}
// 下面位解析Scope是否需要代理,最后把这个Bean注册进去
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
AnnotatedBeanDefinitionReader初始化,构造器如下
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
//ConditionEvaluator完成条件注解的判断,在后面的Spring Boot中有大量的应用
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
//这句会把一些自动注解处理器加入到AnnotationConfigApplicationContext下的BeanFactory的BeanDefinitions中 具体见下面
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
这里将AnnotationConfigApplicationContext注册为管理BeanDefinition的BeanDefinitionRegistry,也就是说,spring中bean的管理完全交给了AnnotationConfigApplicationContext
AnnotationConfigUtils#registerAnnotationConfigProcessors()
我们要用一些注解比如:@Autowired
/@Required
/@Resource
都依赖于各种各样的BeanPostProcessor
来解析(AutowiredAnnotation、RequiredAnnotation、CommonAnnotationBeanPostProcessor等等)
但是向这种非常常用的,让调用者自己去申明,显然使用起来就过重了。所以Spring为我们提供了一种极为方便注册这些BeanPostProcessor的方式(若是xml方式,配置<context:annotation- config/>
,若是全注解驱动的ApplicationContext,就默认会执行)
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
// 把我们的beanFactory从registry里解析出来
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
// 相当于如果没有这个AutowireCandidateResolver,就给设置一份ContextAnnotationAutowireCandidateResolver
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
//这里初始长度放4 是因为大多数情况下,我们只会注册4个BeanPostProcessor 如下(不多说了)
// BeanDefinitionHolder解释:持有name和aliases,为注册做准备
// Spring 4.2之后这个改成6我觉得更准确点
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(4);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
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);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
// 支持JSR-250的一些注解:@Resource、@PostConstruct、@PreDestroy等
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
// 若导入了对JPA的支持,那就注册JPA相关注解的处理器
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 下面两个类,是Spring4.2之后加入进来的,为了更好的使用Spring的事件而提供支持
// 支持了@EventListener注解,我们可以通过此注解更方便的监听事件了(Spring4.2之后)
// 具体这个Processor和ListenerFactory怎么起作用的,且听事件专题分解
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
我们发现:内部定义的class都是带internal的
ConfigurationClassPostProcessor
是一个BeanFactoryPostProcessor
和BeanDefinitionRegistryPostProcessor
处理器,BeanDefinitionRegistryPostProcessor
的处理方法能处理@Configuration
等注解。ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()
方法内部处理@Configuration
,@Import
,@ImportResource
和类内部的@Bean
。
ConfigurationClassPostProcessor类继承了BeanDefinitionRegistryPostProcessor。BeanDefinitionRegistryPostProcessor类继承了BeanFactoryPostProcessor。
通过BeanDefinitionRegistryPostProcessor
可以创建一个特别后置处理器来将BeanDefinition
添加到BeanDefinitionRegistry
中。它和BeanPostProcessor不同,BeanPostProcessor
只是在Bean初始化的时候有个钩子让我们加入一些自定义操作;而BeanDefinitionRegistryPostProcessor
可以让我们在BeanDefinition
中添加一些自定义操作。在Mybatis与Spring的整合中,就利用到了BeanDefinitionRegistryPostProcessor来对Mapper的BeanDefinition进行了后置的自定义处理。
AutowiredAnnotationBeanPostProcessor
是用来处理@Autowired
注解和@Value
注解的- RequiredAnnotationBeanPostProcessor这是用来处理@Required注解
- CommonAnnotationBeanPostProcessor提供对JSR-250规范注解的支持@javax.annotation.Resource、@javax.annotation.PostConstruct和@javax.annotation.PreDestroy等的支持。
EventListenerMethodProcessor
提供@PersistenceContext
的支持。EventListenerMethodProcessor
提供@ EventListener
的支持。@ EventListener
实在spring4.2之后出现的,可以在一个Bean的方法上使用@EventListener
注解来自动注册一个ApplicationListener
。
到此AnnotatedBeanDefinitionReader
初始化完毕。
总结一下,AnnotatedBeanDefinitionReade
读取器用来加载class类型的配置,在它初始化的时候,会预先注册一些BeanPostProcessor
和BeanFactoryPostProcessor
,这些处理器会在接下来的spring初始化流程中被调用
ClassPathBeanDefinitionScanner的初始化:
跟踪构造函数的重载,最终都到这里:
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
//useDefaultFilters为true,所以此处一般都会执行
// 当然我们也可以设置为false,比如@ComponentScan里就可以设置为false,只扫描指定的注解/类等等
if (useDefaultFilters) {
registerDefaultFilters();
}
// 设置环境
setEnvironment(environment);
// 详情如下: 这里resourceLoader传值,还是我们的工厂。否则为null
setResourceLoader(resourceLoader);
}
registerDefaultFilters()
protected void registerDefaultFilters() {
// 这里需要注意,默认情况下都是添加了@Component这个注解的
//(相当于@Service @Controller @Respository等都会扫描,因为这些注解都属于@Component) 另外@Configuration也属于哦
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
//下面两个 是兼容JSR-250的@ManagedBean和330的@Named注解
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
} catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
} catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
// 所以,如果你想Spring连你自定义的注解都扫描,自己实现一个AnnotationTypeFilter就可以啦
}
ClassPathBeanDefinitionScanner
继承于ClassPathScanningCandidateComponentProvider
,它内部维护有两个final类型的List:这两个对象在执行本类的scanCandidateComponents()
方法时就会起作用。
//includeFilters中的就是满足过滤规则的
private final List<TypeFilter> includeFilters = new LinkedList<>();
//excludeFilters则是不满足过滤规则的
private final List<TypeFilter> excludeFilters = new LinkedList<>();
ClassPathScanningCandidateComponentProvider#setResourceLoader
为ResourcePatternResolver,MetadataReaderFactory和CandidateComponentsIndex
设定初始值。
@Override
public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
// Spring5以后才有这句,优化了bean扫描
this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader());
}
备注:若要使用Spring5 的这个功能,需要添加如下包。这样子当工程重新编译的时候(编译期),会在自动生成META-INF/spring-components。然后我们在启动用@ComponentScan扫描时,直接读取这个文件即可,极大的提高了Spring启动的速度。而这期间,可以使用Spring5.0最新提供的注解@Indexed来配合使用。
需要注意的是:这种方式也是存在缺陷的,具体缺陷请参考官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-scanning-index
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>5.0.7.RELEASE</version>
<optional>true</optional>
</dependency>
然后在运行后会生成一个 META-INF/spring.components 的文件,之后只要运行工程发现这个文件都会直接使用他。可以通过环境变量或工程根目录的spring.properties中设置spring.index.ignore=ture来禁用这个功能 (这个功能如果没有什么明确的需求,慎重使用,会提高工程的管理成本。)
ResourcePatternResolver
是一个接口,继承了ResourceLoader
,可以用来获取Resource 实例。返回的实例为PathMatchingResourcePatternResolver
类型MetadataReaderFactory
用于解析资源信息对应的元数据,这里返回的实例为:CachingMetadataReaderFactory
,带有缓存的- CandidateComponentsIndexLoader.loadIndex () 方法是
spring5.0
以后加入的新特性,Spring Framework 5 改进了扫描和识别组件的方法,使大型项目的性能得到提升。(具体是通过编译器完成扫描,并且往本地写索引,然后启动的时候再去扫描索引即可的思路)
至此,ClassPathBeanDefinitionScanner初始化完毕,总结一下:
ClassPathBeanDefinitionScanner
是一个扫描指定类路径中注解Bean定义的扫描器,在它初始化的时候,会初始化一些需要被扫描的注解,初始化用于加载包下的资源的Loader
。
AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner的初始化是spring上线文初始化的起点,很多预加载的类会在spring接下来的初始化中发挥重要作用
包扫描的启动方式
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.fsx.config");
System.out.println(applicationContext.getBean(Parent.class)); //com.fsx.bean.Parent@639c2c1d
}
启动函数为:
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
// 核心在这个scan方法里,见下面
this.scanner.scan(basePackages);
}
下面就是重点看看doScan()方法
:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
// 装载扫描到的Bean
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 这个方法是最重点,把扫描到的Bean就放进来了(比如此处只有RootConfig一个Bean定义,是个配置类)
// 这个是重点,会把该包下面所有的Bean都扫描进去。Spring5和一下的处理方式不一样哦~
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// 拿到Scope元数据:此处为singleton
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 生成Bean的名称,默认为首字母小写。此处为"rootConfig"
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// 此处为扫描的Bean,为ScannedGenericBeanDefinition,所以肯定为true
// 因此进来,执行postProcessBeanDefinition(对Bean定义信息做) 如下详解
// 注意:只是添加些默认的Bean定义信息,并不是执行后置处理器~~~
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
// 显然,此处也是true 也是完善比如Bean上的一些注解信息:比如@Lazy、@Primary、@DependsOn、@Role、@Description @Role注解用于Bean的分类分组,没有太大的作用
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 检查这个Bean 比如
//如果dao包(一般配置的basePakage是这个)下的类是符合mybaits要求的则向spring IOC容器中注册它的BeanDefinition 所以这步检查第三方Bean的时候有必要检查一下
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
//AnnotationConfigUtils类的applyScopedProxyMode方法根据注解Bean定义类中配置的作用域@Scope注解的值,为Bean定义应用相应的代理模式,主要是在Spring面向切面编程(AOP)中使用
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注意 注意 注意:这里已经吧Bean注册进去工厂了,所有doScan()方法不接收返回值,也是没有任何问题的。。。。
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
ClassPathScanningCandidateComponentProvider#findCandidateComponents
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
// 上面说过了CandidateComponentsIndex是Spring5提供的优化扫描的功能
// 显然这里编译器我们没有写META-INF/spring.components索引文件,所以此处不会执行Spring5 的扫描方式,所以我暂时不看了(超大型项目才会使用Spring5的方式)
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
} else {
// Spring 5之前的方式(绝大多数情况下,都是此方式)
return scanCandidateComponents(basePackage);
}
}
scanCandidateComponents:根据basePackage
扫描候选的组件们(非常重要)
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// 1.根据指定包名 生成包搜索路径
//通过观察resolveBasePackage()方法的实现, 我们可以在设置basePackage时, 使用形如${}的占位符, Spring会在这里进行替换 只要在Enviroment里面就行
// 本次值为:classpath*:com/fsx/config/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//2. 资源加载器 加载搜索路径下的 所有class 转换为 Resource[]
// 拿着上面的路径,就可以getResources获取出所有的.class类,这个很强大~~~
// 真正干事的为:PathMatchingResourcePatternResolver#getResources方法
// 此处能扫描到两个类AppConfig(普通类,没任何注解标注)和RootConfig。所以接下里就是要解析类上的注解,以及过滤掉不是候选的类(比如AppConfig)
// 注意:这里会拿到类路径下(不包含jar包内的)的所有的.class文件 可能有上百个,然后后面再交给后面进行筛选~~~~~~~~~~~~~~~~(这个方法,其实我们也可以使用)
// 当然和getResourcePatternResolver和这个模版有关
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
// 记录日志(下面我把打印日志地方都删除)
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
// 接下来的这个for循环:就是把一个个的resource组装成
for (Resource resource : resources) {
//文件必须可读 否则直接返回空了
if (resource.isReadable()) {
try {
//读取类的 注解信息 和 类信息 ,两大信息储存到 MetadataReader
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 根据TypeFilter过滤排除组件。因为AppConfig没有标准@Component或者子注解,所以肯定不属于候选组件 返回false
// 注意:这里一般(默认处理的情况下)标注了默认注解的才会true,什么叫默认注解呢?就是@Component或者派生注解。还有javax....的,这里省略啦
if (isCandidateComponent(metadataReader)) {
//把符合条件的 类转换成 BeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
// 再次判断 如果是实体类 返回true,如果是抽象类,但是抽象方法 被 @Lookup 注解注释返回true (注意 这个和上面那个是重载的方法)
// 这和上面是个重载方法 个人觉得旨在处理循环引用以及@Lookup上
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
}
}
}
}
}
return candidates;
}
// 备注:此时ComponentScan这个注解还并没有解析
ClassPathBeanDefinitionScanner#postProcessBeanDefinition
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
// 位Bean定义 执行些默认的信息
// BeanDefinitionDefaults是个标准的javaBean,有一些默认值
beanDefinition.applyDefaults(this.beanDefinitionDefaults);
// 自动依赖注入 匹配路径(此处为null,不进来)
if (this.autowireCandidatePatterns != null) {
beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
}
}
AbstractBeanDefinition#applyDefaults:
public void applyDefaults(BeanDefinitionDefaults defaults) {
setLazyInit(defaults.isLazyInit());
setAutowireMode(defaults.getAutowireMode());
setDependencyCheck(defaults.getDependencyCheck());
setInitMethodName(defaults.getInitMethodName());
setEnforceInitMethod(false);
setDestroyMethodName(defaults.getDestroyMethodName());
setEnforceDestroyMethod(false);
}
就这样,扫描的Bean就全部注册完成了(若你的Config比较多的情况下,用扫描的方式更加简单些~)
总结
观看Spring源码这么久,最大的感受就是:组件繁多,但管理有序。它完美的实现了单一职责的设计原则,谁的事情谁去干,这点体现得非常好。只有遵循了这个原则,才能做到无状态和组件化,达到随意装配的效果~
另外,还有一种设计思想就是预加载。什么组件在什么时候加载,什么时候可以预加载进去,都是设计的精妙之处
理解ClassPathBeanDefinitionScanner
的工作原理,可以帮助理解Spring IOC
容器的初始化过程。
同时对理解MyBatis 的 Mapper 扫描 也是有很大的帮助。
因为 MyBatis
的MapperScannerConfigurer
的底层实现也是一个ClassPathBeanDefinitionScanner
的子类。就像我们自定义扫描器那样,自定定义了 过滤器的过滤规则。
这两名大将还提供了比如扫描包的核心方法,这个在讲解具体IOC容器流程的时候再具体分析~~~
关注A哥
Author | A哥(YourBatman) |
---|---|
个人站点 | www.yourbatman.cn |
yourbatman@qq.com | |
微 信 | fsx641385712 |
活跃平台 | ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
公众号 | BAT的乌托邦(ID:BAT-utopia) |
知识星球 | BAT的乌托邦 |
每日文章推荐 | 每日文章推荐 |