學習記錄:DAY21

我的開發日志:類路徑掃描、DI 容器與動態代理

前言


我失憶了,完全不記得自己早上干了什么。


日程


早上 10 點左右開始,學了一早上,主要是類路徑掃描相關的調試。
晚上 8 點了,真不能再摸🐟了。


學習記錄


計算機網絡:
1. 子網劃分與子網掩碼


學習內容


省流

  1. 手搓類路徑掃描器
  2. 手搓基礎 DI 容器
  3. 動態代理

1. 手搓類路徑掃描器

1)首先要確定需要掃描的包

String path = packageName.replace('.', '/');

2)然后獲取系統類加載器獲取路徑,并獲取該路徑下的所有資源

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Enumeration<URL> resources = classLoader.getResources(path);

注意:系統類加載器 (ClassLoader.getSystemClassLoader()) 的掃描范圍包括所有在 JVM 啟動時通過 -classpath-cp 指定的路徑(包括 Maven/Gradle 依賴)。

3)遍歷所有的資源,通過 resource.getProtocol() 獲取 URL 對象的協議類型,獲取 URL 對象的類文件

while (resources.hasMoreElements()){URL resource = resources.nextElement();if (resource.getProtocol().equals("file")) {classes.addAll(findClasses(new File(resource.getFile()), packageName, classFilter));}
}

->進入 findClasses 方法

4)獲取文件列表,遍歷文件和子目錄,獲取 clazz 對象,并返回 List<Class<?>> classes 列表

File[] files = directory.listFiles();          
for (File file : files) {if (file.isDirectory()) {String subPackage = packageName + "." + file.getName();classes.addAll(findClasses(file, subPackage, classFilter));} else if (file.getName().endsWith(".class")) {String className = packageName + '.' +file.getName().substring(0, file.getName().length() - 6); //獲取class全類名Class<?> clazz = Class.forName(className, false, Thread.currentThread().getContextClassLoader()); //根據全類名找到clazz對象(不對類進行初始化)if (classFilter.test(clazz)) { //過濾器檢查classes.add(clazz);}}
}
return classes;

5)提供了一個掃描含有對應注解的類

public static List<Class<?>> scanClassesWithAnnotation(String packageName,Class<? extends java.lang.annotation.Annotation> annotation) {return scanClasses(packageName, clazz -> clazz.isAnnotationPresent(annotation));
}

Class<? extends java.lang.annotation.Annotation> 表示接收的 Class 對象是 java.lang.annotation.Annotation 的任意子類。

2. 手搓基礎 DI 容器

0)用 map 來儲存映射,在創建類對象時進行掃描
// 存儲類定義的映射(類名 -> 類對象)
private final Map<String, Class<?>> classRegistry = new HashMap<>();
// 存儲單例實例的映射(類名 -> 實例)
private final Map<String, Object> singletonInstances = new HashMap<>();
// 正在創建的Bean記錄(用于解決循環依賴)
private final Set<String> beansInCreation = new HashSet<>();
//接口到實現類的映射
private final Map<Class<?>, Class<?>> interfaceToImplementation = new HashMap<>();
// 包掃描路徑
private final String basePackage;public ContainerFactory(String basePackage) {this.basePackage = basePackage;scanComponents();initializeInterfaceLinks();initializeSingletons();
}
1)組件掃描
private void scanComponents() {List<Class<?>> componentClasses = ClassPathScanner.scanClassesWithAnnotation(basePackage, KatComponent.class);for (Class<?> clazz : componentClasses) {register(clazz);}
}

-> 進入 register 方法

2)注冊組件
public void register(Class<?> clazz) {if (clazz.isAnnotationPresent(KatComponent.class)) {String beanName = getBeanName(clazz);classRegistry.put(beanName, clazz);}
}

// 獲取Bean名稱

private String getBeanName(Class<?> clazz) {KatComponent component = clazz.getAnnotation(KatComponent.class);return component.value().isEmpty() ? clazz.getSimpleName() : component.value(); //注解沒有指定Bean名稱時,以類名作為Bean名稱
}
3)對單例 Bean 進行初始化
// 初始化所有單例Bean
private void initializeSingletons() {for (Map.Entry<String, Class<?>> entry : classRegistry.entrySet()) {Class<?> clazz = entry.getValue();if (clazz.isAnnotationPresent(KatSingleton.class)) {getBean(clazz); // 觸發單例初始化}}
}

->進入 getBean 方法

4)獲取 Bean 實例

這里采用了依賴注入接口模式,所以要從接口索引中獲取對應的實現類

// 獲取Bean實例(接口映射)
@SuppressWarnings("unchecked") //忽略泛型警告
public <T> T getBean(Class<T> interfaceType) {//接口模式Class<?> implementationClass = interfaceToImplementation.get(interfaceType); if (implementationClass == null) {throw new RuntimeException("No implementation found for " + interfaceType);}return (T) getBean(getBeanName(implementationClass), implementationClass);
}

//初始化接口索引

private void initializeInterfaceLinks() {for (Class<?> clazz : classRegistry.values()) {for (Class<?> intf : clazz.getInterfaces()) {if (!interfaceToImplementation.containsKey(intf)) {interfaceToImplementation.put(intf, clazz);}}}
}

–>進入實現類 Bean 創建

@SuppressWarnings("unchecked") //忽略泛型警告
public <T> T getBean(String beanName, Class<T> clazz) {// 檢查單例緩存if (singletonInstances.containsKey(beanName)) {return (T) singletonInstances.get(beanName);}// 檢查是否已注冊if (!classRegistry.containsKey(beanName)) {throw new RuntimeException("Bean not registered: " + beanName);}// 檢查循環依賴if (beansInCreation.contains(beanName)) {throw new RuntimeException("Circular dependency detected for bean: " + beanName);}beansInCreation.add(beanName);try {Class<?> targetClass = classRegistry.get(beanName);Object instance = createInstance(targetClass); //創建實例// 如果是單例則緩存if (targetClass.isAnnotationPresent(KatSingleton.class)) {singletonInstances.put(beanName, instance);}return (T) instance;} catch (Exception e) {throw new RuntimeException("Failed to create bean: " + beanName, e);} finally {beansInCreation.remove(beanName);}
}

—>進入 createInstance 方法

5)創建實例
private Object createInstance(Class<?> clazz) throws Exception {// 1. 優先使用@KatAutowired構造器Constructor<?> autowiredCtor = findAutowiredConstructor(clazz);if (autowiredCtor != null) {return createInstanceWithConstructor(autowiredCtor);}// 2. 使用默認無參構造器try {Object instance = clazz.getDeclaredConstructor().newInstance();injectFields(instance);return instance;} catch (NoSuchMethodException e) {throw new RuntimeException("No suitable constructor found for " + clazz.getName());}
}

---->進入 findAutowiredConstructor 方法

// 查找@KatAutowired構造器
private Constructor<?> findAutowiredConstructor(Class<?> clazz) {Constructor<?>[] ctors = clazz.getConstructors();for (Constructor<?> ctor : ctors) {if (ctor.isAnnotationPresent(KatAutowired.class)) {return ctor;}}return null;
}

---->進入 createInstanceWithConstructor 方法

// 使用構造器創建實例
private Object createInstanceWithConstructor(Constructor<?> ctor) throws Exception {Class<?>[] paramTypes = ctor.getParameterTypes(); //獲取參數Object[] args = new Object[paramTypes.length];for (int i = 0; i < paramTypes.length; i++) { //添加參數args[i] = getBean(paramTypes[i]);}Object instance = ctor.newInstance(args); //創建實例injectFields(instance); //注入依賴字段return instance;
}

----->進入 injectFields 方法

// 注入字段依賴
private void injectFields(Object instance) throws IllegalAccessException {Class<?> clazz = instance.getClass();//遍歷目標類的所有字段(包括私有字段)for (Field field : clazz.getDeclaredFields()) {// 檢查字段是否被@KatAutowired注解標注if (field.isAnnotationPresent(KatAutowired.class)) {Object dependency = getBean(field.getType());field.setAccessible(true); //允許訪問私有字段field.set(instance, dependency); //注入目標字段}}
}

目前只是一個非常基礎的版本,處理不了復雜的依賴關系,整體效率也比較低。
明天考慮兼容動態代理,多路徑掃描(通過配置文件加載)。

3. 動態代理

在運行時動態創建代理類和對象,而不是在編譯時靜態定義。它對于依賴注入后的事務實現以及 AOP 非常重要。

1)原理分析

InvocationHandler 為例
InvocationHandler 是 Java 動態代理機制中的核心接口,它定義了代理對象方法調用的轉發邏輯。

public interface InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}
  • proxy:動態生成的代理對象實例
  • method:被調用的方法對象
  • args:方法調用時傳入的參數數組

使用示例:

class DebugInvocationHandler implements InvocationHandler {private final Object target;public DebugInvocationHandler(Object target) {this.target = target;}   @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 方法調用前邏輯System.out.printf("調用方法: %s,參數: %s%n", method.getName(), Arrays.toString(args));// 調用真實對象的方法Object result = method.invoke(target, args);// 方法調用后邏輯System.out.printf("方法 %s 調用完成,結果: %s%n", method.getName(), result);return result;}
}public static void main(String[] args) {RealSubject real = new RealSubject(); //真實的對象//創建一個代理對象Subject proxy = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),new Class[]{Subject.class},new DebugInvocationHandler(real));//代理對象.method() → InvocationHandler.invoke() → 真實對象.method()proxy.request();
}

結語


大腦已經宕機。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/81166.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/81166.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/81166.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Agent】MCP協議 | 用高德MCP Server制作旅游攻略

note MCP (Model Context Protocol) 代表了 AI 與外部工具和數據交互的標準建立。MCP 的本質&#xff1a;它是一個統一的協議標準&#xff0c;使 AI 模型能夠以一致的方式連接各種數據源和工具&#xff0c;類似于 AI 世界的"USB-C"接口。 它能夠在 LLM/AI Agent 與外…

使用 Spring Data Redis 實現 Redis 數據存儲詳解

使用 Spring Data Redis 實現 Redis 數據存儲詳解 Spring Data Redis 是 Spring 生態中操作 Redis 的核心模塊&#xff0c;它封裝了 Redis 客戶端的底層細節&#xff08;如 Jedis 或 Lettuce&#xff09;&#xff0c;提供了統一的 API 來操作 Redis 的數據結構。以下是詳細實現…

Qt5與現代OpenGL學習(四)X軸方向旋轉60度

把上面兩張圖像放到D盤1文件夾內&#xff1a; shader.h #ifndef SHADER_H #define SHADER_H#include <QDebug> #include <QOpenGLShader> #include <QOpenGLShaderProgram> #include <QString>class Shader { public:Shader(const QString& verte…

【Machine Learning Q and AI 讀書筆記】- 02 自監督學習

Machine Learning Q and AI 中文譯名 大模型技術30講&#xff0c;主要總結了大模型相關的技術要點&#xff0c;結合學術和工程化&#xff0c;對LLM從業者來說&#xff0c;是一份非常好的學習實踐技術地圖. 本文是Machine Learning Q and AI 讀書筆記的第2篇&#xff0c;對應原…

using var connection = connectionFactory.CreateConnection(); using var 是什么意思

在 .NET 中&#xff0c;??垃圾回收&#xff08;Garbage Collection, GC&#xff09;?? 確實是自動管理內存的機制&#xff0c;但它 ??僅適用于托管資源&#xff08;Managed Resources&#xff09;??&#xff08;如類實例、數組等&#xff09;。然而&#xff0c;對于 ?…

Multicore-TSNE

文章目錄 TSNE使用scikit-learn庫使用Multicore-TSNE庫安裝方法基本使用方法采用不同的距離度量 其他資料 TSNE t-Distributed Stochastic Neighbor Embedding (t-SNE) 是一種高維數據的降維方法&#xff0c;由Laurens van der Maaten和Geoffrey Hinton于2008年提出&#xff0…

SI5338-EVB Usage Guide(LVPECL、LVDS、HCSL、CMOS、SSTL、HSTL)

目錄 1. 簡介 1.1 EVB 介紹 1.2 Si5338 Block Diagram 2. EVB 詳解 2.1 實物圖 2.2 基本配置 2.2.1 Universal Pin 2.2.2 IIC I/F 2.2.3 Input Clocks 2.2.4 Output Frequencies 2.2.5 Output Driver 2.2.6 Freq and Phase Offset 2.2.7 Spread Spectrum 2.2.8 快…

Spring AI應用系列——基于OpenTelemetry實現大模型調用的可觀測性實踐

一、項目背景與目標 在AI應用日益復雜的今天&#xff0c;大模型服務&#xff08;如語言理解和生成&#xff09;的性能監控和問題排查變得尤為關鍵。為了實現對大模型調用鏈路的可觀測性&#xff08;Observability&#xff09;管理&#xff0c;我們基于 Spring Boot Spring AI…

Spyglass:官方Hands-on Training(一)

相關閱讀 Spyglasshttps://blog.csdn.net/weixin_45791458/category_12828934.html?spm1001.2014.3001.5482 本文是對Spyglass Hands-on Training中第一個實驗的翻譯&#xff08;有刪改&#xff09;&#xff0c;Lab文件可以從以下鏈接獲取。Spyglass Hands-on Traininghttps:…

PCB設計工藝規范(三)走線要求

走線要求 1.走線要求2.固定孔、安裝孔、過孔要求3.基準點要求4.絲印要求 1.走線要求 印制板距板邊距離:V-CUT 邊大于 0.75mm&#xff0c;銑槽邊大于0.3mm。為了保證 PCB 加工時不出現露銅的缺陷&#xff0c;要求所有的走線及銅箔距離板邊:V-CUT邊大于 0.75mm&#xff0c;銑槽邊…

抓取工具Charles配置教程(mac電腦+ios手機)

mac電腦上的配置 1. 下載最新版本的Charles 2. 按照以下截圖進行配置 2.1 端口號配置&#xff1a; 2.2 https配置 3. mac端證書配置 4. IOS手機端網絡配置 4.1 先查看電腦上的配置 4.2 配置手機網絡 連接和電腦同一個wifi&#xff0c;然后按照以下截圖進行配置 5. 手機端證書…

【CSS】精通Flex布局(全)

目錄 1. flex布局體驗 1.1 傳統布局 與 flex布局 1.2 初體驗 2. flex布局原理 2.1 布局原理 3. flex布局父項常見屬性 3.1 常見父項屬性 3.2 屬性值 3.3 justify-content 設置主軸上的子元素排列方式 3.4 flex-wrap設置子元素是否換行 3.5 align-items 設置側軸上的…

力扣第447場周賽

這次終于趕上力扣的周賽了, 賽時成績如下(依舊還是三題 )&#xff1a; 1. 統計被覆蓋的建筑 給你一個正整數 n&#xff0c;表示一個 n x n 的城市&#xff0c;同時給定一個二維數組 buildings&#xff0c;其中 buildings[i] [x, y] 表示位于坐標 [x, y] 的一個 唯一 建筑。 如…

AI中常用概念的理解

1. RAG&#xff08;檢索增強生成&#xff09; 通俗理解&#xff1a;就像你寫作業時&#xff0c;先查課本 / 百度找資料&#xff0c;再根據資料寫答案&#xff0c;而不是純靠記憶瞎編。 AI 模型&#xff08;比如 ChatGPT&#xff09;回答問題時&#xff0c;先去 “數據庫 / 互聯…

SQLServer多版本兼容Java方案和數據采集

Maven引入 <dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>sqljdbc4</artifactId><version>4.0</version></dependency><dependency><groupId>net.sourceforge.jtds</groupId><ar…

【每日八股】復習 Redis Day4:線程模型

文章目錄 復習 Redis Day4&#xff1a;線程模型介紹一下 Redis 的線程模型核心線程模型&#xff08;Redis 6.0 之前&#xff09;Redis 6.0 的多線程改進Redis 真的是單線程嗎&#xff1f;Redis 的線程模型剖析 上一篇 Redis 的應用我今天才完成&#xff0c;因此明天一并復習 Re…

樹莓派智能攝像頭實戰指南:基于TensorFlow Lite的端到端AI部署

引言&#xff1a;嵌入式AI的革新力量 在物聯網與人工智能深度融合的今天&#xff0c;樹莓派這一信用卡大小的計算機正在成為邊緣計算的核心載體。本文將手把手教你打造一款基于TensorFlow Lite的低功耗智能監控設備&#xff0c;通過MobileNetV2模型實現實時物體檢測&#xff0…

vs2019編譯occ7.9.0時,出現fatal error C1060: compiler is out of heap space

問題描述 visual studio 2019編譯opencascade 7.9.0時&#xff0c;出現編譯錯誤 fatal error C1060: compiler is out of heap space 解決方案 修改vs2019并行編譯的線程個數&#xff0c;默認是12個&#xff0c;我改成了4個&#xff0c;問題解決 Tools > Project and Sol…

vue跨域問題總結筆記

目錄 一、Websocket跨域問題 1.nginx配置 2.VUE CLI代理 3.env.development配置 4.nginx日志 5.解決 一、解決跨域的幾種常用方法 1.Vue CLI代理 2.JSONP 3.WebSocket 4.NGINX解決跨域問題 6.Java解決跨域 二、Vue跨域問題詳解 1. 什么是跨域 2. 跨域的例子 3.…

數據結構篇:線性表的另一表達—鏈表之單鏈表(下篇)

目錄 1.前言 2.是否使用二級指針 3.插入/刪除 3.1 pos位置前/后插入 3.2 查找函數 3.3 pos位置刪除 3.4 pos位置后面刪除 3.5 函數的銷毀 4.斷言問題 4.1 斷言pphead 4.2 斷言*pphead 5.三個文件的代碼 5.1 頭文件 5.2 具體函數實現 5.3 測試用例 1.前言 之前是講…