在實際開發中,咱們常用Spring的@Component、@Service、@Repository以及 @Controller等註解來實現bean託管給Spring容器管理。Spring是怎麼樣實現的呢?咱們一塊兒跟着源碼看看整個過程吧!java
照舊,先看調用時序圖:app
public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); }
Spring啓動時,會去掃描指定包下的文件。ide
public void scan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); this.scanner.scan(basePackages); }
對應時序圖方法1,ClassPathBeanDefinitionScanner#scan。交給ClassPathBeanDefinitionScanner處理。post
ClassPathBeanDefinitionScanner 初始化時設置了註解過濾器this
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment, @Nullable ResourceLoader resourceLoader) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; if (useDefaultFilters) { // 註冊註解過濾器 registerDefaultFilters(); } setEnvironment(environment); setResourceLoader(resourceLoader); }
protected void registerDefaultFilters() { // 添加Component類型 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); } catch (ClassNotFoundException ex) { } try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false)); } catch (ClassNotFoundException ex) { } }
在includeFilters添加了Component,ManagedBean兩種註解類型。後面用來過濾加載到的class文件是否須要交給Spring容器管理。spa
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { // 掃描包下有Spring Component註解,而且生成BeanDefinition Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { // 設置scope,默認是singleton 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); // 註冊到Spring容器 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
對應時序圖方法2,ClassPathBeanDefinitionScanner#doScan。該方法對包下class文件解析,符合Spring容器管理的類生成BeanDefinition,並註冊到容器中。代理
掃描包下的class文件,把有Component註解的封裝BeanDefinition列表返回。code
public Set<BeanDefinition> findCandidateComponents(String basePackage) { if (this.componentsIndex != null && indexSupportsIncludeFilters()) { return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { return scanCandidateComponents(basePackage); } }
對應時序圖方法3,ClassPathScanningCandidateComponentProvider#findCandidateComponents。component
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { // classpath*:basePackage/**/*.class String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; // 獲取 basePackage 包下的 .class 文件資源 Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); for (Resource resource : resources) { // 判斷是否可讀 if (resource.isReadable()) { try { // 獲取.class文件類信息 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; }
對應時序圖方法4,ClassPathScanningCandidateComponentProvider#scanCandidateComponents。對象
public MetadataReader getMetadataReader(Resource resource) throws IOException { if (this.metadataReaderCache instanceof ConcurrentMap) { // No synchronization necessary... MetadataReader metadataReader = this.metadataReaderCache.get(resource); if (metadataReader == null) { // 獲取.class類元信息 metadataReader = super.getMetadataReader(resource); this.metadataReaderCache.put(resource, metadataReader); } return metadataReader; } else if (this.metadataReaderCache != null) { synchronized (this.metadataReaderCache) { 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); } }
對應時序圖方法5,CachingMetadataReaderFactory#getMetadataReader。 super.getMetadataReader(resource) 調用的是 SimpleMetadataReaderFactory#getMetadataReader。
public MetadataReader getMetadataReader(Resource resource) throws IOException { // 默認是SimpleMetadataReader實例 return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader()); }
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException { // 加載.class文件 InputStream is = new BufferedInputStream(resource.getInputStream()); ClassReader classReader; try { classReader = new ClassReader(is); } catch (IllegalArgumentException ex) { throw new NestedIOException("ASM ClassReader failed to parse class file - " + "probably due to a new Java class file version that isn't supported yet: " + resource, ex); } finally { is.close(); } AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader); // 解析.class元信息 classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; this.classMetadata = visitor; this.resource = resource; }
對應時序圖方法6,SimpleMetadataReader#SimpleMetadataReader。 組裝SimpleMetadataReader。
public void accept( final ClassVisitor classVisitor, final Attribute[] attributePrototypes, final int parsingOptions) { Context context = new Context(); context.attributePrototypes = attributePrototypes; context.parsingOptions = parsingOptions; context.charBuffer = new char[maxStringLength]; ... 省略代碼 // Visit the RuntimeVisibleAnnotations attribute. if (runtimeVisibleAnnotationsOffset != 0) { int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset); int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2; while (numAnnotations-- > 0) { // Parse the type_index field. String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); currentAnnotationOffset += 2; // 這裏面封裝Spring Component註解 currentAnnotationOffset = readElementValues(classVisitor.visitAnnotation(annotationDescriptor,true), currentAnnotationOffset,true,charBuffer); } } ... 省略代碼 }
對應時序圖方法7,ClassReader#accept。該方法把二進制的.class文件解析組裝到AnnotationMetadataReadingVisitor
private int readElementValues( final AnnotationVisitor annotationVisitor, final int annotationOffset, final boolean named, final char[] charBuffer) { ... 省略代碼 if (annotationVisitor != null) { // 主要邏輯還在這裏面 annotationVisitor.visitEnd(); } return currentOffset; }
對應時序圖方法8,ClassReader#readElementValues。
public void visitEnd() { super.visitEnd(); Class<? extends Annotation> annotationClass = this.attributes.annotationType(); if (annotationClass != null) { ... 省略代碼 // 過濾java.lang.annotation包下的註解,及保留Spring註解 if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) { try { // 獲取該類上的全部註解 Annotation[] metaAnnotations = annotationClass.getAnnotations(); if (!ObjectUtils.isEmpty(metaAnnotations)) { Set<Annotation> visited = new LinkedHashSet<>(); for (Annotation metaAnnotation : metaAnnotations) { // 過濾java.lang.annotation包下的註解,及保留Spring註解 recursivelyCollectMetaAnnotations(visited, metaAnnotation); } // 封裝須要的註解 if (!visited.isEmpty()) { Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size()); for (Annotation ann : visited) { metaAnnotationTypeNames.add(ann.annotationType().getName()); } this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); } } } catch (Throwable ex) { } } } }
對應時序圖方法9,AnnotationAttributesReadingVisitor#visitEnd。過濾掉 java.lang.annotation 包下的註解,而後把剩下的註解放到metaAnnotationMap。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }
對應時序圖方法10,ClassPathScanningCandidateComponentProvider#isCandidateComponent。使用前面提過的ClassPathBeanDefinitionScanner初始化時設置的註解類型過濾器,includeFilters 包含ManagedBean和Component類型。
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { if (matchSelf(metadataReader)) { return true; } ... 省略代碼 return false; }
對應時序圖方法11,AbstractTypeHierarchyTraversingFilter#match。
protected boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); return metadata.hasAnnotation(this.annotationType.getName()) || (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); }
對應時序圖方法12,AnnotationTypeFilter#matchSelf。判斷類的metadata中是否包含Component。
總結@Component到Spring bean容器管理過程。第一步,初始化時設置了Component類型過濾器;第二步,根據指定掃描包掃描.class文件,生成Resource對象;第三步、解析.class文件並註解歸類,生成MetadataReader對象;第四步、使用第一步的註解過濾器過濾出有@Component類;第五步、生成BeanDefinition對象;第六步、把BeanDefinition註冊到Spring容器。以上是@Component註解原理,@Service、@Controller和@Repository上都有@Component修飾,因此原理是同樣的。