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
  • 周末,寫點簡單的水一下。 新版本的vs創建項目的時候可以選擇自帶一個swagger。然而這隻是基本的swagger功能。 幾個介面無所謂啦,隨著介面越來越多,就這麼丟給你,一時間也會懵逼,所以這篇文章要做的有兩個功能。 給swagger文檔添加註釋 給swagger添加切換“版本”的功能(也可以理解 ...
  • 大家好,我是沙漠盡頭的狼。 本文首發於Dotnet9,介紹使用Lib.Harmony庫攔截第三方.NET庫方法,達到不修改其源碼並能實現修改方法邏輯、預期行為的效果,並且不限於只攔截public訪問修飾的類及方法,行文目錄: 什麼是方法攔截? 示常式序攔截 非public方法怎麼攔截? 總結 1. ...
  • 問題代碼: xmal:一個按鈕+一個顯示框 1 <Button Width="100" Height="50" Margin="10" Click="Button_Click">test</Button> 2 <TextBox x:Name="display" Width="300" Height= ...
  • 前置條件 ​ 阿裡雲伺服器一臺(可在購買伺服器時勾選安裝寶塔選項,免去後面的寶塔安裝) ​ 設置阿裡雲伺服器密碼並登陸伺服器 ​ 以下操作均在伺服器Linux中進行(使用遠程連接工具登錄) 寶塔登錄 登錄阿裡雲伺服器在Linux命令行中輸入bt,查看寶塔信息 ​ 根據寶塔信息提供的網站登陸寶塔服務( ...
  • GetTokenInformation 用於檢索進程或線程的令牌(Token)信息。Token是一個數據結構,其包含有關進程或線程的安全上下文,代表當前用戶或服務的安全標識符和許可權信息。GetTokenInformation函數也可以用來獲取這些安全信息,通常用於在運行時檢查某個進程或線程的許可權或安... ...
  • matplotlib 在1.0版本之前其實是不支持3D圖形繪製的。 後來的版本中,matplotlib加入了3D圖形的支持,不僅僅是為了使數據的展示更加生動和有趣。更重要的是,由於多了一個維度,擴展了其展示數據分佈和關係的能力,可以一次從三個維度來比較數據。 下麵介紹在matplotlib中繪製各類 ...
  • 編寫一個App就能編譯發佈到iOS、Android和Web等各大平臺的跨平臺技術,各大廠商一直都有研究和發佈對應技術產品,目前最熱門的莫過於Flutter框架了。而Dart作為其唯一的編程語言,今天我們開始來體驗一下…… ...
  • 實現基本的線程池 前提:我們要實現的線程池有如下功能: 基本的線程池模型 能提交和運行任務 能正常關閉線程池 線程的拒絕策略 線程池擴容 縮容線程池 代碼地址: 1、線程池的介紹? 線程池是什麼? 線程池是一種利用池化技術來管理線程的一種技術。 當沒有線程池的時候,我們如何創建線程? 繼承Threa ...
  • SDRAM基本信息 儲存能力計算 4X16X4=256(Mbit),註意不是MByte SDRAM控制 sdram包含兩個部分:sdram_ctrl、fifo_ctrl。 sdram_ctrl:其頂層為SDRAM的控制模塊內部實例化了5個模塊,有初始化、自刷新、寫和讀模塊,還有一個仲裁模塊對這四個不 ...
  • 歡迎訪問我的GitHub 這裡分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos 本篇概覽 欣宸正在為接下新的Java雲原生實戰系列原創做準備,既然是實戰,少不了一套雲原生環境,以下內容是必不可少的: linux操作系統 kuberne ...