前言
@Component和@Service都是工作中常用的註解,Spring如何解析?
1.@Component解析流程
找入口
Spring Framework2.0開始,引入可擴展的XML編程機制,該機制要求XML Schema命名空間需要與Handler建立映射關係。
該關係配置在相對於classpath下的/META-INF/spring.handlers中。
如上圖所示 「ContextNamespaceHandler對應 context
分析的入口。」
「找核心方法」
瀏覽ContextNamespaceHandler
在parse中有一個很重要的註釋
❝// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
❞
大意是:「ClassPathBeanDefinitionScanner#doScan是掃描BeanDefinition並註冊的實現」
ClassPathBeanDefinitionScanner 的源碼如下:
protected Set
doScan
(String... basePackages)
{
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set
beanDefinitions =
new LinkedHashSet<>();
for (String basePackage : basePackages) {
//findCandidateComponents 讀資源裝換爲BeanDefinition
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);
registerBeanDefinition(definitionHolder,
this.registry);
}
}
}
return beanDefinitions;
}
上邊的代碼,從方法名,猜測:
「findCandidateComponents:從classPath掃描組件,並轉換爲備選BeanDefinition,也就是要做的解析@Component的核心方法。」
「概要分析」
「findCandidateComponents在其父類ClassPathScanningCandidateComponentProvider 中。」
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
//省略其他代碼
public Set
findCandidateComponents
(String basePackage)
{
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
private Set
scanCandidateComponents
(String basePackage)
{
Set
candidates =
new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) +
'/' +
this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
//省略部分代碼
for (Resource resource : resources) {
//省略部分代碼
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd =
new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
//省略部分代碼
}
}
catch (IOException ex) {
//省略部分代碼 }
return candidates;
}
}
「findCandidateComponents大體思路如下:」
-
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) + '/' + this.resourcePattern; 將package轉化爲ClassLoader類資源搜索路徑packageSearchPath,例如:com.wl.spring.boot轉化爲classpath*:com/wl/spring/boot/**/*.class -
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 加載搜素路徑下的資源。 -
isCandidateComponent 判斷是否是備選組件 -
candidates.add(sbd); 添加到返回結果的list
ClassPathScanningCandidateComponentProvider#isCandidateComponent其源碼如下:
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
//省略部分代碼
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
includeFilters由registerDefaultFilters()設置初始值,有@Component,沒有@Service啊?
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.trace("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) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
Spring如何處理@Service的註解的呢????
「2.查文檔找思路」
查閱官方文檔,下面這話:
https://docs.spring.io/spring/docs/5.0.17.RELEASE/spring-framework-reference/core.html#beans-meta-annotations
❝@Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component
❞
大意如下:
@Component是任何Spring管理的組件的通用原型。@Repository、@Service和@Controller是派生自@Component。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// @Service 派生自@Component
@Component
public @interface Service {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
@Component是@Service的元註解,Spring 大概率,在讀取@Service,也讀取了它的元註解,並將@Service作爲@Component處理。公衆號搜索,[Java學習之道],回覆'福利',3T資料等你來拿!
3. 探尋@Component派生性流程
回顧ClassPathScanningCandidateComponentProvider 中的關鍵的代碼片段如下:
private Set
scanCandidateComponents
(String basePackage)
{
//省略其他代碼
MetadataReader metadataReader
=getMetadataReaderFactory().getMetadataReader(resource);
if(isCandidateComponent(metadataReader)){
//....
}
}
public final MetadataReaderFactory getMetadataReaderFactory() {
if (this.metadataReaderFactory == null) {
this.metadataReaderFactory = new CachingMetadataReaderFactory();
}
return this.metadataReaderFactory;
}
「1. 確定metadataReader」
CachingMetadataReaderFactory繼承自 SimpleMetadataReaderFactory,就是對SimpleMetadataReaderFactory加了一層緩存。
其內部的SimpleMetadataReaderFactory#getMetadataReader 爲:
public class SimpleMetadataReaderFactory implements MetadataReaderFactory {
@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}
}
這裏可以看出
「MetadataReader metadataReader =new SimpleMetadataReader(...);」
「2.查看match方法找重點方法」
AnnotationTypeFilter#matchself方法如下:
@Override
protected boolean matchSelf(MetadataReader metadataReader) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
return metadata.hasAnnotation(this.annotationType.getName()) ||
(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}
「是metadata.hasMetaAnnotation法,從名稱看是處理元註解,我們重點關注」
「逐步分析」
找metadata.hasMetaAnnotation
metadata=metadataReader.getAnnotationMetadata();
metadataReader =new SimpleMetadataReader(...)
metadata= new SimpleMetadataReader#getAnnotationMetadata()
//SimpleMetadataReader 的構造方法
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
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);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
this.annotationMetadata = visitor;
// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
this.classMetadata = visitor;
this.resource = resource;
}
metadata=new SimpleMetadataReader(...)**.**getAnnotationMetadata()= new AnnotationMetadataReadingVisitor(。。)
也就是說
「metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation」
其方法如下:
public class AnnotationMetadataReadingVisitor{
// 省略部分代碼
@Override
public boolean hasMetaAnnotation(String metaAnnotationType) {
Collection<set
> allMetaTypes =
this.metaAnnotationMap.values();
for (Set
metaTypes : allMetaTypes) {
if (metaTypes.contains(metaAnnotationType)) {
return
true;
}
}
return
false;
}
}
邏輯很簡單,就是判斷該註解的元註解在,在不在metaAnnotationMap中,如果在就返回true。
這裏面核心就是metaAnnotationMap,搜索AnnotationMetadataReadingVisitor類,沒有發現賦值的地方??!。
查找metaAnnotationMap賦值
回到SimpleMetadataReader 的方法,
//這個accept方法,很可疑,在賦值之前執行
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
//省略其他代碼
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
this.annotationMetadata = visitor;
}
發現一個可疑的語句:classReader.accept。
查看accept方法
public class ClassReader {
//省略其他代碼
public void accept(..省略代碼){
//省略其他代碼
readElementValues(
classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
currentAnnotationOffset,
true,
charBuffer);
}
}
查看readElementValues方法
public class ClassReader{
//省略其他代碼
private int readElementValues(
final AnnotationVisitor annotationVisitor,
final int annotationOffset,
final boolean named,
final char[] charBuffer) {
int currentOffset = annotationOffset;
// Read the num_element_value_pairs field (or num_values field for an array_value).
int numElementValuePairs = readUnsignedShort(currentOffset);
currentOffset += 2;
if (named) {
// Parse the element_value_pairs array.
while (numElementValuePairs-- > 0) {
String elementName = readUTF8(currentOffset, charBuffer);
currentOffset =
readElementValue(annotationVisitor, currentOffset + 2, elementName, charBuffer);
}
} else {
// Parse the array_value array.
while (numElementValuePairs-- > 0) {
currentOffset =
readElementValue(annotationVisitor, currentOffset, /* named = */ null, charBuffer);
}
}
if (annotationVisitor != null) {
annotationVisitor.visitEnd();
}
return currentOffset;
}
}
這裏面的核心就是 annotationVisitor.visitEnd();
確定annotationVisitor
這裏的annotationVisitor=AnnotationMetadataReadingVisitor#visitAnnotation
源碼如下,注意這裏傳遞了metaAnnotationMap!!
public class AnnotationMetadataReadingVisitor{
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
String className = Type.getType(desc).getClassName();
this.annotationSet.add(className);
return new AnnotationAttributesReadingVisitor(
className, this.attributesMap,
this.metaAnnotationMap, this.classLoader);
}
}
「annotationVisitor=AnnotationAttributesReadingVisitor」
查閱annotationVisitor.visitEnd()
「annotationVisitor=AnnotationAttributesReadingVisitor#visitEnd()」
public class AnnotationAttributesReadingVisitor{
@Override
public void visitEnd() {
super.visitEnd();
Class annotationClass = this.attributes.annotationType();
if (annotationClass != null) {
List
attributeList =
this.attributesMap.get(
this.annotationType);
if (attributeList ==
null) {
this.attributesMap.add(
this.annotationType,
this.attributes);
}
else {
attributeList.add(
0,
this.attributes);
}
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) {
try {
Annotation[] metaAnnotations = annotationClass.getAnnotations();
if (!ObjectUtils.isEmpty(metaAnnotations)) {
Set
visited =
new LinkedHashSet<>();
for (Annotation metaAnnotation : metaAnnotations) {
recursivelyCollectMetaAnnotations(visited, metaAnnotation);
}
if (!visited.isEmpty()) {
Set
metaAnnotationTypeNames =
new LinkedHashSet<>(visited.size());
for (Annotation ann : visited) {
metaAnnotationTypeNames.add(ann.annotationType().getName());
}
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
}
}
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug(
"Failed to introspect meta-annotations on " + annotationClass +
": " + ex);
}
}
}
}
}
}
內部方法recursivelyCollectMetaAnnotations 遞歸的讀取註解,與註解的元註解(讀@Service,再讀元註解@Component),並設置到metaAnnotationMap,也就是AnnotationMetadataReadingVisitor 中的metaAnnotationMap中。
總結
大致如下:
ClassPathScanningCandidateComponentProvider#findCandidateComponents
-
將package轉化爲ClassLoader類資源搜索路徑packageSearchPath
-
加載搜素路徑下的資源。
-
isCandidateComponent 判斷是否是備選組件。
內部調用的TypeFilter的match方法:
AnnotationTypeFilter#matchself中metadata.hasMetaAnnotation處理元註解
metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation
就是判斷當前註解的元註解在不在metaAnnotationMap中。
AnnotationAttributesReadingVisitor#visitEnd()內部方法recursivelyCollectMetaAnnotations 遞歸的讀取註解,與註解的元註解(讀@Service,再讀元註解@Component),並設置到metaAnnotationMap
-
添加到返回結果的list
本文分享自微信公衆號 - 架構真經(gentoo666)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。