Spring AOP(2)原理(代理模式和源码解析)

目录

一、代理模式

二、静态代理

三、动态代理

1、JDK动态代理

(1)JDK动态代理实现步骤

(2)定义JDK动态代理类

(3)代码简单讲解

2、CGLIB动态代理

(1)CGLIB 动态代理类实现步骤

(2)添加依赖

(3)自定义MethodInterceptor(方法拦截器)

(4)创建代理类,并使用

(5)代码简单讲解

四、Spring AOP 源码剖析(了解)

五、常见面试题

1、什么是 AOP?

2、Spring AOP的实现方式有哪些?

3、Spring AOP 的实现原理?

4、Spring 使用的是哪种代理方式?

5、JDK 和 CGLIB 的区别?

六、总结


        上篇文章学习了 Spring AOP 的应用,接下来我们来学习 Spring AOP 的原理,也就是 Spring 是如何实现 AOP 的。

        Spring AOP 是基于动态代理来实现 AOP 的,咱门学习内容主要分以下两部分:

1、代理模式

2、Spring AOP 源码 剖析


一、代理模式

        代理模式,也叫 委托模式。

定义为其他对象提供一种代理以控制这个对象的访问它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用

       某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。

        使用代理前:

        使用代理后:

        

        生活中的代理:

艺人经纪人:广告商找艺人拍广告,需要经过经纪人,由经纪人来和艺人沟通。

房屋中介:房屋进行租赁时,卖方会把房屋授权给中介,由中介来代理看房,房屋咨询等服务。

经销商:厂商不直接对外销售产品,由经销商负责代理销售。

秘书/助理:合作伙伴找老板谈合作,需要先经过秘书/助理预约。

        代理模式的主要角色:

1、Subject业务接口类。可以是抽象类或者接口(不一定有)

2、RealSubject业务实现类。具体的业务执行,也就是被代理对象

3、Proxy代理类。RealSubject的代理

比如 房屋出租:

        Subject:就是提前定义了房东做的事情,交给中介代理,也是中介要做的事情。

        RealSubject:房东。

        Proxy:中介。

        UML类图如下:

        

        代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强根据代理的创建时期,代理模式分为静态代理和动态代理

        静态代理由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了

        动态代理在程序运行时,运用反射机制动态创建而成


二、静态代理

        静态代理:在程序运行前,代理类的 .class文件 就已经存在了。(在出租房子之前,中介已经做好了相关的工作,就等租户来租房子了)。

        我们通过代码来加深理解。以房租租赁为例:

1、定义接口(定义房东要做的事情,也是中介需要做的事情)

public interface HouseSubject {
    void rentHouse();
}

2、实现接口(房东出租房子)

public class RealHouseSubject implements HouseSubject{
    @Override
    public void rentHouse() {
        System.out.println("我是房东, 我出租房子");
    }
}

3、代理(中介,帮房东出租房子)

public class HouseProxy implements HouseSubject{
    private HouseSubject target;

    public HouseProxy(HouseSubject target) {
        this.target = target;
    }

    @Override
    public void rentHouse() {
        //代理前
        System.out.println("我是中介, 开始代理");
        //出租房子
        target.rentHouse();;
        //代理后
        System.out.println("我是中介, 结束代理");
    }
}

4、使用

public class Main {
    public static void main(String[] args) {
        HouseSubject subject = new RealHouseSubject();
        //创建代理类
        HouseProxy houseProxy = new HouseProxy(subject);
        //通过代理类访问⽬标⽅法
        houseProxy.rentHouse();
    }
}

        运行结果:

        上面这个代理实现方式就是静态代理(仿佛啥也没干)。从上述程序可以看出,虽然静态代理也完成了对目标对象的代理,但是由于代码都写死了,对目标对象的每个方法的增强都是手动完成的,非常不灵活。所以日常开发几乎看不到静态代理的场景。

        接下来新增需求:中介又新增了其他业务:代理房屋出售。我们就需要对上述代码进行修改。

1、接口定义修改:

public interface HouseSubject {
    void rentHouse();
    void saleHouse();
}

2、接口实现修改

public class RealHouseSubject implements HouseSubject{
    @Override
    public void rentHouse() {
        System.out.println("我是房东, 我出租房子");
    }

    @Override
    public void saleHouse() {
        System.out.println("我是房东, 我出售房子");
    }
}

3、代理类修改

public class HouseProxy implements HouseSubject{
    private HouseSubject target;

    public HouseProxy(HouseSubject target) {
        this.target = target;
    }

    @Override
    public void rentHouse() {
        //代理前
        System.out.println("我是中介, 开始代理");
        //出租房子
        target.rentHouse();;
        //代理后
        System.out.println("我是中介, 结束代理");
    }

    @Override
    public void saleHouse() {
        //代理前
        System.out.println("我是中介, 开始代理");
        //出租房子
        target.rentHouse();;
        //代理后
        System.out.println("我是中介, 结束代理");
    }
}

4、使用

public class Main {
    public static void main(String[] args) {
        HouseSubject subject = new RealHouseSubject();
        //创建代理类
        HouseProxy houseProxy = new HouseProxy(subject);
        //通过代理类访问⽬标⽅法
        houseProxy.rentHouse();
        System.out.println("=========");
        houseProxy.saleHouse();
    }
}

        运行结果:

        从上述代码可以看出,我们修改接口(Subject)和业务实现类(RealSubject),还需要修改代理类(Proxy)

        同样的,如果新增接口(Subject)和业务实现类(RealSubject),也需要对每一个业务实现类新增代理类(Proxy)

        既然代理的流程是一样的,有没有一种办法,让他们通过一个代理类来实现呢?这就需要用到动态代理技术了。


三、动态代理

        相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标对象都单独创建一个代理对象,而是把这个创建代理对象的工作推迟到程序运行时由JVM来实现也就是说动态代理在程序运行时,根据需要动态创建生成

        比如房屋中介,我不需要提前预测都有哪些业务,而是业务来了我再根据情况创建。

        先看代码再来理解。Java也对动态代理进行了实现,并给我们提供一些API,常见的实现方式有两种:

        1、JDK动态代理

        2、CGLIB动态代理

动态代理在我们日常开发中使用的相对较少,但是在框架中几乎是必用的一门技术。学会了动态代理之后,对于我们理解和学习各种框架的原理也非常有帮助。

1、JDK动态代理

(1)JDK动态代理实现步骤

1、定义一个接口及其实现类(静态代理中的 HouseSubject 和 RealHouseSubject)

2、自定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中我们会调用目标方法(被代理类的方法),并自定义一些处理逻辑

3、通过 Proxy.newProxyInstance(ClassLoader, Class<?>[ ]  Interfaces, InvocationHandler  h)方法创建代理对象

(2)定义JDK动态代理类

        创建 JDKInvocationHandler类 实现 InvocationHandler 接口:

public class JDKInvocationHandler implements InvocationHandler {
    //目标对象,即被代理的对象
    private Object target;

    public JDKInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //代理增强内容
        System.out.println("我是中介,开始代理");
        //通过反射调用被代理类的方法
        Object result = method.invoke(target, args);
        //代理增强内容
        System.out.println("我是中介,结束代理");
        return result;
    }
}

        创建一个代理对象并使用:

public class Main {
    public static void main(String[] args) {
        /**
         * JDK动态代理
         */

        //创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建
        RealHouseSubject target = new RealHouseSubject();//目标对象
        /**
         * newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
         * loader:加载我们的被代理类的ClassLoad
         * interfaces:要实现的接口
         * h:代理要做的事情,需要实现 InvocationHandler 这个接口
         */
        HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                new Class[]{HouseSubject.class},
                new JDKInvocationHandler(target)
        );
        proxy.rentHouse();
        System.out.println("==============");
        proxy.saleHouse();
    }
}

        运行程序,结果如下:

        假设代理的是类,而不是对象,代码如下:

public class Main {
    public static void main(String[] args) {
        RealHouseSubject target = new RealHouseSubject();
        RealHouseSubject proxy = (RealHouseSubject) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                new Class[]{RealHouseSubject.class},
                new JDKInvocationHandler(target)
        );
        proxy.rentHouse();
        System.out.println("==============");
        proxy.saleHouse();
    }
}

        运行程序,结果如下:(报错了)

        报错原因RealHouseSubject is not an interface(RealHouseSubject 类不是接口)说明JDK 动态代理只能代理接口,不能代理类,不然会报错。

(3)代码简单讲解

        主要是学习API的使用,我们按照 Java API 的规范来使用即可。

1、InvocationHandler:

        InvocationHandler 接口是 Java 动态代理的关键接口之一,它定义了一个单一方法 invoke(),用于处理被代理对象的方法调用。

    public interface InvocationHandler {
        /**
         * 参数说明
         * proxy:被代理对象
         * method:被代理对象需要实现的⽅法,即其中需要重写的⽅法
         * args:method所对应⽅法的参数
         */
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable;
    }

        通过实现 InvocationHandler 接口,可以对被代理对象的方法进行功能增强。

2、Proxy:

        Proxy 类中使用频率最高的方法:newProxyInstance(),这个方法主要用来生成一个代理对象。

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
            throws IllegalArgumentException
    {
        //...代码省略
    }

        这个方法一共有 3 个参数:

loader类加载器,用于加载被代理对象

interface被代理类实现的一些接口(这个参数的定义,也决定了JDK动态代理只能代理实现了接口的一些类)

h代理要做的事情,实现 InvocationHandler 接口的对象

2、CGLIB动态代理

        JDK动态代理有一个最致命的问题是只能代理实现了接口的类

        有些场景下,我们的业务码是直接实现的,并没有接口定义。为了解决这个问题,我们可以用 CGLIB 动态代理机制来解决。

        CGLIB(Code Generation Library)是一个基于 ASM 的字节码生产库,它允许我们在运行时对字节码进行修改和动态生成。

        CGLIB 通过继承方式实现代理,很多知名的开源框架都使用到了 CGLIB。例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。(其中 Spring 是基于动态代理实现的,动态代理是基于反射实现的

(1)CGLIB 动态代理类实现步骤

1、定义一个类(被代理类)

2、自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于增强目标方法,和 JDK 动态代理中的 invoke 方法类似

3、通过 Enhancer 类的 create() 创建代理类

        接下来看实现:

(2)添加依赖

        和 JDK 动态代理不同,CGLIB(Code Generation Library)实际是属于一个开源项目,如果你要使用它的话,需要手动添加相关依赖

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

(3)自定义MethodInterceptor(方法拦截器)

        实现 MethodInterceptor 接口:

import org.springframework.cglib.proxy.MethodInterceptor;
import java.lang.reflect.Method;

public class CGLibInterceptor implements MethodInterceptor {
    private Object target;

    public CGLibInterceptor(Object target) {
        this.target = target;
    }

    /**
     * 调用代理对象的方法
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, org.springframework.cglib.proxy.MethodProxy proxy) throws Throwable {
        //代理增强内容
        System.out.println("我是中介,开始代理");
        Object result = method.invoke(target, args);
        //代理增强内容
        System.out.println("我是中介,结束代理");
        return result;
    }
}

(4)创建代理类,并使用

        代理接口:

public class Main {
    public static void main(String[] args) {
        //目标对象
        HouseSubject target = new RealHouseSubject();
        HouseSubject proxy  = (HouseSubject) Enhancer.create(target.getClass(), new CGLibInterceptor(target));
        proxy.rentHouse();
        System.out.println("=============");
        proxy.saleHouse();
    }
}

        运行程序,执行结果如下:

        代理类:

public class Main {
    public static void main(String[] args) {
        //目标对象
        HouseSubject target = new RealHouseSubject();
        RealHouseSubject proxy  = (RealHouseSubject) Enhancer.create(target.getClass(), new CGLibInterceptor(target));
        proxy.rentHouse();
        System.out.println("=============");
        proxy.saleHouse();
    }
}

        运行程序,执行结果如下:

(5)代码简单讲解

1、MethodInterceptor

        MethodInterceptor 和 JDK动态代理中的 InvocationHandler 类似,它只定义了一个方法 intercept(),用于增强目标方法。

    public interface MethodInterceptor extends Callback {
        /**
         * 参数说明:
         * o: 被代理的对象
         * method: ⽬标⽅法(被拦截的⽅法, 也就是需要增强的⽅法)
         * objects: ⽅法⼊参
         * methodProxy: ⽤于调⽤原始⽅法
         */
        Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
    }

2、Enhancer.create()

    public static Object create(Class type, Callback callback) {
        //...代码省略
    }

        type被代理类的类型(类或接口)

        callback自定义方法拦截器 MethodInterceptor


四、Spring AOP 源码剖析(了解)

        Spring AOP 主要基于两种方式实现的:JDK 及 CGLIB 的方式。

        Spring 源码过于复杂,我们只摘出一些主要内容,以了解为主

        Spring 对于 AOP 的实现,基本都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成 生成代理对象的逻辑在父类 AbstractAutoProxyCreator 中。

protected Object createProxy(Class<?> beanClass,@Nullable String beanName,
@Nullable Object[]specificInterceptors,TargetSource targetSource){
        if(this.beanFactory instanceof ConfigurableListableBeanFactory){
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)
        this.beanFactory,beanName,beanClass);
        }
        //创建代理⼯⼚
        ProxyFactory proxyFactory=new ProxyFactory();
        proxyFactory.copyFrom(this);
        /**
         * 检查proxyTargetClass属性值,spring默认为false
         * proxyTargetClass 检查接⼝是否对类代理, ⽽不是对接⼝代理
         * 如果代理对象为类, 设置为true, 使⽤cglib代理
         */
        if(!proxyFactory.isProxyTargetClass()){
            //是否有设置cglib代理
            if(shouldProxyTargetClass(beanClass,beanName)){
            //设置proxyTargetClass为true,使⽤cglib代理
            proxyFactory.setProxyTargetClass(true);
        }else{
            /**
             * 如果beanClass实现了接⼝,且接⼝⾄少有⼀个⾃定义⽅法,则使⽤JDK代理
             * 否则CGLIB代理(设置ProxyTargetClass为true )
             * 即使我们配置了proxyTargetClass=false, 经过这⾥的⼀些判断还是可能会将其
             设为true
             */
            evaluateProxyInterfaces(beanClass,proxyFactory);
            }
            }
            Advisor[]advisors=buildAdvisors(beanName,specificInterceptors);
            proxyFactory.addAdvisors(advisors);
            proxyFactory.setTargetSource(targetSource);
            customizeProxyFactory(proxyFactory);
    
            proxyFactory.setFrozen(this.freezeProxy);
            if(advisorsPreFiltered()){
            proxyFactory.setPreFiltered(true);
        }

        // Use original ClassLoader if bean class not locally loaded in overriding class loader
        ClassLoader classLoader =getProxyClassLoader();
        if(classLoader instanceof SmartClassLoader&&classLoader != beanClass.getClassLoader()){
            classLoader=((SmartClassLoader)classLoader).getOriginalClassLoader();
        }
//从代理⼯⼚中获取代理
        return proxyFactory.getProxy(classLoader);
        }

        代理工厂有一个重要的属性:proxyTargetClass,默认值为false。也可以通过程序设置

proxyTargetClass⽬标对象代理⽅式
false实现了接口jdk代理
false未实现接口(只有实现类)cglib代理
true实现了接口cglib代理
true未实现接口(只有实现类)cglib代理

        可以通过 @EnableAspectJAutoProxy(proxyTargetClass = true) 来设置。

        注意:

Spring 默认 proxyTargetClass:false,会分为两种情况:

        实现了接口使用 JDK 代理

        普通类:       使用 CGLIB 代理

Spring Boot 2.X 开始,默认使用 proxyTargetClass:true

        默认使用 CGLIB 代理

        SpringBoot设置 @EnableAspectJAutoProxy 无效,因为 Spring Boot 默认使用 AopAutoConfiguration 进行装配。

        可以通过配置项 spring.aop.proxy-target-class=false 来进行修改,设置为 jdk 代理。

        使用 context.getBean() 需要添加注解,使 HouseProxy,RealHouseSubject 被 Spring 管理。测试 AOP 代理,需要把这些类交给 AOP 管理(自定义注解或使用 @Aspect)

        我们现在从源码中点进去看看代理工厂的代码:

public class ProxyFactory extends ProxyCreatorSupport {
    //...代码省略
    //获取代理
    public Object getProxy(@Nullable ClassLoader classLoader) {
    //分两步 先createAopProxy,后getProxy
        return createAopProxy().getProxy(classLoader);
    }

    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }
    //...代码省略
}

        createAopProxy 的实现在 DefaultAopProxyFactory 中

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    //...代码省略
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws
            AopConfigException {
        /**
         * 根据proxyTargetClass判断
         * 如果⽬标类是接⼝, 使⽤JDK动态代理
         * 否则使⽤cglib动态代理
         */
        if (!NativeDetector.inNativeImage() &&
                (config.isOptimize() || config.isProxyTargetClass() ||
                        hasNoUserSuppliedProxyInterfaces(config))) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine
                        target class:" +
                "Either an interface or a target is required for proxy
                creation. ");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) ||
                    ClassUtils.isLambdaClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        } else {
            return new JdkDynamicAopProxy(config);
        }
    }
    //...代码省略
}

        接下来就是创建代理了

JDK动态代理:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    //...代码省略
    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isTraceEnabled()) {
            logger.trace("Creating JDK dynamic proxy: " +
                    this.advised.getTargetSource());
        }
        return Proxy.newProxyInstance(determineClassLoader(classLoader),
                this.proxiedInterfaces, this);
    }
    //...代码省略
}

CGLIB动态代理:

class CglibAopProxy implements AopProxy, Serializable {
    //...代码省略
    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
    //...代码省略
    // Configure CGLIB Enhancer...
        Enhancer enhancer = createEnhancer();
    // Generate the proxy class and create a proxy instance.
        return createProxyClassAndInstance(enhancer, callbacks);
    }
    //...代码省略
}

五、常见面试题

1、什么是 AOP?

        AOP是 面向切面编程,也是一种思想,切面指的是某一类特定问题,所以 AOP 也可以理解为 面向切面编程。

2、Spring AOP的实现方式有哪些?

(1)基于注解(@Aspect 或 自定义注解)

(2)基于 xml

(3)基于代理

3、Spring AOP 的实现原理?

        基于动态代理实现的,其中的动态代理有两种形式:(1)JDK   (2)CGLIB

4、Spring 使用的是哪种代理方式?

        Spring 的 proxyTargetClass 默认为:false,其中:实现了接口,使用 JDK 代理;普通类:使用CGLIB代理。

        Spring Boot 从 2.X 之后,proxyTargetClass 默认为:true,默认使用 CGLIB 代理

         现在我们测试一下:当这个值为 true 时,则使用的是动态代理

        

@SpringBootApplication
public class SpringAopApplication {
    public static void main(String[] args) {
       ApplicationContext context = SpringApplication.run(SpringAopApplication.class, args);
        //代理类
        TestController bean = context.getBean(TestController.class);
        System.out.println(bean);
    }
}

        要打断电才能观察到结果,如图:

        我们获取Spring管理的对象,打断点,观察对象名称,如图:

        可以看到,是 CGLIB代理。

当值设为 false 时;就要看代理对象是不是接口了,是接口用的就是JDK代理,代理对象是类就是CGLIB代理。

        因为要代理接口,所以现在重新创建一个接口,内容如下:
 

public interface IFace {
    void test();
}

        再创建一个成 Controller 类,实现上面这个类

@RequestMapping("/test2")
@RestController
public class TestController2 implements IFace{
    @MyAspect
    @RequestMapping("t1")
    @Override
    public void test() {
        System.out.println("测试测试");
    }
}

        main方法如下:

@SpringBootApplication
public class SpringAopApplication {
    public static void main(String[] args) {
       ApplicationContext context = SpringApplication.run(SpringAopApplication.class, args);

       //代理类
        TestController bean = context.getBean(TestController.class);
        System.out.println(bean);

        //代理接口
        IFace iFace = (IFace) context.getBean("testController2");
        System.out.println(iFace);
    }
}

        现在看看Spring对象,任然要使用断点才能看到,如图:

        看看bean对象,如图:

5、JDK 和 CGLIB 的区别?

        使用JDK 动态代理只能代理接口。

        使用 CGLIB 动态代理 既可以代理接口,也可以代理类。


六、总结

        1、AOP 是一种思想是对某一类事情的集中处理。Spring 框架实现了AOP,称之为 Spring AOP。

        2、Spring AOP 场景的实现方式有两种:(1)基于注解@Aspect来实现(2)基于自定义注解来实现还有一些更原始的方式,比如基于代理、基于 xml 配置的方式,但目标比较少见

        3、Spring AOP 是基于动态代理实现的,有两种方式:(1)基于 JDK 动态代理实现(2)基于 CGLIB 动态代理实现。运行时使用哪种方式与项目配置的代理对象有关

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

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

相关文章

Spring系列-04-事件机制,监听器,模块/条件装配

事件机制&监听器 SpringFramework中设计的观察者模式-掌握 SpringFramework 中, 体现观察者模式的特性就是事件驱动和监听器。监听器充当订阅者, 监听特定的事件&#xff1b;事件源充当被观察的主题, 用来发布事件&#xff1b;IOC 容器本身也是事件广播器, 可以理解成观察…

ArcGIS Pro不能编辑ArcGIS10.X的注记的解决办法

​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 点击学习——>遥感影像综合处理4大遥感软件ArcGISENVIErdaseCognition 一、问题 我们利用ArcGIS Pro编辑ArcGIS10.X系列软件生成的注记要素类的时候&#xff0c;会提示不…

FinClip 率先入驻 AWS Marketplace,加速全球市场布局

近日&#xff0c;凡泰极客旗下的小程序数字管理平台 FinClip 已成功上线亚马逊云科技&#xff08;AWS&#xff09;Marketplace。未来&#xff0c;FinClip 将主要服务于海外市场的开放银行、超级钱包、财富管理、社交电商、智慧城市解决方案等领域。 在全球市场的多样性需求推动…

KAFKA搭建教程

KAFKA搭建教程 期待您的关注 KAFKA学习笔记 帮助更多人 目录 KAFKA搭建教程 1.下载Kafka并解压 2.添加环境变量 3.修改 server.properties 文件 4.将kafka复制到其它节点 5.修改node1、node2节点的broker.id 6.将master的环境变量同步到node1、 node2 7.启动zookeeper…

Elasticsearch:评估搜索相关性 - 第 1 部分

作者&#xff1a;来自 Elastic Thanos Papaoikonomou, Thomas Veasey 这是一系列博客文章中的第一篇&#xff0c;讨论如何在更好地理解 BEIR 基准的背景下考虑评估你自己的搜索系统。我们将介绍具体的技巧和技术&#xff0c;以便在更好地理解 BEIR 的背景下改进你的搜索评估流程…

17_高级进程间通信 UNIX域套接字1

非命名的UNIX域套接字 第1个参数domain&#xff0c;表示协议族&#xff0c;只能为AF_LOCAL或者AF_UNIX&#xff1b; 第2个参数type&#xff0c;表示类型&#xff0c;只能为0。 第3个参数protocol&#xff0c;表示协议&#xff0c;可以是SOCK_STREAM或者SOCK_DGRAM。用SOCK_STR…

视觉巡线小车——STM32+OpenMV(三)

目录 前言 一、OpenMV代码 二、STM32端接收数据 1.配置串口 2.接收数据并解析 总结 前言 通过视觉巡线小车——STM32OpenMV&#xff08;二&#xff09;&#xff0c;已基本实现了减速电机的速度闭环控制。要使小车能够自主巡线&#xff0c;除了能够精准的控制速度之外&#xff0…

【BUG】已解决:raise KeyError(key) from err KeyError: (‘name‘, ‘age‘)

已解决&#xff1a;raise KeyError(key) from err KeyError: (‘name‘, ‘age‘) 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xf…

第十课:telnet(远程登入)

如何远程管理网络设备&#xff1f; 只要保证PC和路由器的ip是互通的&#xff0c;那么PC就可以远程管理路由器&#xff08;用telnet技术管理&#xff09;。 我们搭建一个下面这样的简单的拓扑图进行介绍 首先我们点击云&#xff0c;把云打开&#xff0c;点击增加 我们绑定vmn…

idea如何让包结构分层

文章目录 前言1.选中前项目包结构2.取消后项目包结构3.情况二 前言 在大型项目中&#xff0c;代码的分层管理至关重要。IDEA编辑器提供了强大的package分层结构功能&#xff0c;帮助开发者更好地组织和管理代码。通过合理配置&#xff0c;我们可以清晰地看到各个package之间的…

【BUG】已解决:java.lang.reflect.InvocationTargetException

已解决&#xff1a;java.lang.reflect.InvocationTargetException 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市开发…

Mysql-索引结构

一.什么是索引&#xff1f; 索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引 二.无索引的情况 找到…

【效率提升】程序员常用Shell脚本

文章目录 常用Shell脚本一. 定期更新分区数据二、获取系统资源的使用情况 常用Shell脚本 一. 定期更新分区数据 在某些场景下&#xff0c;我们需要对N年前某一分区的数据进行删除&#xff0c;并添加今年该对应分区的数据&#xff0c;实现数据的流动式存储。 #!/bin/bash dt$…

NFT革命:数字资产的确权、营销与元宇宙的未来

目录 1、NFT&#xff1a;数字社会的数据确权制度 2、基于低成本及永久产权的文化发现 3、PFP&#xff1a;从“小图片”到“身份表达”&#xff0c;再到社区筛选 4、透明表达&#xff1a;NFT 在数字化营销中的商业价值 5、可编程性&#xff1a;赋予 NFT 无限可能的应用 5.…

微信被好友屏蔽朋友圈/拉黑/删除?教你几招悄悄验证

微信这一国民级的社交软件&#xff0c;基本上渗入了大家日常生活的方方面面&#xff0c;沟通、支付、购物、娱乐都可以在上面一站式解决。微信功能虽然很全面&#xff0c;但某些功能细节设计也会让人感到困惑&#xff0c;比如我们被朋友拉黑或者删除&#xff0c;微信是不会通知…

C程序优化与指针传址

最近在写程序时遇到了一些问题&#xff0c;记录一下&#xff1a; 开始程序使用全局变量&#xff0c;程序如下&#xff1a;程序的缺点是全局变量的泛滥。 笔者觉得有些不妥&#xff0c;于是将它修改成这样&#xff1a; 使用结构体进行封装&#xff0c;避免了全局变量&#xff0…

Gitee使用教程2-克隆仓库(下载项目)并推送更新项目

一、下载 Gitee 仓库 1、点击克隆-复制代码 2、打开Git Bash 并输入复制的代码 下载好后&#xff0c;找不到文件在哪的可以输入 pwd 找到仓库路径 二、推送更新 Gitee 项目 1、打开 Git Bash 用 cd 命令进入你的仓库&#xff08;我的仓库名为book&#xff09; 2、添加文件到 …

转移C盘中的conda环境(包括.condarc文件修改,environment.txt文件修改,conda报错)

conda环境一般是默认安装到C盘的&#xff0c;若建立多个虚拟环境&#xff0c;时间长了&#xff0c;容易让本不富裕的C盘更加雪上加霜&#xff0c;下面给出将conda环境从C盘转移到D盘的方法。 目录 电脑软硬件转移方法查看当前conda目录转移操作第一步&#xff1a;.condarc文件修…

C语言迷宫

目录 开头程序程序的流程图程序输入与输出的效果结尾 开头 大家好&#xff0c;我叫这是我58。今天&#xff0c;我要来看我用C语言编译出来的迷宫游戏。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <Windows.h> void printmaze(char s…

鸿蒙Navigation路由能力汇总

基本使用步骤&#xff1a; 1、新增配置文件router_map&#xff1a; 2、在moudle.json5中添加刚才新增的router_map配置&#xff1a; 3、使用方法&#xff1a; 属性汇总&#xff1a; https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-compone…