第十四章 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君 按照之前的思路走:

  1. 哪些地方可以增强?
  2. 如何找到这些地方?
  3. 找到后要怎么增强?
  4. 多个增强如何管理?
  5. 如何对其进行增强?
哪些地方可以增强?

    目标既然已经确定,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 AdviceAfter (Finally) AdviceAround 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 AdviceAfter (Finally) Advice 这类通知需要对方法进行更精细的控制,所以需要实现 MethodInterceptor,而不是直接实现 AdviceA君 先对 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君 上传代码,准备下班咯

在这里插入图片描述

总结

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

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

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

相关文章

搜维尔科技:Haption力触觉交互,虚拟机械装配验证

Haption力触觉交互&#xff0c;虚拟机械装配验证 搜维尔科技&#xff1a;Haption力触觉交互&#xff0c;虚拟机械装配验证

微信小程序——实现二维码扫描功能(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

PostgreSQL 页损坏如何修复

PostgreSQL 错误&#xff1a;关系 base/46501/52712 中的块 480 存在无效的页。 当我们在使用 PostgreSQL 数据库的时候&#xff0c;如果服务器发生 CRASH 或者断电等异常情况的时候&#xff0c;有可能会遇到上面的这个报错信息。那么我们如何去修复这个数据呢&#xff0c;以及…

接口类和抽象类在设计模式中的一些应用

C设计模式中&#xff0c;有些模式需要使用接口类&#xff08;Interface Class&#xff09;和抽象类&#xff08;Abstract Class&#xff09;来实现特定的设计目标。以下是一些常见的设计模式及其需要的原因&#xff0c;并附上相应的代码片段。 1. 策略模式&#xff08;Strateg…

速盾:高防 CDN 的缓存机制是什么?

高防 CDN&#xff08;Content Delivery Network&#xff0c;内容分发网络&#xff09;的缓存机制是其保障网络安全和提升性能的关键所在。以下详细介绍高防 CDN 的缓存机制。 一、缓存内容的类型 &#xff08;一&#xff09;静态内容缓存 和普通 CDN 一样&#xff0c;高防 CD…

linux rocky 9.4部署和管理docker harbor私有源

文章目录 Harbor简介安装Harbor技术细节1.安装系统(略),设置主机名和IP2.安装docker3.安装docker-compose4.安装Harbor私有源仓库5 测试登录1.本机登录2.客户端登录Harbor服务器配置docker源1. 下载镜像2.把镜像上传到Harbor私有仓库源3.客户端下载镜像,并且启动容器linux …

【Elasticsearch入门到落地】1、初识Elasticsearch

一、什么是Elasticsearch Elasticsearch&#xff08;简称ES&#xff09;是一款非常强大的开源搜索引擎&#xff0c;可以帮助我们从海量数据中快速找到需要的内容。它使用Java编写&#xff0c;基于Apache Lucene来构建索引和提供搜索功能&#xff0c;是一个分布式、可扩展、近实…

【算法一周目】双指针(2)

目录 有效三角形的个数 解题思路 C代码实现 和为s的两个数字 解题思路 C代码实现 三数之和 解题思路 C代码实现 四数之和 解题思路 C代码实现 有效三角形的个数 题目链接&#xff1a;611. 有效三角形的个数题目描述&#xff1a;给定一个包含非负整数的数组nums&…

单体架构 IM 系统之 Server 节点状态化分析

基于 http 短轮询模式的单体架构的 IM 系统见下图&#xff0c;即客户端通过 http 周期性地轮询访问 server 实现消息的即时通讯&#xff0c;也就是我们前面提到的 “信箱模型”。“信箱模型” 虽然实现非常容易&#xff0c;但是消息的实时性不高。 我们在上一篇文章&#xff08…

《线程池(Thread Pool):高效多线程处理的核心机制》

在当今的软件开发领域&#xff0c;多线程编程已经成为提升程序性能和响应能力的关键技术。而线程池&#xff08;thread pool&#xff09;作为多线程处理中的一个重要概念&#xff0c;正发挥着不可或缺的作用。今天&#xff0c;我们就来深入探讨一下什么是线程池&#xff0c;以及…

第二节 OSI-物理层

三种数据传输形式&#xff1a; 第一代&#xff1a;电路交换&#xff1a;共同维护逻辑的一条链路-仅仅供两端设备之间访问&#xff0c;如果链路未释放&#xff0c;其他终端仍然无法使用链路 第二代&#xff1a;报文交换&#xff1a;在传输数据外面&#xff0c;添加发送者的信息…

一种基于深度学习的反无人机无人值守系统及方法

技术领域 本发明涉及光电技术领域,尤其涉及采用综合性技术防范无人机。 背景技术 近年来,“低小慢”等无人航空器迅猛发展,其“获取容易、使用隐蔽、升空突然、处置困难”的特点,易被敌对势力利用,造成失控肇事、干扰航管、窥视窃密、恐怖袭击、空防作战等危害,对党政军机…

让AI为你发声!Windows电脑快速部署ChatTTS文本转语音神器

文章目录 前言1. 下载运行ChatTTS模型2. 安装Cpolar工具3. 实现公网访问4. 配置ChatTTS固定公网地址 前言 嘿&#xff0c;朋友们&#xff01;今天我们来聊聊如何在Windows系统上快速搭建ChatTTS&#xff0c;一个超酷的开源文本转语音项目。更棒的是&#xff0c;我们还可以用Cp…

基于迭代重加权最小二乘法的算法及例程

基于迭代重加权最小二乘法&#xff08;Iteratively Reweighted Least Squares&#xff0c;IRLS&#xff09;是一种用于解决具有异方差误差结构的最小二乘问题的迭代优化算法。 IRLS 算法的思想是通过迭代的方式&#xff0c;逐步调整权重并解决加权最小二乘问题&#xff0c;以得…

RSTP的配置

RSTP相对于STP在端口角色、端口状态、配置BPDU格式、配置BPDU的处理方式、快速收敛机制、拓扑变更机制和4种保护特性方面的详细改进说明&#xff1a; 端口角色&#xff1a; STP中定义了三种端口角色&#xff1a;根端口&#xff08;Root Port&#xff09;、指定端口&#xff0…

elementui el-table中给表头 el-table-column 加一个鼠标移入提示说明

前言 在使用el-table 表格中有些表格的表头需要加入一些提示&#xff0c;鼠标移入则出现提示&#xff0c;非常实用&#xff0c;我是通过el-table中的el-tooltip实现的&#xff0c;以下的效果预览 代码实现 <el-table ref"multipleTable" :data"data"…

ubuntu18.04 安装与卸载NCCL conda环境安装PaddlePaddle

cuda版本11.2 说明PaddlePaddle需要安装NCCL 1、Log in | NVIDIA Developer 登录官网 找到对应版本 官方提供了多种安装方式&#xff0c;本文使用Local installers (x86)本地安装 点击对应的版本下载如&#xff1a; nccl-local-repo-ubuntu1804-2.8.4-cuda11.2_1.0-1_amd6…

vue 选项式|组合式 v2.x 是不支持的

1.查看vue 版本 在main.js 中 console.log("vue版本&#xff1a;"Vue.version); 如果你是单文件组件 &#xff08;.vue结尾且为vue3.X&#xff09; <script setup> // setup 是一个特殊的钩子&#xff0c;专门用于组合式 API。import { ref } from vue…

机器学习—决定下一步做什么

现在已经看到了很多不同的学习算法&#xff0c;包括线性回归、逻辑回归甚至深度学习或神经网络。 关于如何构建机器学习系统的一些建议 假设你已经实现了正则化线性回归来预测房价&#xff0c;所以你有通常的学习算法的成本函数平方误差加上这个正则化项&#xff0c;但是如果…

【Rust中的项目管理】

Rust中的项目管理 前言Package&#xff0c;Crate&#xff0c;Module &use &#xff0c;Path通过代码示例解释 Crate&#xff0c;Module &#xff0c;use&#xff0c;Path创建一个package&#xff1a;代码组织化skin.rs 中的代码struct & enum 相对路径和绝对路径引用同…