day12-實現Spring底層機制-02

来源:https://www.cnblogs.com/liyuelian/archive/2023/01/28/17071340.html
-Advertisement-
Play Games

實現Spring底層機制-02 3.實現任務階段1 3.1知識拓展-類載入器 Java的類載入器有三種: Bootstrap類載入器 對應路徑 jre/lib Ext類載入器 對應路徑 jre/lib/ext App類載入器 對應路徑 classpath classpath 類路徑,就是java.e ...


實現Spring底層機制-02

3.實現任務階段1

3.1知識拓展-類載入器

  1. Java的類載入器有三種:
    • Bootstrap類載入器 ----- 對應路徑 jre/lib
    • Ext類載入器 ----- 對應路徑 jre/lib/ext
    • App類載入器 ----- 對應路徑 classpath
  2. classpath 類路徑,就是java.exe執行時,指定的路徑。

3.2分析

階段1目標:編寫自己的spring容器,實現掃描包,得到bean的class對象

image-20230128174626959

3.3代碼實現

1.創建新的maven項目,註意把項目的 language level 改為支持 java8

image-20230128163553180

在pom.xml文件中指定編譯版本:

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
</properties>

2.創建的架構如下:

image-20230128180112092

3.自定義ComponentScan註解,用於標記要掃描的包

package com.li.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 李
 * @version 1.0
 * 模仿spring原生註解,自定義一個註解
 * 1. @Target(ElementType.TYPE) 指定ComponentScan註解可以修飾TYPE元素
 * 2. @Retention(RetentionPolicy.RUNTIME) 指定ComponentScan註解 的保留範圍
 * 3. String value() default "";  表示 ComponentScan 可以傳入一個value值
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    //通過value指定要掃描的包
    String value() default "";
}

4.自定義Component註解,用於標記要掃描的類

package com.li.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    //通過value給要註入的bean指定名字
    String value() default "";
}

5.自定義配置類,相當於原生spring的容器配置文件

package com.li.spring.ioc;

import com.li.spring.annotation.ComponentScan;

/**
 * @author 李
 * @version 1.0
 * 這是一個配置類,作用類似我們原生 spring 的容器配置文件 beans.xml
 */
@ComponentScan(value = "com.li.spring.component")
public class MySpringConfig {

}

6.自定義spring容器,類似原生ioc容器。(未完成)

目前的功能:

(1)在初始化時,根據傳入的配置類.class文件,讀取要掃描的包路徑

(2)遍歷包路徑下的文件,找出需要註入的bean

package com.li.spring.ioc;

import com.li.spring.annotation.Component;
import com.li.spring.annotation.ComponentScan;

import java.io.File;
import java.net.URL;

/**
 * @author 李
 * @version 1.0
 * MySpringApplicationContext 類的作用類似Spring原生的ioc容器
 */
public class MySpringApplicationContext {
    private Class configClass;

    //構造器
    public MySpringApplicationContext(Class configClass) {
        this.configClass = configClass;
        //步驟一:獲取要掃描的包
        //1.先得到 MySpringConfig配置類的註解 @ComponentScan(value = "com.li.spring.component")
        ComponentScan componentScan =
                (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.通過 componentScan的 value=>得到要掃描的包路徑
        String path = componentScan.value();
        System.out.println("要掃描的包=" + path);

        //步驟二:得到要掃描的包下的所有資源(類.class)
        //1.得到類的載入器-->App 類載入器
        ClassLoader classLoader = MySpringApplicationContext.class.getClassLoader();
        //2.通過類的載入器獲取到要掃描的包的資源 url=>類似一個路徑
        path = path.replace(".", "/");//將原先路徑的.替換成/ ==> com/li/component
        URL resource = classLoader.getResource(path);
        //resource=file:/D:/IDEA-workspace/spring/out/production/spring/com/li/component
        System.out.println("resource=" + resource);
        //3.將要載入的資源(.class)路徑下的文件進行遍歷
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();//將當前目錄下的所有文件放到files數組中(這裡沒有實現遞歸)
            for (File f : files) {
                //System.out.println("============");
                //System.out.println("AbsolutePath=" + f.getAbsolutePath());
                //獲取文件的絕對路徑
                String fileAbsolutePath = f.getAbsolutePath();

                //只處理.class文件
                if (fileAbsolutePath.endsWith(".class")) {

                    //步驟三:獲取全類名反射對象,並放入容器中
                    //將其轉變為 com.li.spring.component.MyComponent.class 形式
                    //1.先獲取到類名
                    String className = fileAbsolutePath.substring(
                            fileAbsolutePath.lastIndexOf("\\") + 1,
                            fileAbsolutePath.indexOf(".class"));
                    //2.獲取類的完整路徑(全類名)
                    // path.replace("/", ".") => com.li.component
                    String classFullName = path.replace("/", ".") + "." + className;
                    //3.判斷該class文件是否要註入到容器中(該類是否有特定註解)
                    try {
                        /*
                        得到該類的Class對象:
                        (1)Class.forName(className) 可以反射載入類
                        (2)classLoader.loadClass(className)也可以反射類的Class
                        主要區別是:(1)的方式會調用該類的靜態方法,(2)的方法不會
                         */
                        //因為這裡只是要判斷該類有沒有註解,因此使用比較輕量級的方式
                        Class<?> clazz = classLoader.loadClass(classFullName);
                        //判斷該類是否有特定註解
                        if (clazz.isAnnotationPresent(Component.class)) {
                            //以 Component註解為例,如果有其他註解,邏輯一致
                            //如果該類使用了 @Component ,說明是spring bean
                            System.out.println("是一個spring bean=" + clazz + " 類名=" + className);
                        } else {
                            //如果沒有使用,則說明不是spring bean
                            System.out.println("不是一個 spring bean=" + clazz + " 類名=" + className);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("===========================");
        }
    }

    //編寫方法,返回容器對象
    public Object getBean(String name) {
        return null;
    }
}

7.創建兩個自定義 Spring bean,一個普通類作為測試

(1)MonsterService

package com.li.spring.component;

import com.li.spring.annotation.Component;

/**
 * @author 李
 * @version 1.0
 * MonsterService 是一個 Service
 * 1.如果指定了value,那麼在註入spring容器時,以你指定的為準
 * 2.如果沒有指定value,則使用類名(首字母小寫)作為預設名
 */
@Component(value = "monsterService") //將 MonsterService註入到自己的spring容器中
public class MonsterService {
}

(2)MonsterDao

package com.li.spring.component;

import com.li.spring.annotation.Component;

/**
 * @author 李
 * @version 1.0
 */
@Component(value = "monsterDao")
public class MonsterDao {
}

(3)Car,普通類

package com.li.spring.component;

/**
 * @author 李
 * @version 1.0
 */
public class Car {
}

8.進行測試

package com.li.spring.test;

import com.li.spring.ioc.MySpringApplicationContext;
import com.li.spring.ioc.MySpringConfig;

/**
 * @author 李
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) {
        MySpringApplicationContext ioc =
                new MySpringApplicationContext(MySpringConfig.class);
    }
}

測試結果:成功區分指定包下的 bean 和普通類

image-20230128174211834

4.實現任務階段2

4.1分析

階段2目標:掃描指定包,將bean信息封裝到BeanDefinition對象,並放入到Map

image-20230128175457567

BeanDefinitionMap以k-v形式存放bean對象的信息。

  1. key為bean對象的id
  2. value為BeanDefinition對象,該對象存放bean信息。如果bean為prototype,應保存bean的class對象,這樣在調用getBean()方法時可以動態創建對象。

新添加的註解和類:

image-20230128205250738

4.2代碼實現

1.自定義註解,用於指定 bean 是單例還是多例

package com.li.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 李
 * @version 1.0
 * Scope 用於指定 bean的作用範圍 [singleton/prototype]
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    //通過 value 指定 bean 是 singleton 或 prototype
    String value() default "";
}

2.修改MonsterService,添加Scope註解

image-20230128180722015

3.BeanDefinition 用於封裝/記錄 Bean對象的信息

package com.li.spring.ioc;

/**
 * @author 李
 * @version 1.0
 * 用於封裝/記錄 Bean對象的信息:
 * 1.scope
 * 2.Bean對應的 Class對象,用於反射生成對應對象
 */
public class BeanDefinition {
    private String scope;
    private Class clazz;

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    @Override
    public String toString() {
        return "BeanDefinition{" +
                "scope='" + scope + '\'' +
                ", clazz=" + clazz +
                '}';
    }
}

4.修改自定義spring容器 MySpringApplicationContext

增加的功能:

(1)定義屬性beanDefinitionMap,用於存放BeanDefinition對象,BeanDefinition對象存儲bean信息,包括bean是單例還是多例,bean的class對象

(2)將MySpringApplicationContext構造器的所有代碼封裝成一個方法。

部分代碼:

package com.li.spring.ioc;

//...

/**
 * @author 李
 * @version 1.0
 * MySpringApplicationContext 類的作用類似Spring原生的ioc容器
 */
public class MySpringApplicationContext {
    private Class configClass;
    //定義屬性 BeanDefinitionMap->存放BeanDefinition對象
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //構造器
    public MySpringApplicationContext(Class configClass) {
        beanDefinitionByScan(configClass);
        System.out.println("beanDefinitionMap=" + beanDefinitionMap);
    }

    //該方法完成對指定包的掃描,並將Bean信息封裝到BeanDefinition對象,再放入map中
    public void beanDefinitionByScan(Class configClass) {
        //步驟一:獲取要掃描的包
        //...

        //步驟二:得到要掃描的包下的所有資源(類.class)
        //...

        //步驟三:獲取全類名反射對象,並放入容器中
        //...
        //判斷該類是否有特定註解
        if (clazz.isAnnotationPresent(Component.class)) {
            //如果該類使用了 @Component ,說明是spring bean
            System.out.println("是一個spring bean=" + clazz + " 類名=" + className);
            //-------------------新增代碼----------------------
            //得到 BeanName-key
            //1.得到 Component 註解
            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
            //2.得到Component註解的value值
            String beanName = componentAnnotation.value();
            //如果沒有指定,就使用類名(首字母小寫)作為beanName
            if ("".equals(beanName)) {
                beanName = StringUtils.uncapitalize(className);
            }
            //將 Bean信息封裝到 BeanDefinition對象-value
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setClazz(clazz);
            //1.獲取scope
            if (clazz.isAnnotationPresent(Scope.class)) {
                //如果配置了Scope,就設置配置的值
                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                beanDefinition.setScope(scopeAnnotation.value());
            } else {
                //如果沒有配置Scope,就設置預設值singleton
                beanDefinition.setScope("singleton");
            }

            //將beanDefinition對象放入Map中
            beanDefinitionMap.put(beanName, beanDefinition);
            //--------------------新增代碼------------------------
        } else {
            //如果沒有使用,則說明不是spring bean
            System.out.println("不是一個 spring bean=" + clazz + " 類名=" + className);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
System.out.println("===========================");
}}}

    //編寫方法,返回容器對象
    public Object getBean(String name) {
        return null;
    }
}

ps:這裡使用了一個工具包

<dependencies>
    <dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>2.6</version>
    </dependency>
</dependencies>

5.為了測試,將MonsterService的@Component的value註釋

image-20230128191312206

6.測試類

//...
public class AppMain {
    public static void main(String[] args) {
        MySpringApplicationContext ioc =
                new MySpringApplicationContext(MySpringConfig.class);
    }
}

測試結果:成功掃描指定包,並將bean對象的信息放入到beanDefinitionMap中,沒有指定beanId的對象以預設規則作為id。

image-20230128191323395

5.實現任務階段3

5.1分析

階段3目標:初始化bean單例池,並完成getBean方法,createBean方法

image-20230128193359672

5.2代碼實現

1.修改自定義spring容器 MySpringApplicationContext

增加的功能:

(1)增加方法createBean(),用於通過反射創建bean對象

(2)在構造方法中,初始化單例池:如果bean是單例,就通過createBean()將其實例化,然後放入單例池。

(3)實現getBean方法:通過beanName,返回bean對象。

部分代碼:

//構造器
public MySpringApplicationContext(Class configClass) {
    beanDefinitionByScan(configClass);
    //後期封裝成方法---------
    Enumeration<String> keys = beanDefinitionMap.keys();
    //遍歷
    while (keys.hasMoreElements()) {
        //得到 beanName
        String beanName = keys.nextElement();
        //通過beanName得到對應的 beanDefinition 對象
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        //判斷該 bean 是單例還是多例
        if ("singleton".equals(beanDefinition.getScope())) {
            //將該bean實例放入到singletonObjects中
            singletonObjects.put(beanName, createBean(beanDefinition));
        }
    }
    System.out.println("singletonObjects 單例池=" + singletonObjects);
    //------------
    System.out.println("beanDefinitionMap=" + beanDefinitionMap);
}

//完成createBean(BeanDefinition)方法
public Object createBean(BeanDefinition beanDefinition) {
    //得到Bean的class對象
    Class clazz = beanDefinition.getClazz();
    try {
        //反射創建bean實例
        Object instance = clazz.getDeclaredConstructor().newInstance();
        return instance;
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    //如果反射對象失敗
    return null;
}

//編寫方法,返回容器對象
public Object getBean(String name) {
    //傳入的beanName是否在 beanDefinitionMap中存在
    if (beanDefinitionMap.containsKey(name)) {//存在
        //從 beanDefinitionMap中獲取 beanDefinition對象
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
            //如果是單例 bean,直接從單例池獲取
            return singletonObjects.get(name);
        } else {//如果不是單例,調用createBean(),反射創建對象
            return createBean(beanDefinition);
        }
    } else {//不存在
        //拋出空指針異常
        throw new NullPointerException("不存在該bean=" + name);
    }
}

2.測試類

//...
public class AppMain {
    public static void main(String[] args) {
        MySpringApplicationContext ioc =
                new MySpringApplicationContext(MySpringConfig.class);
        //Object xxx = ioc.getBean("xxx");//拋出空指針異常
        //多實例對象的獲取
        MonsterService monsterService = (MonsterService) ioc.getBean("monsterService");
        MonsterService monsterService2 = (MonsterService) ioc.getBean("monsterService");
        System.out.println("monsterService=" + monsterService);
        System.out.println("monsterService2=" + monsterService2);
        //單實例對象的獲取
        MonsterDao monsterDao = (MonsterDao) ioc.getBean("monsterDao");
        MonsterDao monsterDao2 = (MonsterDao) ioc.getBean("monsterDao");
        System.out.println("monsterDao=" + monsterDao);
        System.out.println("monsterDao2=" + monsterDao);
    }
}

測試結果:在創建MySpringApplicationContext對象時,成功初始化了單例池,beanDefinitionMap。並根據beanName返回bean對象。MonsterService添加了Scope=“prototype”註解,因此每一次獲取的對象都是不同的。

image-20230128204117832

6.實現任務4

6.1分析

階段4目標:完成依賴註入

6.2代碼實現

1.自定義註解AutoWired,用於標記需要依賴註入的屬性

package com.li.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 李
 * @version 1.0
 */
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoWired {
    //如果為true,就完成依賴註入
    boolean required() default true;
}

2.為了測試,在MonsterDao,MonsterService中添加測試代碼。

(1)修改MonsterDao,在該類中增加方法

image-20230128210515635

(2)修改MonsterService,在該類中添加屬性monsterDao,並使用AutoWired註解修飾。在方法m1()中調用屬性對象的方法。

image-20230128213305193

3.修改自定義spring容器 MySpringApplicationContext (部分代碼)

修改方法createBean(),因為依賴註入需要在反射創建bean對象時完成。

//完成createBean(BeanDefinition)方法
public Object createBean(BeanDefinition beanDefinition) {
    //得到Bean的class對象
    Class clazz = beanDefinition.getClazz();
    try {
        //反射創建bean實例
        Object instance = clazz.getDeclaredConstructor().newInstance();
        //todo 這裡要加入依賴註入的業務邏輯
        //1.遍歷當前要創建對象的所有屬性欄位
        for (Field declaredField : clazz.getDeclaredFields()) {
            //2.判斷欄位是否有AutoWired註解
            if (declaredField.isAnnotationPresent(AutoWired.class)) {
                //判斷是否需要自動裝配
                AutoWired autoWiredAnnotation = 
                        declaredField.getAnnotation(AutoWired.class);
                if (autoWiredAnnotation.required()) {
                    //3.得到欄位的名稱
                    String name = declaredField.getName();
                    //4.通過getBean()方法獲取要組裝的對象
                    //如果name對應的對象時單例的,就到單例池去獲取,如果是多例的,就反射創建並返回
                    Object bean = getBean(name);
                    //5.進行組裝
                    //暴破
                    declaredField.setAccessible(true);
                    declaredField.set(instance, bean);
                }
            }
        }
        return instance;
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    //如果反射對象失敗
    return null;
}

4.測試類

//...
public class AppMain {
    public static void main(String[] args) {
        MySpringApplicationContext ioc =
                new MySpringApplicationContext(MySpringConfig.class);
        MonsterService monsterService = (MonsterService) ioc.getBean("monsterService");
        monsterService.m1();
    }
}

測試結果:自動裝配成功

image-20230128213708640
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文基於此: Flutter中文網 一、安裝和運行Flutter的系統環境要求 想要安裝並運行 Flutter,你的開發環境需要最低滿足以下要求: 操作系統:macOS 磁碟空間:2.8 GB(不包括IDE/tools的磁碟空間)。 工具:Flutter使用git進行安裝和升級。我們建議安裝Xcod ...
  • 主題說明 打開博客園的隨筆詳細頁、標簽頁等,都是整頁重新載入,比較影響體驗。SPA 應用可以減少整頁載入,實現局部刷新,本皮膚通過 Vue3 + TS + Vite 開發的。有些細節待日後逐步完善,隨筆的閱讀和使用基本上沒有問題,文章、日記、部分側邊欄內容還沒有實現。 倉庫地址:GitHub,請點個 ...
  • 值和類型 八種數據類型 undefined、null、boolean、number、string、symbol、bigint、object 原始值和引用值 原始值:按值訪問。值存儲在棧中。變數賦值傳遞時會創建該值的副本,兩個變數(複製與被覆制)完全獨立。 常見原始值類型:undefined、null ...
  • 參考:https://www.cnblogs.com/lxlx1798/articles/16969244.html 要麼使用流讀取器,要麼使用 Reponse 的方法來獲取結果,不能同時使用兩種方法來讀取相同的響應。 直接獲取: Response.blob() 方法返回一個 resolve 返回值 ...
  • 說起轉義字元,大家最先想到的肯定是使用反斜杠,這也是我們最常見的,很多編程語言都支持。 轉義字元從字面上講,就是能夠轉變字元原本的意義,得到新的字元。常用在特殊字元的顯示以及特定的編碼環境中。 除了反斜杠以外,在前端開發中,還有其他幾種轉義字元,也是較常見的,本文將對這些做一個總結。 字元串中的轉義 ...
  • 張建飛是阿裡巴巴高級技術專家,一直在致力於應用架構和代碼複雜度的治理。最近,他在看零售通商品域的代碼。面對零售通如此複雜的業務場景,如何在架構和代碼層面進行應對,是一個新課題。結合實際的業務場景,他沉澱了一套“如何寫複雜業務代碼”的方法論,在此分享給大家,相信同樣的方法論可以複製到大部分複雜業務場景... ...
  • *以下內容為本人的學習筆記,如需要轉載,請聲明原文鏈接 微信公眾號「englyf」https://mp.weixin.qq.com/s/2GFLTstDC7w6u3fTJxflNA 本文大概 1685 個字,閱讀需花 6 分鐘內容不多, 但也花了一些精力如要交流, 歡迎關註我然後評論區留言 謝謝你的 ...
  • 題目描述 運行 C 程式,輸出 100 至 200 之間的質數。 輸入描述 無 輸出描述 輸出 100 至 200 之間的質數,每行輸出一個質數,每個質數前面需要帶有序號。 輸出樣例 解題思路 在《一文解決如何使用 C 語言判斷質數(素數)》一文中,我詳細講解了質數以及如何使用 C 語言判斷質數,本 ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...