java方法句柄-----1.方法句柄類型、調用

来源:https://www.cnblogs.com/tangliMeiMei/archive/2020/05/28/12983627.html
-Advertisement-
Play Games

方法句柄 方法句柄(method handle)是JSR 292中引入的一個重要概念,它是對Java中方法、構造方法和域的一個強類型的可執行的引用。這也是句柄這個詞的含義所在。通過方法句柄可以直接調用該句柄所引用的底層方法。從作用上來說,方法句柄的作用類似於2.2節中提到的反射API中的Method ...


目錄

方法句柄

  方法句柄(method handle)是JSR 292中引入的一個重要概念,它是對Java中方法、構造方法和域的一個強類型的可執行的引用。這也是句柄這個詞的含義所在。通過方法句柄可以直接調用該句柄所引用的底層方法。從作用上來說,方法句柄的作用類似於2.2節中提到的反射API中的Method類,但是方法句柄的功能更強大、使用更靈活、性能也更好。實際上,方法句柄和反射API也是可以協同使用的,下麵會具體介紹。
在Java標準庫中,方法句柄是由java.lang.invoke.MethodHandle類來表示的。

1.方法句柄的類型

  對於一個方法句柄來說,它的類型完全由它的參數類型和返回值類型來確定,而與它所引用的底層方法的名稱和所在的類沒有關係。比如引用String類的length方法和Integer類的intValue方法的方法句柄的類型就是一樣的,因為這兩個方法都沒有參數,而且返回值類型都是int。
  在得到一個方法句柄,即MethodHandle類的對象之後,可以通過其type方法來查看其類型。該方法的返回值是一個java.lang.invoke.MethodType類的對象。MethodType類的所有對象實例都是不可變的,類似於String類。所有對MethodType類對象的修改,都會產生一個新的MethodType類對象。兩個MethodType類對象是否相等,只取決於它們所包含的參數類型和返回值類型是否完全一致。

1.1MethodType類的對象實例的創建

  MethodType類的對象實例只能通過MethodType類中的靜態工廠方法來創建。這樣的工廠方法有三類。

1.1.1 通過指定參數和返回值的類型來創建MethodType.【顯式地指定返回值和參數的類型】

  這主要是使用methodType方法的多種重載形式。使用這些方法的時候,至少需要指定返回值類型,而參數類型則可以是0到多個。
  返回值類型總是出現在methodType方法參數列表的第一個後面緊接著的是0到多個參數的類型。類型都是由Class類的對象來指定的。如果返回值類型是void,可以用void.class或java.lang.Void.class來聲明。
  代碼清單2-31中給出了使用methodType方法的幾個示例。註意:最後一個methodType方法調用中使用了另外一個MethodType的參數類型作為當前MethodType類對象的參數類型

代碼清單2-31 MethodType類中的methodType方法的使用示例

public void generateMethodTypes(){
//String.length()
MethodType mt1=MethodType.methodType(int.class);
//String.concat(String str)
MethodType mt2=MethodType.methodType(String.class, String.class);
//String.getChars(int srcBegin, int srcEnd, char[]dst, int dstBegin)
MethodType mt3=MethodType.methodType(void.class, int.class, int.class, char[].class, int.class);
//String.startsWith(String prefix)
MethodType mt4=MethodType.methodType(boolean.class, mt2);
}

1.1.2 通過靜態工廠方法genericMethodType來創建的

  除了顯式地指定返回值和參數的類型之外,還可以生成通用的MethodType類型,即返回值和所有參數的類型都是Object類
  方法genericMethodType有兩種重載形式:
  第一種形式只需要指明方法類型中包含的Object類型的參數個數即可。
  第二種形式可以提供一個額外的參數來說明是否在參數列表的後面添加一個Object[]類型的參數。
  在代碼清單2-32中,mt1有3個類型為Object的參數,而mt2有2個類型為Object的參數和後面的Object[]類型參數。

代碼清單2-32 生成通用MethodType類型的示例

public void generateGenericMethodTypes(){
MethodType mt1=MethodType.genericMethodType(3);
MethodType mt2=MethodType.genericMethodType(2,true);
}

1.1.2 通過靜態工廠方法fromMethodDescriptorString來創建的

  最後介紹的一個工廠方法是比較複雜的fromMethodDescriptorString。這個方法允許開發人員指定方法類型在位元組代碼中的表示形式作為創建MethodType時的參數。這個方法的複雜之處在於位元組代碼中的方法類型格式不是很好理解。
  比如代碼清單2-31中的String.getChars方法的類型在位元組代碼中的表示形式是“(II[CI)V”。不過這種格式比逐個聲明返回值和參數類型的做法更加簡潔,適合於對Java位元組代碼格式比較熟悉的開發人員。
  在代碼清單2-33中,“(Ljava/lang/String;)Ljava/lang/String;”所表示的方法類型是返回值和參數類型都是java.lang.String,相當於使用MethodType.methodType(String.class, String.class)。
代碼清單2-33 使用方法類型在位元組代碼中的表示形式來創建MethodType

public void generateMethodTypesFromDescriptor(){
ClassLoader cl=this.getClass().getClassLoader();
String descriptor="(Ljava/lang/String;)Ljava/lang/String;";
MethodType mt1=MethodType.fromMethodDescriptorString(descriptor, cl);
}

註意:在使用fromMethodDescriptorString方法的時候,需要指定一個類載入器。該類載入器用來載入方法類型表達式中出現的Java類。如果不指定,預設使用系統類載入器。

2 對MethodType類的對象實例的修改

2.1 圍繞返回值和參數類型的精確修改

  在通過工廠方法創建出MethodType類的對象實例之後,可以對其進行進一步修改。這些修改都圍繞返回值和參數類型展開。所有這些修改方法都返回另外一個新的MethodType對象。

代碼清單2-34 對MethodType中的返回值和參數類型進行修改的示例

public void changeMethodType(){
//(int, int)String
MethodType mt=MethodType.methodType(String.class, int.class, int.class);
//(int, int, float)String
mt=mt.appendParameterTypes(float.class);
//(int, double, long, int, float)String
mt=mt.insertParameterTypes(1,double.class, long.class);
//(int, double, int, float)String
mt=mt.dropParameterTypes(2,3);
//(int, double, String, float)String
mt=mt.changeParameterType(2,String.class);
//(int, double, String, float)void
mt=mt.changeReturnType(void.class);
}

2.2 一次性對返回值和所有參數的類型進行修改

  除了上面這幾個精確修改返回值和參數的類型的方法之外,MethodType還有幾個可以一次性對返回值和所有參數的類型進行處理的方法。
  代碼清單2-35給出了這幾個方法的使用示例,其中wrap和unwrap用來在基本類型及其包裝類型之間進行轉換generic方法把所有返回值和參數類型都變成Object類型,而erase只把引用類型變成Object,並不處理基本類型。修改之後的方法類型同樣以註釋的形式給出。

代碼清單2-35 一次性修改MethodType中的返回值和所有參數的類型的示例

public void wrapAndGeneric(){
//(int, double)Integer
MethodType mt=MethodType.methodType(Integer.class, int.class, double.class);
//(Integer, Double)Integer
MethodType wrapped=mt.wrap();
//(int, double)int
MethodType unwrapped=mt.unwrap();
//(Object, Object)Object
MethodType generic=mt.generic();
//(int, double)Object
MethodType erased=mt.erase();
}

由於每個對MethodType對象進行修改的方法的返回值都是一個新的MethodType對象,可以很容易地通過方法級聯來簡化代碼。

3.方法句柄的調用

  在獲取到了一個方法句柄之後,最直接的使用方法就是調用它所引用的底層方法。在這點上,方法句柄的使用類似於反射API中的Method類。但是方法句柄在調用時所提供的靈活性是Method類中的invoke方法所不能比的。

3.1 通過invokeExact方法實現

  最直接的調用一個方法句柄的做法是通過invokeExact方法實現的。這個方法與直接調用底層方法是完全一樣的。
  invokeExact方法參數依次是作為方法接收者的對象調用時候的實際參數列表
比如在代碼清單2-36中,這種調用方式就相當於直接調用"Hello World".substring(1,3)
代碼清單2-36 使用invokeExact方法調用方法句柄

public void invokeExact()throws Throwable{
// 1.先獲取String類中substring的方法句柄.
MethodHandles.Lookup lookup=MethodHandles.lookup(); 
MethodType type=MethodType.methodType(String.class, int.class, int.class);
MethodHandle mh=lookup.findVirtual(String.class,"substring",type);
// 2.再通過invokeExact來進行調用。
String str=(String)mh.invokeExact("Hello World",1,3);
System.out.println(str);
}

  在這裡強調一下靜態方法和一般方法之間的區別靜態方法在調用時不需要指定方法的接收對象的,而一般的方法則是需要的。如果方法句柄mh所引用的是java.lang.Math類中的靜態方法min,那麼直接通過mh.invokeExact(3,4)就可以調用該方法。
  註意:invokeExact方法在調用的時候要求嚴格的類型匹配,方法的返回值類型也是在考慮範圍之內的。代碼清單2-36中的方法句柄所引用的substring方法的返回值類型是String,因此在使用invokeExact方法進行調用時,需要在前面加上強制類型轉換,以聲明返回值的類型。
  如果去掉這個類型轉換,而直接賦值給一個Object類型的變數,在調用的時候會拋出異常,因為invokeExact會認為方法的返回值類型是Object。如下圖所示:
invokeExact方法調用後返回值類型的嚴格匹配
去掉類型轉換但是不進行賦值操作也是錯誤的,因為invokeExact會認為方法的返回值類型是void,也不同於方法句柄要求的String類型的返回值。
invokeExact方法調用後返回值類型的嚴格匹配

3.1 通過invoke方法實現

  與invokeExact所要求的類型精確匹配不同的是,invoke方法允許更加鬆散的調用方式。它會嘗試在調用的時候進行返回值和參數類型的轉換工作。這是通過MethodHandle類的asType方法來完成的。asType方法的作用是把當前的方法句柄適配到新的MethodType上,並產生一個新的方法句柄。當方法句柄在調用時的類型與其聲明的類型完全一致的時候,調用invoke等同於調用invokeExact;否則,invoke會先調用asType方法來嘗試適配到調用時的類型。如果適配成功,調用可以繼續;否則會拋出相關的異常。這種靈活的適配機制,使invoke方法成為在絕大多數情況下都應該使用的方法句柄調用方式。
  進行類型適配的基本規則是比對返回值類型和每個參數的類型是否都可以相互匹配。只要返回值類型或某個參數的類型無法完成匹配,那麼整個適配過程就是失敗的。從待轉換的源類型S到目標類型T匹配成功的基本原則如下:

  • 1)可以通過Java的類型轉換來完成,一般是從子類轉換成父類,介面的實現類轉換成介面,比如從String類轉換到Object類
  • 2)可以通過基本類型的轉換來完成,只能進行類型範圍的擴大,比如從int類型轉換到long類型。
  • 3)可以通過基本類型的自動裝箱和拆箱機制來完成,比如從int類型到Integer類型。
  • 4)如果S有返回值類型,而T的返回值是void, S的返回值會被丟棄。
  • 5)如果S的返回值是void,而T的返回值是引用類型,T的返回值會是null。
  • 6)如果S的返回值是void,而T的返回值是基本類型,T的返回值會是0。
    滿足上面規則時進行兩個方法類型之間的轉換是會成功的。

let's see how it's possible to use the invoke() with a boxed argument:

 @Test
    public void givenReplaceMethodHandle_whenInvoked_thenCorrectlyReplaced() throws Throwable {
        MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
        MethodType mt = MethodType.methodType(String.class, char.class, char.class);
        MethodHandle replaceMH = publicLookup.findVirtual(String.class, "replace", mt);
        String replacedString = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a');
        String replacedString3 = (String) replaceMH.invoke("jovo",  'o', 'a');
        String replacedString2 = (String) replaceMH.invoke("jovo",  new Character('o'), 'a');
        String replacedString4 = (String) replaceMH.invokeExact("jovo",  'o', 'a');
        String replacedString5 = (String) replaceMH.invokeExact("jovo",  new Character('o'), 'a'); //不能使用包裝類,報錯
        assertEquals("java", replacedString);
    }

In this case, the replaceMH requires char arguments, the invoke() performs an unboxing on the Character argument before its execution.通過MethodHandle類的asType方法嘗試在調用的時候進行參數類型的轉換工作
invokeExact方法調用後返回值類型的嚴格匹配

3.3 通過invokeWithArguments方法實現

  最後一種調用方式是使用invokeWithArguments。該方法在調用時可以指定任意多個Object類型的參數。完整的調用方式是首先根據傳入的實際參數的個數.

    1. 通過MethodType的genericMethodType方法得到一個返回值和參數類型都是Object的新方法類型。
    1. 再把原始的方法句柄通過asType轉換後得到一個新的方法句柄
    1. 最後通過新方法句柄的invokeExact方法來完成調用

這個方法相對於invokeExact和invoke的優勢在於,它可以通過Java反射API被正常獲取和調用,而invokeExact和invoke不可以這樣。它可以作為反射API和方法句柄之間的橋梁。

MethodType mt = MethodType.methodType(List.class, Object[].class);

MethodHandle asList = publicLookup.findStatic(Arrays.class, "asList", mt);
 
List<Integer> list = (List<Integer>) asList.invokeWithArguments(1,2);
 
assertThat(Arrays.asList(1,2), is(list));

methodHandle類中的invokeWithArguments方法

    public Object invokeWithArguments(Object... arguments) throws Throwable {
        MethodType invocationType = MethodType.genericMethodType(arguments == null ? 0 : arguments.length);
        return invocationType.invokers().spreadInvoker(0).invokeExact(asType(invocationType), arguments);
    }

4.參數長度可變的方法句柄 --- 簡化方法調用時的語法

  在方法句柄中,所引用的底層方法中包含長度可變的參數是一種比較特殊的情況。雖然最後一個長度可變的參數實際上是一個數組,但是仍然可以簡化方法調用時的語法。對於這種特殊的情況,方法句柄也提供了相關的處理能力,主要是一些轉換的方法,允許在可變長度的參數和數組類型的參數之間互相轉換,以方便開發人員根據需求選擇最適合的調用語法.

4.1 MethodHandle的asVarargsCollector方法

  MethodHandle中第一個與長度可變參數相關的方法是asVarargsCollector。它的作用是把原始的方法句柄中的最後一個數組類型的參數轉換成對應類型的可變長度參數
  如代碼清單2-37所示,方法normalMethod的最後一個參數是int類型的數組,引用它的方法句柄在通過asVarargsCollector方法轉換之後,得到的新方法句柄在調用時就可以使用長度可變參數的語法格式,而不需要使用原始的數組形式。在實際的調用中,int類型的參數3、4和5組成的數組被傳入到了normalMethod的參數arg3中。
代碼清單2-37 asVarargsCollector方法的使用示例

public class Varargs {
    public void normalMethod(String arg1,int arg2,int[]arg3){
        System.out.println(arg3);  // args 
    }
    @Test
    public void asVarargsCollector()throws Throwable{
        MethodHandles.Lookup lookup=MethodHandles.lookup();
        MethodHandle mh=lookup.findVirtual(Varargs.class,"normalMethod", MethodType.methodType(void.class, String.class,
                int.class, int[].class));
        mh = mh.asVarargsCollector(int[].class);
        mh.invoke(this,"Hello",2,1,4,5,7,8);
    }
}

asVarargsCollector方法的使用

4.2 MethodHandle的asCollector方法

  第二個方法asCollector的作用與asVarargsCollector類似,不同的是該方法只會把指定數量的參數;收集到原始方法句柄所對應的底層方法的數組類型參數中,而不像asVarargsCollector那樣可以收集任意數量的參數。
  如代碼清單2-38所示,還是以引用normalMethod的方法句柄為例,asCollector方法調用時的指定參數為2,即只有2個參數會被收集到整數類型數組中。在實際的調用中,int類型的參數3和4組成的數組被傳入到了normalMethod的參數args中。

代碼清單2-38 asCollector方法的使用示例

public class Varargs {
    public void normalMethod(String arg1,int arg2,int[]arg3){
        System.out.println(arg3);
    }
    @Test
    public void asCollector()throws Throwable{
        MethodHandles.Lookup lookup=MethodHandles.lookup();
        MethodHandle mh=lookup.findVirtual(Varargs.class,"normalMethod", MethodType.methodType(void.class, String.class,
                int.class, int[].class));
        mh = mh.asCollector(int[].class,2);
        mh.invoke(this,"Hello",2,1,4);
//        mh.invoke(this,"Hello",2,1,4,5,7,8); // 報錯了指定最後一個入參數組的長度為2
    }
}

asCollector方法的使用

4.3MethodHandle的asSpreader方法

  上面的兩個方法把數組類型參數轉換為長度可變的參數,自然還有與之對應的執行反方向轉換的方法。
  代碼清單2-39給出的asSpreader方法就把長度可變的參數轉換成數組類型的參數。轉換之後的新方法句柄在調用時使用數組作為參數,而數組中的元素會被按順序分配給原始方法句柄中的各個參數。在實際的調用中,toBeSpreaded方法所接受到的參數arg2、arg3和arg4的值分別是3、4和5。

代碼清單2-39 asSpreader方法的使用示例

    public void toBeSpreaded (String arg1,int arg2,int arg3,int arg4){

        
    }

    public void asSpreader()throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle mh = lookup.findVirtual(Varargs.class, "toBeSpreaded", MethodType.methodType(void.class, String.class,
                int.class, int.class, int.class));
        mh = mh.asSpreader(int[].class, 3);
        mh.invoke(this, "Hello", new int[]{3, 4, 5});
    }
}

4.3MethodHandle的asFixedArity方法

  最後一個方法asFixedArity是把參數長度可變的方法轉換成參數長度不變的方法。經過這樣的轉換之後,最後一個長度可變的參數實際上就變成了對應的數組類型。在調用方法句柄的時候,就只能使用數組來進行參數傳遞。
  如代碼清單2-40所示,asFixedArity會把引用參數長度可變方法varargsMethod的原始方法句柄轉換成固定長度參數的方法句柄。

代碼清單2-40 asFixedArity方法的使用示例

 public void varargsMethod(String arg1,int...args){

    }

    public void asFixedArity()throws Throwable{
        MethodHandles.Lookup lookup=MethodHandles.lookup();
        MethodHandle mh=lookup.findVirtual(Varargs.class,"varargsMethod",MethodType.methodType(void.class, String.class,
                int[].class));
        mh=mh.asFixedArity();
        mh.invoke(this,"Hello",new int[]{2,4});
    }

5.參數綁定

  在前面介紹過,如果方法句柄在調用時引用的底層方法不是靜態的,調用的第一個參數應該是該方法調用的接收者。這個參數的值一般在調用時指定,也可以事先進行綁定。通過MethodHandle的bindTo方法可以預先綁定底層方法的調用接收者,在實際調用的時候,只需要傳入實際參數即可,不需要再指定方法的接收者。
  代碼清單2-41給出了對引用String類的length方法的方法句柄的兩種調用方式:

  • 第一種沒有進行綁定,調用時需要傳入length方法的接收者;
  • 第二種方法預先綁定了一個String類的對象,因此調用時不需要再指定。

代碼清單2-41 參數綁定的基本用法

public void bindTo()throws Throwable{
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle mh=lookup.findVirtual(String.class,"length",MethodType.methodType(int.class));
int len=(int)mh.invoke("Hello");//值為5
mh=mh.bindTo("Hello World");
len=(int)mh.invoke();//值為11
}

  優點:這種預先綁定參數的方式的靈活性在於它允許開發人員只公開某個方法,而不公開該方法所在的對象。開發人員只需要找到對應的方法句柄,並把適合的對象綁定到方法句柄上,客戶代碼就可以只獲取到方法本身,而不會知道包含此方法的對象。綁定之後的方法句柄本身就可以在任何地方直接運行。

  實際上,MethodHandle的bindTo方法只是綁定方法句柄的第一個參數而已,並不要求這個參數一定表示方法調用的接收者。對於一個MethodHandle,可以多次使用bindTo方法來為其中的多個參數綁定值。代碼清單2-42給出了多次綁定的一個示例。方法句柄所引用的底層方法是String類中的indexOf方法,同時為方法句柄的前兩個參數分別綁定了具體的值。
代碼清單2-42 多次參數綁定的示例

@Test
    public void multipleBindTo()throws Throwable{
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle mh = lookup.findVirtual(String.class,"indexOf",MethodType.methodType(
        int.class, String.class, int.class));
        mh = mh.bindTo("Hello").bindTo("l");
        int index = "Hello".indexOf('l',2); 
        assertEquals(index, mh.invoke(2)); // true

}

  需要註意的是,在進行參數綁定的時候,只能對引用類型的參數進行綁定。無法為int和float這樣的基本類型綁定值。對於包含基本類型參數的方法句柄,可以先使用wrap方法把方法類型中的基本類型轉換成對應的包裝類,再通過方法句柄的asType將其轉換成新的句柄。轉換之後的新句柄就可以通過bindTo來進行綁定,如代碼清單2-43所示。

代碼清單2-43 基本類型參數的綁定方式

    @Test
    public void multipleBindTo()throws Throwable{
        MethodHandles.Lookup lookup = MethodHandles.lookup();
//        MethodHandle mh = lookup.findVirtual(String.class,"indexOf",MethodType.methodType(
//        int.class, String.class, int.class));
//        mh = mh.bindTo("Hello").bindTo("l");
//        int index = "Hello".indexOf('l',2);
//        assertEquals(index, mh.invoke(2));
        MethodHandle mh=lookup.findVirtual(String.class,"substring",MethodType.methodType(String.class, int.class,
        int.class));
        mh=mh.asType(mh.type().wrap());
        mh=mh.bindTo("Hello World").bindTo(3);
        String  str = "Hello World".substring(3,5);
        System.out.println(mh.invoke(5));//值為“lo”
        assertEquals(str, mh.invoke(5));
    }

參考:
   https://www.baeldung.com/java-method-handles

參考資料:《java程式員修煉之道》、《深入理解java7核心技術與最佳實踐》
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、前言 之前項目裡面都是基於Springboot +vue 開發,接下里公司準備做一個物聯網平臺和企業信息化系統,還是幾年前使用過springcloud的我決定系統的總結springcloud中各個組件,供自己學習以及各位博友討論。在此過程中涉及到的架構代碼都將在碼雲或者 github 共用出來, ...
  • C 數組允許定義可存儲相同類型數據項的變數,結構是 C 編程中另一種用戶自定義的可用的數據類型,它允許您存儲不同類型的數據項。 結構用於表示一條記錄,假設您想要跟蹤圖書館中書本的動態,您可能需要跟蹤每本書的下列屬性: Title Author Subject Book ID 定義結構 為了定義結構, ...
  • 前言 前一篇已經開發了大部分框架,包含視頻上下滑動播放,這次將上次未完成的數據顯示友好顯示,以及底部音樂走馬燈特效,另外優化了載入數據的bug,在dart語言里 & 會自動變成&amp; 也不知道這個bug啥時候修複哈. 本系列會持續更新,將各個模塊及功能持續完善. 修複Dart語言 URL顯示錯誤 ...
  • 編程學習本身就是一個枯燥的過程,面對一個新鮮的東西一定是一開始比較好奇,起初比較有興趣,但是越學越覺得枯燥。學習任何東西都是一樣的,但是一定要堅持下去(如果決定要做這一行)。 電腦語言的學習其實就是學習別人的思想,因為這些東西是別人發明出來的,這些東西不是憑空就來了,而是基於很多理論和為解決具體的 ...
  • 前言 本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 加企鵝群695185429即可免費獲取,資料全在群文件里。資料可以領取包括不限於Python實戰演練、PDF電子文檔、面試集錦、學習資料等 1、使用代理 適用情況:限制IP地 ...
  • 1.思路 原先圖片匹配一般都是缺口匹配全圖 優化點: 1.缺口圖片匹配缺口所在圖片那一行圖片可以提高他識別率 2.移動後再進行2次匹配計算距離 2.代碼 def get_image_deviation(): ##讀取滑塊圖 block = cv.imread("img.png", -1) #完整圖片 ...
  • from itertools import groupbyresult = [list(g) for k, g in groupby(data, lambda x:x=='') if not k]print(result) ...
  • laravel 安裝jwt-auth及驗證 1、使用composer安裝jwt,cmd到項目文件夾中; composer require tymon/jwt-auth 1.0.*(這裡版本號根據自己的需要寫) 安裝jwt ,參考官方文檔https://jwt-auth.readthedocs.io/ ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...