第十四章 Spring之假如让你来写AOP——雏形篇
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源码阅读目录
- 第一部分——IOC篇
- 第二部分——AOP篇
- 前言
- 尝试动手写IOC容器
- 第十六版 AOP雏形
- 哪些地方可以增强?
- 如何找到这些地方?
- 找到后要怎么增强?
- 多个增强如何管理?
- 如何对其进行增强?
- 总结
前言
对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》
书接上回,在上篇 第十三章 Spring之假如让你来写AOP——AOP联盟篇 中,A君 先把 AOP联盟 规范的接口定义好了。接下来看看 A君 会有什么骚操作吧
尝试动手写IOC容器
出场人物:A君(苦逼的开发)、老大(项目经理)
背景:老大要求A君在一周内开发个简单的 IOC容器
前情提要: 昨天,A君 定义了 AOP联盟 规范的接口 。。。
第十六版 AOP雏形
俗话说:人无远虑,必有近忧。 这句话用来描述 A君 此时的状态再合适不过了。 A君 现在 看着这一堆接口,一脸懵逼,这要怎么继续。纠结了良久,A君 按照之前的思路走:
- 哪些地方可以增强?
- 如何找到这些地方?
- 找到后要怎么增强?
- 多个增强如何管理?
- 如何对其进行增强?
哪些地方可以增强?
目标既然已经确定,A君 开始着手准备实现 Joinpoint(连接点) 相关内容。按照之前 A君 了解到的知识:Joinpoint(连接点) 就是定义 AOP 对目标对象可以增强的地方。 A君 的计划是实现方法增强即可,也就是 Joinpoint(连接点) 就是方法,即需要实现 MethodInvocation 接口,这点不再有任何疑虑。确定完需要实现的接口,接着还要确定一下类需要实现的功能,这不经让 A君 一阵皱眉。Joinpoint(连接点) 无疑是方法执行的地方,如果仅仅执行目标方法,显然不够。需要加上 Advice(通知),那么有多个 Advice(通知) 怎么解决?
思来想去,A君 决定让 Joinpoint(连接点) 接收所有 Advice(通知) ,然后通过链式调用去执行每一个 Advice(通知)。思路清晰后,A君 撸袖子开干,定义 ReflectiveMethodInvocation 类,代码如下:
import com.hqd.ch03.v16.aopalliance.intercept.MethodInterceptor;
import com.hqd.ch03.v16.aopalliance.intercept.MethodInvocation;import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.List;/*** 连接点类,用以执行通知及目标方法*/
public class ReflectiveMethodInvocation implements MethodInvocation {private Object proxy;private Method method;private Object[] args;private List<MethodInterceptor> mis;private int currentInterceptorIndex = -1;public ReflectiveMethodInvocation(Object proxy, Method method,Object[] args, List<MethodInterceptor> mis) {this.proxy = proxy;this.method = method;this.args = args;this.mis = mis;}@Nonnull@Overridepublic Method getMethod() {return method;}@Nonnull@Overridepublic Object[] getArguments() {return args;}@Nullable@Overridepublic Object proceed() throws Throwable {/*** 链式调用通知*/if (mis.size() - 1 == currentInterceptorIndex) {return getMethod().invoke(proxy, args);}MethodInterceptor methodInterceptor = this.mis.get(++currentInterceptorIndex);return methodInterceptor.invoke(this);}@Nullable@Overridepublic Object getThis() {return proxy;}@Nonnull@Overridepublic AccessibleObject getStaticPart() {return null;}
}
如何找到这些地方?
“现在 Joinpoint(连接点) 有了,还需要一个可以找到对应 Joinpoint(连接点) 的 Pointcut(切点) ” ,A君 思忖。对于 Pointcut(切点),A君 之前用 Spring 也有接触过,类似于:execution( .test(…)) ,这块内容 Spring 也并非自己实现,而是使用 AspectJ。想到这里,A君 决定也站在巨人的肩膀上,使用 AspectJ 实现对应的 Joinpoint(连接点) 匹配。那么 Pointcut(切点) 的就简单了,接收对应的表达式,调用 AspectJ 进行匹配即可。捋清楚之后,A君 开始撸代码,定义 AspectJExpressionPointcut 类,其代码如下:
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParameter;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Set;/*** 切点表达式匹配*/
public class AspectJExpressionPointcut {private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();static {/*** 定义 AspectJ 匹配器*/SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);}/*** 切点表达式*/private String expression;private String[] pointcutParameterNames = new String[0];private Class<?>[] pointcutParameterTypes = new Class<?>[0];public AspectJExpressionPointcut(String expression) {this.expression = expression;}public PointcutExpression buildPointcutExpression() {PointcutParser parser = initializePointcutParser(Thread.currentThread().getContextClassLoader());PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];/*** 匹配对应的参数*/for (int i = 0; i < pointcutParameters.length; i++) {pointcutParameters[i] = parser.createPointcutParameter(this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);}return parser.parsePointcutExpression(getExpression(),null, pointcutParameters);}/*** 初始化切点匹配器** @param classLoader* @return*/private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) {PointcutParser parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, classLoader);return parser;}public String getExpression() {return this.expression;}
}
找到后要怎么增强?
注:此处理解有误,Spring 应该由于历史原因,才有不同的实现。这里大家别信,看个乐呵。后边有专门的篇章解释
在找到 Joinpoint(连接点) 之后,接下来要解决的就是怎么增强目标方法 。 前置、后置、环绕、异常增强 这些都是耳熟能详的词,但是实现上却有些不一样。首先是 Before Advice(前置通知) 和 After Returning Advice,这两个可以直接调用增强方法,不需要特殊的操作,而 After Throwing Advice、After (Finally) Advice、 Around Advice(环绕通知) 却不一样,需要对方法进行更细致的操作。故而 A君 把他们分成两种,Before Advice(前置通知) 和 After Returning Advice 直接拓展 Advice 接口,A君 照猫画虎,定义一个 BeforeAdvice 接口,作为 Before Advice(前置通知) 的标记接口,代码如下:
import com.hqd.ch03.v16.aopalliance.aop.Advice;/*** 前置通知标记接口*/
public interface BeforeAdvice extends Advice {}
只有标记接口,显然是干不了任何事的,所以还需要定义一个真正干活的接口—— MethodBeforeAdvice,代码如下:
import javax.annotation.Nullable;
import java.lang.reflect.Method;public interface MethodBeforeAdvice extends BeforeAdvice {void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}
接口有了,按照惯例,接下来就是具体实现了,不急不急。A君 经过一番观察,发现这些 Advice(通知) 都是一个套路,虽然有分前后置,但是都是先确定 Pointcut(切点) 是否匹配,在执行相应的方法。咦?有公共代码,那么他就要来了,没错,就是他——抽象类。A君 定义 AbstractAspectJAdvice 类,代码如下:
import com.hqd.ch03.v16.aopalliance.aop.Advice;import javax.annotation.Nonnull;
import java.lang.reflect.Method;public abstract class AbstractAspectJAdvice implements Advice {private Method aspectJAdviceMethod;private Object aspect;private AspectJExpressionPointcut pointcut;public AbstractAspectJAdvice(AspectJExpressionPointcut pointcut, Method aspectJAdviceMethod, Object aspect) {this.aspectJAdviceMethod = aspectJAdviceMethod;this.aspect = aspect;this.pointcut = pointcut;}protected void invokeAdviceMethod(@Nonnull Method method) {if (match(method)) {try {aspectJAdviceMethod.invoke(aspect);} catch (Exception e) {throw new RuntimeException(e);}}}/*** 判断是否匹配** @param method* @return*/protected boolean match(Method method) {return pointcut.buildPointcutExpression().matchesMethodExecution(method).alwaysMatches();}
}
提取完公共代码,接下来就要回到正题——实现 MethodBeforeAdvice。不过这玩意确实也没有什么东西,直接调用父类方法即可。A君 定义 AspectJMethodBeforeAdvice 类,代码如下:
import com.hqd.ch03.v16.aop.framework.MethodBeforeAdvice;import javax.annotation.Nullable;
import java.lang.reflect.Method;public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice {public AspectJMethodBeforeAdvice(AspectJExpressionPointcut pointcut, Method aspectJAdviceMethod, Object aspect) {super(pointcut, aspectJAdviceMethod, aspect);}@Overridepublic void before(Method method, Object[] args, @Nullable Object target) throws Throwable {invokeAdviceMethod(method);}
}
“咦?不太对劲。” A君 警觉。在定义 ReflectiveMethodInvocation 类时,A君 要求通知必须要时 MethodInterceptor 类型,因为只有类型为 MethodInterceptor 整个链式调用才能玩的动,而 AspectJMethodBeforeAdvice 显然不是。没办法,套个马甲把,于是乎,A君 有定义了一个 MethodBeforeAdviceInterceptor 类,适配器?随便了。代码如下:
import com.hqd.ch03.v16.aop.framework.BeforeAdvice;
import com.hqd.ch03.v16.aop.framework.MethodBeforeAdvice;
import com.hqd.ch03.v16.aopalliance.intercept.MethodInterceptor;
import com.hqd.ch03.v16.aopalliance.intercept.MethodInvocation;
import lombok.AllArgsConstructor;import javax.annotation.Nonnull;
import javax.annotation.Nullable;@AllArgsConstructor
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice {private final MethodBeforeAdvice advice;@Nullable@Overridepublic Object invoke(@Nonnull MethodInvocation mi) throws Throwable {advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());return mi.proceed();}
}
OK,搞定。这么一来, Joinpoint(连接点) 的链式调用就玩的转了。接下来 AfterReturningAdvice 也是同理,限于篇幅,A君 就不再列举了
After Throwing Advice、After (Finally) Advice 这类通知需要对方法进行更精细的控制,所以需要实现 MethodInterceptor,而不是直接实现 Advice。A君 先对 After (Finally) Advice 进行实现,见名知意,这个应该是在 finally 代码块里面实现的, A君 定义 AspectJAfterAdvice 类,代码如下:
import com.hqd.ch03.v16.aop.framework.AfterAdvice;
import com.hqd.ch03.v16.aopalliance.intercept.MethodInterceptor;
import com.hqd.ch03.v16.aopalliance.intercept.MethodInvocation;import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Method;public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice {public AspectJAfterAdvice(AspectJExpressionPointcut pointcut, Method aspectJAdviceMethod, Object aspect) {super(pointcut, aspectJAdviceMethod, aspect);}@Nullable@Overridepublic Object invoke(@Nonnull MethodInvocation mi) throws Throwable {try {return mi.proceed();} finally {invokeAdviceMethod(mi.getMethod());}}
}
After Throwing Advice 自然就是在 catch 代码块执行了,只有异常时才执行,代码类似,A君 也不列举了
多个增强如何管理?
管理 Advice(通知) 这部分内容就比较好实现了,无非定义一个管理类,可以对 Advice(通知) 进行增删查改排序就行了,这玩意难不倒 A君,A君 直接定义 AdvisedSupport 类,让它对 Advice(通知) 进行管理,代码如下:
import com.hqd.ch03.v16.aopalliance.intercept.MethodInterceptor;
import org.apache.commons.collections4.CollectionUtils;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;/*** advice管理类*/
public class AdvisedSupport {private List<MethodInterceptor> advisors = new ArrayList<>();public void addAdvisors(Collection<MethodInterceptor> advisors) {if (CollectionUtils.isNotEmpty(advisors)) {this.advisors.addAll(advisors);}}public void addAdvisor(MethodInterceptor advisor) {if (advisor != null) {this.advisors.add(advisor);}}public List<MethodInterceptor> getAdvisors() {return advisors;}
}
如何对其进行增强?
现在万事俱备,只剩最后一部分内容了,如何把通知和目标方法进行关联?先创建代理对象,这一步毋庸置疑。但是要怎么对代理对象进行增强,嘿嘿,这个简单,老套路,套个马甲就行了。给代理类添加个默认的拦截器,让它去执行整个通知链路。思量完毕,A君 先定义 ProxyFactory 类,作为代理工厂,用以创建代理对象,并创建默认的方法拦截器。代码如下:
import lombok.AllArgsConstructor;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.io.Serializable;
import java.lang.reflect.Method;@AllArgsConstructor
public class ProxyFactory extends AdvisedSupport {private Class<?> superClass;private Object target;private Method method;public Object getProxy() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(superClass);/*** 添加默认拦截器,执行增强*/enhancer.setCallbacks(new Callback[]{new DynamicAdvisedInterceptor(this, target, method)});return enhancer.create();}@AllArgsConstructorprivate static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {private AdvisedSupport advisedSupport;private Object target;private Method method;@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {ReflectiveMethodInvocation rmi = new ReflectiveMethodInvocation(target, this.method, null, advisedSupport.getAdvisors());return rmi.proceed();}}
}
至此,A君 所有的步骤都已经实现了,现在需要测试一下,来验证自己的努力的成果。测试代码如下:
@Testpublic void v16() throws Throwable {System.out.println("############# 第十六版:aop雏形 #############");AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* *.test(..))");Object ap = new AopBean();Method method = AopBean.class.getDeclaredMethod("test");AopTest aop = new AopTest();Method beforeTest = aop.getClass().getDeclaredMethod("beforeTest");Method afterTest = aop.getClass().getDeclaredMethod("afterTest");Method afterReturnTest = aop.getClass().getDeclaredMethod("afterReturnTest");List<MethodInterceptor> list = new ArrayList<>();//前置通知AspectJMethodBeforeAdvice aspectJMethodBeforeAdvice = new AspectJMethodBeforeAdvice(pointcut, beforeTest, aop);MethodBeforeAdviceInterceptor methodBeforeAdviceInterceptor = new MethodBeforeAdviceInterceptor(aspectJMethodBeforeAdvice);list.add(methodBeforeAdviceInterceptor);//后置通知list.add(new AspectJAfterAdvice(pointcut, afterTest, aop));//返回通知AspectJAfterReturningAdvice aspectJAfterReturningAdvice = new AspectJAfterReturningAdvice(pointcut, afterReturnTest, aop);list.add(new AfterReturningAdviceInterceptor(aspectJAfterReturningAdvice));ProxyFactory proxyFactory = new ProxyFactory(AopBean.class, ap, method);proxyFactory.addAdvisors(list);AopBean proxy = (AopBean) proxyFactory.getProxy();proxy.test();
从结果来看,是符合预期的,A君 打算先给 老大 看下,毕竟 老大 有自己的想法,不是个好糊弄的主。今天先到这里,A君 上传代码,准备下班咯
总结
正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)
相关文章:

第十四章 Spring之假如让你来写AOP——雏形篇
Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…...

群控系统服务端开发模式-应用开发-前端个人资料开发
一、总结 其实程序开发到现在,简单的后端框架就只剩下获取登录账号信息及获取登录账号菜单这两个功能咯。详细见下图: 1、未登录时总业务流程图 2、登录后总业务流程图 二、获取登录账号信息对接 在根目录下src文件夹下store文件夹下modules文件夹下的us…...
动态规划技巧点
动规五部曲(来自b站卡哥):1、确定DP数组中i、j…的含义。2、确定DP推导式。3、DP数组初始化。4、DP数组遍历顺序。5、DP数组打印校验。 父问题、子问题有些许区别:LeetCode343.整数拆分 今天在哔哩哔哩上刷到了一个非常有意思的l…...

深度学习之pytorch常见的学习率绘制
文章目录 0. Scope1. StepLR2. MultiStepLR3. ExponentialLR4. CosineAnnealingLR5. ReduceLROnPlateau6. CyclicLR7. OneCycleLR小结参考文献 https://blog.csdn.net/coldasice342/article/details/143435848 0. Scope 在深度学习中,学习率(Learning R…...

Spring Boot集成SQL Server快速入门Demo
1.什么是SQL Server? SQL Server是由Microsoft开发和推广的以客户/服务器(c/s)模式访问、使用Transact-SQL语言的关系数据库管理系统(DBMS),它最初是由Microsoft、Sybase和Ashton-Tate三家公司共同开发的&…...

低代码牵手 AI 接口:开启智能化开发新征程
一、低代码与 AI 接口的结合趋势 低代码开发平台近年来在软件开发领域迅速崛起。随着企业数字化转型的需求不断增长,低代码开发平台以其快速构建应用程序的优势,满足了企业对高效开发的需求。例如,启效云低代码平台通过范式化和高颗粒度的可配…...

【已解决】git push一直提示输入用户名及密码、fatal: Could not read from remote repository的问题
问题描述: 在实操中,git push代码到github上一直提示输入用户名及密码,并且跳出的输入框输入用户名和密码后,报错找不到远程仓库 实际解决中,发现我环境有两个问题解决: git push一直提示输入用户名及密码…...
python语言基础-4 常用模块-4.13 其他模块
声明:本内容非盈利性质,也不支持任何组织或个人将其用作盈利用途。本内容来源于参考书或网站,会尽量附上原文链接,并鼓励大家看原文。侵删。 4.13 其他模块 除此之外python中还有大量的功能模块,如: pill…...
微信小程序=》基础=》常见问题=》性能总结
文章目录 微信小程序开发应用 实例小程序生命周期 以及 各生命周期应用实例小程序图片 展示方案 小程序打包应用方案技术细节(分包应用实例)技术细节(压缩处理)一、准备工作二、JavaScript 代码压缩三、WXML 文件优化(…...
JWT深度解析:Java Web中的安全传输与身份验证
标题:JWT深度解析:Java Web中的安全传输与身份验证 引言 JSON Web Token(JWT)是一种轻量级的身份验证和授权标准,它允许在各方之间安全地传输信息。在Java Web开发中,JWT因其无状态、可扩展性和跨域支持而…...

使用Java爬虫获取商品订单详情:从API到数据存储
在电子商务日益发展的今天,获取商品订单详情成为了许多开发者和数据分析师的需求。无论是为了分析用户行为,还是为了优化库存管理,订单数据的获取都是至关重要的。本文将详细介绍如何使用Java编写爬虫,通过API获取商品订单详情&am…...
Mybatis中批量插入foreach优化
数据库批量入库方常见方式:Java中foreach和xml中使用foreach 两者的区别: 通过Java的foreach循环批量插入: 当我们在Java通过foreach循环插入的时候,是一条一条sql执行然后将事物统一交给spring的事物来管理(Transa…...

Word VBA如何间隔选中多个(非连续)段落
实例需求:Word文档中的有多个段落,段落总数量不确定,现在需要先选中所有基数段落,即:段落1,段落3 … ,然后一次性设置粗体格式。 也许有的读者会认为这个无厘头的需求,循环遍历遍历文…...
Linux系统常用操作与命令指南
一、快捷分类 1、移动光标 h, j, k, l 左, 下, 上, 右 Ctrl-F:下翻一页 Ctrl-B:上翻一页 Ctrl-U:上翻半页 Ctrl-d:下翻半页 0:跳至行首,不管有无缩进,就是跳到第0个字…...

StructuredStreaming (一)
一、sparkStreaming的不足 1.基于微批,延迟高不能做到真正的实时 2.DStream基于RDD,不直接支持SQL 3.流批处理的API应用层不统一,(流用的DStream-底层是RDD,批用的DF/DS/RDD) 4.不支持EventTime事件时间(一般流处理都会有两个时间:事件发生的事件&am…...

由播客转向个人定制的音频频道(1)平台搭建
项目的背景 最近开始听喜马拉雅播客的内容,但是发现许多不方便的地方。 休息的时候收听喜马拉雅,但是还需要不断地选择喜马拉雅的内容,比较麻烦,而且黑灯操作反而伤眼睛。 喜马拉雅为代表的播客平台都是VOD 形式的࿰…...
[自然语言处理] [AI]深入理解语言与情感分类:从基础到深度学习的进展
语言是人类智能的核心组成部分,具有极高的复杂性和多样性。理解语言,尤其是语言中的隐含部分,向来是人工智能研究的一个巨大挑战。图灵测试本身便是一场关于语言生成与理解的比赛,旨在检验机器是否能够模拟人类的语言能力。随着深度学习的飞速发展,语音识别、情感分析等自…...

【GPTs】Gif-PT:DALL·E制作创意动图与精灵动画
博客主页: [小ᶻZ࿆] 本文专栏: AIGC | GPTs应用实例 文章目录 💯GPTs指令💯前言💯Gif-PT主要功能适用场景优点缺点 💯小结 💯GPTs指令 中文翻译: 使用Dalle生成用户请求的精灵图动画&#…...
云原生周刊:Istio 1.24.0 正式发布
云原生周刊:Istio 1.24.0 正式发布 开源项目推荐 Kopf Kopf 是一个简洁高效的 Python 框架,只需几行代码即可编写 Kubernetes Operator。Kubernetes(K8s)作为强大的容器编排系统,虽自带命令行工具(kubec…...

Linux设置jar包开机启动
操作系统环境:CentOS 7 【需要 root 权限,使用 root 用户进行操作 或 普通用户使用 sudo 进行操作】 一、系统服务的方式 原理:利用系统服务管理应用程序的生命周期, systemctl 为系统服务管理工具 systemctl start applicati…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...

02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...