[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
英文地址,請注明出處:http://en.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流程…