J2EE-028 Spring容器源码解析(三)this().Scanner构建过程和scan构建

前言

前面2章我们梳理了AnnotatedBeanDefinitionReader的构造过程
今天我们来梳理ClassPathBeanDefinitionScanner的构造过程


源码解析

我们首先顺着ClassPathBeanDefinitionScanner这个类的构造函数看

  • 首先看有没有父类,有父类的话看父类的构造函数
public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) {
        this.logger = LogFactory.getLog(this.getClass());
        this.resourcePattern = "**/*.class";
        this.includeFilters = new LinkedList();
        this.excludeFilters = new LinkedList();
        if (useDefaultFilters) {
            this.registerDefaultFilters();
        }

        this.setEnvironment(environment);
        this.setResourceLoader((ResourceLoader)null);
    }

第一个遇见是日志,第二个语句是加载的资源路径,第三个是创建List容器,第四个if我们传入的是true,默认加载配置文件,

我们调转到registerDefaultFilters这个方法中,发现了这样的一句话

 this.includeFilters.add(new AnnotationTypeFilter(Component.class));

这个是默认加载所有包含了Component这个注解的类,意味着我们的service,controller本质上都是Component
这里是如何实现找到根部注解Component
用递归。这里需要排除元注解@Doument避免出现死循环。

加载完成后注入之前this中加载的环境对象

this.setEnvironment(environment);

下一步装载资源对象,我们走进去看一看

 this.setResourceLoader(resourceLoader);
public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
        this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader());
    }
  • 第一句:提供了一个ResourcePatternUtils工具类,根据资源类型对象获得一个资源解析器对象
  • 第二句:根据资源类型构建一个元数据工厂,我们进入进去看看
 public CachingMetadataReaderFactory(@Nullable ResourceLoader resourceLoader) {
        super(resourceLoader);
        if (resourceLoader instanceof DefaultResourceLoader) {
            this.metadataReaderCache = ((DefaultResourceLoader)resourceLoader).getResourceCache(MetadataReader.class);
        } else {
            this.setCacheLimit(256);
        }
    }

获取元数据工厂的代码=》

  1. super(resourceLoader);看资源有没有实例,没有创建一个默认实例
    2.从缓存里拿一个对象付给Map集合

第三句:得到一个类加载器放置进缓存当中返回一个下标。

到现在为止,ClassPathBeanDefinitionScanner类就构建完成了。

this()总结

this()关键字做的事情
1.构建BeanFctory工厂
2.构建后置处理器
3.将注入的对象转为BeanDefinitionHolder对象
4.在Scanner添加一个Compont过滤器

this()图解

在这里插入图片描述
在这里插入图片描述

Scan()构建

        this();
        this.scan(basePackages);
        this.refresh();

解析scan方法

 public int scan(String... basePackages) {
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        this.doScan(basePackages);
        if (this.includeAnnotationConfig) {
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }
        return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
    }

第一行代码是获得Bean数量
第二行代码我们走进去看看

 public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        return this.componentsIndex != null && this.indexSupportsIncludeFilters() ? this.addCandidateComponentsFromIndex(this.componentsIndex, basePackage) : this.scanCandidateComponents(basePackage);
    }

这里我们没有实例化出来componentsIndex 对象因此走的是scanCandidateComponents方法
findCandidateComponents>scanCandidateComponents

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
//构建集合
        LinkedHashSet candidates = new LinkedHashSet();
        try {
        //凭借扫描包的路径
        //classpath*:E:\J2EE-CSDN\Spring-Source code\Spring-SourceCode01\target\classes/**/*.class ,找到该路径下面所有.class文件
        //这里resolveBasePackage调用的是刚刚初始化ApplicationContext强转为的RsourceLoader对象
            String packageSearchPath = "classpath*:" + this.resolveBasePackage(basePackage) + '/' + this.resourcePattern;
            Resource[] resources = this.getResourcePatternResolver().getResources(packageSearchPath);
            boolean traceEnabled = this.logger.isTraceEnabled();
            boolean debugEnabled = this.logger.isDebugEnabled();
            Resource[] var7 = resources;
            int var8 = resources.length;

            for(int var9 = 0; var9 < var8; ++var9) {
                Resource resource = var7[var9];
                if (traceEnabled) {
                    this.logger.trace("Scanning " + resource);
                }

                if (resource.isReadable()) {
                    try {
                    //拿到元数据
                        MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(resource);
                        //判断是否有Component注解
                        if (this.isCandidateComponent(metadataReader)) {
                        //创建Bean
                            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                            //设置进Bean定义对象
                            sbd.setSource(resource);
                            if (this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) {
                                if (debugEnabled) {
                                    this.logger.debug("Identified candidate component class: " + resource);
                                }

                                candidates.add(sbd);
                            } else if (debugEnabled) {
                                this.logger.debug("Ignored because not a concrete top-level class: " + resource);
                            }
                        } else if (traceEnabled) {
                            this.logger.trace("Ignored because not matching any filter: " + resource);
                        }
                    } catch (Throwable var13) {
                        throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, var13);
                    }
                } else if (traceEnabled) {
                    this.logger.trace("Ignored because not readable: " + resource);
                }
            }

            return candidates;
        } catch (IOException var14) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", var14);
        }
    }

this.getResourcePatternResolver().getResources(packageSearchPath);对这个详解

  public Resource[] getResources(String locationPattern) throws IOException {
        Assert.notNull(locationPattern, "Location pattern must not be null");
        if (locationPattern.startsWith("classpath*:")) {
            return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));
        } else {
            int prefixEnd = locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(58) + 1;
            return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};
        }
    }

这里是递归我们把程序跑起来看看
第一次进判断否包含classpath*:在这里插入图片描述
判断是否包含满足含*条件
this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length()))

这里自然满足走进了findPathMatchingResources构建资源容器代码
然后拿到头尾字段名称
在这里插入图片描述
根据拿到头尾名称判断是否是jar包,是不是vfs文件
如果不是则调用doFindPathMatchingJarResources在这里插入图片描述
doFindPathMatchingJarResources中是一个返回绝对路径的方法和调用doFindMatchingFileSystemResources方法

这个方法递归加载所有文件

   protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
        if (logger.isTraceEnabled()) {
            logger.trace("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
        }
        Set<File> matchingFiles = this.retrieveMatchingFiles(rootDir, subPattern);
        Set<Resource> result = new LinkedHashSet(matchingFiles.size());
        Iterator var5 = matchingFiles.iterator();
        while(var5.hasNext()) {
            File file = (File)var5.next();
            result.add(new FileSystemResource(file));
        }
        return result;
    }

在这里插入图片描述
最后将资源对象返回回去在这里插入图片描述
梳理完getResources获得资源代码后我们再
回过头我们去梳理下扫描的这个核心代码scanCandidateComponents
在这里插入图片描述
我们再看下一步 this.getMetadataReaderFactory().getMetadataReader(resource);元数据梳理的梳理。

首先.getMetadataReaderFactory()从缓存拿了一个了一个元数据工厂的时候会调用构造函数

 public CachingMetadataReaderFactory(@Nullable ClassLoader classLoader) {
        super(classLoader);
        this.setCacheLimit(256);
    }

父类构造了一个资源加载对象,加载元数据需要加载类帮助吧。
设置了一下最大值

  public SimpleMetadataReaderFactory(@Nullable ClassLoader classLoader) {
        this.resourceLoader = classLoader != null ? new DefaultResourceLoader(classLoader) : new DefaultResourceLoader();
    }

.getMetadataReaderFactory()从缓存拿了一个了一个元数据工厂

 public final MetadataReaderFactory getMetadataReaderFactory() {
        if (this.metadataReaderFactory == null) {
            this.metadataReaderFactory = new CachingMetadataReaderFactory();
        }
        return this.metadataReaderFactory;
    }

获得metadataReaderCacheMap走第一个分叉,判断是否含有这个资源对象没有则构建一个对象super.getMetadataReader(resource);

 public MetadataReader getMetadataReader(Resource resource) throws IOException {
        if (this.metadataReaderCache instanceof ConcurrentMap) {
            MetadataReader metadataReader = (MetadataReader)this.metadataReaderCache.get(resource);
            if (metadataReader == null) {
                metadataReader = super.getMetadataReader(resource);
                this.metadataReaderCache.put(resource, metadataReader);
            }
            return metadataReader;
        } else if (this.metadataReaderCache != null) {
            synchronized(this.metadataReaderCache) {
                MetadataReader metadataReader = (MetadataReader)this.metadataReaderCache.get(resource);
                if (metadataReader == null) {
                    metadataReader = super.getMetadataReader(resource);
                    this.metadataReaderCache.put(resource, metadataReader);
                }
                return metadataReader;
            }
        } else {
            return super.getMetadataReader(resource);
        }
    }

super.getMetadataReader(resource);进行字节码的解析
在这里插入图片描述
是否是内部类,是否是运行时的编码
在这里插入图片描述

到现在为止doScan里的方法 Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);就基本解析完了
我们返回到doScan方法中

 protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
        String[] var3 = basePackages;
        int var4 = basePackages.length;
        for(int var5 = 0; var5 < var4; ++var5) {
            String basePackage = var3[var5];
            //扫描加载将字节码文件转为 Set<BeanDefinition>对象
            Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
            Iterator var8 = candidates.iterator();
            while(var8.hasNext()) {
                BeanDefinition candidate = (BeanDefinition)var8.next();
                //是咧对象拿到默认的作用域(静态属性)
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
               //设置scope的作用域
                candidate.setScope(scopeMetadata.getScopeName());
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
                }
                if (this.checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    this.registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }

详看this. .resolveScopeMetadata(candidate);其实是new了一个scopeMetadata对象里面静态字段为设置为单列

 public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
        ScopeMetadata metadata = new ScopeMetadata();//这句
        if (definition instanceof AnnotatedBeanDefinition) {
        //转为AnnotatedBeanDefinition 对象,提供.getMetadata()方法,
            AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition)definition;
            //将元数据为Key,Socpe为value转为linkHashMap<key,object>对象
            AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
            if (attributes != null) {
                metadata.setScopeName(attributes.getString("value"));
                ScopedProxyMode proxyMode = (ScopedProxyMode)attributes.getEnum("proxyMode");
                if (proxyMode == ScopedProxyMode.DEFAULT) {
                    proxyMode = this.defaultProxyMode;
                }

                metadata.setScopedProxyMode(proxyMode);
            }
        }

在这里插入图片描述
看看我们候选bean的scope已经变为singleton
在这里插入图片描述
在设置好scope特性后我们看看如何拿到BeanName的。
this.beanNameGenerator.generateBeanName(candidate, this.registry);
首先看beanNameGenerator,发现它等于一个单列对象AnnotationBeanNameGenerator.INSTANCE

我们找到AnnotationBeanNameGenerator中的generateBeanName方法
进去后发现获得一个注解的元数据,再重中获取元数据的特性个数

 AnnotationMetadata amd = annotatedDef.getMetadata();
        Set<String> types = amd.getAnnotationTypes();

遍历个数,传入实例和属性获得当前特性对象

AnnotationAttributes attributes AnnotationConfigUtils.attributesFor(amd, type)

拿到所有注解类型

  Set<String> metaTypes = (Set)this.metaAnnotationTypesCache.computeIfAbsent(type, (key) -> {
                    Set<String> result = amd.getMetaAnnotationTypes(key);
                    return result.isEmpty() ? Collections.emptySet() : result;
                });

this.isStereotypeWithNameValue(type, metaTypes, attributes)判断当前类型是否是Compont注解类型,是否包含value属性,如果包含则BeanNameValue的值,没有则返回一个空null

  if (this.isStereotypeWithNameValue(type, metaTypes, attributes)) {
                    Object value = attributes.get("value");
                    if (value instanceof String) {
                        String strVal = (String)value;
                        if (StringUtils.hasLength(strVal)) {
                            if (beanName != null && !strVal.equals(beanName)) {
                                throw new IllegalStateException("Stereotype annotations suggest inconsistent component names: '" + beanName + "' versus '" + strVal + "'");
                            }
                            beanName = strVal;
                        }
                    }
                }

如果返回为空则执行return this.buildDefaultBeanName(definition, registry);兜底方案默认首字母小写设置BeanName

图解

this()和Scan()图解
在这里插入图片描述

总结:

这里面有很多方法我没有写上去因为太过臃肿了,我把核心代码的解读和核心条件判断写了上去,其他的非核心没写,但是各位下去如果试着解读一下也能解读个大概意思出来。
其实读源码最重要的是猜想和验证,不然对着全是ABC,试着去猜想你会怎么做后读起来很快

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值