主頁 > 後端開發 > 最近迷上了原始碼,Tomcat原始碼,看我這篇就夠了

最近迷上了原始碼,Tomcat原始碼,看我這篇就夠了

2022-10-19 07:25:30 後端開發

1 Apache Tomcat原始碼環境構建

1.1 Apache Tomcat原始碼下載

https://tomcat.apache.org/download-80.cgi

環境:jdk11

下載對應的zip包

file
下載到本地任意磁盤下

1.2 Tomcat原始碼環境配置

1.2.1 增加POM依賴管理檔案

解壓 apache-tomcat-8.5.63-src壓縮包,

得到?錄 apache-tomcat-8.5.63-src 進? apache-tomcat-8.5.63src ?錄,創建?個pom.xml?件,

?件內容如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>apache-tomcat-8.5.63-src</artifactId>
    <name>Tomcat8.5</name>
    <version>8.5</version>
    <build>
        <!--指定源?錄-->
        <finalName>Tomcat8.5</finalName>
        <sourceDirectory>java</sourceDirectory>
        <resources>
            <resource>
                <directory>java</directory>
            </resource>
        </resources>
        <plugins>
            <!--引?編譯插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <!--tomcat 依賴的基礎包-->
    <dependencies>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>ant</groupId>
            <artifactId>ant</artifactId>

            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.xml</groupId>
            <artifactId>jaxrpc</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.5.1</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.soap</groupId>
            <artifactId>javax.xml.soap-api</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>
</project>

1.2.3 IDEA環境匯入與啟動

idea匯入maven專案,注意環境:

idea: 2020.3

jdk: 11

執行 Bootstrap.java 的main方法即可,非常簡單

1)常見錯誤一

Error:(505, 53) java: 程式包 sun.rmi.registry 不可見 (程式包 sun.rmi.registry 已在模塊 java.rmi 中宣告, 但該模塊未將它匯出到未命名模塊)

file

原因:sun的包對ide編譯環境不可見造成的,滑鼠放在代碼中報紅的地方,根據idea的提示操作即可,

file

注意!不要用maven去編譯它,這個引數你加入的是idea的環境,所以,用idea編譯和啟動

file

2)常見錯誤二

file
原因:jdk版本的事,選jdk11

file - project structure

file

3)常見錯誤三

運? Bootstrap 類的 main 函式,此時就啟動了tomcat,啟動時候會去加載所配置的 conf ?錄下 的server.xml等配置?件,所以訪問8080端?即可,但此時我們會遇到如下的?個錯誤

file
原因是Jsp引擎Jasper沒有被初始化,從??法編譯JSP,我們需要在tomcat的原始碼ContextConfig類中 的configureStart?法中增加??代碼將 Jsp 引擎初始化,如下

org.apache.catalina.startup.ContextConfig#configureStart

..................略

     webConfig();
        //初始化JSP決議引擎
        context.addServletContainerInitializer(new JasperInitializer(),null);

        if (!context.getIgnoreAnnotations()) {
            applicationAnnotationsConfig();
        }
        
        
 ...................略

啟動Boostrap檔案

file
訪問

http://localhost:8080/

file
可以看到,tomcat成功啟動,

2 Tomcat架構與原始碼剖析

2.1 Apache Tomcat總體架構

file
從Tomcat安裝目錄下的/conf/server.xml 檔案里可以看到最頂層的是server,

對照上面的關系圖,一個Tomcat實體對應一個server,一個 Server 中有一個或者多個 Service,

一個 Service 中有多個連接器和一個容器,Service組件本身沒做其他事

只是把連接器和容器組裝起來,連接器與容器之間通過標準的 ServletRequest 和 ServletResponse 通信

Server:Server容器就代表一個Tomcat實體(Catalina實體),其下可以有一個或者多個Service容器;

Service:Service是提供具體對外服務的(默認只有一個),一個Service容器中又可以有多個Connector組件(監聽不同埠請求,決議請求)和一個Servlet容器(做具體的業務邏輯處理);

Engine和Host:Engine組件(引擎)是Servlet容器Catalina的核心,它支持在其下定義多個虛擬主機(Host),虛擬主機允許Tomcat引擎在將配置在一臺機器上的多個域名,比如www.baidu.com、www.bat.com分割開來互不干擾;

Context:每個虛擬主機又可以支持多個web應用部署在它下邊,這就是我們所熟知的背景關系物件Context,背景關系是使用由Servlet規范中指定的Web應用程式格式表示,不論是壓縮過的war包形式的檔案還是未壓縮的目錄形式;

Wrapper:在背景關系中又可以部署多個servlet,并且每個servlet都會被一個包裝組件(Wrapper)所包含(一個wrapper對應一個servlet)

去掉注釋的server.xml

file
file

虛擬主機

把webapps復制一份,叫webapps2,然后修改里面ROOT的index.jsp , 隨便改一下

修改web.xml添加虛擬主機,參考下面:(記得把 localhost2 加入到 hosts檔案中)

重啟訪問 http://localhost2/ 試試,和localhost對比一下

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>
  <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
      </Host>
      <Host name="localhost2"  appBase="webapps2"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
      </Host>
    </Engine>
  </Service>
</Server>

2.2 Apache Tomcat連接器

負責對外交流的連接器(Connector)

連接器主要功能:

1、網路通信應

2、用層協議決議讀取請求資料

3、將Tomcat 的Request/Response轉成標準的Servlet Request/Response

因此Tomcat設計者又設計了三個組件來完成這個三個功能,分別是EndPoint、Processor和Adaptor,其中EndPoint和Processor又一起抽象成ProtocalHandler組件,畫圖理解下

這里大家先有個印象,下面原始碼會看到互相之間的呼叫

file
下面的原始碼我們會詳細看到處理的轉交程序:

Connector 給 handler, handler最終呼叫 endpoint

Processor 負責提供 Tomcat Request 物件給 Adapter

Adapter 負責提供 ServletRequest 物件給容器

2.3 Apache Tomcat原始碼剖析

重點分析兩個階段:啟動,請求

2.3.1 start.sh如何啟動

用過Tomcat的我們都知道,可以通過Tomcat的/bin目錄下的腳本startup.sh來啟動Tomcat,那么這個腳本肯定就是Tomcat的啟動入口了,執行過這個腳本之后發生了什么呢?

file
1、Tomcat本質上也是一個Java程式,因此startup.sh腳本會啟動一個JVM來運行Tomcat的啟動類 Bootstrap

2、Bootstrap的主要任務是初始化Tomcat的類加載器,并且創建Catalina,

3、Catalina是一個啟動類,它通過決議server.xml,創建相應的組件,并呼叫 Server的start方法

4、Server組件的職責就是管理Service組件,它會負責呼叫Service的start方法

5、Service組件的職責就是管理連接器和頂層容器Engine,它會呼叫連接器和 Engine的start方法

6、Engine組建負責啟動管理子容器,通過呼叫Host的start方法,將Tomcat各層容器啟動起來(這里是分層級的,上層容器管理下層容器

2.3.2 生命周期統一管理組件

LifeCycle介面

Tomcat要啟動,肯定要把架構中提到的組件進行實體化(實體化創建–>銷毀等:生命周期),

Tomcat中那么多組件,為了統一規范他們的生命周期,Tomcat抽象出了LifeCycle生命周期介面

大家先知道這個內部的類關系,這是一個介面,server.xml 里的節點都是它的實作類

LifeCycle生命周期介面方法:

file
原始碼如下

public interface Lifecycle {
    // 添加監聽器
    public void addLifecycleListener(LifecycleListener listener);
    // 獲取所以監聽器
    public LifecycleListener[] findLifecycleListeners();
    // 移除某個監聽器
    public void removeLifecycleListener(LifecycleListener listener);
    // 初始化方法
    public void init() throws LifecycleException;
 
 
  ......................略
  }

這里我們把LifeCycle介面定義分為兩部分

一部分是組件的生命周期方法,比如init()、start()、stop()、destroy(),

另一部分是擴展介面就是狀態和監聽器,

tips: (畫圖便于理解)

因為所有的組件都實作了LifeCycle介面,

在父組件的init()方法里創建子組件并呼叫子組件的init()方法,

在父組件的start()方法里呼叫子組件的start()方法,

那么呼叫者就可以無差別的只呼叫最頂層組件,也就是Server組件的init()和start()方法,整個Tomcat就被啟動起來了

2.3.3 Tomcat啟動入口在哪里

(1)啟動流程圖

startup.sh --> catalina.sh start --> java xxxx.jar org.apache.catalina.startup.Bootstrap(main) start(引數)

file

tips:

Bootstrap.init

Catalina.load

Catalina.start

//偽代碼:呼叫關系,我們重點看下面標注的 1 2 3 
//startup.bat 或 sh
Bootstrap{
  main(){
    init();  // 1
    load(){  // 2
      Catalina.load(){
        createServer();
        Server.init(){
          Service.init(){
            Engine.init(){
              Host.init(){
                Context.init();
              }
            }
            Executor.init();
            Connector.init(){ //8080
              ProtocolHaldler.init(){
                EndPoint.init(); 
              }
            }
          }
        }
      }
    }
    
    start(){  // 3
      
      //與load方法一致
    }
  }
  
}

(2)系統配置與入口

Bootstrap類的main方法

// 知識點【需要debug學習的幾個點】

// BootStrap  static 塊 :  確定Tomcat運行環境的根目錄
// main里的init : 入口
// CatalinaProperties:  配置資訊加載與獲取工具類
//  			static { loadProperties() }: 加載

2.3.4 Bootstrap的init方法剖析

目標

//1、初始化類加載器
//2、加載catalina類,并且實體化
//3、反射呼叫Catalina的setParentClassLoader方法
//4、實體 賦值

file

    //1、初始化類加載器
    //2、加載catalina類,并且實體化
    //3、反射呼叫Catalina的setParentClassLoader方法
    //4、實體 賦值
    public void init() throws Exception {
        // 1. 初始化Tomcat類加載器(3個類加載器)
        initClassLoaders();

        Thread.currentThread().setContextClassLoader(catalinaLoader);

        SecurityClassLoad.securityClassLoad(catalinaLoader);

        // Load our startup class and call its process() method
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        // 2. 實體化Catalina實體
        Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.getConstructor().newInstance();

        // Set the shared extensions class loader
        if (log.isDebugEnabled())
            log.debug("Setting startup class properties");
        String methodName = "setParentClassLoader";
        Class<?> paramTypes[] = new Class[1];
        paramTypes[0] = Class.forName("java.lang.ClassLoader");
        Object paramValues[] = new Object[1];
        paramValues[0] = sharedLoader;
        // 3. 反射呼叫Catalina的setParentClassLoader方法,將sharedLoader設定為Catalina的parentClassLoader成員變數
        Method method =
                startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);
        //4、將catalina實體賦值
        catalinaDaemon = startupInstance;
    }

2.3.4 Catalina的load方法剖析

tips

org.apache.catalina.startup.Bootstrap#main中的load方法

呼叫的是catalina中的方法

1)load初始化流程

load(包括下面的start)的呼叫流程核心技術在于,這些類都實作了 2.3.2 里的 生命周期介面,

模板模式:

每個節點自己完成的任務后,會接著呼叫子節點(如果有的話)的同樣的方法,引起鏈式反應,

反映到流程圖如下,下面的debug,包括start我們以圖跟代碼結合debug:

file

2)load初始化原始碼

進入到catalina的load方法,即可開啟鏈式反應……

    // 1. 決議server.xml,實體化各Tomcat組件
    // 2. 為Server組件實體設定Catalina相關成員value
    // 3. 呼叫Server組件的init方法,初始化Tomcat各組件, 開啟鏈式反應的點!
   

3)關鍵點

load這里,一堆的節點,其實其他并不重要,我們重點看Connector的init

這涉及到tomcat的一個核心問題: 它到底是如何準備好接受請求的!

// Connector.java:

initInternal(){
	//斷點到這里!
	protocolHandler.init();  // ===>  開啟秘密的地方
}

2.3.5 Catalina的start方法剖析

1)start初始化流程

流程圖

與load程序很相似

file

2)start啟動原始碼

Catalina的start方法

    /**
     * 反射呼叫Catalina的start方法
     *
     * @throws Exception Fatal start error
     */
    public void start() throws Exception {
        if (catalinaDaemon == null) {
            init();
        }
        //呼叫catalina的start方法,啟動Tomcat的所有組件
        Method method = catalinaDaemon.getClass().getMethod("start", (Class[]) null);
        method.invoke(catalinaDaemon, (Object[]) null);
    }

//真實內容: Catalina.start 方法!

start(){
  getServer.start(); // ===> 核心點
}

3)關鍵點

Connector.java 的 start

我們直接把斷點打在 Connector.java 的 startInterval()

Connector(){

	startInterval() {
		//斷點打到這里!
		protocolHandler.start();
	}

}

//最終目的:發現在  NioEndpoint.Acceptor.run() 里, socket.accept來等待和接受請求,

//至此啟動階段結束!

2.3.6 請求的處理

啟動完就該接受請求了!

那么請求是如何被tomcat接受并回應的???

在除錯請求前,必須有個請求的案例,我們先來實作它

1)案例

file

原始碼:

DemoServlet.java

package com.itheima.test;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class DemoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-----do get----");
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
 Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1"
         metadata-complete="true">
    <servlet>
        <servlet-name>demoServlet</servlet-name>
        <servlet-class>com.itheima.test.DemoServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>demoServlet</servlet-name>
        <url-pattern>/test.do</url-pattern>
    </servlet-mapping>
</web-app>

debug重啟tomcat,訪問 http://localhost:8080/demo/test.do

確認控制臺列印資訊,打斷點可以正常進來:

file

基于請求的環境準備作業完成!

2)url的決議

回顧開篇,server.xml 、 url與對應的容器:

http://localhost:8080/demo/test.do

localhost: Host

8080: Connector

demo: Context

test.do: Url

3)類關系

tomcat靠Mapper來完成對url各個部分的映射

  • idea追蹤MapElement的繼承實作
  • 從MappedHost類打開入口,看擁有的屬性和關系

file

4)接受請求的流程

file

5)代碼追蹤

溫馨提示:征程開始,下面將是漫長的debug之路,別跟丟了!

代碼入口:

NioEndpoint:



// 真正的入口:
NioEndPoint.Poller{
  
  run(){
    //斷點打在這里!!!
    processKey(sk, socketWrapper);
  }
}

2.3.7 tomcat的關閉

tomcat啟動后就一直處于運行狀態,那么它是如何保持活動的?又是如何觸發退出的?

1)代碼追蹤

1、標志位全域控制

org.apache.catalina.startup.Bootstrap#main

通過setAwait這個標志位來控制

 
else if (command.equals("start")) {
                daemon.setAwait(true);//主執行緒是否退出全域控制閾值
                daemon.load(args);//2、呼叫Catalina#load(args)方法,始化一些資源,優先加載conf/server.xml
                daemon.start();//3、呼叫Catalina.start()開始啟動

2、進入到Catalina#start方法

org.apache.catalina.startup.Catalina#start

.................................略
   if (await) {
            await();
            stop();
        }
    }

3、進入到await方法

org.apache.catalina.core.StandardServer#await

重點關注

awaitSocket = new ServerSocket..

@Override
    public void await() {

      // 監聽 8005 socket
      // 阻塞等待指令,10s超時,繼續回圈
      
      // 收到SHUTDOWN ,退出回圈
      
    }

結論:通過阻塞來實作主執行緒存活!

2)操作演練

xml定義的埠 8005

file

將斷點打在 org.apache.catalina.startup.Catalina#start, 下面的 stop() 一行

在命令列鍵入:telnet ip port 后,然后鍵入大寫的SHUTDOWN,其中port默認為8005

file
然后輸入大寫【SHUTDOWN】,會被斷點捕獲到,

結論:通過使用telnet關閉8005埠也正好印證了上面的 結論,

shutdown.bat和上面的原理也是一樣的

本文由傳智教育博學谷教研團隊發布,

如果本文對您有幫助,歡迎關注點贊;如果您有任何建議也可留言評論私信,您的支持是我堅持創作的動力,

轉載請注明出處!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/517500.html

標籤:Java

上一篇:Bloom Filter概念和實作原理

下一篇:JAVA獲取jvm和作業系統相關資訊

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more