从0开始带你阅读Sping源码阅读(一)

前置

还有设计模式可以看我以前发的文章,以及源码中常用的命名规范
lambda表达式、Stream、反射、代理等前置知识一定要掌握

Hiera用来查看接口的层次 key = alt+T
Translation翻译插件   key = ctrl+shift+y

我们看的是Spring 6可以使用idea 上面的SpringBoot3来自动创建

点击下一步记得一定要勾选

这样我们就能得到一个创建好的SpringBoot项目了

在Maven中我们可以直接通过点击下载源代码和文件就能直接配置了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NVU3fQVV-1679755070203)(assets/image-20230325221410988.png)]

第一篇 基础

第一章 bean的元数据

一、回顾bean的注入方式

1、xml
用字符串的形式来描述一个bean的所有的形式
<bean id="user" class="com.ydlclass.User" scope="prototype" autowire="byType" init-method-"init" 
depends-on="a,b" >
	<property name="name" value="jerry"/>
    <property name="age" value="18"/>
</bean>
2、注解
@Controller
public class UserController{}
@Service
@DependsOn(value = {"xxx","yyy"})
public class UserServices{}
@Repository
public class UserDao{}
@Compoment
@DependsOn(value = {"xxx","yyy"})
public class RedisTemplate{}
3、配置类
@Configuration
public class UserConfiguration {
    @Bean("user")
	public User user(){
		User user = new User();
		user.setName("1ily");
		user.setAge(20);
		return user;
    }
}
4、import注解
public class MySelector implements ImportSelector {
	@Override
	public String[] selectImports (AnnotationMetadata importingClassMetadata) {
		return new String[]{"com. yd1class . userService" ,"com. yd1class . userDao"};
    }
}
@Configuration
@Import(MySelector.class)
public class UserConfiguration {}

二、BeanDefiniiton详解

spring在构造bean时,不可能像我们主动构造那样,随心所欲的new,赋值、已经完成方法调用。他需要将千差万别的class概括
成为一种【统一的描述性】语言, spring提供了一个接口BeanDefintion为我们统一了这种描述bean的元数据。

bean的元数据通常是我们使用xml或者注解进行配置的数据,我们的spring容器启动之间第一步就是加载配置数据,这些元数据会
被加载到内存以一个个beanDefinition的形式保存在一个map中。

一个BeanDefiniton大概保存了以下信息:

  1. 定义了id、别名与Bean的对应关系(BeanDefinitionHolder)
  2. 具体的工厂方法(Class类型) ,包括工厂方法的返回类型,厂方法的Method对象
  3. 构造函数、构造函数形参类型
  4. Bean的class对象
  5. 作用范围、是否懒加载等等

以下是BeanDefiniton的类的结构图,这里没有用类图,我觉得这里用这样的图好看一些:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8hfxtC7k-1679755070205)(assets/image-20230325141311733.png)]beanDefinition来源不同可能会有不同实现,目前我们最常用的实现就是GenericBeanDefinition这个实现类。
小知识: Generic (- 般的,通用的),几乎所有以这个单词打头的实现,都是spring的通用实现。

1、认识BeanDefinition

BeanDefinition接口定义了大量的常量和方法:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    
    // 常量来标识一个bean的作用和范围
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
    
    //设置父BeanDefinition,可以只对有父子关系的bean
    void setParentName(@Nullable String parentName);
    String getParentName();
    // Bean的类的全限定类名
    void setBeanClassName(@Nullable String beanClassName);
    String getBeanClassName();
    
    void setScope(@Nullable String scope);
    String getScope();
    
    //设置是否懒加载
    void setLazyInit(boolean lazyInit);
    boolean isLazyInit();
    
    //设置依赖性,被依赖的bean会优先被创建
    void setDependsOn(@Nullable String... dependsOn);
    String[] getDependsOn();
    
    //是否允许自动装配
    void setAutowireCandidate(boolean autowireCandidate);
    boolean isAutowireCandidate();
    
    //设置是否为主要的Bean
    void setPrimary(boolean primary);
    boolean isPrimary();
    
    //工厂bean和工厂方法
    void setFactoryBeanName(@Nullable String factoryBeanName);
    String getFactoryBeanName();
    void setFactoryMethodName(@Nullable String factoryMethodName);
    String getFactoryMethodName();
    
    ConstructorArgumentValues getConstructorArgumentValues();
    default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}
    
    //使用setter注入时的key-value对都保留在这里
    MutablePropertyValues getPropertyValues();
    default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}
    // @since 5.1初始化方法和销毁方法
    void setInitMethodName(@Nullable String initMethodName);
    String getInitMethodName();
    void setDestroyMethodName(@Nullable String destroyMethodName);
    String getDestroyMethodName();
    
    //为bean设置角色
    void setRole(int role);
    int getRole();
    
    //bean的描述
    void setDescription(@Nullable String description);
    String getDescription();
    
    //返回此bean定义的可解析类型,基于bean类或其他特定的元数据
    //这通常在运行时合并bean定义上完全解决但不一定在配配置时定义实例上
    ResolvableType getResolvableType();
    boolean isSingleton();
    boolean isPrototype();
    boolean isAbstract();
    String getResourceDescription();
    BeanDefinition getOriginatingBeanDefinition();
    
}
2、AbstractBeanDefinition

该类对通用核心方法完成了实现(这也是抽象类的作用),同时对一些成员变量提供l默认值

@SuppressWarnings("serial")
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor 
    	implements BeanDefinition, Cloneable {
	//定义了一些常量
	public static final String SCOPE_DEFAULT = "";
	public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
	public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME
	public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
	public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
	//..........还有很多


	//构造器
	protected AbstractBeanDefinition() {
		this(null, null);
	}
	//指定构造器参数和属性参数
	protected AbstractBeanDefinition(@Nullable ConstructorArgumentValues cargs, @Nullable MutablePropertyValues pvs) {
		this.constructorArgumentValues = cargs;
		this.propertyValues = pvs;
	}

	//使用深拷贝创建一个新的
	protected AbstractBeanDefinition(BeanDefinition original) {
	}
	//复制一个bean的定义到当前bean,通常父子bean合并使用
	public void overrideFrom(BeanDefinition other) {}

	//....此处省略其他的方法实现

}
3、GenericBeanDefinition
public class GenericBeanDefinition extends AbstractBeanDefinition {
    
    private String parentName;
    
    public GenericBeanDefinition() {
		super();
	}
    //通过深拷贝创建一个bean
    public GenericBeanDefinition(BeanDefinition original) {
		super(original);
	}
    @Override
	public void setParentName(@Nullable String parentName) {
		this.parentName = parentName;
	}
    @Override
	public AbstractBeanDefinition cloneBeanDefinition() {
		return new GenericBeanDefinition(this);
	}

	@Override
	public boolean equals(@Nullable Object other) {
		if (this == other) {
			return true;
		}
		if (!(other instanceof GenericBeanDefinition that)) {
			return false;
		}
		return (ObjectUtils.nullSafeEquals(this.parentName, that.parentName) && super.equals(other));
	}

	@Override
	public String toString() {
		if (this.parentName != null) {
			return "Generic bean with parent '" + this.parentName + "': " + super.toString();
		}
		return "Generic bean: " + super.toString();
	}
}

此时,我们可以编写如下的此时用例:

public class User {
    private String username;
    private Integer age;
}
public static void testRegistryByJava(){
        //创建一个GenericBeanDefinition对象
        //在Spring框架中,BeanDefinition是描述一个bean的元数据对象。它包含了该bean的类型、作用域、属性等信息。
        // GenericBeanDefinition是BeanDefinition接口的一个实现类,用于创建一个通用的BeanDefinition对象。
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        //设置bean的类名和作用域
        //通过setBeanClassName()方法设置bean的类名为"com.yaojing.demo.User",这意味着Spring框架将会使用反射机制创建一个com.yaojing.demo.User类的实例,并将其作为一个bean注册到Spring的容器中。
        beanDefinition.setBeanClassName("com.yaojing.demo.User");
        //通过setScope()方法设置bean的作用域为"prototype",这意味着Spring容器会在每次请求该bean时创建一个新的实例。与之相对的是"singleton"作用域,该作用域表示Spring容器只会创建一个bean实例,并在后续的请求中返回同一个实例。
        beanDefinition.setScope("prototype");
        //设置bean的初始化方法 通过setInitMethodName()方法设置bean的初始化方法为"init"。该方法将在bean实例创建完成后被调用。
        beanDefinition.setInitMethodName("init");
        //此处类似setter注入的描述
        //使用MutablePropertyValues对象来设置bean的属性值。MutablePropertyValues是一个可变的属性值集合类,
        // 可以通过addPropertyValue()方法向其中添加属性名和属性值。在这个例子中,bean的"name"属性被设置为"lily","age"属性被设置为"12"。
        //最终,这个BeanDefinition对象可以被注册到Spring容器中,这样在其他地方可以通过容器获取到对应的bean实例,并使用它的属性和方法。
        MutablePropertyValues values = new MutablePropertyValues();
        values.addPropertyValue("name","lily");
        values.addPropertyValue("age","12");
        beanDefinition.setPropertyValues(values);
    }

注:这个测试用例极其简单,但是他却可以使用字符串的形式描述一些类

我们再测一测 具有继承关系的bean

public class Dog {
    private String color;
    private Integer age;
}
public class TeddyDog extends Dog {
    private String name;
}

xml可以这么定义

<bean id=" dog" class="com.yaojing.demo.Dog">
	<property name="color" value="white"/>
	<pToperty name="age" value="3"/>
</bean>
<bean id=" teddyDog" class="com.yaojing.demo.Dog" parent=" dog">
	<property name="name" value= "小红"/>
</bean>

如果是手动定义如下:

  public void testRootBeanDefinition() throws Exception {
        RootBeanDefinition dog = new RootBeanDefinition();
        dog.setBeanClassName("com.yaojing.demo.Dog");
        BeanMetadataAttribute color = new BeanMetadataAttribute("color", "white");
        BeanMetadataAttribute age = new BeanMetadataAttribute("age", "3");
        dog.addMetadataAttribute(color);
        dog.addMetadataAttribute(age);
        //子Definition的创建需要依賴父Definition
        ChildBeanDefinition teddy = new ChildBeanDefinition(" dog");
        teddy.setBeanClassName("com.yaojing.demo.TeddyDog");
        BeanMetadataAttribute name = new BeanMetadataAttribute("name", "doudou");
        teddy.addMetadataAttribute(name);
    }

GenericBeanDefinition在很多场景可以替换以上的内容,但是由于历史等原因, RootBeanDefinition依旧存在而且很重要, 后期
的归一处理还是要将不同的BeanDefinition转换或合并至一个RootBeanDefinition:

  • RootBeanDefinition与AbstractBeanDefinition是互补关系, RootBeanDefinition在AbstractBeanDefinition的基础上定义了
    更多属性。
  • RootBeanDefinition不能有父BeanDefinition, 可以和ChildBeanDefinition配合使用构建父子关系 (bean是可以继承的)。
    目前最常用的BeanDefinition是GenericBeanDefinition及其子类的实现,GenericBeanDefinition很强大, 也可以很轻松的独
    立的构建父子关系。
  • 有时为了统一调用,不同的BeanDefinition可以合并、拷贝等。
//转换的
GenericBeanDefinition definition = new GenericBeanDefinition(teddy);
//合并的
definition.overrideFrom(dog);

三、BeanDefinition注册器

有了统一标准的元数据之后,我们就可以统一管理,这就需要一个容器去存储,当然我们可以使用map这样的集合类,当
然spring差不多也是这样做的,他为我们提供了一个接口BeanDefinitionRegistry。 只要实现了这个接口,就会拥有注册
beanDefinition的能力。
BeanDefinitionRegistry接口如下:

//AliasRegistry 别名注册器
public interface BeanDefinitionRegistry extends AliasRegistry {
    //注册一个BeanDefinition
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	boolean containsBeanDefinition(String beanName);
	String[] getBeanDefinitionNames();
	int getBeanDefinitionCount();
	boolean isBeanNameInUse(String beanName);
}

一个bean可以有多个id,和多个名字或者别名,AliasRegistry为我们提供了注册别名的能力

public interface AliasRegistry {
	void registerAlias(String name, String alias);
	void removeAlias(String alias);
	boolean isAlias(String name);
	String[] getAliases(String name);
}

spring给我们提供了一个超级简单的实现SimpleBeanDefinitionRegistry如下

public class SimpleBeanDefinitionRegistry extends SimpleAliasRegistry implements BeanDefinitionRegistry {
	//维持一个map来保存beanDefinitonMap
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);

	//下面都是对bean的增删查改查 看名字就知道了 具体方法不写了
	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
	}
	@Override
	public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {}
	@Override
	public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {}
	@Override
	public boolean containsBeanDefinition(String beanName) {}
	@Override
	public String[] getBeanDefinitionNames() {}
	@Override
	public int getBeanDefinitionCount() {}
	@Override
	public boolean isBeanNameInUse(String beanName) {}
}

编写测试用例

 public static void testRegistryByJava2() {
        //定义一个注册器,用来注册和管理BeanDefinition
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
        //代码方式创建
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClassName("com. yd1class . User");
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("name", "1i1y");
        propertyValues.addPropertyValue("age", 12);
        beanDefinition.setPropertyValues(propertyValues);
        //进行注册
        registry.registerBeanDefinition("user", beanDefinition);
        System.out.println("The beanClassName is" + beanDefinition.getBeanClassName());

    }

四、加载BeanDefinition

当然我们不可能为每一个类手动编写与之对应的BeanDefinition,元数据还是要从xml或注解或配置类中获取,spring也为我们提
供了对应的工具。

1、读取xmI配置文件

该类通过解析xml完成BeanDefinition的读取,并且将它解析的BeanDefinition注册到一个注册器中:

 public void testRegistryByxml() {
        //定义一个注册器,用来注册和管理BeanDefinition
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
        //通过xml文件加裁
        XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(registry);
        xmlReader.loadBeanDefinitions("classpath:spring.xml");
        System.out.println(Arrays.toString(registry.getBeanDefinitionNames()));
    }

原理其实就是解析字符反射代理

2、加载带注解的的bean
public void testRegistryByAnnotation(){
        //定义一个注册器,用来注册和管理BeanDefinition
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
        //通过配置文件加载
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
        reader.register(User.class);
        System.out.println(Arrays.toString(registry.getBeanDefinitionNames()));
    }
3、读取配置类

ConfigurationClassBeanDefinitionReader可以读取配置类,只是这个类不让我们使用,该类提供了如下方法:

loadBeanDefinitionsForConfigurationClass
registerBeanDefinitionForImportedConfigurationClass
loadBeanDefinitionsForBeanMethod

他会将读取的元数据封装成为: ConfigurationClassBeanDefinition

4、类路径扫描
  public void testRegistryByScanner() {
        //定义一个注册器,用来注册和管理BeanDefinition
        BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
        //通过扫描包的方式
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
        scanner.scan(" com. yd1class");
        System.out.println(Arrays.toString(registry.getBeanDefinitionNames()));
    }
5、包扫描的过程

无论是扫包还是其他方式,我们我们解析-个类无非有几种方式:

  • 加载一个类到内存,获取Class文件, 通过反射获取元数据
  • 直接操纵字节码文件(.class) ,读取字节码内的元数据

无疑问spring选择了第二种,

  • 首先:第二种性能要优于第一种
  • 其次:第一种会将扫描的类全部加载到堆内存,无疑会浪费空间,增加gc次数,第二种可以根据元数据按需加载

我们以包扫描的doScan方法为例(ClassPathBeanDefinitionScanner类) :

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
    	//BeanDefinitionHolder持有 beanDefinition实例和名字以及别名
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
            // 这里是具体的扫描过程,找出全部符合过滤器要求的beanDefinition
            // 返回的beanDefinition的实际类型为ScannedGenericBeanDefinition
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            //根据不同的Bean类型做统一的处理,如附默认值等
            //因为有些数据我们没有配置,需要在这里进行默认处理
			for (BeanDefinition candidate : candidates) {
                //如果存在,则解释@Scope注解,为候选bean设置代理的方式ScopedProxyMode,XML属性也能配置:scope-resolver、scope-proxy,可以指定代理方式为jdk或者cglib
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
                //首先从注解中获取bean的名字,如果没有
				//使用beanName生成器beanNameGenerator来生成beanName
				//在注解中的bean的默认名称和xm1中是不一致的
				//注解中如果没有指定名字本质是通过ClassUtil的getshortName方法获取的
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                 //将进一步设置应用于给定的BeanDefinition,使用AbstractBeanDefinition的一些默认属性值
				//设置autowireCandidate属性,即XML的autowire-candidate属性,IoC学习的时候就见过该属性,默认为true,表示该bean支持成为自动注入候选bean
				if (candidate instanceof AbstractBeanDefinition abstractBeanDefinition) {
					postProcessBeanDefinition(abstractBeanDefinition, beanName);
				}            
                //如果bean定义是AnnotatedBeanDefiniti on类型,ScannedGeneri cBeanDefiniti on同样属于AnnotatedBeanDefinition类型.
				if (candidate instanceof AnnotatedBeanDefinition annotatedBeanDefinition) {
                    // 4处理类上的其他通用注解: @Lazy,@Primary, @DependsOn, @Role,@Description
					AnnotationConfigUtils.processCommonDefinitionAnnotations(annotatedBeanDefinition);
				}
                //检查给定的beanName,确定相应的bean定义是否需要注册或与现有bean定义兼容
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    //根据proxyMode属性的值,判断是否需要创建scope代理,一般都是不需要的
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

我们可以紧接着看一个很重要的方法

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
            // Spring5的新特性,直接从"META-INF/spring.components"组件索引文件中加载符合条件的bean,避免了包扫描,用于提升启动速度
// Spring5升级的其中一个重点就提升了注解驱动的启动性能,"META-INF/spring.components"这个文件类似于一个“组件索引"文件,我们将需要加载的组件(beean定义)预先的以键值对的样式配置到该文件中,当项目中存在"META-INF/spring.components"文件并且文件中配置了属性时,Spring不会进行包扫描,而是直接读取"META-INF/spring.components "中组件的定义并直接加载,从而达到提升性能的目的。

			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			return scanCandidateComponents(basePackage);
		}
	}

我们可以添加如下的依赖,自动生成索引部分

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-indexer</artifactId>
	<version>6.0.3</version>
</dependency>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ES6cpeXT-1679755070206)(assets/image-20230325211913598.png)]

当然我们更关注的是scanCandidateComponents方法

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
            //生成完整的资源解析路径
			//com.yaojing -> classpath* :com/yaojing/**/*.class
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
            //加载所有路径下的资源,我们看到前缀是"classpath*",因此项目依赖的jar包中的相同路径下资源都会被加载进来
			// Spring会将每一个定义的字节码文件加载成为一个Resource资源(包括内部类都是一个Resource资源)
			//此处是以资源(流)的方式加载(普通文件),而不是将一个类使用类加载器加载到jvm中。
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
            //遍历所有文件
			for (Resource resource : resources) {
				String filename = resource.getFilename();
                //此处忽略CGLAB生成的代理文件
				if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
					// Ignore CGLIB-generated classes in the classpath
					continue;
				}
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				try {
                    //getMetadataReader方法会生成一个元数据读取器---(类上的注解或注释)
                    //我们的例子中是SimpleMetadataReader
					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    //检查读取到的类是否可以作为候选组件,即是否符合TypeFilter类型过滤器的要求
					//使用IncludeFilter。 就算目标类上没有QComponent注解,它也会被扫描成为一个Bean
					//使用ExcludeFilter,就算目标类上面有QComponent注解也不会成为Bean
					if (isCandidateComponent(metadataReader)) {
                        //构建一个ScannedGenericBeanDefinition
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						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 (FileNotFoundException ex) {
					if (traceEnabled) {
						logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
					}
				}
				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;
	}

上边的源码中我们看到读取类文件的真实的实例是simpleMetadataReader, spring选用了 [read+visitor] 的方式来读取字节
码, read负责暴露接口,visitor负责真正的读取工作:

final class SimpleMetadataReader implements MetadataReader {

	private static final int PARSING_OPTIONS = ClassReader.SKIP_DEBUG
			| ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES;

	private final Resource resource;

	private final AnnotationMetadata annotationMetadata;


	SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
		SimpleAnnotationMetadataReadingVisitor visitor = new SimpleAnnotationMetadataReadingVisitor(classLoader);
        //这里是核心,一个reader需要结合一个visitor
		getClassReader(resource).accept(visitor, PARSING_OPTIONS);
		this.resource = resource;
        //元数据都是visitor的能力,典型的访问者设计模式
		this.annotationMetadata = visitor.getMetadata();
	}
	//通过资源获取一个ClassReader
	private static ClassReader getClassReader(Resource resource) throws IOException {
		try (InputStream is = resource.getInputStream()) {
			try {
				return new ClassReader(is);
			}
			catch (IllegalArgumentException ex) {
				throw new IOException("ASM ClassReader failed to parse class file - " +
						"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
			}
		}
	}

	
	@Override
	public Resource getResource() {
		return this.resource;
	}
	//提供了通用能力
	@Override
	public ClassMetadata getClassMetadata() {
		return this.annotationMetadata;
	}

	@Override
	public AnnotationMetadata getAnnotationMetadata() {
		return this.annotationMetadata;
	}
}

SimpleAnnotationMetadataReadingVisitor类使用了大量asm的内容,由此可见spring在读取元数据的时候, 是直接读取
class文件的内容,而非加载后通过反射获取,我们列举其中的个别属性和方法,大致能窥探一二

final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {

	//访问一个内部的方法
	@Override
	public void visitInnerClass(String name, @Nullable String outerName, String innerName, int access) {

	}
	//访问一个注解的方法
	@Override
	@Nullable
	public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
	
	}
	//访问方法的方法
	@Override
	@Nullable
	public MethodVisitor visitMethod(
			int access, String name, String descriptor, String signature, String[] exceptions) {
	}

	//得到元数据
	public SimpleAnnotationMetadata getMetadata() {
	}

}

.使用asm的demo

 public void testAsm() throws I0Exception {
        Resource resource = new ClassPathResource("com/yaojing.demo/user.class");
        ClassReader classReader = new ClassReader(resource.getInputStream());
        logger.info(classReader.getClassNameO);
		//缺少visitor的reader能力优先,我们只做几个简单的实现
		// visitor实现相对复杂,我们没有必要去学习
		// classReader.accept(xxxVisitor);
		//返回的对应的常量池的偏移量+1
		// 0-3 cafebaba 4-7 主次版本号8-9第一个是10+1
		//二进制可以使用bined插件查看
        logger.info("The first item is {}.", classReader.getItem(1));
        logger.info("The first item is {}.", classReader.getItem(2));
		// 00 3A这是字节码文件看到的,
		//常量池的计数是1-57 0表示不引用任何一个常量池项目
        logger.ipfo "The first item is {}.", classReader.getItemCount();
		//通过javap -V.\User.class class文件访问标志
		// flags: (0x0021) ACC PUBLIC, ACC_ SUPER 十进制就是33
		// ACC_ SUPER 0x00 20 是 否允许使用invokespecia1字节码指令的新语义.
		// ACC. PUBLIC 0x0001 是 否为Pub1ic类型
        logger.info("classReader . getAccess() is {}", classReader.getAccess());
    }

注:不同的扫描方式形成了不同的Defination子类如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KghCDUUK-1679755070207)(assets/image-20230325220450183.png)]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值