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

news2024/9/20 12:33:31

目录

一、代理模式

二、静态代理

三、动态代理

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 动态代理实现。运行时使用哪种方式与项目配置的代理对象有关

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

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

相关文章

RockyLinux 9 PXE Server bios+uefi 自动化部署 RockLinux 8 9

pxe server 前言 PXE&#xff08;Preboot eXecution Environment&#xff0c;预启动执行环境&#xff09;是一种网络启动协议&#xff0c;允许计算机通过网络启动而不是使用本地硬盘。PXE服务器是实现这一功能的服务器&#xff0c;它提供了启动镜像和引导加载程序&#xff0c;…

Java学习Day14:基础篇4

数组 1.理解数组 数组也是一种类型&#xff0c;引用类型&#xff01; 2.数组的定义 3.数组的初始化 静态&#xff1a; 动态&#xff1a; 不同数据类型数组初始化值&#xff1a; null不可用.equal方法&#xff0c;会报错&#xff01; 4.使用数组 5.foreach循环 6.深入数组 7&am…

绘制拟合联合密度分布

绘制拟合联合密度分布 import numpy as np import matplotlib.pyplot as plt from sklearn.neighbors import KernelDensity# 生成模拟数据 np.random.seed(42) mean [0, 0] cov [[1, 0.5], [0.5, 1]] data np.random.multivariate_normal(mean, cov, size200) a data[:, 0…

基于jeecgboot-vue3的Flowable流程-自定义业务表单流程历史信息显示

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、对于自定义业务表单的流程历史记录信息做了调整&#xff0c;增加显示自定义业务表单 <el-tab-pane label"表单信息" name"form"><div v-if"customF…

德国云手机:企业移动办公解决方案

在现代商业环境中&#xff0c;移动办公已经成为一种趋势。德国云手机作为一种高效的解决方案&#xff0c;为企业提供了强大的支持。本文将探讨德国云手机如何优化企业的移动办公环境。 一、德国云手机的主要优势 高灵活性 德国云手机具有高度的灵活性&#xff0c;能够根据用户需…

鸿蒙HarmonyOS开发:用户通知服务Noification的详细使用指南

文章目录 一、Notification Kit简介二、能力范围三、业务流程四、通知样式&#xff1a;五、约束限制六、开发步骤6.1、导入模块。6.2、构造NotificationRequest对象&#xff0c;并发布通知。6.2.1、普通文本类型。6.2.2、长文本类型。6.2.3、多行文本类型。 6.3、为通知添加行为…

SpringBoot源码(1)ApplicationContext和BeanFactory

1、调用getBean方法 SpringBootApplication public class SpringBootDemoApplication {public static void main(String[] args) {ConfigurableApplicationContext applicationContext SpringApplication.run(SpringBootDemoApplication.class, args);applicationContext.get…

javaFx 系统最小化托盘(及其避坑)、开机自启动

1、系统最小化托盘 参考资料&#xff1a; https://blog.51cto.com/u_14191/6310480 javafx 设置最小窗口大小 - Java (1) - 芒果文档 注意事项 1.直接使用第一个参考资料中的MinWindow类即可。 #最小化托盘 -- 一般放置在 start(Stage stage)处 MinWindow.getInstance().…

鸿蒙仓颉语言【cryptocj 库】(介绍与SHA、MD5、HMAC摘要算法)

cryptocj 库 介绍 cryptocj 是一个安全的密码库&#xff0c;包括常用的密码算法、常用的密钥生成和签名验证。 该库是对 C 语言的 openSSL 封装的仓颉加密算法 1 提供SHA、MD5、HMAC摘要算法。 前置条件&#xff1a;NA 场景&#xff1a; OHOS&#xff0c; Linux&#xff…

昇思25天学习打卡营第17天 | CycleGAN图像风格迁移互换

通过深入学习CycleGAN模型&#xff0c;我对无监督图像到图像的转换技术有了更深的理解。CycleGAN不仅能在没有成对训练样本的情况下实现域之间的转换&#xff0c;而且在保持内容结构的同时成功转换图像风格&#xff0c;这在许多应用中都非常有用&#xff0c;如艺术风格转换、季…

C++树(二)【直径,中心】

目录&#xff1a; 树的直径&#xff1a; 树的直径的性质&#xff1a; 性质1&#xff1a;直径的端点一定是叶子节点 性质2&#xff1a;任意点的最长链端点一定是直径端点。 性质3&#xff1a;如果一棵树有多条直径,那么它们必然相交&#xff0c;且有极长连…

Linux搭建Kubernetes集群(单Master)【附图文】

文章目录 一、集群环境配置要求二、主机准备三、初始环境准备1.关闭防火墙2.关闭 selinux3.关闭swap4.加载 br_netfilter 模块5.允许iptables转发流量6.设置时间同步 四、安装Docker五、安装kubeadm, kubectl, kubelet六、在Master节点部署集群七、将 node 节点加入集群八、部署…

springcloud rocketmq 新增的消费者组从哪里开始消费

如果新建一个新的消费者组&#xff0c;是否会消费历史消息&#xff0c;导致重复消费&#xff1f; 直接在 console 界面新增消费者组&#xff0c;但是没有办法绑定订阅关系&#xff0c;没有找到入口&#xff0c;在 控制台项目源码 rocketmq-externals 也没有找到可以确定订阅关系…

Windows 11预览补丁KB5040527影响火绒驱动加载的解决办法

7 月 11 日&#xff0c;微软更新Windows 11 预览版本补丁 KB5040527&#xff0c;补丁安装后会影响火绒驱动加载导致火绒安全软件服务异常&#xff0c;补丁相关信息如下&#xff1a; https://blogs.windows.com/windows-insider/2024/07/11/releasing-windows-11-builds-22621-…

微信视频号下载又一工具,免费简单易用!

Res-downloader嗅探资源下载器请收好 官方称支持微信视频号、抖音、快手、小红书等网络资源下载 使用方法 第一步&#xff0c;下载软件 文末扫码 或者搜索关注公众号AIshape 回复 RES 获取 或 自行百度搜索下载 第二步&#xff0c;软件设置 打开软件会弹出接受传入网络链…

CSI-RS在信道中传输的过程

简单介绍CSI-RS信号生成&#xff0c;在信道中传输和接收的过程 1.载波配置 首先需要配置载波相关的参数 系统带宽和子载波间隔 5G NR中&#xff0c;系统带宽和子载波间隔是两个关键参数&#xff0c;共同决定无线资源的分配和使用 系统带宽 5G NR支持广泛的系统带宽&…

【SOC 芯片设计 DFT 学习专栏 -- DFT OCC 与 ATPG的介绍】

请阅读【嵌入式及芯片开发学必备专栏】 请阅读【芯片设计 DFT 学习系列 】 如有侵权&#xff0c;请联系删除 转自&#xff1a; 简矽芯学堂 简矽芯学堂 2024年01月18日 09:00 陕西 文章目录 OCC 介绍Fast ScanFull chip ATPGPartition ATPGHierarchical ATPG OCC 介绍 OCC&am…

Ubuntu下载jdk:cannot execute binary file

虚拟机上Ubuntu系统安装jdk且配置环境之后&#xff0c;java -version显示cannot execute binary file&#xff0c;多番查阅推测是由于系统和jdk版本不兼容的原因。 uname -m查看系统版本位i686&#xff0c;是32位的&#xff0c;和64位的jdk版本不兼容。因此&#xff0c;下载32位…

QT--进程

一、进程QProcess QProcess 用于启动和控制外部进程&#xff0c;管理其输入输出流。 使用方法 start()&#xff1a;启动一个新进程。setStandardInputFile()&#xff1a;将文件作为标准输入。将进程的标准输入&#xff08;stdin&#xff09;重定向到指定的文件。换句话说&am…

【Linux】从零开始认识多线程 --- 线程互斥

人生有许多事情 正如船后的波纹 总要过后才觉得美的 -- 余光中 线程互斥 1 线程类的封装1.1 框架搭建1.2 线程启动1.3 线程终止1.4 线程等待1.5 运行测试 2 线程互斥2.1 多线程访问的问题2.2 解决办法 --- 锁2.3 从原理角度理解锁 Thanks♪(&#xff65;ω&#xff65;)&am…