第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇

Spring源码阅读目录

第一部分——IOC篇

第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇

第二部分——AOP篇

第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇


文章目录

  • Spring源码阅读目录
    • 第一部分——IOC篇
    • 第二部分——AOP篇
  • 前言
  • 尝试动手写IOC容器
      • 第二十一版 Weaver(织入器)篇
  • 总结


前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
在这里插入图片描述

    所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


    书接上回,在上篇 第二十章 Spring之假如让你来写AOP——Aspect(切面)篇 中,A君 已经完成了 Aspect(切面) 部分,对切点和通知进行了统一管理。接下来看看 A君 会有什么骚操作吧

尝试动手写IOC容器

    出场人物:A君(苦逼的开发)、老大(项目经理)

    背景:老大要求A君在一周内开发个简单的 IOC容器

    前情提要: A君 已经完成了 Aspect(切面) 部分,对切点和通知进行了统一管理 。。。

第二十一版 Weaver(织入器)篇

    “ A君,现在AOP部分已经快进入尾声了。还有个重要的部分——Weaver(织入器)。对于这部分内容,想必你或多或少都接触过。简而言之,就是创建代理对象,把对应的切面织入进去。当然啦,代理有基于JDK的,也有基于CGLIB的,这两个都要实现。A君,你有什么思路吗?” 老大 问到

    “那自然是定义个接口,包含创建代理的方法。接口代理也好,CGLIB代理也好,都遵循此接口。” A君 不假思索回答道

    “不错,可是这样子定义,使用者必须清楚代理有多少种实现,才能正确的创建出来代理对象!举个例子:以后新增一个创建代理的方式,那么使用者就得跟着改动,这在软件设计中可是大忌。创建代理中间是否少了点东西?” 老大 循循善诱道

    “工厂模式!?” A君 疑惑道

    “是的,你需要个工厂。使用者传入对应的配置信息即可,这样,即使以后有多少个实现,对于调用者来说是一样的,什么都不用改。除此之外,我看了你之前写的代码,缺少配置信息。我们可以根据是否存在接口,来推断使用哪个代理,但是不要剥夺用户自己指定配置的权利,这样用户没配置我们自己推断,配置了就以用户的为准,才能立于不败之地。” 老大 笑道

    “明白了!” A君 忽然有种醍醐灌顶的感觉:也许,任何设计都是被用户折腾出来的,逼得程序员处处考虑拓展性、灵活性

    “先这样子吧,你回去好好想想怎么做吧!” 老大 开始下逐客令

    “好的。” A君 默默的走回自己的工位。开始思索:如何实现 老大 所说的内容。后续创建代理都要依赖于配置,先从配置开始吧。可是配置要有哪些内容呢?A君 略一沉吟,列出下面几点:

  1. 是否使用类代理,在有接口的情况下,默认会使用JDK代理,但是保不准用户就是想用类代理
  2. 代理对象是否可见,正常情况下,代理对用户是黑盒,但是由于 AOP 存在自调用问题,即 this.xxx() 时切面无法正常执行,故而由此配置。例:

在这里插入图片描述
原因也简单,this是当前对象,也就是目标对象,没有经过代理对象,所以无法增强

  1. Advise 是否可见,默认为true
  2. 是否冻结,冻结后,配置无法再进行修改

A君 平时玩游戏时,总是选择简单难度,其实游戏本身是存在多种难度的,简单难度是面向普罗大众的,困难难度是为了给高玩提供的,各个阶层玩家都能有足够的游戏体验。这里配置也是一样的道理,对于普通用户来说,有些配置不需要去关心,想要掌控全部配置,那得对 AOP 有充足的认识才行。A君 定义 ProxyConfig 作为配置类,代码如下:

public class ProxyConfig {/*** advise是否可见*/boolean opaque = false;/*** 允许代理对象内部通过 AopContext 访问当前代理,适用于自调用场景*/boolean exposeProxy = false;/*** 是否使用CGLIB代理*/private boolean proxyTargetClass = false;/*** 代理配置是否被冻结,防止运行时修改*/private boolean frozen = false;public boolean isProxyTargetClass() {return this.proxyTargetClass;}public void setProxyTargetClass(boolean proxyTargetClass) {this.proxyTargetClass = proxyTargetClass;}public boolean isOpaque() {return this.opaque;}public void setOpaque(boolean opaque) {this.opaque = opaque;}/*** Return whether the AOP proxy will expose the AOP proxy for* each invocation.*/public boolean isExposeProxy() {return this.exposeProxy;}public void setExposeProxy(boolean exposeProxy) {this.exposeProxy = exposeProxy;}public boolean isFrozen() {return this.frozen;}public void setFrozen(boolean frozen) {this.frozen = frozen;}public void copyFrom(ProxyConfig other) {this.proxyTargetClass = other.proxyTargetClass;this.exposeProxy = other.exposeProxy;this.frozen = other.frozen;this.opaque = other.opaque;}
}

Advised 去统一管理这些配置,Advised 接口新增方法如下:

在这里插入图片描述

    配置类简单,难得是如何根据配置类,去实现不同功能代理,不过,接口倒是可以先定义出来。A君 定义 AopProxy 作为所有代理实现的标准,代码如下:

/*** 创建代理接口*/
public interface AopProxy {Object getProxy();Object getProxy(ClassLoader classLoader);
}

纠结良久,A君 打算先从 JDK代理 开始弄,相较于 CGCLIB,平时在工作中用到,比较熟悉点。 JDK代理 必须实现 InvocationHandler 接口,那入手点就在对应的方法上,在调用 invoke 方法时,如果方法是对应的增强方法,就调用对应的通知链。不过在此之前,需要给他加点料。嘿嘿,加啥料呢?主要就是针对上诉配置添加的,尽量让用户方便。主要添加三个接口,如下:

  • SpringImitationProxy 接口:添加防伪标识,标记这个代理类是由框架生成的。嘿嘿
  • Advised 接口:针对 opaque 配置,使其可以访问 AOP 的所有内容
  • DecoratingProxy 接口:这个是专门给 JDK代理 用的,用以获取目标类 Class对象

A君AopProxyUtils 工具类中,添加如下方法:

 static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();if (specifiedInterfaces.length == 0) {Class<?> targetClass = advised.getTargetClass();if (targetClass != null) {if (targetClass.isInterface()) {//目标类是接口advised.setInterfaces(targetClass);} else if (Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {//类、lambda就添加对应的接口advised.setInterfaces(targetClass.getInterfaces());}specifiedInterfaces = advised.getProxiedInterfaces();}}List<Class<?>> proxiedInterfaces = new ArrayList<>(specifiedInterfaces.length + 3);proxiedInterfaces.addAll(Arrays.asList(specifiedInterfaces));/*** 添加标记接口*/if (!advised.isInterfaceProxied(SpringImitationProxy.class)) {proxiedInterfaces.add(SpringImitationProxy.class);}/*** 目标类可见,且不包含Advised接口*/if (!advised.isOpaque() && !advised.isInterfaceProxied(Advised.class)) {proxiedInterfaces.add(Advised.class);}/*** 用于jdk代理中,获取对应的目标class*/if (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class)) {proxiedInterfaces.add(DecoratingProxy.class);}return proxiedInterfaces.toArray(new Class[0]);}

方法没什么难度,就是根据对应的配置去添加对应的接口,料添加完了,剩下的就没有什么了,就是根据调用的方法去匹配对应的通知,有则调用整个通知链。无则直接调用目标方法。唯二需要注意的点是:第一点,需要根据 exposeProxy 配置,判断是否需要绑定到线程中。第二点,如果返回值 this,需要确定下是否需要返回代理对象。JdkDynamicAopProxy 代码如下:

import com.hqd.ch03.v21.aopalliance.intercept.MethodInvocation;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;public class JdkDynamicAopProxy implements InvocationHandler, AopProxy {private final AdvisedSupport advised;private final Class<?>[] proxiedInterfaces;public JdkDynamicAopProxy(AdvisedSupport config) {this.advised = config;this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);}@Overridepublic Object getProxy() {return getProxy(Thread.currentThread().getContextClassLoader());}@Overridepublic Object getProxy(ClassLoader classLoader) {return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Object target = this.advised.getTarget();try {Object retVal;/*** 代理对象可见,绑定到线程中去*/if (this.advised.exposeProxy) {oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, this.advised.getTargetClass());if (chain.isEmpty()) {Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = method.invoke(target, argsToUse);} else {MethodInvocation invocation =new ReflectiveMethodInvocation(target, proxy, method, args, chain.toArray());retVal = invocation.proceed();}Class<?> returnType = method.getReturnType();/*** 返回值是否是this,如果是this,则判断是否需要返回代理对象*/if (retVal != null && retVal == target &&returnType != Object.class && returnType.isInstance(proxy) &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {retVal = proxy;} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {throw new RuntimeException("Null return value from advice does not match primitive return type for: " + method);}return retVal;} finally {if (setProxyContext) {AopContext.setCurrentProxy(oldProxy);}}}
}

JDK代理 到这就结束了,比想象中的简单呢。” A君 暗喜

    接下来就是 CGLIB代理 了,其实和 JDK代理 逻辑差不多,在此之前,A君 通过 Callback数组 把通知织入进去,重点就在这里。JDK代理 中,是根据方法名、参数等信息去对调用对应的方法,而 CGLIB代理 则是通过 Callback数组 下标。那怎么知道数组下标呢?那自然是实现对应的接口了,这个接口就是 CallbackFilter。剩下的基本就一样了。A君 定义 ProxyCallbackFilter 类,实现 CallbackFilter 接口。代码如下:

/*** 规定callbacks数组顺序*/private static final int AOP_PROXY = 0;//通知方法private static final int INVOKE_TARGET = 1;//目标对象方法private static final int NO_OVERRIDE = 2;//空操作private static final int DISPATCH_TARGET = 3;//获取目标类class对象private static final int DISPATCH_ADVISED = 4;//advised 相关的方法private static class ProxyCallbackFilter implements CallbackFilter {private final AdvisedSupport advised;public ProxyCallbackFilter(AdvisedSupport advised) {this.advised = advised;}/*** 这里返回值就是callbacks的下标* 也就是说,这里是去匹配对应的回调函数** @return*/@Overridepublic int accept(Method method) {/*** 空操作*/if (AopUtils.isFinalizeMethod(method)) {return NO_OVERRIDE;}/*** Advised相关的方法委托给ADVISED分发器*/if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {return DISPATCH_ADVISED;}Class<?> targetClass = this.advised.getTargetClass();List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);boolean haveAdvice = !chain.isEmpty();boolean exposeProxy = this.advised.isExposeProxy();boolean isFrozen = this.advised.isFrozen();/*** 方法存在通知,使用增强拦截器*/if (haveAdvice || !isFrozen) {return AOP_PROXY;} else {//不存在,调用目标方法if (exposeProxy) {return INVOKE_TARGET;}Class<?> returnType = method.getReturnType();if (targetClass != null && returnType.isAssignableFrom(targetClass)) {return INVOKE_TARGET;} else {return DISPATCH_TARGET;}}}}

ProxyCallbackFilter 出来后,主体逻辑就出来了,接着只需要根据定义的顺序去定义 Callback数组 即可。A君 定义个方法,用来组装 Callback数组。代码如下:

   private Callback[] getCallbacks(Class<?> rootClass) {boolean exposeProxy = this.advised.isExposeProxy();boolean isStatic = true;//1.切面增强调用拦截器Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);Callback targetInterceptor;/*** 2.代理对象拦截器,是否暴露代理对象* 两个拦截器的唯一区别就是,是否暴露代理对象*/if (exposeProxy) {targetInterceptor = new StaticUnadvisedExposedInterceptor(this.advised.getTarget());} else {targetInterceptor = new StaticUnadvisedInterceptor(this.advised.getTarget());}//4.序列化拦截器Callback targetDispatcher = new StaticDispatcher(this.advised.getTarget());Callback[] mainCallbacks = new Callback[]{aopInterceptor,targetInterceptor,new SerializableNoOp(),targetDispatcher, this.advisedDispatcher};return mainCallbacks;}

为了文章整体的整洁性,A君 就贴主体逻辑。getProxy 方法代码如下:

public Object getProxy(ClassLoader classLoader) {Enhancer enhancer = new Enhancer();Class<?> rootClass = this.advised.getTargetClass();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);}}enhancer.setSuperclass(proxySuperClass);/*** 指定命名策略,防止冲突*/enhancer.setNamingPolicy(SpringImitationNamingPolicy.INSTANCE);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));if (classLoader != null) {enhancer.setClassLoader(classLoader);}/*** 添加默认拦截器,执行增强*/Callback[] callbacks = getCallbacks(rootClass);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy()));enhancer.setCallbacks(callbacks);enhancer.setCallbackTypes(types);/*** 创建代理对象*/return createProxyClassAndInstance(enhancer, callbacks);}

好了, CGLIB代理JDK代理 都已经实现了。就剩下一个工厂了

    工厂的实现并没有什么特殊的地方,无非就是根据配置和接口推断使用哪个代理。A君 定义 AopProxyFactory 接口。代码如下:

public interface AopProxyFactory {AopProxy createAopProxy(AdvisedSupport config);
}

默认实现为 DefaultAopProxyFactory。代码如下:

import com.hqd.ch03.v21.aop.SpringImitationProxy;
import com.hqd.ch03.v21.utils.ClassUtils;import java.lang.reflect.Proxy;public class DefaultAopProxyFactory implements AopProxyFactory {@Overridepublic AopProxy createAopProxy(AdvisedSupport config) {if (config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new RuntimeException("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 CglibAopProxy(config);} else {return new JdkDynamicAopProxy(config);}}private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {Class<?>[] ifcs = config.getProxiedInterfaces();return (ifcs.length == 0 || (ifcs.length == 1 && SpringImitationProxy.class.isAssignableFrom(ifcs[0])));}
}

    至此整个 Weaver(织入器) 就全部完事了。A君 按捺不住心中的喜悦,开始编写测试代码。代码如下:

    @Testpublic void v21() throws Throwable {System.out.println("############# 第二十一版 Weaver(织入器)篇 #############");System.out.println("############# 接口代理 #############");com.hqd.ch03.v21.aop.framework.aspectj.AspectJExpressionPointcut pointcut =new com.hqd.ch03.v21.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..)) and args(msg)");Object ap = new AopBean();//前置通知AopTest aop = new AopTest();Method beforeTest = aop.getClass().getDeclaredMethod("beforeTestV19", JoinPoint.class, String.class);List list = new ArrayList<>();//前置通知com.hqd.ch03.v21.aop.framework.aspectj.AspectJMethodBeforeAdvice beforeAdvice =new com.hqd.ch03.v21.aop.framework.aspectj.AspectJMethodBeforeAdvice(pointcut, beforeTest, aop);com.hqd.ch03.v21.aop.Advisor advisor = new com.hqd.ch03.v21.aop.framework.aspectj.AspectJPointcutAdvisor(beforeAdvice);list.add(com.hqd.ch03.v21.aop.interceptor.ExposeInvocationInterceptor.ADVISOR);list.add(advisor);com.hqd.ch03.v21.aop.framework.ProxyFactory proxyFactory = new com.hqd.ch03.v21.aop.framework.ProxyFactory(ap);proxyFactory.addInterface(IAopBean.class);proxyFactory.addAdvisors(list);IAopBean jdkProxy = (IAopBean) proxyFactory.getProxy();jdkProxy.test1("111");System.out.println("############# cglib代理 #############");proxyFactory.setProxyTargetClass(true);AopBean cgProxy = (AopBean) proxyFactory.getProxy();cgProxy.test1("111");}

测试结果如下:

在这里插入图片描述

“好了,Weaver(织入器) 部分已经完成了,可以交差了。” A君 欢呼

总结

    正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)

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

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

相关文章

04 - Clickhouse-21.7.3.14-2单机版安装

目录 一、准备工作 1、确定防火墙处于关闭状态 2、CentOS 取消打开文件数限制 3、安装依赖 4、CentOS取消SELINUX 二、单机安装 2.1、下载安装 2.2、安装这4个rpm包 2.3、修改配置文件 2.4、启动服务 2.5、关闭开机自启 2.6、使用Client连接server 一、准备工作 1…

Python脚本-linux远程安装某个服务

需求&#xff1a; 某公司因为网站服务经常出现异常&#xff0c;需要你开发一个脚本对服务器上的服务进行监控&#xff1b;检测目标服务器上是否存在nginx软件(提供web服务的软件)&#xff0c;如果不存在则安装(服务器可能的操作系统Ubuntu24/RedHat9)&#xff1b;如果nginx软件…

芯片之殇——“零日漏洞”(文后附高通64款存在漏洞的芯片型号)

芯片之殇——“零日漏洞”(文后附高通64款存在漏洞的芯片型号) 本期是平台君和您分享的第113期内容 前一段时间,高通公司(Qualcomm)发布安全警告称,提供的60多款芯片潜在严重的“零日漏洞”,芯片安全再一次暴露在大众视野。 那什么是“零日漏洞”?平台君从网上找了一段…

x-cmd mod | x pixi - 兼容 Conda 生态的极速包管理器,conda 和 mamba 用户的另一选择

目录 介绍使用语法参数子命令 介绍 x pixi 模块是由 x-cmd 团队使用 posix shell 实现的 pixi 命令增强工具。它能优化 pixi 命令的安装和使用体验&#xff0c;具体如下&#xff1a; 提供带有 advise 的自动补全功能。对于中国区用户&#xff0c;我们还提供了汉化版的 advise…

Rust derive macro(Rust #[derive])Rust派生宏

参考文章&#xff1a;附录 D&#xff1a;派生特征 trait 文章目录 Rust 中的派生宏 #[derive]基础使用示例&#xff1a;派生 Debug 派生其他常用特征示例&#xff1a;派生 Clone 和 Copy 派生宏的限制和自定义派生自定义派生宏上面代码运行时报错了&#xff0c;以下是解释 结论…

Node.js | npm下载安装及环境配置教程

前言&#xff1a; npm 是 Nodejs 下的包管理器&#xff0c;在下载 Node.js 后自动安装&#xff0c;因此本文同时适合 Node.js / npm 的下载安装及环境配置。 一、软件安装 Node.js中文网官网下载页&#xff1a;Node.js 中文网 (nodejs.com.cn) 1&#xff09;进入下载页&#xf…

pytorch奇怪错误

ValueError: At least one stride in the given numpy array is negative, and tensors with negative strides are not currently supported. (You can probably work around this by making a copy of your array with array.copy().) 今天在这里遇到了一个奇怪的bug impor…

EDA实验设计-led灯管动态显示;VHDL;Quartus编程

EDA实验设计-led灯管动态显示&#xff1b;VHDL&#xff1b;Quartus编程 引脚配置实现代码RTL引脚展示现象记录效果展示 引脚配置 #------------------GLOBAL--------------------# set_global_assignment -name RESERVE_ALL_UNUSED_PINS "AS INPUT TRI-STATED" set_…

151页PDF | XX集团数字化转型SAP项目规划方案(限免下载)

一、前言 这份报告是XX集团数字化转型SAP项目规划方案&#xff0c;该报告涵盖了集团战略解读、管理痛点分析、信息化建设目标、整体架构方案、实施策略、SAP系统价值和预期收益&#xff0c;旨在通过数字化推动集团运营模式变革&#xff0c;实现降本增效和价值创新。 《XX集团…

共建智能软件开发联合实验室,怿星科技助力东风柳汽加速智能化技术创新

11月14日&#xff0c;以“奋进70载&#xff0c;智创新纪元”为主题的2024东风柳汽第二届科技周在柳州盛大开幕&#xff0c;吸引了来自全国的汽车行业嘉宾、技术专家齐聚一堂&#xff0c;共襄盛举&#xff0c;一同探寻如何凭借 “新技术、新实力” 这一关键契机&#xff0c;为新…

在 cmd 输入 python.exe 后不报错也无反应的问题

在 cmd 输入 python.exe 后不报错&#xff1a;‘python.exe ’不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件&#xff0c;也无反应。只是显示这样一个弹窗&#xff1a; 查了下环境变量path&#xff0c;看看有什么地方有python.exe&#xff0c;发现原来在C:\Us…

算法日记 30 day 动态规划(背包问题)

今天是动态规划的另一个大类&#xff0c;背包问题。 背包问题的分类 这张图中涵盖了我们能遇到的绝大部分背包问题。 首先是01背包问题 01背包问题 01背包问题&#xff1a;有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value…

如何修复WordPress卡在维护模式

当你管理WordPress网站时&#xff0c;没有什么比看到它卡在维护模式更令人沮丧的了。特别是在你进行重要更新或期望大量流量的时候&#xff0c;这种情况会更加令人不安。 维护模式可能由许多因素引起&#xff0c;从简单的文件损坏到更复杂的插件冲突或存在的.maintenance文件。…

Oracle OCP认证考试考点详解082系列22

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 105. 第105题&#xff1a; 题目 解析及答案&#xff1a; 题目翻译&#xff1a; 关于Oracle数据库中的事务请选择两个正确的陈述&#xf…

目录背景缺少vscode右键打开选项

目录背景缺少vscode右键打开选项 1.打开右键管理 下载地址&#xff1a;https://wwyz.lanzoul.com/iZy9G2fl28uj 2.开始搜索框搜索vscode&#xff0c; 找到其源目录 3.目录背景里面&#xff0c; 加入vscode.exe 3.然后在目录背景下&#xff0c; 右键&#xff0c; code就可以打…

第三届航空航天与控制工程国际学术会议 (ICoACE 2024)

重要信息 会议官网&#xff1a;www.icoace.com 线下召开&#xff1a;2024年11月29日-12月1日 会议地点&#xff1a;陕西西安理工大学金花校区 &#xff08;西安市金花南路5号&#xff09; 大会简介 2024年第三届航空航天与控制工程国际学术会议&#xff08;ICoACE 2024&a…

【团购核销】抖音生活服务商家应用快速接入①——基础工作

文章目录 一、前言二、抖音开放平台&#xff08;服务商平台&#xff09;三、认证服务能力四、第三方生活服务商家应用五、APPID和AppSecret六、申请接口权限七、开发配置八、参考 一、前言 目的&#xff1a;将抖音团购核销的功能集成到我们自己开发的App和小程序中 名词解释技术…

十六.SpringCloudAlibaba极简入门-整合Grpc代替OpenFeign

前言 他来了他来了&#xff0c;停了快2个月了终于又开始更新文章啦&#xff0c;这次带来的绝对是干货&#xff01;&#xff01;&#xff01;。由于公司项目进行重构的时候考虑到&#xff0c;OpenFeign做为服务通信组件在高并发情况下有一定的性能瓶颈&#xff0c;所以将其替换…

MySQL库和表的操作

目录 一. 查看数据库 二. 创建数据库 三. 字符集和校验规则 四. 修改和删除数据库 4.1 数据库修改 4.2 数据库删除 五. 备份与恢复 5.1 备份 5.2 还原 5.3 注意事项 5.4 查看连接情况 六. 创建表 七. 查看表结构 八. 修改表 九. …

在Linux下配置gitee与Github的远程仓库

目录 前言 云服务器下载git 检测是否下载成功git Linux下配置gitee远程仓库 代码提交演示 git三板斧 Linux下配置Github远程仓库 最后的提醒 前言 那么本篇文章将是在&#xff0c;你已经创建了本地仓库的基础上&#xff0c;在Linux下配置gitee的远程仓库的步骤&#xff…