4.springboot代理实现

1.概述

上篇本文介绍spring动态代理创建器自动创建动态代理的过程。本文介绍spring代理的实现方式(jdk动态代理 & cglib动态代理);

2.动态代理简介

我们先熟悉下两种动态代理的实现方式;

A.jdk动态代理

  • 定义一个接口Car(jdk动态代理类必须实现至少一个接口);
  • 定义一个实现类Suv(被代理的目标类);
  • 定义一个InvocationHandler实现类CarProxy(持有Suv对象的引用,实现接口中的invoke方法);
  • 使用Proxy.newProxyInstance创建代理对象carProxy;
  • 使用代理对象调用目标类的方法: carProxy.drive(“张三”);
/**
 * 测试jdk动态代理
 * 1.定义一个接口Car(jdk动态代理类必须实现至少一个接口)
 * 2.定义一个实现类Suv(被代理的目标类)
 * 3.定义一个InvocationHandler实现类CarProxy(持有Suv对象的引用,实现接口中的invoke方法)
 * 4.使用Proxy.newProxyInstance创建代理对象carProxy
 * 5.使用代理对象调用目标类的方法: carProxy.drive("张三")
 */
public interface Car {

    void drive(String name);

    /**
     * 实现类
     */
    @Slf4j
    class Suv implements Car {

        @Override
        public void drive(String name) {
            log.info("司机:{}开车", name);
        }
    }

    /**
     * 代理逻辑
     */
    @Slf4j
    class CarProxy implements InvocationHandler {

        private final Car car;

        public CarProxy(Car car) {
            this.car = car;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            log.info("代理,目标方法运行之前");
            Object rst = method.invoke(car, args);
            log.info("代理,目标方法运行之后");

            return rst;
        }
    }

    static void main(String[] args) {
        Car car = new Suv();
        Class<?> cla = car.getClass();

        //**创建动态代理
        Car carProxy = (Car)Proxy.newProxyInstance(cla.getClassLoader(), cla.getInterfaces(), new CarProxy(car));
        carProxy.drive("张三");
        carProxy.toString();
    }
}

B.cglib动态代理

  • 定义一个类Suv(不用实现接口,当然也可以实现接口);
  • 定义一个MethodInterceptor(Callback的子接口)实现类CarProxy;
  • 使用Enhancer.create创建代理对象carProxy;
  • 使用代理对象调用目标类的方法: carProxy.drive(“张三”);
/**
 * 测试cglib动态代理
 * 1.定义一个类Suv(不用实现接口,当然也可以实现接口)
 * 2.定义一个MethodInterceptor(Callback的子接口)实现类CarProxy
 * 3.使用Enhancer.create创建代理对象carProxy
 * 4.使用代理对象调用目标类的方法: carProxy.drive("张三")
 */
@Slf4j
public class Suv {

    public void drive(String name) {
        log.info("司机:{}开车", name);
    }

    @Override
    public String toString() {
        log.info("suv toString");
        return "suv";
    }

    /**
     * 代理逻辑
     * MethodInterceptor为Callback的子接口
     */
    @Slf4j
    static class CarProxy implements MethodInterceptor {

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            log.info("代理,目标方法运行之前");
            Object rst = proxy.invokeSuper(obj, args);
            log.info("代理,目标方法运行之后");

            return rst;
        }
    }

    public static void main(String[] args) {
        //**创建动态代理
        Suv carProxy = (Suv) Enhancer.create(Suv.class, new CarProxy());
        carProxy.drive("张三");
        carProxy.toString();
    }
}

  • cglib动态代理每一个代理逻辑就是一个Callback。创建代理类时,可同时定义多个Callback,然后通过CallbackFilter查找指定方法需执行的Callback;
/**
 * 过滤器
 * 使用CallbackHelper快速实现CallbackFilter
 */
public class CarFilter extends CallbackHelper {

    public CarFilter(Class superclass, Class[] interfaces) {
        super(superclass, interfaces);
    }

    @Override
    protected Object getCallback(Method method) {
        //**只代理drive方法
        return "drive".equals(method.getName()) ? new CarProxy() : NoOp.INSTANCE;
    }
}

public static void main(String[] args) {
    //**创建动态代理,使用过滤器
    CarFilter filter = new CarFilter(Suv.class, null);
    Suv carProxy1 = (Suv) Enhancer.create(Suv.class, null, filter, filter.getCallbacks());
    carProxy1.drive("李四");
    carProxy1.toString();
}

3.动态代理实现

在上一步我们了解的动态代理的基本概念和实现方式,这里我们看一下spring如何使用jdk动态代理 & cglib动态代理创建动态代理类;

概念

  • 是否静态的(TargetSource.isStatic()):
    TargetSource是对原始bean的封装。每次调用TargetSource.getTarget()返回的原始bean都是同一个对象,则为true。一般是指原始bean为单例;
  • 是否暴露代理(AdvisedSupport.isExposeProxy()):
    如果允许暴露代理,在执行目标方法的过程中会把代理对象设置到ThreadLocal中,可从该ThreadLocal获取当前执行的代理对象;
  • 是否冻结(AdvisedSupport.isFrozen()):
    如果是冻结的,表示针对原始bean的拦截器链是固定不变的。即配置的aop代理是固定的,没有动态配置;

核心类图

«Interface»
InvocationHandler
//**jdk动态代理接口
+//**jdk动态代理方法()
+Object invoke(Object proxy, Method method, Object[] args)
«Interface»
AopProxy
//**Aop代理接口
+//**获取代理对象()
+Object getProxy()
+//**获取代理对象()
+Object getProxy(ClassLoader classLoader)
+//**获取代理class对象()
+Class getProxyClass(ClassLoader classLoader)
JdkDynamicAopProxy
//**jdk动态代理实现
//**AdvisedSupport,代理相关的配置
- AdvisedSupport advised;
//**代理的接口
- Class[] proxiedInterfaces;
//**代理的接口是否定义了equals方法
- boolean equalsDefined;
//**代理的接口是否定义了hashCode方法
- boolean hashCodeDefined;
-//**在代理类的接口查找equals和hashCode方法()
-void findDefinedEqualsAndHashCodeMethods(Class[] proxiedInterfaces)
+//**重写equals方法()
+boolean equals(@Nullable Object other)
+//**重写hashCode方法()
+int hashCode()
CglibAopProxy
//**Cglib代理实现
//**验证原始bean的方法是否支持cglib代理,并打印日志
- Map, Boolean> validatedClasses;
//**AdvisedSupport,代理相关的配置
# AdvisedSupport advised;
//**原始bean的构造方法参数
# Object[] constructorArgs;
//**原始bean构造方法参数的类型
# Class[] constructorArgTypes;
//**Callback的子类,把代理bean方法的调用转发给AdvisedSupport
- AdvisedDispatcher advisedDispatcher;
- Map fixedInterceptorMap;
- int fixedInterceptorOffset;
-//**创建代理bean的核心逻辑()
-buildProxy(ClassLoader classLoader, boolean classOnly)
-//**验证原始bean的方法是否支持cglib代理()
-validateClassIfNecessary(Class proxySuperClass, ClassLoader classLoader)
-//**获取代理逻辑Callback()
-Callback[] getCallbacks(Class rootClass)
#//**创建代理bean的class对象()
#Class createProxyClass(Enhancer enhancer)
#//**创建代理bean()
#Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks)
#//**创建代理Enhancer()
#Enhancer createEnhancer()
ObjenesisCglibAopProxy
//**基于Objenesis的Cglib代理实现
//**Objenesis创建实例时不需构造函数
#//**创建代理bean的class对象()
#Class createProxyClass(Enhancer enhancer)
#//**创建代理bean()
#Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks)

核心类说明

A.AopProxy

创建动态代理接口,提供3个方法,创建创建代理bean和代理bean的class对象;

B.JdkDynamicAopProxy

  • AopProxy的jdk动态代理实现,使用jdk动态代理创建代理bean和class;
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
	...
	//**使用jdk动态代理创建代理bean
	return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}

@Override
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
    //**使用jdk动态代理创建代理bean的class对象
	return Proxy.getProxyClass(classLoader, this.proxiedInterfaces);
}
  • JdkDynamicAopProxy同时继承InvocationHandler,实现了invoke方法;
    • 调用equals和hashCode方法
      如果原始bean的接口定义了equals和hashCode方法,则直接调用原始bean的实现,否则调用JdkDynamicAopProxy的实现;
    • 调用Advised接口方法
      转为调用Advised实例的方法。一般场景调用不到Advised接口的方法,可能使用反射可以调用;
    • 调用其它方法(原始bean的接口定义的方法)
      先查找目标方法的代理配置。如果存在代理配置就调用代理逻辑,然后调用原始bean的目标方法。如果不存在就直接调用原始bean的目标方法;
@Override
@Nullable //**如果可能会返回null,加上这个标识,提示调用者
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	Object oldProxy = null;
	boolean setProxyContext = false;

	TargetSource targetSource = this.advised.targetSource;
	Object target = null;

	try {//**如果接口没有定义equals和hashCode方法,则调用此类的equals和hashCode方法
		if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
			return equals(args[0]);
		}
		else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
			return hashCode();
		}
		//**没发现实际用处
		else if (method.getDeclaringClass() == DecoratingProxy.class) {
			return AopProxyUtils.ultimateTargetClass(this.advised);
		}
		//**调用Advised接口的方法
		else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
				method.getDeclaringClass().isAssignableFrom(Advised.class)) {
			return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
		}

		Object retVal;
		
		//**把代理对象设置到ThreadLocal中
		if (this.advised.exposeProxy) {
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}

		//**获取原始bean和class对象
		target = targetSource.getTarget();
		Class<?> targetClass = (target != null ? target.getClass() : null);

		//**查找调用方法的拦截
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

		if (chain.isEmpty()) {//**如果Interceptor列表为空,直接调用目标方法
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
		}
		else {//**调用Interceptor链(依次调用每个Interceptor)
			MethodInvocation invocation =
					new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
			retVal = invocation.proceed();
		}

		//**处理返回值类型
		
		return retVal;
	}
	finally {
		//**释放资源
	}
}

C.CglibAopProxy

AopProxy的cglib动态代理实现,使用cglib动态代理创建代理bean和class。创建代理bean的核心代码;

  • 创建Enhancer(用于创建cglib动态代理bean);
  • 获取Callback(把代理逻辑封装成Callback);
  • 设置CallbackFilter(CallbackFilter可以查找指定方法需执行的Callback);
  • 创建代理bean/class;
//**创建代理bean和class的核心逻辑
private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) {
	try {//**获取&验证原始bean的class对象
		Class<?> rootClass = this.advised.getTargetClass();
		Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
		
		//**如果原始bean仍然是cglib代理bean,获取上一级的原始bean和接口
		Class<?> proxySuperClass = rootClass;
		if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
			proxySuperClass = rootClass.getSuperclass();
			Class<?>[] additionalInterfaces = rootClass.getInterfaces();
			for (Class<?> additionalInterface : additionalInterfaces) {
				this.advised.addInterface(additionalInterface);
			}
		}

		//**验证原始bean的方法是否支持cglib代理,不符合也只是打印日志而已
		validateClassIfNecessary(proxySuperClass, classLoader);

		//**创建Enhancer(用于创建cglib动态代理bean)
		Enhancer enhancer = createEnhancer();
		if (classLoader != null) {
			enhancer.setClassLoader(classLoader);
			if (classLoader instanceof SmartClassLoader smartClassLoader &&
					smartClassLoader.isClassReloadable(proxySuperClass)) {
				enhancer.setUseCache(false);
			}
		}
		enhancer.setSuperclass(proxySuperClass);
		enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		enhancer.setAttemptLoad(true);
		enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
		
		//**获取Callback(把代理逻辑封装成Callback)
		Callback[] callbacks = getCallbacks(rootClass);
		//**获取Callback的class对象
		Class<?>[] types = new Class<?>[callbacks.length];
		for (int x = 0; x < types.length; x++) {
			types[x] = callbacks[x].getClass();
		}
		
		//**设置CallbackFilter(CallbackFilter可以查找指定方法需执行的Callback)
		//**ProxyCallbackFilter是一个CallbackFilter的实现,内部类&私有,外部无法访问
		enhancer.setCallbackFilter(new ProxyCallbackFilter(
				this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
		enhancer.setCallbackTypes(types);

		//**创建代理bean/class
		return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
	}
	//**异常处理
}

根据原始bean的class对象获取Callback列表;

  • 创建创建Callback数组。调用代理bean的目标方法时,会根据CallbackFilter过滤。根据使用场景分类如下:
    • 支持目标方法有一个/多个代理配置
      调用时会按顺序调用拦截器链,最后调用原始bean的目标方法;
      • DynamicAdvisedInterceptor,默认的Callback。支持TargetSource和拦截器链是动态的,调用目标方法时才会获取TargetSource和拦截器链;
      • FixedChainStaticTargetInterceptor,静态的Callback。TargetSource和拦截器链必须是固定不变的,创建时就会初始化,调用时直接使用初始化的TargetSource和拦截器链;
    • 目标方法没有任何代理配置
      原始bean的任何方法配置了代理,就会创建代理bean,但是有些方法是没有配置代理的,不需要调用其它代理逻辑;
      • StaticUnadvisedExposedInterceptor & StaticUnadvisedInterceptor适用于静态TargetSource & 无代理配置。前者会暴露代理;
      • DynamicUnadvisedExposedInterceptor & DynamicAdvisedInterceptor适用于动态TargetSource & 无代理配置。前者会暴露代理;
    • 目标调度程序,会把调用转发给其它对象
      StaticDispatcher & AdvisedDispatcher,前者转发给原始bean,后者转发给AdvisedSupport;
    • Spring针对固定方法生成默认的代理逻辑
      EqualsInterceptor & HashCodeInterceptor,支持equals和HashCode方法调用的Callback;
  • 针对静态的(原始bean为单例) & 冻结的(Advice链是固定的),则进行一些优化。避免调用目标方法时动态去过滤需要执行的Callback;
    • 为每个目标方法创建一个固定的Callback(FixedChainStaticTargetInterceptor)。即每个方法都有一个Callback;
    • 把所有的FixedChainStaticTargetInterceptor添加到Callback数组的末尾;
    • 记录每个方法对应FixedChainStaticTargetInterceptor在Callback数组的索引;
    • 进行上述优化的好处: 调用目标方法时可以直接根据索引找到Callback,避免进行额外的查找;
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
	//**代理配置: 是否暴露代理
	boolean exposeProxy = this.advised.isExposeProxy();
	//**代理配置: 是否冻结
	boolean isFrozen = this.advised.isFrozen();
	//**否是静态的(每次调用getTarget()返回的原始bean都是同一个对象,一般是指原始bean为单例)
	boolean isStatic = this.advised.getTargetSource().isStatic();
	
	//**开始创建Callback数组,每个Callback元素处理一种类型。调用代理bean的目标方法时,会根据CallbackFilter查找Callback

	/**
	 * 1.DynamicAdvisedInterceptor,适用于执行设置了aop拦截的目标方法
	 *   DynamicAdvisedInterceptor会按顺序调用拦截器链,最后调用原始bean的目标方法
	 **/
	Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
	
	/**
	 * 以下的4个Callback都是适用于直接执行原始bean的目标方法,没有代理逻辑。
	 * 前两个和后两个的区别:后两个次调用时需要先调用this.targetSource.getTarget()获取原始bean
	 * 2.StaticUnadvisedExposedInterceptor,StaticUnadvisedInterceptor
	 *   适用于静态TargetSource的Callback,区别就是是否暴露代理
	 * 3.DynamicUnadvisedExposedInterceptor,DynamicAdvisedInterceptor
	 *   适用于非静态TargetSource的Callback,区别就是是否暴露代理
	 **/
	Callback targetInterceptor;
	if (exposeProxy) { //**如果允许暴露代理,在执行目标方法的过程中会把代理对象设置到ThreadLocal中
		targetInterceptor = (isStatic ?
				new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
				new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
	}
	else {
		targetInterceptor = (isStatic ?
				new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
				new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
	}

	//**静态目标调度程序,直接转发给原始bean
	Callback targetDispatcher = (isStatic ?
			new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());
			
	//**主要的Callback数组
	Callback[] mainCallbacks = new Callback[] {
			aopInterceptor,  //**默认的Callback
			targetInterceptor,  //**直接调用原始bean的Callback
			new SerializableNoOp(),  //**空Callback。Callback不支持空值,以此代替空值
			targetDispatcher, this.advisedDispatcher, //**目标调度程序,前者转发给原始bean,后者转发给AdvisedSupport
			new EqualsInterceptor(this.advised), //**支持equals方法的Callback
			new HashCodeInterceptor(this.advised) //**支持hashCode方法的Callback
	};

	Callback[] callbacks;

	/**
	 * 如果是静态的(原始bean为单例) & 是冻结的(Advice链是固定的),则进行优化
	 * 1.查找每个方法的拦截器链,并封装为FixedChainStaticTargetInterceptor。即每个方法都有一个FixedChainStaticTargetInterceptor
	 * 2.把所有的FixedChainStaticTargetInterceptor添加到Callback数组的末尾
	 * 3.记录每个方法对应FixedChainStaticTargetInterceptor在Callback数组的索引
	 * 进行上述优化的好处: 调用目标方法时可以直接根据索引找到Callback,避免进行额外的查找
	 **/
	if (isStatic && isFrozen) {
		Method[] methods = rootClass.getMethods();
		Callback[] fixedCallbacks = new Callback[methods.length];
		this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length);

		for (int x = 0; x < methods.length; x++) {
			Method method = methods[x];
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
			fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
					chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
			this.fixedInterceptorMap.put(method, x);
		}

		callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
		System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
		System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
		this.fixedInterceptorOffset = mainCallbacks.length;
	}
	else {
		callbacks = mainCallbacks;
	}
	return callbacks;
}

创建代理bean/class的逻辑,创建实例使用到原始bean的构造器和参数;

//**创建代理bean的class对象
protected Class<?> createProxyClass(Enhancer enhancer) {
	enhancer.setInterceptDuringConstruction(false);
	return enhancer.createClass();
}

//**创建代理bean
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
	enhancer.setInterceptDuringConstruction(false);
	//**设置Callback(代理逻辑)
	enhancer.setCallbacks(callbacks);
	//**使用构造器和参数创建代理bean
	return (this.constructorArgs != null && this.constructorArgTypes != null ?
			enhancer.create(this.constructorArgTypes, this.constructorArgs) :
			enhancer.create());
}

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

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

相关文章

【WRF教程第3.2期】预处理系统 WPS详解:以4.5版本为例

预处理系统 WPS 详解&#xff1a;以4.5版本为例 WPS 嵌套域&#xff08;WPS Nested Domains&#xff09;USGS 和 MODIS 土地利用重力波拖拽方案静态数据&#xff08;Gravity Wave Drag Scheme Static Data&#xff09;1. 什么是重力波拖拽方案&#xff08;GWDO&#xff09;静态…

Stealthy Attack on Large Language Model based Recommendation

传统RS依赖id信息进行推荐&#xff0c;攻击&#xff1a;生成虚假用户&#xff0c;这些用户对特定目标物体给于高评价&#xff0c;从而影响模型的训练。 基于llm的RS&#xff1a;llm利用语义理解&#xff0c;将用户兴趣转化为语义向量&#xff0c;通过计算用户兴趣向量与物品向…

Pytorch | 从零构建EfficientNet对CIFAR10进行分类

Pytorch | 从零构建EfficientNet对CIFAR10进行分类 CIFAR10数据集EfficientNet设计理念网络结构性能特点应用领域发展和改进 EfficientNet结构代码详解结构代码代码详解MBConv 类初始化方法前向传播 forward 方法 EfficientNet 类初始化方法前向传播 forward 方法 训练过程和测…

【Linux 网络 (五)】Tcp/Udp协议

Linux 网络 一前言二、Udp协议1&#xff09;、Udp协议特点2&#xff09;、Udp协议格式3&#xff09;、Udp报文封装和解包过程4&#xff09;、UDP的缓冲区 三、TCP协议1&#xff09;、TCP协议特点2&#xff09;、TCP协议格式1、4位首部长度、源端口、目的端口2、16位窗口大小3、…

重温设计模式--命令模式

文章目录 命令模式的详细介绍C 代码示例C代码示例2 命令模式的详细介绍 定义与概念 命令模式属于行为型设计模式&#xff0c;它旨在将一个请求封装成一个对象&#xff0c;从而让你可以用不同的请求对客户端进行参数化&#xff0c;将请求的发送者和接收者解耦&#xff0c;并且能…

Python langchain ReAct 使用范例

0. 介绍 ReAct: Reasoning Acting &#xff0c;ReAct Prompt 由 few-shot task-solving trajectories 组成&#xff0c;包括人工编写的文本推理过程和动作&#xff0c;以及对动作的环境观察。 1. 范例 langchain version 0.3.7 $ pip show langchain Name: langchain Ver…

Java设计模式 —— 【结构型模式】外观模式详解

文章目录 概述结构案例实现优缺点 概述 外观模式又名门面模式&#xff0c;是一种通过为多个复杂的子系统提供一个一致的接口&#xff0c;而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口&#xff0c;外部应用程序不用关心内部子系统的具体的细节&#xff0c;这…

【自用】通信内网部署rzgxxt项目_01,后端pipeDemo部署(使用nssm.exe仿照nohup)

做完这些工作之后&#xff0c;不要忘记打开 Windows Server 的防火墙端口&#xff0c;8181、8081、8080、22、443、1521 做完这些工作之后&#xff0c;不要忘记打开 Windows Server 的防火墙端口&#xff0c;8181、8081、8080、22、443、1521 做完这些工作之后&#xff0c;不要…

Apache RocketMQ 5.1.3安装部署文档

官方文档不好使&#xff0c;可以说是一坨… 关键词&#xff1a;Apache RocketMQ 5.0 JDK 17 废话少说&#xff0c;开整。 1.版本 官网地址&#xff0c;版本如下。 https://rocketmq.apache.org/download2.配置文件 2.1namesrv端口 在ROCKETMQ_HOME/conf下 新增namesrv.pro…

【网络安全】网站常见安全漏洞—服务端漏洞介绍

文章目录 网站常见安全漏洞—服务端漏洞介绍引言1. 第三方组件漏洞什么是第三方组件漏洞&#xff1f;如何防范&#xff1f; 2. SQL 注入什么是SQL注入&#xff1f;如何防范&#xff1f; 3. 命令执行漏洞什么是命令执行漏洞&#xff1f;如何防范&#xff1f; 4. 越权漏洞什么是越…

【计算机视觉基础CV-图像分类】01- 从历史源头到深度时代:一文读懂计算机视觉的进化脉络、核心任务与产业蓝图

1.计算机视觉定义 计算机视觉&#xff08;Computer Vision&#xff09;是一个多学科交叉的研究领域&#xff0c;它的核心目标是使计算机能够像人类一样“看”并“理解”视觉信息。换句话说&#xff0c;它希望赋予计算机从图像、视频中自动提取、有意义地分析、理解并解释视觉场…

JVM系列(十三) -常用调优工具介绍

最近对 JVM 技术知识进行了重新整理&#xff0c;再次献上 JVM系列文章合集索引&#xff0c;感兴趣的小伙伴可以直接点击如下地址快速阅读。 JVM系列(一) -什么是虚拟机JVM系列(二) -类的加载过程JVM系列(三) -内存布局详解JVM系列(四) -对象的创建过程JVM系列(五) -对象的内存分…

electron-vite【实战】登录/注册页

效果预览 项目搭建 https://blog.csdn.net/weixin_41192489/article/details/144611858 技术要点 路由默认跳转到登录页 src/renderer/src/router/index.ts routes: [// 默认跳转到登录页{path: /,redirect: /login},...routes]登录窗口的必要配置 src/main/index.ts 中 cons…

蓝桥杯嵌入式备赛教程(1、led,2、lcd,3、key)

一、工程模版创建流程 第一步 创建新项目 第二步 选择型号和管脚封装 第三步 RCC使能 外部时钟&#xff0c;高速外部时钟 第四步晶振时钟配置 由数据手册7.1可知外部晶振频率为24MHz 最后一项设置为80 按下回车他会自动配置时钟 第五步&#xff0c;如果不勾选可能程序只会…

C++----------函数的调用机制

栈帧的创建与销毁 栈帧创建过程 当一个函数被调用时&#xff0c;系统会在程序的栈空间中为该函数创建一个栈帧。首先&#xff0c;会将函数的返回地址&#xff08;即调用该函数的下一条指令的地址&#xff09;压入栈中&#xff0c;这确保函数执行完后能回到正确的位置继续执行后…

C语言初阶习题【9】数9的个数

1.编写程序数一下 1到 100 的所有整数中出现多少个数字9 2.思路 循环遍历1到100&#xff0c;需要判断每一位的个位数是否为9&#xff0c;十位数是否为9&#xff0c;每次符合条件就count进行计数&#xff0c;最后输出count&#xff0c;即可 3.code #define _CRT_SECURE_NO_W…

模型 课题分离

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。明确自我与他人责任。 1 课题分离的应用 1.1课题分离在心理治疗中的应用案例&#xff1a;李晓的故事 李晓&#xff0c;一位28岁的软件工程师&#xff0c;在北京打拼。他面临着工作、家庭和感情的多重…

Docker 入门:如何使用 Docker 容器化 AI 项目(一)

引言 在人工智能&#xff08;AI&#xff09;项目的开发和部署过程中&#xff0c;环境配置和依赖管理往往是开发者遇到的挑战之一。开发者通常需要在不同的机器上运行同样的代码&#xff0c;确保每个人使用的环境一致&#xff0c;才能避免 “在我的机器上可以运行”的尴尬问题。…

Android修行手册 - 移动端几种常用动画方案对比

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

抢单人机交互「新红利」!哪些细分赛道“多金”?

受终端用户的智能座舱体验需求驱动&#xff0c;视觉、听觉、触觉等人机交互方式加速焕新。 一方面&#xff0c;人机多模交互引领&#xff0c;车载声学进入新周期。根据高工智能汽车研究院统计数据&#xff0c;单车的车载扬声器搭载量正在快速起量。 很显然&#xff0c;作为智…