【qdox】Java 代碼解析利器 QDox

来源:https://www.cnblogs.com/kiwifly/archive/2020/03/22/12548682.html
-Advertisement-
Play Games

【qdox】Java 代碼解析利器 QDox 前言 最近在寫 maven 插件,涉及到了 java 代碼解析這塊內容。需要解析 java 源碼,然後對於類中的不同部分進行處理。發現手寫還是很難的,找了一圈發現了兩個不錯的工具可以使用,一個是 "javaparser" ,另一個是 "qdox" 。個人 ...


【qdox】Java 代碼解析利器 QDox

image.png

前言

最近在寫 maven 插件,涉及到了 java 代碼解析這塊內容。需要解析 java 源碼,然後對於類中的不同部分進行處理。發現手寫還是很難的,找了一圈發現了兩個不錯的工具可以使用,一個是 javaparser,另一個是 qdox 。個人感覺 javaparser 強大一些,更新與維護也比較勤,但是相對來說上手難一點,從他的使用文檔獨立成書在買,可見一斑,而 qdox 比較小巧,上手很快,功能也滿足大部分需求,最終還是選擇了 qdox。

什麼是 QDox

官方的介紹是:

QDox - full extractor of Java class/interface/method definitions (including annotations, parameters, param names)

大概意思是一款完整的 java 類、介面、方法定義的提取器,包括了註釋、參數及參數名稱。其實核心功能就是我輸入一個 java 類的源碼,他可以把這個 java 類解析成一個對象,我們通過這個對象可以獲取很方便的獲取解析的類的不同組成,比如我可以獲得這個類有哪些方法,這個方法的參數是什麼,返回值又是什麼,他們的類型又分別是什麼?還有這個方法上有哪些註釋、哪些 tag。也能獲取類中有哪些的 field。。。總之把這個類庖丁解牛般解析好,使得調用者很方便的獲取到自己感興趣的信息。

為什麼使用 QDox

除了上面說的 QDox 上手比較快外,他的運行速度及占用空間都十分優秀。

另外不得不說的是這個項目可以說是一個上古時期的項目了,看了 github 上的提交記錄,最早的一條提交記錄是 2002 年的時候,因為這個項目之前使用的是 svn,所以具體時間可能更早。一個開源項目維護了快 20 年也是一件挺令人欽佩的事。不過到目前為止,這個項目在 github 上只有 151 個 star,如果這個項目對你有所幫助,希望大家可以給作者一個 star。github 上有太多類似的項目默默無聞的出現,又默默無聞的消逝。

扯遠了,雖然項目關註的人比較少,但是使用它的項目還是比較多的。maven 的官方 javadoc 插件 maven-javadoc-plugin 就是使用它來解析代碼中的 doc tags 的。所以可能你沒有直接使用它,但是它其中已經在你本地的 maven 倉庫內躺著了。有官方背書,對於它的使用就比較放心了。

什麼情況下適合使用 QDox

這個就比較多了,通常只要我們需要解析源碼的內容就可以使用,比如我想獲得指定類文件中的全部方法。就可以使用。可能有些人感到不解了,為什麼不通過反射拿到這些內容,這樣不是更方便嗎?首先,反射的前提是你能拿到這個類的實例,或者你項目中就有這個類。即使這些條件都滿足,但是一個很常見的需求反射沒法滿足,比如說拿到方法的註釋及 tags 等,這類在編譯時就被抹除了。這種情況就不得不用源碼解析的方法了。

另外它不只是能解析,他同時可以生成 java 類文件,所以你可以動態的生成一些 java 類。

無論是解析還是生成,在寫插件的時候肯定需要會有這樣的場景,比如我想通過代碼里的 javadoc 這些 tags 生成一個介面文檔給前端,這樣就不用我一個一個手寫了。再比如我想通過數據表的信息,自動生成 model 類,service 類。。。使用場景的限制主要是個人的想象力。

如何使用 QDox

創建 java 項目 builder 對象

        // 創建 java 項目 builder 對象
        JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();

添加 java 源文件

        // 添加 java 源文件
        javaProjectBuilder.addSource(new File("/Users/kiwi/study/code/study-example/study-qdox-example/src/main/java/cn/coder4j/study/example/qdox/Demo.java"));

demo 示例是通過文件添加的,其實支持很多種類型,比如 URL、Reader 甚至直接添加一個目錄,框架會自己掃描目錄下的所有 java 文件

image.png

獲得解析後的 JavaClass 對象

經過上面兩步,準備工作就已經結束了,可以直接獲得解析後的 JavaClass 對象了,有兩種方式獲取,一種是直接獲得解析後的類集合,為什麼是集合呢?因為上面也說了是可以添加目錄的,而且 addSource 可以多次調用,添加多個文件。另一種是在知道類名稱的情況下直接使用 getClassByName 獲得

image.png

        // 獲得解析後的類
        Collection<JavaClass> classes = javaProjectBuilder.getClasses();
        for (JavaClass javaClass : classes) {
            
        }

JavaClass 介面定義

package com.thoughtworks.qdox.model;

/*
 * 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.
 */

import java.util.List;

import com.thoughtworks.qdox.library.ClassLibrary;

/**
 * Equivalent of {@link java.lang.Class}, providing the most important methods.
 * Where the original Class is using an Array, this model is using a List.
 * 
 * @author Robert Scholte
 */
public interface JavaClass extends JavaModel, JavaType, JavaAnnotatedElement, JavaGenericDeclaration
{
    /**
     * The compilation unit, which includes the imports, the public and anonymous classes
     * 
     * @return the {@link JavaSource} of this element
     */
    JavaSource getSource();

    /**
     * (API description of {@link java.lang.Class#isInterface()})
     * <p>
     * Determines if the specified <code>Class</code> object represents an interface type.
     * </p>
     * 
     * @return <code>true</code> if this object represents an interface, otherwise <code>false</code>
     */
    boolean isInterface();

    /**
     * (API description of {@link java.lang.Class#isEnum()})
     * <p>
     * Returns <code>true</code> if and only if this class was declared as an enum in the source code.
     * </p>
     * 
     * @return <code>true</code> if this object represents an enum, otherwise <code>false</code>

     */
    boolean isEnum();

    /**
     * (API description of {@link java.lang.Class#isAnnotation()})
     * <p>Returns true if this <code>Class</code> object represents an annotation type. 
     *    Note that if this method returns true, {@link #isInterface()} would also return true, as all annotation types are also interfaces.
     * </p>
     * 
     * @return <code>true</code> if this object represents an annotation, otherwise <code>false</code>
     * @since 2.0 
     */
    boolean isAnnotation();
    
    JavaClass getDeclaringClass();

    JavaType getSuperClass();

    /**
     * Shorthand for getSuperClass().getJavaClass() with null checking.
     * @return the super class as {@link JavaClass}
     */
    JavaClass getSuperJavaClass();

    List<JavaType> getImplements();

    /**
     * Equivalent of {@link java.lang.Class#getInterfaces()}
     *  Determines the interfaces implemented by the class or interface represented by this object. 
     * 
     * @return a list of interfaces, never <code>null</code>
     * @since 2.0
     */
    List<JavaClass> getInterfaces();

    String getCodeBlock();

    JavaSource getParentSource();

    /**
     * Equivalent of {@link java.lang.Class#getPackage()}
     * @return the package
     */
    JavaPackage getPackage();

    /**
     * If this class has a package, the packagename will be returned.
     * Otherwise an empty String.
     * 
     * @return the name of the package, otherwise an empty String
     */
    String getPackageName();

    /**
     * @since 1.3
     * @return <code>true</code> if this class is an inner class, otherwise <code>false</code>
     */
    boolean isInner();

    /**
     * Equivalent of {@link java.lang.Class#getMethods()}
     * 
     * @return the methods declared or overridden in this class
     */
    List<JavaMethod> getMethods();
    
    /**
     * Equivalent of {@link java.lang.Class#getConstructors()}
     * 
     * @return the list of constructors
     * @since 2.0
     */
    List<JavaConstructor> getConstructors();
    
    
    /**
     * 
     * @param parameterTypes the parameter types of the constructor, can be <code>null</code>
     * @return the matching constructor, otherwise <code>null</code>
     * @since 2.0
     */
    JavaConstructor getConstructor(List<JavaType> parameterTypes);
    
    /**
     * 
     * @param parameterTypes the parameter types of the constructor, can be <code>null</code>
     * @param varArg define is the constructor has varArgs
     * @return the matching constructor, otherwise <code>null</code>
     * @since 2.0
     */
    JavaConstructor getConstructor(List<JavaType> parameterTypes, boolean varArg);
    

    /**
     * Return declared methods and optionally the inherited methods
     *
     * @param superclasses {@code true} if inherited methods should be returned as well 
     * @return all methods
     * @since 1.3
     */
    List<JavaMethod> getMethods( boolean superclasses );

    /**
     * 
     * @param name the name of the method
     * @param parameterTypes the parameter types of the method, can be <code>null</code>.
     * @return the matching method, otherwise <code>null</code>
     */
    JavaMethod getMethodBySignature( String name, List<JavaType> parameterTypes );

    /**
     * This should be the signature for getMethodBySignature.
     * 
     * @param name the name of the method
     * @param parameterTypes the parameter types of the method, can be {@code null}
     * @param varArgs define if the method has varArgs
     * @return the matching method, otherwise  {@code null}
     */
    JavaMethod getMethod( String name, List<JavaType> parameterTypes, boolean varArgs );

    /**
     * 
     * @param name the name of the method
     * @param parameterTypes the parameter types of the method, can be  {@code null}
     * @param superclasses to define if superclasses should be included as well
     * @return the matching method, otherwise  {@code null}
     */
    JavaMethod getMethodBySignature( String name, List<JavaType> parameterTypes, boolean superclasses );

    /**
     * 
     * @param name the name of the method
     * @param parameterTypes the parameter types of the method, can be  {@code null}
     * @param superclasses {@code true} if inherited methods should be matched as well
     * @param varArg define if the method has varArgs
     * @return the matching method, otherwise  {@code null}
     */
    JavaMethod getMethodBySignature( String name, List<JavaType> parameterTypes, boolean superclasses, boolean varArg );

    /**
     * 
     * @param name the name of the method
     * @param parameterTypes the parameter types of the method, can be  {@code null}
     * @param superclasses {@code true} if inherited methods should be matched as well
     * @return the matching methods, otherwise  {@code null}
     */
    List<JavaMethod> getMethodsBySignature( String name, List<JavaType> parameterTypes, boolean superclasses );

    /**
     * 
     * @param name the name of the method
     * @param parameterTypes the parameter types of the method, can be  {@code null}
     * @param superclasses {@code true} if inherited methods should be matched as well
     * @param varArg define if the method has varArgs
     * @return the matching methods, otherwise  {@code null}
     */
    List<JavaMethod> getMethodsBySignature( String name, List<JavaType> parameterTypes, boolean superclasses,
                                                   boolean varArg );

    /**
     * Equivalent of {@link java.lang.Class#getFields()}
     * 
     * @return a list of fiels, never  {@code null}
     */
    List<JavaField> getFields();

    /**
     * Equivalent of {@link java.lang.Class#getField(String)}, where this method can resolve every field
     * 
     * @param name the name of the field
     * @return the field
     */
    JavaField getFieldByName( String name );
    
    /**
     * Based on {@link java.lang.Class#getEnumConstants()}.
     *  
     * 
     * @return a List of enum constants if this class is an <code>enum</code>, otherwise  {@code null}
     */
    List<JavaField> getEnumConstants();

    /**
     * 
     * @param name the name of the enum constant
     * @return the enumConstant matching the {@code name}, otherwise <code>null</code>
     */
    JavaField getEnumConstantByName( String name );

    /**
     * Equivalent of {@link Class#getDeclaredClasses()}
     * 
     * @return a list of declared classes, never <code>null</code>
     * @since 1.3
     */
    List<JavaClass> getNestedClasses();

    JavaClass getNestedClassByName( String name );

    /**
     * @param fullyQualifiedName the FQN to match with
     * @return {@code true} if this is of type FQN, otherwise {@code false}
     * @since 1.3
     */
    boolean isA( String fullyQualifiedName );

    /**
     * @param javaClass the JavaClass to match with
     * @return {@code true} if this is of type {@literal javaClass}, otherwise {@code false}
     * @since 1.3
     */
    boolean isA( JavaClass javaClass );

    /**
     * Returns the depth of this array, 0 if it's not an array
     * 
     * @return The depth of this array, at least <code>0</code>
     * @since 2.0
     */
    int getDimensions();
    
    /**
     * 
     * @return <code>true</code> if this JavaClass is an array, otherwise <code>false</code>
     * @since 2.0
     */
    boolean isArray();
    
    /**
     * 
     * @return <code>true</code> if this JavaClass is a void, otherwise <code>false</code>
     * @since 2.0 (was part of Type since 1.6)
     */
    boolean isVoid();
    
    /**
     * Equivalent of {@link Class#getComponentType()}
     * If this type is an array, return its component type
     * 
     * @return the type of array if it's one, otherwise <code>null</code>
     */
    JavaClass getComponentType();

    /**
     * Gets bean properties without looking in superclasses or interfaces.
     *
     * @return the bean properties
     * @since 1.3
     */
    List<BeanProperty> getBeanProperties();

    /**
     * 
     * @param superclasses to define if superclasses should be included as well
     * @return the bean properties
     * @since 1.3
     */
    List<BeanProperty> getBeanProperties( boolean superclasses );

    /**
     * Gets bean property without looking in superclasses or interfaces.
     *
     * @param propertyName the name of the property
     * @return the bean property
     * @since 1.3
     */
    BeanProperty getBeanProperty( String propertyName );

    /**
     * @param propertyName the name of the property
     * @param superclasses to define if superclasses should be included as well
     * @return the bean property
     * @since 1.3
     */
    BeanProperty getBeanProperty( String propertyName, boolean superclasses );

    /**
     * Equivalent of {@link Class#getClasses()}
     * Gets the known derived classes. That is, subclasses or implementing classes.
     * @return the derived classes
     */
    List<JavaClass> getDerivedClasses();

    List<DocletTag> getTagsByName( String name, boolean superclasses );

    ClassLibrary getJavaClassLibrary();
    
    /**
     * A list if {@link JavaInitializer}, either static or instance initializers.  
     * 
     * @return a List of initializers
     */
    List<JavaInitializer> getInitializers();

    /**
     * Equivalent of {@link java.lang.Class#getName()}.
     * 
     * @return the name of the entity (class, interface, array class, primitive type, or void) represented by this Class object, as a String.
     */
    String getName();
    
    /**
     * Equivalent of {@link java.lang.Class#getSimpleName()}.
     * 
     * @return the simple name of the underlying class as given in the source code.
     * @since 2.0
     */
    String getSimpleName();
    
    /**
     * Equivalent of {@link Class#getModifiers()}
     * 
     * <strong>This does not follow the java-api</strong>
     * The Class.getModifiers() returns an <code>int</code>, which should be decoded with the {@link java.lang.reflect.Modifier}.
     * This method will return a list of strings representing the modifiers.
     * If this member was extracted from a source, it will keep its order. 
     * Otherwise if will be in the preferred order of the java-api.
     * 
     * @return all modifiers is this member
     */
    List<String> getModifiers();
    
    /**
     * (API description of {@link java.lang.reflect.Modifier#isPublic(int)})
     * <p>
     * Return <code>true</code> if the class includes the public modifier, <code>false</code> otherwise.
     * <p>
     * 
     * @return <code>true</code> if class has the public modifier, otherwise <code>false</code>
     */
    boolean isPublic();
    
    /**
     * (API description of {@link java.lang.reflect.Modifier#isProtected(int)})
     * <p>
     * Return <code>true</code> if the class includes the protected modifier, <code>false</code> otherwise.
     * </p>
     * 
     * @return <code>true</code> if class has the protected modifier, otherwise <code>false</code>
     */
    boolean isProtected();
    
    /**
     * (API description of {@link java.lang.reflect.Modifier#isPrivate(int)})
     * <p>
     * Return <code>true</code> if the class includes the private modifier, <code>false</code> otherwise.
     * </p>
     * 
     * @return <code>true</code> if class has the private modifier, otherwise <code>false</code>
     */
    boolean isPrivate();
    
    /**
     * (API description of {@link java.lang.reflect.Modifier#isFinal(int)})
     * <p>
     * Return <code>true</code> if the class includes the final modifier, <code>false</code> otherwise.
     * </p>
     * 
     * @return <code>true</code> if class has the final modifier, otherwise <code>false</code>
     */
    boolean isFinal();
    
    /**
     * (API description of {@link java.lang.reflect.Modifier#isStatic(int)})
     * <p>
     * Return <code>true</code> if the class includes the static modifier, <code>false</code> otherwise.
     * </p>
     * 
     * @return <code>true</code> if class the static modifier, otherwise <code>false</code>
     */
    boolean isStatic();
    
    /**
     * (API description of {@link java.lang.reflect.Modifier#isAbstract(int)})
     * 
     * Return <code>true</code> if the class includes the abstract modifier, <code>false</code> otherwise.
     * 
     * @return <code>true</code> if class has the abstract modifier, otherwise <code>false</code>
     */
    boolean isAbstract();
    
    /**
     *  Equivalent of  {@link java.lang.Class#isPrimitive()}
     *  
     * @return <code>true</code> if this class represents a primitive, otherwise <code>false</code>
     */
    boolean isPrimitive();
    
    /**
     * (API description of {@link java.lang.Class#toString()})
     * 
     * Converts the object to a string. 
     * The string representation is the string "class" or "interface", followed by a space, and then by the fully qualified name of the class in the format returned by <code>getName</code>. 
     * If this <code>Class</code> object represents a primitive type, this method returns the name of the primitive type. 
     * If this <code>Class</code> object represents void this method returns "void".
     *  
     * @return a string representation of this class object.
     */
    @Override
    String toString();
}

可以看到 JavaClass 提供的方法還是很多的,主要有如下這些,大概可以分成兩類:

一類是getXXX 這個是通過獲得類中不同的組成部分的,比較常用有 getFields ,可以獲得類所有的 Field 變數的對象,通過 Field 又可以獲得 Field 上的註解以及註釋,類型。。。所有關於 field 的信息。又比如 getTags、getMethods 顧名思義是獲得 javadoc 的註釋及方法列表。

另一類是 isXXX ,這個是用來判斷類的一定特性的,比如 isEnum 判斷類是否是枚舉,isInterface 判斷是否是介面。

// 這些方法名,其實也是用 QDox 列印出來的
getBeanProperties
getBeanProperty
getCodeBlock
getComponentType
getConstructor
getConstructors
getDeclaringClass
getDerivedClasses
getDimensions
getEnumConstantByName
getEnumConstants
getFieldByName
getFields
getImplements
getInitializers
getInterfaces
getJavaClassLibrary
getMethod
getMethodBySignature
getMethods
getMethodsBySignature
getModifiers
getName
getNestedClassByName
getNestedClasses
getPackage
getPackageName
getParentSource
getSimpleName
getSource
getSuperClass
getSuperJavaClass
getTagsByName
isA
isAbstract
isAnnotation
isArray
isEnum
isFinal
isInner
isInterface
isPrimitive
isPrivate
isProtected
isPublic
isStatic
isVoid

完整 Demo

/*
 *
 *  * *
 *  *  * blog.coder4j.cn
 *  *  * Copyright (C) 2016-2020 All Rights Reserved.
 *  *
 *
 */
package cn.coder4j.study.example.qdox;

import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author buhao
 * @version DemoParser.java, v 0.1 2020-03-22 19:03 buhao
 */
public class DemoParser {
    public static void main(String[] args) throws IOException {
        // 創建 java 項目 builder 對象
        JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();
        // 添加 java 源文件
        javaProjectBuilder.addSource(new File("/Users/kiwi/study/code/study-example/study-qdox-example/src/main/java/cn/coder4j/study/example/qdox/Demo.java"));
        // 獲得解析後的類
        Collection<JavaClass> classes = javaProjectBuilder.getClasses();
        for (JavaClass javaClass : classes) {
            // 列印類相關信息
            System.out.println("類名:" + javaClass.getName());
            System.out.println("實現了哪些類:" + javaClass.getImplements());
            System.out.println("繼承哪個類:" + javaClass.getSuperJavaClass());
            System.out.println("註釋:" + javaClass.getComment());
            // 獲得方法列表
            List<JavaMethod> methods = javaClass.getMethods();
            for (JavaMethod method : methods) {
                System.out.println("方法名是:" + method.getName());
                System.out.println("方法的 Tags 有哪些:" + method.getTags().stream().map(it -> it.getName() + "->"+ it.getValue()).collect(Collectors.joining("\n")));
                System.out.println("方法的參數有哪些:" + method.getParameters());
                System.out.println("方法的返回值有哪些:" + method.getReturns());
            }
        }
    }
}

執行結果

類名:Demo
實現了哪些類:[java.io.Serializable]
繼承哪個類:class java.lang.Object
註釋:這是一個 demo 類
方法名是:hello
方法的 Tags 有哪些:param->name 姓名
return->hello {name}
方法的參數有哪些:[String name]
方法的返回值有哪些:java.lang.String

其它

項目代碼

因為篇幅有限,無法貼完所有代碼,如遇到問題可到 github 上查看源碼。

關於我

image.png

歡迎關註我的個人公眾號 KIWI的碎碎念 ,關註後回覆 學習資料,海量學習內容直接分享!
image.png


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

-Advertisement-
Play Games
更多相關文章
  • 以前看到過NSQ這個東西,也一直沒去看。今天剛好有時間就搭建了下,簡單嘗試了下這個Go語言下的消息隊列NSQ,我這裡簡要記錄下。 其實,NSQ國內用的是比較少的,我這裡也是算瞭解這麼個東西吧 ,稍微看下源碼,學到東西而已。 NSQ簡介 NSQ是一個基於Go語言的分散式實時消息平臺, 它具有分散式、去 ...
  • 現在springboot的火熱程度已經超過了spring了,因為springboot簡單快速方便,springboot的初衷就是為了簡化spring的配置,是的開發中集成新功能時更快,簡化或者減少相關的配置。springboot的基礎是“約定大於配置”。整合了所有的框架,可以把springboot當 ...
  • ...
  • [TOC] 環境 idea 2019.1 Meavn 3.6.0 SpringBoot 2.2.5 jdk 1.8 構建eureka server 新建工程 啟動類添加註解 @EnableEurekaServer 其他配置 構建eureka client 新建工程 pom文件添加依賴,解決啟動失敗 ...
  • 距離Java 8發佈已經過去了7、8年的時間,Java 14也剛剛發佈。Java 8中關於函數式編程和新增的Stream流API至今飽受“爭議”。 如果你不曾使用Stream流,那麼當你見到Stream操作時一定對它發出過鄙夷的聲音,併在心裡說出“這都寫的什麼玩意兒”。 如果你熱衷於使用Stream ...
  • 恢復內容開始 1.背景:現在很多app或者網站都想要接入微信登錄,可以使用戶不需要註冊就能快速使用APP或網站。 2.微信登錄需要一些前置操作 2.1 搜索:微信開放平臺 鏈接:https://open.weixin.qq.com/ 2.2 註冊成功,獲取到開發所需要的appID和appsecret ...
  • elastic4s是elasticsearch一個第三方開發的scala語言終端工具庫(Elastic4s is a concise, idiomatic, reactive, type safe Scala client for Elasticsearch.)。scala用戶可以用elastic4 ...
  • 1. 不可變的PyIntObject "Python源碼剖析 對象初探" 我們對 PyIntObject 已經有了初步的瞭解。 Python 中的對象可以分為固定長度和可變長度兩種類型。除此之外,也可以按照可變和不可變進行劃分。 PyIntObject 則屬於長度固定且不可變的對象。相比其他的對象而 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...