[Java] 模拟Jdk 以及 CGLib 代理原理

文章目录

  • JDK
    • arthas 反编译jdk代理对象
      • arthas 反编译的结果是:
  • CGlib
    • methodProxy 不经过反射调用方法的原理
    • MethodProxy原理
      • 模拟 结合目标对象使用
      • 模拟结合 代理对象使用

JDK

Jdk代理的最简单模拟, 由前文可知 JDK动态代理需要实现接口,所以基于此,进行最简单的模拟。

package com.example.proxy;public class Jdk {interface Foo {void foo();}static class Target implements Foo {@Overridepublic void foo() {System.out.println("foo");}}// 代理类static class $Proxy0 implements Foo {@Overridepublic void foo() {// 1. 功能增强System.out.println("before");// 2. 调用目标new Target().foo();}}public static void main(String[] args) {Foo f = new $Proxy0();f.foo();}
}

虽然简单实现了代理,但是目前增强是固定的,但是在实际应用中,使用到代理类,方法是不可能固定的,所以接下来进行优化一下。使用抽象类+模版方法设置代理的执行逻辑。

package com.example.proxy;public class Jdk {interface Foo {void foo();}static abstract class InvokeHandler {abstract Object invoke();}// 代理类static class $Proxy0 implements Foo {private final InvokeHandler invokeHandler;$Proxy0(InvokeHandler invokeHandler) {this.invokeHandler = invokeHandler;}@Overridepublic void foo() {// 1. 功能增强System.out.println("before");// 2. 调用目标invokeHandler.invoke();}}public static void main(String[] args) {Foo f = new $Proxy0(new InvokeHandler() {@OverrideObject invoke() {System.out.println(">>>>>>>> foo");return null;}});f.foo();}
}

至此,方法就是可以不再固定。但是很显然,代理的对象不可能永远只有一个方法,所以想办法动态设置。

package com.example.proxy;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class Jdk {interface Foo {void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;void bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;}static abstract class InvokeHandler {abstract Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException;}// 代理类static class $Proxy0 implements Foo {private final InvokeHandler invokeHandler;$Proxy0(InvokeHandler invokeHandler) {this.invokeHandler = invokeHandler;}@Overridepublic void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {// 1. 功能增强System.out.println("before");// 2. 调用目标invokeHandler.invoke(Foo.class.getMethod("foo"), new Object[0]);}@Overridepublic void bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {// 1. 功能增强System.out.println("before");// 2. 调用目标invokeHandler.invoke(Foo.class.getMethod("bar"), new Object[0]);}}static class Target implements Foo {@Overridepublic void foo() {System.out.println("target foo");}@Overridepublic void bar() {System.out.println("target bar");}}public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Foo f = new $Proxy0(new InvokeHandler() {@OverrideObject invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException {// 传入代理对象method.invoke(new Target(), params);return null;}});f.foo();f.bar();}
}

/**
运行结果
before
target foo
before
target bar
**/

到这里,可以发现,多方法的代理对象也可以正常执行。但是如果执行方法有值返回呢,这个也简单,小修改一波。

package com.example.proxy;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class Jdk {interface Foo {Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;}static abstract class InvokeHandler {abstract Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException;}// 代理类static class $Proxy0 implements Foo {private final InvokeHandler invokeHandler;$Proxy0(InvokeHandler invokeHandler) {this.invokeHandler = invokeHandler;}@Overridepublic Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {// 1. 功能增强System.out.println("before");// 2. 调用目标return invokeHandler.invoke(Foo.class.getMethod("foo"), new Object[0]);}@Overridepublic Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {// 1. 功能增强System.out.println("before");// 2. 调用目标return invokeHandler.invoke(Foo.class.getMethod("bar"), new Object[0]);}}static class Target implements Foo {@Overridepublic Integer foo() {System.out.println("target foo");return 1;}@Overridepublic String  bar() {System.out.println("target bar");return "hello";}}public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Foo f = new $Proxy0(new InvokeHandler() {@OverrideObject invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException {// 传入代理对象return method.invoke(new Target(), params);}});System.out.println(f.foo());System.out.println(f.bar());}
}
/**
运行结果
before
target foo
1
before
target bar
hello**/

在源码实现中,方法还可以被缓存复用,不需要每次都重新创建。

package com.example.proxy;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class Jdk {interface Foo {Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;}static abstract class InvokeHandler {abstract Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException;}// 代理类static class $Proxy0 implements Foo {private final InvokeHandler invokeHandler;private final Map<String, Method> cache = new HashMap<>();$Proxy0(InvokeHandler invokeHandler) {this.invokeHandler = invokeHandler;}@Overridepublic Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {// 1. 功能增强System.out.println("before");// 2. 调用目标Method foo = cache.getOrDefault("foo", null);if(foo == null) {foo = Foo.class.getMethod("foo");System.out.println(">>>>>> 新创建方法");cache.put("foo", foo);}return invokeHandler.invoke(foo, new Object[0]);}@Overridepublic Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {// 1. 功能增强System.out.println("before");// 2. 调用目标Method bar = cache.getOrDefault("bar", null);if(bar == null) {bar = Foo.class.getMethod("foo");System.out.println(">>>>>> 新创建方法");cache.put("bar", bar);}return invokeHandler.invoke(bar, new Object[0]);}}static class Target implements Foo {@Overridepublic Integer foo() {System.out.println("target foo");return 1;}@Overridepublic String  bar() {System.out.println("target bar");return "hello";}}public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Foo f = new $Proxy0(new InvokeHandler() {@OverrideObject invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException {// 传入代理对象return method.invoke(new Target(), params);}});System.out.println(f.foo());System.out.println(f.bar());System.out.println(f.foo());System.out.println(f.bar());}
}

/**
before
>>>>>> 新创建方法
target foo
1
before
>>>>>> 新创建方法
target foo
1
before
target foo
1
before
target foo
1
**/

到此,代理方法只会被寻找一次。

JDK 动态代理生成的代理类是以字节码的形式存在的,并不存在所谓的 .java 文件,但也不是说就没办法看到生成的代理类信息了。不过可
以使用 arthas反编译,看到字节码。

arthas 反编译jdk代理对象

比如:

package com.example.proxy;import java.io.IOException;
import java.lang.reflect.Proxy;public class Jdk1 {interface Foo {void foo();}static final class Target implements Foo {@Overridepublic void foo() {System.out.println("target foo");}}public static void main(String[] args) throws IOException {// 原始对象Target target = new Target();// 用来加载在运行期间动态生成的字节码ClassLoader classLoader = Jdk1.class.getClassLoader();Foo proxy = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, (p, method, params) -> {System.out.println("before...");// 目标.方法(参数) --> 方法.invoke(目标, 参数)Object result = method.invoke(target, params);System.out.println("after...");// 也返回目标方法执行的结果return result;});// 打印代理类的全限定类名System.out.println(proxy.getClass());proxy.foo();// 只要不在控制台上输入并回车,程序就不会终端System.in.read();}}

打印的结果是:

class com.example.proxy.$Proxy0
before...
target foo
after...

arthas 反编译的结果是:

[arthas@60054]$ jad com.example.proxy.$Proxy0

ClassLoader:
±jdk.internal.loader.ClassLoaders A p p C l a s s L o a d e r @ 251 a 69 d 7 + − j d k . i n t e r n a l . l o a d e r . C l a s s L o a d e r s AppClassLoader@251a69d7 +-jdk.internal.loader.ClassLoaders AppClassLoader@251a69d7+jdk.internal.loader.ClassLoadersPlatformClassLoader@17747fbe

Location:


/** Decompiled with CFR.** Could not load the following classes:*  com.example.proxy.Jdk1$Foo*/
package com.example.proxy;import com.example.proxy.Jdk1;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;final class $Proxy0
extends Proxy
implements Jdk1.Foo {private static final Method m0;private static final Method m1;private static final Method m2;private static final Method m3;private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup lookup) throws IllegalAccessException {if (lookup.lookupClass() == Proxy.class && lookup.hasFullPrivilegeAccess()) {return MethodHandles.lookup();}throw new IllegalAccessException(lookup.toString());}public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}static {try {m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m3 = Class.forName("com.example.proxy.Jdk1$Foo").getMethod("foo", new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}}public final void foo() {try {this.h.invoke(this, m3, null);return;}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final boolean equals(Object object) {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final int hashCode() {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}}

CGlib

cglib 代理类继承目标对象。

public class Target {public void save() {System.out.println("0");}public void save(int i) {System.out.println(i);}public void save(long l) {System.out.println(l);}
}//- cglib 代理模拟
public class Proxy extends Target{private  MethodInterceptor methodInterceptor;public void setMethodInterceptor(MethodInterceptor methodInterceptor) {this.methodInterceptor = methodInterceptor;}private static final Method save0;private static final Method save1;private static final Method save2;static {try {save0 = Target.class.getMethod("save");save1 = Target.class.getMethod("save", int.class);save2 = Target.class.getMethod("save", long.class);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}@Overridepublic void save()  {try {methodInterceptor.intercept(this, save0, new Object[0], null);} catch (Throwable e) {throw new RuntimeException(e);}}@Overridepublic void save(int i) {try {methodInterceptor.intercept(this, save1, new Object[]{i}, null);} catch (Throwable e) {throw new RuntimeException(e);}}@Overridepublic void save(long l) {try {methodInterceptor.intercept(this, save2, new Object[]{l}, null);} catch (Throwable e) {throw new RuntimeException(e);}}
}//- 测试类
public class ProxyTest {public static void main(String[] args) {Target target = new Target();Proxy proxy = new Proxy();proxy.setMethodInterceptor((obj, method, args1, proxy1) -> {System.out.println("before----");return method.invoke(target, args1);});proxy.save();proxy.save(1);proxy.save(2L);}
}

methodProxy 不经过反射调用方法的原理

在在上述 Proxy 类中,重写了父类中的方法,并在重写的方法中调用了 intercept() 方法,重写的这些方法相当于是带增强功能的方法。
在 JDK 的动态代理中,使用反射对方法进行调用,而在 CGLib 动态代理中,可以使用 intercept() 方法中 MethodProxy 类型的参数实现不经过反射来调用方法。
接收的 MethodProxy 类型的参数可以像 Method 类型的参数一样,在静态代码块中被实例化。

public class Proxy extends Target{private  MethodInterceptor methodInterceptor;public void setMethodInterceptor(MethodInterceptor methodInterceptor) {this.methodInterceptor = methodInterceptor;}static Method save0;static Method save1;static Method save2;static MethodProxy save0Proxy;static MethodProxy save1Proxy;static MethodProxy save2Proxy;static {try {save0 = Target.class.getMethod("save");save1 = Target.class.getMethod("save", int.class);save2 = Target.class.getMethod("save", long.class);save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");} catch (NoSuchMethodException e) {throw new NoSuchMethodError(e.getMessage());}}// >>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法public void saveSuper() {super.save();}public void saveSuper(int i) {super.save(i);}public void saveSuper(long i) {super.save(i);}// >>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法@Overridepublic void save() {try {methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}@Overridepublic void save(int i) {try {methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}@Overridepublic void save(long i) {try {methodInterceptor.intercept(this, save2, new Object[]{i}, save2Proxy);} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}
}//- 测试类
public class ProxyTest {public static void main(String[] args) {Target target = new Target();Proxy proxy = new Proxy();proxy.setMethodInterceptor((obj, method, args1, methodProxy) -> {System.out.println("before----");
//            return method.invoke(target, args1);
//            return methodProxy.invoke(target, args1);  // 内部无反射调用 结合目标对象使用return methodProxy.invokeSuper(obj, args1); // 内部无反射调用, 结合代理对象使用});proxy.save();proxy.save(1);proxy.save(2L);}
}

MethodProxy原理

其内部是通过一个 FastClass+ 方法签名实现

模拟 结合目标对象使用

Target target = new Target();Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (obj, method, args, methodProxy) -> {System.out.println("before...");// 内部没使用反射,需要目标(spring 的选择)Object result = methodProxy.invoke(target, args);System.out.println("after...");return result;
});
package com.example.proxy;import org.springframework.cglib.core.Signature;public class TargetFastClass {static Signature s0 = new Signature("save", "()V");static Signature s1 = new Signature("save", "(I)V");static Signature s2 = new Signature("save", "(J)V");/*** <p>获取目标方法的编号</p>* <p>* Target 目标类中的方法:* save()             0* save(int)          1* save(long)         2* </p>** @param signature 包含方法名称、参数返回值* @return 方法编号*/public int getIndex(Signature signature) {if (s0.equals(signature)) {return 0;}if (s1.equals(signature)) {return 1;}if (s2.equals(signature)) {return 2;}return -1;}/*** 根据 getIndex() 方法返回的方法编号正常调用目标对象方法** @param index       方法编号* @param target       目标对象* @param args 调用目标对象方法需要的参数* @return 方法返回结果*/public Object invoke(int index, Object target, Object[] args) {if (index == 0) {((Target) target).save();return null;}if (index == 1) {((Target) target).save((int) args[0]);return null;}if (index == 2) {((Target) target).save((long) args[0]);return null;}throw new RuntimeException("无此方法");}public static void main(String[] args) {TargetFastClass fastClass = new TargetFastClass();int index = fastClass.getIndex(new Signature("save", "()V"));fastClass.invoke(index, new Target(), new Object[0]);index = fastClass.getIndex(new Signature("save", "(J)V"));fastClass.invoke(index, new Target(), new Object[]{2L});}
}

模拟结合 代理对象使用

Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (obj, method, args, methodProxy) -> {System.out.println("before...");// 内部没使用反射,需要代理Object result = methodProxy.invokeSuper(obj, args);System.out.println("after...");return result;
});
package com.example.proxy;import org.springframework.cglib.core.Signature;public class ProxyFastClass {static Signature s0 = new Signature("saveSuper", "()V");static Signature s1 = new Signature("saveSuper", "(I)V");static Signature s2 = new Signature("saveSuper", "(J)V");/*** <p>获取代理方法的编号</p>* <p>* Proxy 代理类中的方法:* saveSuper()             0* saveSuper(int)          1* saveSuper(long)         2* </p>** @param signature 包含方法名称、参数返回值* @return 方法编号*/public int getIndex(Signature signature) {if (s0.equals(signature)) {return 0;}if (s1.equals(signature)) {return 1;}if (s2.equals(signature)) {return 2;}return -1;}/*** 根据 getIndex() 方法返回的方法编号正常调用代理对象中带原始功能的方法** @param index 方法编号* @param proxy 代理对象* @param args  调用方法需要的参数* @return 方法返回结果*/public Object invoke(int index, Object proxy, Object[] args) {if (index == 0) {((Proxy) proxy).saveSuper();return null;}if (index == 1) {((Proxy) proxy).saveSuper((int) args[0]);return null;}if (index == 2) {((Proxy) proxy).saveSuper((long) args[0]);return null;}throw new RuntimeException("无此方法");}public static void main(String[] args) {ProxyFastClass fastClass = new ProxyFastClass();int index = fastClass.getIndex(new Signature("saveSuper", "()V"));fastClass.invoke(index, new Proxy(), new Object[0]);int index1 = fastClass.getIndex(new Signature("saveSuper", "(J)V"));fastClass.invoke(index1, new Proxy(), new Object[]{2L});}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/194215.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

★543. 二叉树的直径

543. 二叉树的直径 简单题&#xff0c;确实不难。 相当于就是求节点的深度。左孩子的最大深度 右孩子的最大深度 1 根节点深度。 本题要求的就是路径数&#xff0c;这里的路径数 节点数 - 1&#xff0c;然后想一下&#xff0c;对于一个节点来说&#xff0c;以他为根左右两…

数据结构与算法设计分析——NP完全理论

目录 一、P类问题与NP类问题的定义二、常见的NP类问题&#xff08;一&#xff09;旅行商问题&#xff08;TSP&#xff09;&#xff08;二&#xff09;哈密尔顿回路问题&#xff08;三&#xff09;判断回路问题&#xff08;四&#xff09;图的着色问题&#xff08;五&#xff09…

使用AOS实现网页动画效果

在现代Web开发中&#xff0c;动画效果是提升用户体验和页面交互性的重要因素之一。而AOS&#xff08;Animate On Scroll&#xff09;作为一个强大的动画库&#xff0c;可以帮助我们轻松地实现网页元素的滚动动画效果。 什么是AOS&#xff1f; AOS是一个基于CSS3和JavaScript的…

Transformer

目录 Encoder Add&Norm:&#xff08;LayerNorm&#xff09;Transformer中的归一化(五)&#xff1a;Layer Norm的原理和实现 & 为什么Transformer要用LayerNorm - 知乎 (zhihu.com) LayerNorm怎么做的&#xff1f; Feed Forward: FeedForward代码&#xff1a; 公式…

Java Stream流对多个字段动态指定字段排序

Java 8 的 Stream 使用了函数式编程模式,它可以被用来对集合或数组进行链状流式的排序就需要搬出Stream sort方法进行排序,重写其中的Comparator。 本文重点介绍使用Java Stream流排序器Comparator对List集合进行排序的技巧,包括复杂实体对象多字段升降序排序方法。 1为什么…

学习TypeScrip5(函数扩展)

函数的类型 注意&#xff0c;参数不能多传&#xff0c;也不能少传 必须按照约定的类型来 const fn (name: string, age:number): string > {return name age } fn(张三,18) 函数的可选参数? 通过?表示该参数为可选参数 const fn (name: string, age?:number): stri…

Android 12 及以上授权精确位置和模糊位置

请求位置信息权限 为了保护用户隐私&#xff0c;使用位置信息服务的应用必须请求位置权限。 请求位置权限时&#xff0c;请遵循与请求任何其他运行时权限相同的最佳做法。请求位置权限时的一个重要区别在于&#xff0c;系统中包含与位置相关的多项权限。具体请求哪项权限以及…

栈和队列的OJ题——14.用栈实现队列

14.用栈实现队列 232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; /* 解题思路&#xff1a; 此题可以用两个栈实现&#xff0c;一个栈进行入队操作&#xff0c;另一个栈进行出队操作 出队操作&#xff1a; 当出队的栈不为空是&#xff0c;直接进行出栈操作&#xff…

算法通关村第七关—迭代实现二叉树的遍历(黄金)

迭代实现二叉树的遍历 迭代法实现前序遍历 前序遍历是中左右&#xff0c;如果还有左子树就一直向下找。完了之后再返回从最底层逐步向上向右找。不难写出如下代码&#xff1a;&#xff08;注意代码中&#xff0c;空节点不入栈&#xff09; public List<Integer>preorde…

VSCode 中将头文件和头文件函数分离,编译主函数跳出 undefined reference to 的问题解决

VSCode 编写 C &#xff08;.h&#xff0c;.cpp 文件分离&#xff09;代码&#xff0c;编写完成后&#xff0c;编译遇到了编译错误 undefined reference to xxx。 开始还以为使用了 -stdc20 而不能使用 #include “xxx.h" 方式头文件&#xff0c;但仔细一想虽然引入了 im…

前端这个岗位对于整个项目的意义

在软件开发这个领域&#xff0c;开发一款软件就意味着一个项目的诞生&#xff0c;那么&#xff0c;一个项目诞生它包括很多人的共同协作和努力去实现&#xff0c;那么大概包括以下这些步骤&#xff1a; 1.需求分析&#xff1a;这是软件开发的第一步&#xff0c;商务人员和产品…

基于YOLOv7算法的的高精度实时通用目标检测识别系统(PyTorch+Pyside6+YOLOv7)

摘要&#xff1a;基于YOLOv7算法的高精度实时检测识别系统可用于日常生活中检测与定位多种目标&#xff0c;此系统可完成对输入图片、视频、文件夹以及摄像头方式的目标检测与识别&#xff0c;同时本系统还支持检测结果可视化与导出。本系统采用YOLOv7目标检测算法来训练数据集…

OpenHarmony 应用(HarmonyOS 原生应用)- 写一个 Hello World 并在华为手机上跑起来

OpenHarmony 简介 ArkUI 开发框架 OpenHarmony 提供了一套UI开发框架&#xff0c;即方舟开发框架&#xff08;ArkUI 框架&#xff09;。 两种开发范式 支持两种开发范式&#xff0c;分别是基于ArkTS的声明式开发范式&#xff08;简称“声明式开发范式”&#xff09;和兼容J…

【Leetcode题单】(01 数组篇)刷题关键点总结03【数组的改变、移动】

【Leetcode题单】&#xff08;01 数组篇&#xff09;刷题关键点总结03【数组的改变、移动】&#xff08;3题&#xff09; 数组的改变、移动453. 最小操作次数使数组元素相等 Medium665. 非递减数列 Medium283. 移动零 Easy 大家好&#xff0c;这里是新开的LeetCode刷题系列&…

弦理论的技术探索

弦理论的技术探索 一、引言 弦理论,作为现代物理学中的一个重要分支,旨在揭示宇宙的终极规律。它认为,宇宙中的一切物质和能量都是由微小的弦振动产生的。本文将深入探讨弦理论的技术层面,包括其数学基础、物理应用以及计算机模拟等方面。 二、弦理论的数学基础 弦理论的…

Go连接mysql数据库

package main import ("database/sql""fmt"_ "github.com/go-sql-driver/mysql" ) //go连接数据库示例 func main() {// 数据库信息dsn : "root:roottcp(192.168.169.11:3306)/sql_test"//连接数据库 数据库类型mysql,以及数据库信息d…

【Tkinter 入门教程】

【Tkinter 入门教程】 1. Tkinter库的简介&#xff1a;1.1 GUI编程1.2 Tkinter的定位 2. Hello word! 程序起飞2.1 第⼀个程序2.2 字体颜色主题 3. 组件讲解3.1 tkinter 的核⼼组件3.2 组件的使⽤3.3 标签Label3.3.1 标签显示内容3.3.2 多标签的应⽤程序3.3.3 总结 3.4 按钮but…

Wireshark 协议插件Lua开发 -数据包内嵌协议的解释

概述 因为公司项目涉及的协议打包&#xff0c;协议包内又嵌了一层IP包的奇葩套娃结构&#xff0c;为了方便抓包调试&#xff0c;利用Wireshark的协议插件开发功能&#xff0c;写了一个插件&#xff0c;博文记录以备忘。 环境信息 Wireshark 4.0.3 协议结构体套娃图 插件安装…

使用unity开发Pico程序,场景中锯齿问题

1、问题 使用unity【非HDR】开发Pico程序&#xff0c;场景中锯齿问题&#xff0c;设置了unity的抗锯齿和渲染方式,及悬挂抗锯齿的脚本&#xff0c;都不能很好的解决项目中图片、文字的锯齿问题&#xff0c;通过摸索找到了妥善的方法 1、修改项目中图片的 GenerateMIpMaps 为勾…

【系统运维】Centos部署Haproxy+Keepalived+RabbitMQ高可用集群

1.RabbitMQ高可用集群方案 &#xff08;1&#xff09;RabbitMQ搭建集群的作用&#xff1a;提高可用性、可靠性和处理能力&#xff0c;确保系统提供高效的消息传递服务 高可用性&#xff1a;通过集群&#xff0c;即使其中一个节点发生故障&#xff0c;其他节点仍然可以继续提供…