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
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...