作者:shihuaping0918@163.com转载请注明作者
前面的文章分析了bean的实例化,bean字段/成员变量的填充。这一篇来看一下@Bean注解。最早分析的是读xml中的<bean></bean>定义,现在来看一下代码中的@Bean又是怎么生效的。文章的篇幅越来越大,因为代码贴上去占了太多空间,但是只摘取一段代码,这样又对读者不友好,读文章的时候又要不停返回去看代码对照。篇幅大就大吧,我尽量不贴图片,这样的话,文字再多,占的体积也是有限的。这一篇文章又会很长。
从AnnotationConfigApplicationContext讲起,基于xml配置时,一般使用ClassPathXmlApplication进行spring应用上下文加载。基于java配置时,就使用AnnotationConfigApplicationContext进行spring应用上下文加载。它会注册@Bean注释的方法所返回的bean。这里有一个要注意的地方,就是要使用这两个构造方法,才会去注册bean。
/**
* Create a new AnnotationConfigApplicationContext, deriving bean definitions
* from the given component classes and automatically refreshing the context.
* @param componentClasses one or more component classes — for example,
* {@link Configuration @Configuration} classes
*/
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh(); //熟悉吧
}
/**
* Create a new AnnotationConfigApplicationContext, scanning for components
* in the given packages, registering bean definitions for those components,
* and automatically refreshing the context.
* @param basePackages the packages to scan for component classes
*/
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh(); //很眼熟
}
refresh这里就不讲了,这个类也只是为了提供一个入口,下面来看scan方法。
/**
* Perform a scan within the specified base packages.
* <p>Note that {@link #refresh()} must be called in order for the context
* to fully process the new classes.
* @param basePackages the packages to scan for component classes
* @see #register(Class...)
* @see #refresh()
*/
@Override
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
再看一下scanner,是在无参构造方法里赋值的。
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*/
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
/**
* Create a new AnnotationConfigApplicationContext with the given DefaultListableBeanFactory.
* @param beanFactory the DefaultListableBeanFactory instance to use for this context
*/
public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
好,下面去ClassPathBeanDefinitionScanner里看看scan方法。注意一下scan方法参数传的是package路径,包路径。
/**
* Perform a scan within the specified base packages.
* @param basePackages the packages to check for annotated classes
* @return number of beans registered
*/
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);
}
去doScan里看看
/**
* Perform a scan within the specified base packages,
* returning the registered bean definitions.
* <p>This method does <i>not</i> register an annotation config processor
* but rather leaves this up to the caller.
* @param basePackages the packages to check for annotated classes
* @return set of beans registered if any for tooling registration purposes (never {@code null})
*/
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { //这里传参是a,b,c,d,e,f,g这种形式
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) { //遍历路径
Set<BeanDefinition> candidates = findCandidateComponents(basePackage); //取bean
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)) { //检查当前bean和已注册的有没有冲突
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry); //注册bean
}
}
}
return beanDefinitions;
}
从代码看,就是遍历package,然后取出里面的bean。再对bean进行遍历,判定没冲突就注册,一共两个for循环,代码不复杂,要继续关注的是怎么从package取bean。再提一句,这一篇分析所有的scan相关的代码是在refresh之前的,也就是完全不涉及bean的实例化,属性初始化。
/**
* Scan the class path for candidate components.
* @param basePackage the package to check for annotated classes
* @return a corresponding Set of autodetected bean definitions
*/
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) { //不进这里componentsIndex为null
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage); //进这里
}
}
返回的是BeanDefinition的集合。根据前面的代码,结合ClassPathScanningCandidateComponentProvider的构造方法,可以确定this.componentsIndex为空。
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); //获取resource
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) { //遍历resource
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) { //可读取
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); //从resource里取bean信息
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
看看ScannerGenericBeanDefinition
/**
* Create a new ScannedGenericBeanDefinition for the class that the
* given MetadataReader describes.
* @param metadataReader the MetadataReader for the scanned target class
*/
public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
Assert.notNull(metadataReader, "MetadataReader must not be null");
this.metadata = metadataReader.getAnnotationMetadata();
setBeanClassName(this.metadata.getClassName());
}
这个类得到的信息都是从metadataReader里获取的,取了类名,还有元数据。还是去看看metadataReader做了什么。metadataReader来源于CachingMetadataReaderFactory。
/**
* Return the MetadataReaderFactory used by this component provider.
*/
public final MetadataReaderFactory getMetadataReaderFactory() {
if (this.metadataReaderFactory == null) {
this.metadataReaderFactory = new CachingMetadataReaderFactory();
}
return this.metadataReaderFactory;
}
CachingMetadataReaderFactory是使用无参构造方法创建的。去看看构造方法。
/**
* Create a new CachingMetadataReaderFactory for the default class loader,
* using a local resource cache.
*/
public CachingMetadataReaderFactory() {
super();
setCacheLimit(DEFAULT_CACHE_LIMIT);
}
可以看到这个构造方法没有做其它的事情,只有一个方法调用。分析代码的时候一定要随时注意参数值,参数类型。这个参数是
public static final int DEFAULT_CACHE_LIMIT = 256;
/**
* Specify the maximum number of entries for the MetadataReader cache.
* <p>Default is 256 for a local cache, whereas a shared cache is
* typically unbounded. This method enforces a local resource cache,
* even if the {@link ResourceLoader} supports a shared resource cache.
*/
public void setCacheLimit(int cacheLimit) {
if (cacheLimit <= 0) {
this.metadataReaderCache = null;
}
else if (this.metadataReaderCache instanceof LocalResourceCache) {
((LocalResourceCache) this.metadataReaderCache).setCacheLimit(cacheLimit);
}
else { //走到这里了
this.metadataReaderCache = new LocalResourceCache(cacheLimit);
}
}
而LocalResourceCache的代码是
@SuppressWarnings("serial")
private static class LocalResourceCache extends LinkedHashMap<Resource, MetadataReader> {
private volatile int cacheLimit;
public LocalResourceCache(int cacheLimit) {
super(cacheLimit, 0.75f, true);
this.cacheLimit = cacheLimit;
}
public void setCacheLimit(int cacheLimit) {
this.cacheLimit = cacheLimit;
}
public int getCacheLimit() {
return this.cacheLimit;
}
@Override
protected boolean removeEldestEntry(Map.Entry<Resource, MetadataReader> eldest) {
return size() > this.cacheLimit;
}
}
上面看的代码实际上都是为了好分析下面这个方法
@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
if (this.metadataReaderCache instanceof ConcurrentMap) { //不进这里
// No synchronization necessary...
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 = this.metadataReaderCache.get(resource);
if (metadataReader == null) { //新的resource
metadataReader = super.getMetadataReader(resource); //进了这里
this.metadataReaderCache.put(resource, metadataReader); //缓存起来
}
return metadataReader;
}
}
else {
return super.getMetadataReader(resource);
}
}
看看super.getMetadataReader
@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}
继续看SimpleMetadataReader这个类
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
SimpleAnnotationMetadataReadingVisitor visitor = new SimpleAnnotationMetadataReadingVisitor(classLoader);
getClassReader(resource).accept(visitor, PARSING_OPTIONS);
this.resource = resource;
this.annotationMetadata = visitor.getMetadata(); //metaData来自于visitor
}
任务是在SimpleAnnotationMetadataReadingVisitor中完成的。但是class的二进制格式读取是ClassReader完成的,这个类读class的二进制,将二进制分解。分解过程中,使用visitor去转换。
private static ClassReader getClassReader(Resource resource) throws IOException {
try (InputStream is = new BufferedInputStream(resource.getInputStream())) {
try {
return new ClassReader(is); //这个是asm下的ClassReader,解析class二进制
}
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);
}
}
}
然后看ClassReader.accept,这个方法非常长,感兴趣的可以去看一下java虚拟机里对class文件格式的描述,也有专门的书《深入java虚拟机》,代码我就不一一解释了,主要标注有visitor的地方。
public void accept(
final ClassVisitor classVisitor, //visitor传入
final Attribute[] attributePrototypes,
final int parsingOptions) {
Context context = new Context();
context.attributePrototypes = attributePrototypes;
context.parsingOptions = parsingOptions;
context.charBuffer = new char[maxStringLength];
// Read the access_flags, this_class, super_class, interface_count and interfaces fields.
char[] charBuffer = context.charBuffer;
int currentOffset = header; //跳过文件头
int accessFlags = readUnsignedShort(currentOffset);
String thisClass = readClass(currentOffset + 2, charBuffer); //读自己
String superClass = readClass(currentOffset + 4, charBuffer); //读父类
String[] interfaces = new String[readUnsignedShort(currentOffset + 6)];
currentOffset += 8;
for (int i = 0; i < interfaces.length; ++i) {
interfaces[i] = readClass(currentOffset, charBuffer);
currentOffset += 2;
} //读接口
// Read the class attributes (the variables are ordered as in Section 4.7 of the JVMS).
// Attribute offsets exclude the attribute_name_index and attribute_length fields.
// - The offset of the InnerClasses attribute, or 0.
int innerClassesOffset = 0;
// - The offset of the EnclosingMethod attribute, or 0.
int enclosingMethodOffset = 0;
// - The string corresponding to the Signature attribute, or null.
String signature = null;
// - The string corresponding to the SourceFile attribute, or null.
String sourceFile = null;
// - The string corresponding to the SourceDebugExtension attribute, or null.
String sourceDebugExtension = null;
// - The offset of the RuntimeVisibleAnnotations attribute, or 0.
int runtimeVisibleAnnotationsOffset = 0;
// - The offset of the RuntimeInvisibleAnnotations attribute, or 0.
int runtimeInvisibleAnnotationsOffset = 0;
// - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
int runtimeVisibleTypeAnnotationsOffset = 0;
// - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
int runtimeInvisibleTypeAnnotationsOffset = 0;
// - The offset of the Module attribute, or 0.
int moduleOffset = 0;
// - The offset of the ModulePackages attribute, or 0.
int modulePackagesOffset = 0;
// - The string corresponding to the ModuleMainClass attribute, or null.
String moduleMainClass = null;
// - The string corresponding to the NestHost attribute, or null.
String nestHostClass = null;
// - The offset of the NestMembers attribute, or 0.
int nestMembersOffset = 0;
// - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
// This list in the <i>reverse order</i> or their order in the ClassFile structure.
Attribute attributes = null;
int currentAttributeOffset = getFirstAttributeOffset();
for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
// Read the attribute_info's attribute_name and attribute_length fields.
String attributeName = readUTF8(currentAttributeOffset, charBuffer);
int attributeLength = readInt(currentAttributeOffset + 2);
currentAttributeOffset += 6;
// The tests are sorted in decreasing frequency order (based on frequencies observed on
// typical classes).
if (Constants.SOURCE_FILE.equals(attributeName)) {
sourceFile = readUTF8(currentAttributeOffset, charBuffer);
} else if (Constants.INNER_CLASSES.equals(attributeName)) {
innerClassesOffset = currentAttributeOffset;
} else if (Constants.ENCLOSING_METHOD.equals(attributeName)) {
enclosingMethodOffset = currentAttributeOffset;
} else if (Constants.NEST_HOST.equals(attributeName)) {
nestHostClass = readClass(currentAttributeOffset, charBuffer);
} else if (Constants.NEST_MEMBERS.equals(attributeName)) {
nestMembersOffset = currentAttributeOffset;
} else if (Constants.SIGNATURE.equals(attributeName)) {
signature = readUTF8(currentAttributeOffset, charBuffer);
} else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
runtimeVisibleAnnotationsOffset = currentAttributeOffset;
} else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
runtimeVisibleTypeAnnotationsOffset = currentAttributeOffset;
} else if (Constants.DEPRECATED.equals(attributeName)) {
accessFlags |= Opcodes.ACC_DEPRECATED;
} else if (Constants.SYNTHETIC.equals(attributeName)) {
accessFlags |= Opcodes.ACC_SYNTHETIC;
} else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) {
sourceDebugExtension =
readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]);
} else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
runtimeInvisibleAnnotationsOffset = currentAttributeOffset;
} else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset;
} else if (Constants.MODULE.equals(attributeName)) {
moduleOffset = currentAttributeOffset;
} else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) {
moduleMainClass = readClass(currentAttributeOffset, charBuffer);
} else if (Constants.MODULE_PACKAGES.equals(attributeName)) {
modulePackagesOffset = currentAttributeOffset;
} else if (!Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
// The BootstrapMethods attribute is read in the constructor.
Attribute attribute =
readAttribute(
attributePrototypes,
attributeName,
currentAttributeOffset,
attributeLength,
charBuffer,
-1,
null);
attribute.nextAttribute = attributes;
attributes = attribute;
}
currentAttributeOffset += attributeLength;
}
// Visit the class declaration. The minor_version and major_version fields start 6 bytes before
// the first constant pool entry, which itself starts at cpInfoOffsets[1] - 1 (by definition).
classVisitor.visit(
readInt(cpInfoOffsets[1] - 7), accessFlags, thisClass, signature, superClass, interfaces);
// Visit the SourceFile and SourceDebugExtenstion attributes.
if ((parsingOptions & SKIP_DEBUG) == 0
&& (sourceFile != null || sourceDebugExtension != null)) {
classVisitor.visitSource(sourceFile, sourceDebugExtension);
}
// Visit the Module, ModulePackages and ModuleMainClass attributes.
if (moduleOffset != 0) {
readModuleAttributes(
classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass);
}
// Visit the NestHost attribute.
if (nestHostClass != null) {
classVisitor.visitNestHost(nestHostClass);
}
// Visit the EnclosingMethod attribute.
if (enclosingMethodOffset != 0) {
String className = readClass(enclosingMethodOffset, charBuffer);
int methodIndex = readUnsignedShort(enclosingMethodOffset + 2);
String name = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex], charBuffer);
String type = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex] + 2, charBuffer);
classVisitor.visitOuterClass(className, name, type);
}
// 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;
// Parse num_element_value_pairs and element_value_pairs and visit these values.
currentAnnotationOffset =
readElementValues(
classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
currentAnnotationOffset,
/* named = */ true,
charBuffer); //读取annotation,注解
}
}
// Visit the RuntimeInvisibleAnnotations attribute.
if (runtimeInvisibleAnnotationsOffset != 0) {
int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset);
int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2;
while (numAnnotations-- > 0) {
// Parse the type_index field.
String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
currentAnnotationOffset += 2;
// Parse num_element_value_pairs and element_value_pairs and visit these values.
currentAnnotationOffset =
readElementValues(
classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
currentAnnotationOffset,
/* named = */ true,
charBuffer);
}
}
// Visit the RuntimeVisibleTypeAnnotations attribute.
if (runtimeVisibleTypeAnnotationsOffset != 0) {
int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
while (numAnnotations-- > 0) {
// Parse the target_type, target_info and target_path fields.
currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
// Parse the type_index field.
String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
currentAnnotationOffset += 2;
// Parse num_element_value_pairs and element_value_pairs and visit these values.
currentAnnotationOffset =
readElementValues(
classVisitor.visitTypeAnnotation(
context.currentTypeAnnotationTarget,
context.currentTypeAnnotationTargetPath,
annotationDescriptor,
/* visible = */ true),
currentAnnotationOffset,
/* named = */ true,
charBuffer);
}
}
// Visit the RuntimeInvisibleTypeAnnotations attribute.
if (runtimeInvisibleTypeAnnotationsOffset != 0) {
int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset);
int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2;
while (numAnnotations-- > 0) {
// Parse the target_type, target_info and target_path fields.
currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
// Parse the type_index field.
String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
currentAnnotationOffset += 2;
// Parse num_element_value_pairs and element_value_pairs and visit these values.
currentAnnotationOffset =
readElementValues(
classVisitor.visitTypeAnnotation(
context.currentTypeAnnotationTarget,
context.currentTypeAnnotationTargetPath,
annotationDescriptor,
/* visible = */ false),
currentAnnotationOffset,
/* named = */ true,
charBuffer);
}
}
// Visit the non standard attributes.
while (attributes != null) {
// Copy and reset the nextAttribute field so that it can also be used in ClassWriter.
Attribute nextAttribute = attributes.nextAttribute;
attributes.nextAttribute = null;
classVisitor.visitAttribute(attributes);
attributes = nextAttribute;
}
// Visit the NestedMembers attribute.
if (nestMembersOffset != 0) {
int numberOfNestMembers = readUnsignedShort(nestMembersOffset);
int currentNestMemberOffset = nestMembersOffset + 2;
while (numberOfNestMembers-- > 0) {
classVisitor.visitNestMember(readClass(currentNestMemberOffset, charBuffer));
currentNestMemberOffset += 2;
}
}
// Visit the InnerClasses attribute.
if (innerClassesOffset != 0) {
int numberOfClasses = readUnsignedShort(innerClassesOffset);
int currentClassesOffset = innerClassesOffset + 2;
while (numberOfClasses-- > 0) {
classVisitor.visitInnerClass(
readClass(currentClassesOffset, charBuffer),
readClass(currentClassesOffset + 2, charBuffer),
readUTF8(currentClassesOffset + 4, charBuffer),
readUnsignedShort(currentClassesOffset + 6));
currentClassesOffset += 8;
}
}
// Visit the fields and methods.
int fieldsCount = readUnsignedShort(currentOffset);
currentOffset += 2;
while (fieldsCount-- > 0) {
currentOffset = readField(classVisitor, context, currentOffset);
}
int methodsCount = readUnsignedShort(currentOffset);
currentOffset += 2;
while (methodsCount-- > 0) {
currentOffset = readMethod(classVisitor, context, currentOffset);
}
// Visit the end of the class.
classVisitor.visitEnd();
}
到了这里,可以知道,所有的metadata,其实都是从class二进制流里得来的。看一下metadata到底都有什么内容。
@Override
public void visitEnd() {
String[] memberClassNames = StringUtils.toStringArray(this.memberClassNames);
MethodMetadata[] annotatedMethods = this.annotatedMethods.toArray(new MethodMetadata[0]);
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
this.metadata = new SimpleAnnotationMetadata(this.className, this.access,
this.enclosingClassName, this.superClassName, this.independentInnerClass,
this.interfaceNames, memberClassNames, annotatedMethods, annotations);
} //类名,access privilege,inner class,interfaces,annotions,annotatedMethos(注意@Bean修饰的方法就是一种)
public SimpleAnnotationMetadata getMetadata() {
Assert.state(this.metadata != null, "AnnotationMetadata not initialized");
return this.metadata;
}
到此,一个bean的class读取,注册就分析完了。还遗留了一个问题,如果是扫描一个包下面的所有类,那么spring是怎么过滤掉接口,抽象类,内部类这些它不需要的东西。
/**
* Determine whether the given bean definition qualifies as candidate.
* <p>The default implementation checks whether the class is not an interface
* and not dependent on an enclosing class.
* <p>Can be overridden in subclasses.
* @param beanDefinition the bean definition to check
* @return whether the bean definition qualifies as a candidate component
*/
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
简单分析一下,第一种:不是接口,不是抽象类,还必须是顶层类(比如,不是内部嵌套类)。第二种:是抽象类,但是有注解修饰的方法。到这里,整个流程分析完了,spring对于bean的加载,是扫描式的,先全部扫进来。扫描进来以后,再做过滤。过滤完了以后,再注册bean。
写在最后:接单,有后台活java/cpp/lua/go联系shihuaping0918@163.com。不上班了也要有点收入才行。