Spring源码阅读(基础) 骑猪看日落 2024-03-26 20:11 15阅读 0赞 ## ## ## 第一章:bean的元数据 ## ### 1.bean的注入方式: ### 1.1 xml文件 1.2 注解 @Component(自己写的类才能在上面加这些注解) 1.3配置类: @Configuration 注入第三方数据源之类 ![255a6f94161549c4a084f9a20857fffd.png][] 1.4 import注解 (引用了Myselector类下的selectImports方法的返回值的String数组中的UserService类)![ed7ff319e6684cc1ae8db4ea724fb61d.png][] ### 2.BeanDefiniton ### 在上一步中,都是一些对于bean的描述性信息,spring需要将这些前差万别的class概括为=一种统一的描述性语言,spring提供了一个BeanDefiniton接口,为我们统一了这种描述bean的元数据。怎么将描述性信息抽象成一个具体的bean,就是这个BeanDefiniton做的事情。 bean的元数据通常是我们使用xml或者注解配置的数据,spring容器启动的第一步,就是加载配置数据,这些元数据会被加载到内存,以一个个beanDefinition的形式保存在map中。 ![88064e4e6c014ae8a84de8c080204af1.png][] ![14a33dd22d824e75b829232cc6555588.png][] 2.5以后主要使用的是GenericBeanDefinition类,旁边的两个不怎么用 AnnotatadBeanDefinition类是实现注解的接口 ![f600d22cb362416193720aca09acacb2.png][] ## 3.BeanDefinition注册器 ## ![6d2d38e32cc946588341b903c46a26b6.png][] 因为我们要将beanDefinition放到spring里面,所以需要一个注册器。一个简单的例子如下(SimpleBeanDefinitionRegistry是spring提供的beanDefinition的实现类): @Test public void testRegistryByJava(){ // 定义一个注册器,用来注册和管理BeanDefinition BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry(); // 代码方式创建 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClassName("com.ydlclass.User"); MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.addPropertyValue("name","lily"); propertyValues.addPropertyValue("age",12); beanDefinition.setPropertyValues(propertyValues); // 进行注册 registry.registerBeanDefinition("user",beanDefinition); logger.info("The beanClassName is {}.",beanDefinition.getBeanClassName()); } ![addf7033edf34bb990e22579c55dca75.png][] ![114f178d3b3f4409b0af1bda53bda5e8.png][] ## 5.包扫描过程 ## 无论是扫包还是其他方式,解析一个类无外乎两种方式: 1.加载到内存,然后通过反射获取元数据 2.直接操作字节码文件,读取字节码内的元数据 spring选择的是直接操作字节码文件 ,原因: 1.直接操作字节码文件性能更优 2.第一种方法会将扫描的类全部加入到堆栈内存中,无疑会浪费空间,增加gc次数。第二种可以根据元数据按需加载。 我们以包扫描的doScan方法为例(ClassPathBeanDefinitionScanner类): protected Set<BeanDefinitionHolder> doScan(String... basePackages) { // 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、scoped-proxy,可以指定代理方式jdk或者cglib ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // 首先从注解中获取bean的名字,如果没有 // 使用beanName生成器beanNameGenerator来生成beanName // 在注解中的bean的默认名称和xml中是不一致的 // 注解中如果没有指定名字本质是通过ClassUtil 的 getShortName 方法获取的 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 将进一步设置应用于给定的BeanDefinition,使用AbstractBeanDefinition的一些默认属性值 //设置autowireCandidate属性,即XML的autowire-candidate属性,IoC学习的时候就见过该属性,默认为true,表示该bean支持成为自动注入候选bean if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } // 如果bean定义是AnnotatedBeanDefinition类型,ScannedGenericBeanDefinition同样属于AnnotatedBeanDefinition类型 if (candidate instanceof AnnotatedBeanDefinition) { // 4 处理类上的其他通用注解:@Lazy, @Primary, @DependsOn, @Role, @Description AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 检查给定的 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> 当然我们**更加关注**的是scanCandidateComponents方法: private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { // 生成完整的资源解析路径 // com.ydlclass -> classpath*:com/ydlclass/**/*.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(); // 此处忽略CGLIB生成的代理类文件,这个应该不陌生 if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { continue; } if (traceEnabled) { logger.trace("Scanning " + resource); } try { // getMetadataReader方法会生成一个元数据读取器 // 我们的例子中是SimpleMetadataReader MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); // 检查读取到的类是否可以作为候选组件,即是否符合TypeFilter类型过滤器的要求 // 使用IncludeFilter。就算目标类上没有@Component注解,它也会被扫描成为一个Bean // 使用ExcludeFilter,就算目标类上面有@Component注解也不会成为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); } } } } } return candidates; } 上边的源码中我们看到读取类文件的真实的实例是simpleMetadataReader,spring选用了【read+visitor】的方式来读取字节码,read负责暴露接口,visitor负责真正的读取工作: final class SimpleMetadataReader implements MetadataReader { 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); } } } // 提供了通用能力 @Override public ClassMetadata getClassMetadata() { return this.annotationMetadata; } @Override public AnnotationMetadata getAnnotationMetadata() { return this.annotationMetadata; } } ![b1ea6ad30566c3e266ad1fdd60e88546.png][] 这些类都是对注解和类的元数据进行了封装,提供更简单的访问方式,很简单。 我们写一个简单的小例子,来看看: @Test public void testAsm() throws IOException { Resource resource = new ClassPathResource("com/ydlclass/User.class"); ClassReader classReader = new ClassReader(resource.getInputStream()); logger.info(classReader.getClassName()); // 缺少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.info("The first item is {}.",classReader.getItemCount()); // 通过javap -v .\User.class class文件访问标志 // flags: (0x0021) ACC_PUBLIC, ACC_SUPER 十进制就是33 // ACC_SUPER 0x00 20 是否允许使用invokespecial字节码指令的新语义. // ACC_PUBLIC 0x00 01 是否为Public类型 logger.info("classReader.getAccess() is {}",classReader.getAccess()); } 注:不同的扫描方式形成了不同的Definition子类如下: ![76b94ea0499a8343e7f36ed0ecbe7522.png][] ### 6.发布订阅 ### spring为我们提供了event multicaster,可以十分简单的实现发布订阅模式: multicast(组播): 也叫多播, 多点广播或群播。 指把信息同时传递给一组目的地址。它使用策略是最高效的,因为消息在每条网络链路上只需传递一次,而且只有在链路分叉的时候,消息才会被复制。 ![68a63740449d024b2d0227a023e022eb.png][] 这其中有很多复杂的情况,比如listener是我们直接手动注册的实例呢,还是spring工厂中的bean呢,如果是bean是singleton还是prototype呢? 因为源码比较复杂,我们需要结合下图一起看: ![5f9cb07b7f6e041bda1d51db248a0d11.png][] 图二显示了当listener被调用执行后,如何进行了缓存: ![3fc3cd68cc0ae27abb6a6dadd4e307c0.png][] ![8e299a04b0524155b129a50d34424dc8.jpeg][]’ [255a6f94161549c4a084f9a20857fffd.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/473ebf54e5ee4b0c86d908016f887289.png [ed7ff319e6684cc1ae8db4ea724fb61d.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/1f3375c6b4294a4f8ca2b3cc44c74535.png [88064e4e6c014ae8a84de8c080204af1.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/2c1899db8386488382ba9a5c56fa6f76.png [14a33dd22d824e75b829232cc6555588.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/9a3bd0f6d5e742a7b2536a1bef32bd78.png [f600d22cb362416193720aca09acacb2.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/98b413522ba74667990d66a66ee626ad.png [6d2d38e32cc946588341b903c46a26b6.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/f9197fdb5b85479dab38f485bbce46b5.png [addf7033edf34bb990e22579c55dca75.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/6c383bdb35d746f5a115a7b25a711cab.png [114f178d3b3f4409b0af1bda53bda5e8.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/30a4ac8d7d094d689121d9575625d1d9.png [b1ea6ad30566c3e266ad1fdd60e88546.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/31c8b69da9534fe89663fa3a6df43945.png [76b94ea0499a8343e7f36ed0ecbe7522.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/ee35bab2fe754e828b2f0e1631ade88f.png [68a63740449d024b2d0227a023e022eb.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/747234d054eb40c3805ad224c89017a9.png [5f9cb07b7f6e041bda1d51db248a0d11.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/c0df5d05599e4257a468d6d253315982.png [3fc3cd68cc0ae27abb6a6dadd4e307c0.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/a5a84c55a76e4d28a4132ba2e38cb9fb.png [8e299a04b0524155b129a50d34424dc8.jpeg]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/26/d634f91b1a6744d090c743c8d994a80e.png
还没有评论,来说两句吧...