[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 {

        @Override
        public void foo() {
            System.out.println("foo");
        }
    }

    // 代理类
    static class $Proxy0 implements Foo {

        @Override
        public 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;
        }

        @Override
        public void foo() {
            // 1. 功能增强
            System.out.println("before");
            // 2. 调用目标
            invokeHandler.invoke();
        }
    }
    

    public static void main(String[] args) {
        Foo f = new $Proxy0(new InvokeHandler() {
            @Override
            Object 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;
        }

        @Override
        public void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            // 1. 功能增强
            System.out.println("before");
            // 2. 调用目标
            invokeHandler.invoke(Foo.class.getMethod("foo"), new Object[0]);
        }

        @Override
        public 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 {
        @Override
        public void foo() {
            System.out.println("target foo");
        }

        @Override
        public void bar() {
            System.out.println("target bar");
        }
    }


    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Foo f = new $Proxy0(new InvokeHandler() {
            @Override
            Object 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;
        }

        @Override
        public Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            // 1. 功能增强
            System.out.println("before");
            // 2. 调用目标
            return invokeHandler.invoke(Foo.class.getMethod("foo"), new Object[0]);
        }

        @Override
        public 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 {
        @Override
        public Integer foo() {
            System.out.println("target foo");
            return 1;
        }

        @Override
        public 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() {
            @Override
            Object 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;
        }

        @Override
        public 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]);
        }

        @Override
        public 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 {
        @Override
        public Integer foo() {
            System.out.println("target foo");
            return 1;
        }

        @Override
        public 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() {
            @Override
            Object 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 {
        @Override
        public 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);
        }
    }



    @Override
    public void save()  {
        try {
            methodInterceptor.intercept(this, save0, new Object[0], null);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void save(int i) {
        try {
            methodInterceptor.intercept(this, save1, new Object[]{i}, null);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public 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);
    }


    // >>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
    @Override
    public void save() {
        try {
            methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }


    @Override
    public void save(int i) {
        try {
            methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public 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.mfbz.cn/a/213875.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python--自动化办公(Word)

python自动化办公之—Word python-docx库 1、安装python-docx库 pip install python-docx2、基本语法 1、打开文档 document Document() 2、加入标题 document.add_heading(总标题,0) document.add_heading(⼀级标题,1) document.add_heading(⼆级标题,2) 3、添加文本 para…

Spring IOC—基于XML配置和管理Bean 万字详解(通俗易懂)

目录 一、前言 二、通过类型来获取Bean 0.总述&#xff08;重要&#xff09; : 1.基本介绍 : 2.应用实例 : 三、通过指定构造器为Bean注入属性 1.基本介绍 : 2.应用实例 : 四、通过p命名空间为Bean注入属性 1.基本介绍 : 2.应用实例 : 五、通过ref引用实现Bean的相…

手机也能“敲”代码?

除了PC个人电脑外&#xff0c;很多电子产品也可以实现代码的编辑&#xff0c;比如智能手机。现在主流的手机操作系统只有两种&#xff0c;一种是大部分手机厂商选择的安卓系统&#xff0c;另外一种是苹果公司独创的ios操作系统。而Android系统是基于Linux开发的专属于移动设备的…

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

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

Java数据结构之《构造哈夫曼树》题目

一、前言&#xff1a; 这是怀化学院的&#xff1a;Java数据结构中的一道难度中等(偏难理解)的一道编程题(此方法为博主自己研究&#xff0c;问题基本解决&#xff0c;若有bug欢迎下方评论提出意见&#xff0c;我会第一时间改进代码&#xff0c;谢谢&#xff01;) 后面其他编程题…

【蓝桥杯】翻硬币

翻硬币 思路&#xff1a; 其实有点贪心的意思&#xff0c;依次比较&#xff0c;不同就1&#xff0c;然后修改自己的字符串和下一个的字符串&#xff0c;再匹配。 #include<iostream> #include<string> using namespace std;string now,res;int main(void) {cin&g…

MQ - 消息系统

消息系统 1、消息系统的演变 在大型系统中&#xff0c;会需要和很多子系统做交互&#xff0c;也需要消息传递&#xff0c;在诸如此类系统中&#xff0c;你会找到源系统&#xff08;消息发送方&#xff09;和 目的系统&#xff08;消息接收方&#xff09;。为了在这样的消息系…

java高校实验室排课学生考勤系统springboot+vue

随着各高校办学规模的迅速扩大,学科专业的不断拓宽,传统的实验教学和实验室管理方法已经不能适应学校管理的要求,特别是化学实验室的管理,化学实验室仪器药品繁杂多样,管理任务繁重,目前主要使用人工记录方法管理,使用不便,效率低下,而且容易疏漏.时间一长将产生大量的文件和数…

【面试经典150 | 二分查找】搜索二维矩阵

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;二分查找 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等…

c# OpenCV 读取、显示和写入图像(二)

读取、显示和写入图像是图像处理和计算机视觉的基础。即使在裁剪、调整大小、旋转或应用不同的滤镜来处理图像时&#xff0c;您也需要先读取图像。因此&#xff0c;掌握这些基本操作非常重要。 imread()读取图像imshow()在窗口中显示图像imwrite()将图像保存到文件目录里 我们…

细说CountDownLatch

CountDownLatch 概念 CountDownLatch可以使一个获多个线程等待其他线程各自执行完毕后再执行。 CountDownLatch 定义了一个计数器&#xff0c;和一个阻塞队列&#xff0c; 当计数器的值递减为0之前&#xff0c;阻塞队列里面的线程处于挂起状态&#xff0c;当计数器递减到0时…

力扣226:翻转二叉树

力扣226&#xff1a;翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 2&#xff1a; 输入&#xff1a;root [2,1,3]…

分享一个国内可用的免费AI-GPT网站

背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具&#xff0c;近期的热度直接沸腾&#x1f30b;。 我们也忍不住做了一个基于ChatGPT的网站&#xff0c;可以免登陆&#xff01;&#xff01;国内可直接对话AI&#xff0c;也有各种提供工作效率的工具供大家使用。 可以这…

判断二叉树是否为完全二叉树

具体思路&#xff1a; 将二叉树层序遍历&#xff08;节点&#xff09;插进队列中&#xff0c;遇到空时就break&#xff08;退出循环&#xff09;&#xff0c;再重新遍历一遍&#xff0c;若空的后面又再次出现数据&#xff0c;则返回false&#xff08;不是完全二叉树&#xff0…

SpringSecurity和JWT实现认证和授权

SpringSecurity和JWT实现认证和授权 框架介绍SpringSecurityJWT组成实例JWT实现认证和授权的原理 Hutool 使用表整合SpringSecurity及JWT在pom.xml中添加依赖添加JWT token的工具类添加RbacAdminService&#xff1a;添加自定义mapper创建SpringSecurity配置类添加ProjectSecuri…

软件工程精品课程教学网站的设计与实现

系统功能需求分析 本系统要求采用Browser/Server模式设计开发&#xff0c;可以作为一般高等院校的网络学堂&#xff1b;可以为教师的辅助教学或者网络教学提供一个完善的教学网站&#xff1b;学生可以利用本教学网站来完成一些课程的学习任务。 2.2.1 功能划分 《软件工程》教学…

分享一个简单的基于C语言嵌入式GUI界面切换代码

目录 前言 一、数据类型 二、页面调度 三、页面显示 四、视频展示 前言 最近在用LVGL写一个简单的UI界面&#xff0c;需要进行几个页面的切换&#xff0c;所以就自己写了一个简单页面切换代码&#xff0c;方便进行页面切换&#xff0c;同时使UI代码结构更加清晰。这个结构…

如何使用注解实现接口的幂等性校验

如何使用注解实现接口的幂等性校验 背景什么是幂等性为什么要实现幂等性校验如何实现接口的幂等性校验1. 数据库唯一主键2. 数据库乐观锁3. 防重 Token 令牌4. redis 如何将这几种方式都组装到一起结语 背景 最近在小组同学卷的受不了的情况下&#xff0c;我决定换一个方向卷去…

人工智能轨道交通行业周刊-第67期(2023.11.27-12.3)

本期关键词&#xff1a;列车巡检机器人、城轨智慧管控、制动梁、断路器、AICC大会、Qwen-72B 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro…

使用Prometheus监控Padavan路由器

Prometheus监控Padavan路由器 1、背景 近期在Synology&#xff08;群辉&#xff09;中安装一套Prometheus监控程序&#xff0c;目前已经监控Synology&#xff0c;然后家中有有路由器&#xff08;Padavan&#xff09;型号&#xff0c;也准备使用PrometheusGrafan进行监控。 ‍…
最新文章