[spring6: TypeFilter MetadataReader MetadataReaderFactory]-源码解析

源码

MetadataReaderFactory

MetadataReaderFactory 是用于创建 MetadataReader 实例的工厂接口,支持通过类名或资源读取类的元数据并可实现缓存优化。

类型类/接口名功能描述是否需要加载类访问方式
抽象接口AnnotatedTypeMetadata访问某类型(类或方法)的注解信息抽象,支持字节码读取
抽象接口AnnotationMetadata访问某类的注解信息抽象,支持字节码读取
抽象接口ClassMetadata访问某类的基本元数据(类名、父类、接口等)抽象,支持字节码读取
抽象接口MethodMetadata访问某方法的注解和方法签名抽象,支持字节码读取
反射实现StandardAnnotationMetadata基于反射访问类注解元数据Java反射
反射实现StandardClassMetadata基于反射访问类的基本元数据Java反射
反射实现StandardMethodMetadata基于反射访问方法元数据Java反射
public interface MetadataReaderFactory {MetadataReader getMetadataReader(String className) throws IOException;MetadataReader getMetadataReader(Resource resource) throws IOException;
}

SimpleMetadataReaderFactory

SimpleMetadataReaderFactory 根据类名或资源路径加载对应的 .class 文件,创建用于读取类元数据的 MetadataReader 实例。

public class SimpleMetadataReaderFactory implements MetadataReaderFactory {private final ResourceLoader resourceLoader;public SimpleMetadataReaderFactory() {this.resourceLoader = new DefaultResourceLoader();}public SimpleMetadataReaderFactory(@Nullable ResourceLoader resourceLoader) {this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());}public SimpleMetadataReaderFactory(@Nullable ClassLoader classLoader) {this.resourceLoader = (classLoader != null ? new DefaultResourceLoader(classLoader) : new DefaultResourceLoader());}public final ResourceLoader getResourceLoader() {return this.resourceLoader;}@Overridepublic MetadataReader getMetadataReader(String className) throws IOException {try {// "classpath:" + ClassUtils.convertClassNameToResourcePath(className) + ".class"String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;Resource resource = this.resourceLoader.getResource(resourcePath);return getMetadataReader(resource);}catch (FileNotFoundException ex) {int lastDotIndex = className.lastIndexOf('.');if (lastDotIndex != -1) {String innerClassName =className.substring(0, lastDotIndex) + '$' + className.substring(lastDotIndex + 1);String innerClassResourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +ClassUtils.convertClassNameToResourcePath(innerClassName) + ClassUtils.CLASS_FILE_SUFFIX;Resource innerClassResource = this.resourceLoader.getResource(innerClassResourcePath);if (innerClassResource.exists()) {return getMetadataReader(innerClassResource);}}throw ex;}}@Overridepublic MetadataReader getMetadataReader(Resource resource) throws IOException {return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());}
}

CachingMetadataReaderFactory

CachingMetadataReaderFactorySimpleMetadataReaderFactory 的缓存增强版,针对每个 .class 文件资源缓存对应的 MetadataReader 实例,避免重复解析,提高读取类元数据的效率,并支持缓存大小限制与线程安全访问。

public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory {public static final int DEFAULT_CACHE_LIMIT = 256;@Nullableprivate Map<Resource, MetadataReader> metadataReaderCache;public CachingMetadataReaderFactory() {super();setCacheLimit(DEFAULT_CACHE_LIMIT);}public CachingMetadataReaderFactory(@Nullable ClassLoader classLoader) {super(classLoader);setCacheLimit(DEFAULT_CACHE_LIMIT);}public CachingMetadataReaderFactory(@Nullable ResourceLoader resourceLoader) {super(resourceLoader);if (resourceLoader instanceof DefaultResourceLoader defaultResourceLoader) {this.metadataReaderCache = defaultResourceLoader.getResourceCache(MetadataReader.class);}else {setCacheLimit(DEFAULT_CACHE_LIMIT);}}public void setCacheLimit(int cacheLimit) {if (cacheLimit <= 0) {this.metadataReaderCache = null;}else if (this.metadataReaderCache instanceof LocalResourceCache localResourceCache) {localResourceCache.setCacheLimit(cacheLimit);}else {this.metadataReaderCache = new LocalResourceCache(cacheLimit);}}public int getCacheLimit() {if (this.metadataReaderCache instanceof LocalResourceCache localResourceCache) {return localResourceCache.getCacheLimit();}else {return (this.metadataReaderCache != null ? Integer.MAX_VALUE : 0);}}@Overridepublic MetadataReader getMetadataReader(Resource resource) throws IOException {if (this.metadataReaderCache instanceof ConcurrentMap) {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) {metadataReader = super.getMetadataReader(resource);this.metadataReaderCache.put(resource, metadataReader);}return metadataReader;}}else {return super.getMetadataReader(resource);}}public void clearCache() {if (this.metadataReaderCache instanceof LocalResourceCache) {synchronized (this.metadataReaderCache) {this.metadataReaderCache.clear();}}else if (this.metadataReaderCache != null) {setCacheLimit(DEFAULT_CACHE_LIMIT);}}@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;}@Overrideprotected boolean removeEldestEntry(Map.Entry<Resource, MetadataReader> eldest) {return size() > this.cacheLimit;}}
}

MetadataReader

MetadataReader 是一个用于读取类文件元数据(包括类信息和注解信息)的简单门面接口,常用于组件扫描过程中的类型过滤判断。

public interface MetadataReader {Resource getResource();ClassMetadata getClassMetadata();AnnotationMetadata getAnnotationMetadata();
}

SimpleMetadataReader

final class SimpleMetadataReader implements MetadataReader {// 性能优化:不加载无用信息,加快 ClassReader 处理速度。// 避免类初始化:避免加载类体或触发静态初始化,保证扫描阶段安全。// 只关注元数据:如类名、注解、父类、接口、方法签名等,无需方法体逻辑。// 跳过类的调试信息、方法字节码和帧信息,只保留元数据,从而提升类扫描效率。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);getClassReader(resource).accept(visitor, PARSING_OPTIONS);this.resource = resource;this.annotationMetadata = visitor.getMetadata();}private static ClassReader getClassReader(Resource resource) throws IOException {try (InputStream is = resource.getInputStream()) {try {return new ClassReader(is);}catch (IllegalArgumentException ex) {throw new ClassFormatException("ASM ClassReader failed to parse class file - " +"probably due to a new Java class file version that is not supported yet. " +"Consider compiling with a lower '-target' or upgrade your framework version. " +"Affected class: " + resource, ex);}}}@Overridepublic Resource getResource() {return this.resource;}@Overridepublic ClassMetadata getClassMetadata() {return this.annotationMetadata;}@Overridepublic AnnotationMetadata getAnnotationMetadata() {return this.annotationMetadata;}}

SimpleAnnotationMetadataReadingVisitor

SimpleAnnotationMetadataReadingVisitor 是基于 ASM 的 ClassVisitor实现,用于读取类的注解、方法、父类、接口等元信息,并构建 SimpleAnnotationMetadata。

final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {@Nullableprivate final ClassLoader classLoader;// 当前类的全限定名private String className = "";// 类的访问修饰符(如 public, abstract, interface)private int access;// 父类名称(不包括 Object)@Nullableprivate String superClassName;// 若当前类是内部类,记录其外部类名@Nullableprivate String enclosingClassName;// 是否是静态内部类private boolean independentInnerClass;// 实现的接口集合private final Set<String> interfaceNames = new LinkedHashSet<>(4);// 当前类包含的成员类(如内部类)private final Set<String> memberClassNames = new LinkedHashSet<>(4);// 所有类级别的合并注解private final Set<MergedAnnotation<?>> annotations = new LinkedHashSet<>(4);// 所有非构造器、非桥方法的元数据private final Set<MethodMetadata> declaredMethods = new LinkedHashSet<>(4);@Nullableprivate SimpleAnnotationMetadata metadata;@Nullableprivate Source source;SimpleAnnotationMetadataReadingVisitor(@Nullable ClassLoader classLoader) {super(SpringAsmInfo.ASM_VERSION);this.classLoader = classLoader;}@Overridepublic void visit(int version, int access, String name, String signature,@Nullable String supername, String[] interfaces) {this.className = toClassName(name);this.access = access;if (supername != null && !isInterface(access)) {this.superClassName = toClassName(supername);}for (String element : interfaces) {this.interfaceNames.add(toClassName(element));}}@Overridepublic void visitOuterClass(String owner, String name, String desc) {this.enclosingClassName = toClassName(owner);}@Overridepublic void visitInnerClass(String name, @Nullable String outerName, String innerName, int access) {if (outerName != null) {String className = toClassName(name);String outerClassName = toClassName(outerName);if (this.className.equals(className)) {this.enclosingClassName = outerClassName;this.independentInnerClass = ((access & Opcodes.ACC_STATIC) != 0);}else if (this.className.equals(outerClassName)) {this.memberClassNames.add(className);}}}// 当扫描到类上的注解时,visitAnnotation 方法会创建一个 MergedAnnotationReadingVisitor 来读取注解内容,并将其添加到注解集合中。@Override@Nullablepublic AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {return MergedAnnotationReadingVisitor.get(this.classLoader, getSource(),descriptor, visible, this.annotations::add);}// visitMethod 方法跳过构造方法和桥接方法,仅为普通用户方法创建 SimpleMethodMetadataReadingVisitor,用于收集方法注解信息并添加到集合中。@Override@Nullablepublic MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {// Skip bridge methods and constructors - we're only interested in original user methods.if (isBridge(access) || name.equals("<init>")) {return null;}return new SimpleMethodMetadataReadingVisitor(this.classLoader, this.className,access, name, descriptor, this.declaredMethods::add);}// 在类字节码扫描结束时,将收集的类名、访问权限、父类、接口、内部类、方法和注解等信息封装为 SimpleAnnotationMetadata 实例,完成类的注解元数据构建。@Overridepublic void visitEnd() {MergedAnnotations annotations = MergedAnnotations.of(this.annotations);this.metadata = new SimpleAnnotationMetadata(this.className, this.access,this.enclosingClassName, this.superClassName, this.independentInnerClass,this.interfaceNames, this.memberClassNames, this.declaredMethods, annotations);}public SimpleAnnotationMetadata getMetadata() {Assert.state(this.metadata != null, "AnnotationMetadata not initialized");return this.metadata;}private Source getSource() {Source source = this.source;if (source == null) {source = new Source(this.className);this.source = source;}return source;}private String toClassName(String name) {return ClassUtils.convertResourcePathToClassName(name);}private boolean isBridge(int access) {return (access & Opcodes.ACC_BRIDGE) != 0;}private boolean isInterface(int access) {return (access & Opcodes.ACC_INTERFACE) != 0;}/*** {@link MergedAnnotation} source.*/private static final class Source {private final String className;Source(String className) {this.className = className;}}}

SimpleMethodMetadataReadingVisitor

SimpleMethodMetadataReadingVisitor 是基于 ASM 的 MethodVisitor 实现,用于读取方法的访问权限、签名、注解等元数据,并将这些方法信息封装成简化的元数据对象供框架使用。

final class SimpleMethodMetadataReadingVisitor extends MethodVisitor {@Nullableprivate final ClassLoader classLoader;private final String declaringClassName;private final int access;private final String methodName;private final String descriptor;private final List<MergedAnnotation<?>> annotations = new ArrayList<>(4);private final Consumer<SimpleMethodMetadata> consumer;@Nullableprivate Source source;SimpleMethodMetadataReadingVisitor(@Nullable ClassLoader classLoader, String declaringClassName,int access, String methodName, String descriptor, Consumer<SimpleMethodMetadata> consumer) {super(SpringAsmInfo.ASM_VERSION);this.classLoader = classLoader;this.declaringClassName = declaringClassName;this.access = access;this.methodName = methodName;this.descriptor = descriptor;this.consumer = consumer;}@Override@Nullablepublic AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {return MergedAnnotationReadingVisitor.get(this.classLoader, getSource(),descriptor, visible, this.annotations::add);}@Overridepublic void visitEnd() {String returnTypeName = Type.getReturnType(this.descriptor).getClassName();MergedAnnotations annotations = MergedAnnotations.of(this.annotations);SimpleMethodMetadata metadata = new SimpleMethodMetadata(this.methodName, this.access,this.declaringClassName, returnTypeName, getSource(), annotations);this.consumer.accept(metadata);}private Object getSource() {Source source = this.source;if (source == null) {source = new Source(this.declaringClassName, this.methodName, this.descriptor);this.source = source;}return source;}/*** {@link MergedAnnotation} source.*/static final class Source {private final String declaringClassName;private final String methodName;private final String descriptor;@Nullableprivate String toStringValue;Source(String declaringClassName, String methodName, String descriptor) {this.declaringClassName = declaringClassName;this.methodName = methodName;this.descriptor = descriptor;}}}

MergedAnnotationReadingVisitor

MergedAnnotationReadingVisitor 通过 ASM 访问字节码中的注解及其属性,递归解析注解及嵌套注解,最终构建出 Spring 框架通用的 MergedAnnotation 元数据对象,便于框架高效统一地读取和处理注解信息。

class MergedAnnotationReadingVisitor<A extends Annotation> extends AnnotationVisitor {@Nullableprivate final ClassLoader classLoader;@Nullableprivate final Object source;private final Class<A> annotationType;private final Consumer<MergedAnnotation<A>> consumer;// 存储注解的所有属性名和值private final Map<String, Object> attributes = new LinkedHashMap<>(4);public MergedAnnotationReadingVisitor(@Nullable ClassLoader classLoader, @Nullable Object source, Class<A> annotationType, Consumer<MergedAnnotation<A>> consumer) {super(SpringAsmInfo.ASM_VERSION);this.classLoader = classLoader;this.source = source;this.annotationType = annotationType;this.consumer = consumer;}@Overridepublic void visit(String name, Object value) {if (value instanceof Type type) {value = type.getClassName();}this.attributes.put(name, value);}@Overridepublic void visitEnum(String name, String descriptor, String value) {visitEnum(descriptor, value, enumValue -> this.attributes.put(name, enumValue));}@Override@Nullablepublic AnnotationVisitor visitAnnotation(String name, String descriptor) {return visitAnnotation(descriptor, annotation -> this.attributes.put(name, annotation));}@Overridepublic AnnotationVisitor visitArray(String name) {return new ArrayVisitor(value -> this.attributes.put(name, value));}@Overridepublic void visitEnd() {Map<String, Object> compactedAttributes = (this.attributes.isEmpty() ? Collections.emptyMap() : this.attributes);MergedAnnotation<A> annotation = MergedAnnotation.of(this.classLoader, this.source, this.annotationType, compactedAttributes);this.consumer.accept(annotation);}@SuppressWarnings("unchecked")public <E extends Enum<E>> void visitEnum(String descriptor, String value, Consumer<E> consumer) {String className = Type.getType(descriptor).getClassName();Class<E> type = (Class<E>) ClassUtils.resolveClassName(className, this.classLoader);consumer.accept(Enum.valueOf(type, value));}@SuppressWarnings("unchecked")@Nullableprivate <T extends Annotation> AnnotationVisitor visitAnnotation(String descriptor, Consumer<MergedAnnotation<T>> consumer) {String className = Type.getType(descriptor).getClassName();if (AnnotationFilter.PLAIN.matches(className)) {return null;}Class<T> type = (Class<T>) ClassUtils.resolveClassName(className, this.classLoader);return new MergedAnnotationReadingVisitor<>(this.classLoader, this.source, type, consumer);}@SuppressWarnings("unchecked")@Nullablestatic <A extends Annotation> AnnotationVisitor get(@Nullable ClassLoader classLoader,@Nullable Object source, String descriptor, boolean visible,Consumer<MergedAnnotation<A>> consumer) {if (!visible) {return null;}String typeName = Type.getType(descriptor).getClassName();if (AnnotationFilter.PLAIN.matches(typeName)) {return null;}try {Class<A> annotationType = (Class<A>) ClassUtils.forName(typeName, classLoader);return new MergedAnnotationReadingVisitor<>(classLoader, source, annotationType, consumer);}catch (ClassNotFoundException | LinkageError ex) {return null;}}/*** {@link AnnotationVisitor} to deal with array attributes.*/private class ArrayVisitor extends AnnotationVisitor {private final List<Object> elements = new ArrayList<>();private final Consumer<Object[]> consumer;ArrayVisitor(Consumer<Object[]> consumer) {super(SpringAsmInfo.ASM_VERSION);this.consumer = consumer;}@Overridepublic void visit(String name, Object value) {if (value instanceof Type type) {value = type.getClassName();}this.elements.add(value);}@Overridepublic void visitEnum(String name, String descriptor, String value) {MergedAnnotationReadingVisitor.this.visitEnum(descriptor, value, this.elements::add);}@Override@Nullablepublic AnnotationVisitor visitAnnotation(String name, String descriptor) {return MergedAnnotationReadingVisitor.this.visitAnnotation(descriptor, this.elements::add);}@Overridepublic void visitEnd() {Class<?> componentType = getComponentType();Object[] array = (Object[]) Array.newInstance(componentType, this.elements.size());this.consumer.accept(this.elements.toArray(array));}private Class<?> getComponentType() {if (this.elements.isEmpty()) {return Object.class;}Object firstElement = this.elements.get(0);if (firstElement instanceof Enum<?> enumeration) {return enumeration.getDeclaringClass();}return firstElement.getClass();}}
}

TypeFilter

TypeFilter 是一个函数式接口,用于基于类的元数据判断该类是否符合组件扫描条件。

@FunctionalInterface
public interface TypeFilter {boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException;
}

org.springframework.core.type.filter 是 spring core包中用于类路径扫描过程中的类型过滤支持包,提供了多种 TypeFilter 实现,可按注解、父类、正则、AspectJ 表达式等条件筛选类。

类名用途特点
TypeFilter过滤接口核心接口,所有过滤器的基础
AbstractClassTestingTypeFilter抽象类提供 ClassMetadata 访问
AbstractTypeHierarchyTraversingFilter抽象类支持递归检查父类和接口
AnnotationTypeFilter注解过滤匹配指定注解,可配置是否继承
AssignableTypeFilter父类/接口匹配匹配指定类型的子类或实现类
RegexPatternTypeFilter类名正则使用正则表达式匹配类名
AspectJTypeFilterAspectJ 表达式匹配功能强大,适合复杂匹配需求

例子

@Component
public class CustomConfig {@Beanpublic Object customObj() {return new Object();}
}
public class CustomTypeFilter implements TypeFilter {@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {String className = metadataReader.getClassMetadata().getClassName();// 只对 CustomConfig 类进行打印(可以根据需要改成其他判断)if (className.contains("CustomConfig")) {// 打印类上的所有注解MergedAnnotations classAnnotations = metadataReader.getAnnotationMetadata().getAnnotations();System.out.println("Class Annotations of " + className + ":");classAnnotations.stream().forEach(annotation -> {System.out.println(" - " + annotation.getType().getName());});// 打印方法上的所有注解System.out.println("Method Annotations:");for (MethodMetadata methodMetadata : metadataReader.getAnnotationMetadata().getDeclaredMethods()) {System.out.println(" Method: " + methodMetadata.getMethodName());MergedAnnotations methodAnnotations = methodMetadata.getAnnotations();methodAnnotations.stream().forEach(annotation -> {System.out.println("   - " + annotation.getType().getName());});}}// 根据实际需要返回 true 或 false,这里只是打印,返回 false 不影响扫描return false;}
}
@Configuration
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = CustomTypeFilter.class))
public class Application {@Beanpublic CommandLineRunner commandLineRunner(ApplicationContext ctx) {return args -> ctx.getBean(CustomConfig.class);}public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
Class Annotations of xyz.idoly.demo.config.CustomConfig:- org.springframework.stereotype.Component- org.springframework.stereotype.Indexed
Method Annotations:Method: customObj- org.springframework.context.annotation.Bean

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/90763.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/90763.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于redis的分布式session共享管理之销毁事件不生效问题

一、前言首先介绍下分布式session共享管理在Springboot项目中&#xff0c;经常提到分布式的概念&#xff0c;当实际部署应用后&#xff0c;多台服务器各自存储用户登录会话无法共享&#xff0c;导致操作A按钮还是正常&#xff0c;操作B按钮就提示登录过期需要重新登录。这是因为…

技术面试问题总结二

一、lvs的四种工作模式: LVS 有四种主要工作模式&#xff1a;NAT 模式、DR 模式、TUN 模式和Full-NAT 模式 1、NAT模式&#xff1a; 工作原理 LVS 作为客户端和真实服务器&#xff08;RS&#xff09;之间的中间节点&#xff0c;接收客户端请求后&#xff0c;修改请求的目标…

软考(软件设计师)软件工程-软件过程模型,敏捷开发

软件过程模型 瀑布模型 #mermaid-svg-daxck2eQmqfYelkV {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-daxck2eQmqfYelkV .error-icon{fill:#552222;}#mermaid-svg-daxck2eQmqfYelkV .error-text{fill:#552222;stro…

MySQL 中图标字符存储问题探究:成因、解决方案及单字段编码调整的利弊分析——仙盟创梦IDE

在 MySQL 数据库应用中&#xff0c;常出现无法正确保存图标字符&#xff0c;读出时显示为 “????” 的问题。本文深入剖析了该问题产生的原因&#xff0c;主要涉及字符编码设置不匹配等因素。同时&#xff0c;提出了全面的解决方案&#xff0c;包括全局和单字段的字符编码调…

快速上手UniApp(适用于有Vue3基础的)

作为一位有Vue3基础的开发者&#xff0c;学习UniApp将会是一个相对平滑的过程。UniApp是一个使用Vue.js开发跨平台应用的前端框架&#xff0c;可以编译到iOS、Android、H5以及各种小程序平台。 一、UniApp简介 UniApp是基于Vue.js的跨平台开发框架&#xff0c;具有以下特点&a…

background和background-color的区别

前言&#xff1a;由于全局切换变量时&#xff0c;发现空页面按钮变量颜色未生效&#xff0c;审查元素发现变量未定义。实际上是背景色由纯色变成了渐变色&#xff0c;而background-color不支持渐变色导致变量不生效特性backgroundbackground-color功能设置‌所有‌背景属性&…

Vue Vue-route (5)

Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue-route History模式和路由懒加载 目录 History模式 设置history模式 后端配置 Apache 路由懒加载 配置 总结 History模式 设置history模式 Vue-route默认hash模式——使用URL的hash来模拟一个完整的URL&#xff0c…

家用智能摄像机PRV文件删除的恢复方法

家用智能摄像头一般采用的是mp4或者mov视频方案&#xff0c;这一类方案文件通用性强、使用简单&#xff0c;以MP4为例无论是APP在线播放还是TF卡接电脑查看都很轻松。即便如此&#xff0c;有些厂商还是走上了“自定义”的道路&#xff0c;自定义的文件结构导致无法正常播放&…

聊下easyexcel导出

直接上干货&#xff0c;首先pom文件引入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></dependency>接下来是java代码 public void export(List<Liquidity…

[Python] Flask 多线程绘图时报错“main thread is not in main loop”的解决方案

在构建基于 Flask 的后端服务过程中,使用 matplotlib 绘图时,很多开发者会遇到一个经典的运行时错误: RuntimeError: main thread is not in main loop这通常出现在服务开启多线程时调用 matplotlib,本文将从原理、解决方式到部署建议进行全面解析。 一、问题来源:matpl…

dbEaver连接hbase,各种问题的终极解决

网上有不少文章&#xff0c;但基本都不行&#xff0c;主要还是hbase版本和phoenix版本的问题&#xff0c;经我测试&#xff0c;如下方法保证能连接成功。 1、下载phoenix: https://phoenix.apache.org/download.html 要选择和你的hbase版本对应的版本。 2、解压phoenix-hbase-2…

selenium中find_element()用法进行元素定位

1. 导入必要的模块首先需要导入 By 类&#xff1a;from selenium.webdriver.common.by import By2. 常用定位方式(1) 通过ID定位element driver.find_element(By.ID, "username") element.send_keys("testuser") # 输入内容 (2) 通过Name定位element dr…

第八讲~~数据库技术

前言&#xff1a;什么是数据库&#xff1f;存储数据的仓库。常见的数据库有哪些&#xff1f;————SQL Server&#xff08;数据库较大 5G&#xff09;————Access————Oracle&#xff08;大型数据库700多兆-200多兆&#xff09;&#xff08;付费&#xff09;————My…

无人机雷达模块运行与技术解析

一、运行方式1. 传感器数据采集 雷达发射高频电磁波&#xff08;X/Ku波段或毫米波&#xff09;&#xff0c;接收无人机反射的回波信号。 多传感器协同&#xff1a;雷达与光电、无线电侦测、声学设备并行扫描空域&#xff0c;覆盖不同频段与物理特性&#xff08;如热信号、声纹…

STM32中ADC详解

前言 在嵌入式系统中&#xff0c;模拟信号与数字信号的转换是连接物理世界与数字系统的核心环节。ADC&#xff08;Analog-to-Digital Converter&#xff0c;模数转换器&#xff09;作为实现这一转换的关键外设&#xff0c;被广泛应用于传感器数据采集&#xff08;如温湿度、光照…

机器学习(ML)、深度学习(DL)、强化学习(RL)关系和区别

机器学习&#xff08;ML&#xff09;、深度学习&#xff08;DL&#xff09;、强化学习&#xff08;RL&#xff09;关系和区别区别一、机器学习的技术分层与范畴二、深度学习&#xff08;DL&#xff09; vs. 强化学习&#xff08;RL&#xff09;&#xff1a;在ML中的对比三、深度…

医疗AI前端开发中的常见问题分析和解决方法

一、 前端性能优化问题 (医疗AI场景尤其关键) 页面加载速度慢的原因及解决方案 原因: 海量数据加载: 加载高分辨率DICOM影像序列、大型患者数据集、复杂模型参数。复杂计算: 在浏览器端运行轻量级AI推理(如分割预览)、大型图表渲染。第三方库臃肿: 医学可视化库(Corners…

python库之jieba 库

jieba 库jieba 库的原理分析jieba库可用于将中文的一段语句分解为单词,通常用于解析中文语句的含义。例如外国人需要学习中文而中文语句是一直连续的文字组合。例如“我们在学习Python办公自动化”这句话,外国人在理解这句话的含义时,首先需要将这句话正确地分解为一个个单词,即…

基于Hadoop的航空公司客户数据分析与客户群体K-measn聚类分析(含LRFMC模型)

文章目录有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍数据源介绍数据预处理hadoop集群分析建模分析总结每文一语有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍 本研究依托全国范围内的航空公司…

实习内容总结

相关来自AI非内部资料 Monorepo 大仓 + pnpm + Turborepo 工程化实践原理 核心概念解释 1. Monorepo (单仓库架构) 概念:将多个项目(packages)放在同一个代码仓库中管理,而非分散在多个仓库。优势:统一管理依赖、版本一致性、跨项目复用代码、原子化提交、简化CI/CD流程…