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
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...