Spring之AOP编程

一.静态代理设计模式

1.为什么需要代理设计模式?

在JavaEE开发中,哪个层次最为重要?

DAO层->Service层->Controller层。最重要的是Service层

Service层包含了哪些代码?

1.核心功能:业务运算+DAO调用

2.额外功能/附加功能:事务、日志、性能……

额外功能书写在Service层中好不好?

从Service层调用者(controller)来看,这是很好的,应当书写额外功能

从软件设计者的角度来看:Service层不需要额外功能

所以就要引入代理类,他要做两件事:第一,增加额外功能,第二,调用原始类(目标类)的功能

这就好像租房,房东要出租房屋,他的核心功能就是签合同收房租,而他的额外功能就是发布广告。但是房东嫌发广告看房很麻烦,不想要这样的额外功能。于是就有了中介,中介就是进行发广告,带房客看房的任务。这里面,房东就是软件设计者,房客就是controler,中介就是代理

2.代理设计模式

概念

通过代理类,为原始类增加额外功能

好处:利于原始类的维护

代理类开发的核心要素

代理类=目标类+额外功能+与原始类实现相同的接口

1.代理类中要有目标类,这是因为代理还要调用目标类的原始方法(就是核心功能)

2.为什么要实现相同的接口?

首先,原始类一般都是某个接口的实现,就比如UserServiceImpl是要实现UserService接口中的两个方法。其次,中介的方法名要和原始类的一致,这样才能迷惑调用者,就比如房客只是想买房,所以房东和中介的方法名称都是卖房。所以要实现相同的接口,从而实现相同的抽象方法

3.静态代理开发步骤

创建一个proxy包,创建UserService接口,创建UserServiceImpl原始类,创建UserServiceProxy代理类,然后原始类和代理类都implementsUserService接口,实现register和login接口。主要代理类中要有原始类对象,并在额外功能的书写前或后去调原始方法

仔细看代理类,打印的proxy log就代表了日志这个额外功能,最后还要调用原始类的核心功能(也就是业务运算和dao调用)

最后进行测试:

4.缺点

静态代理的文件数量过多,不利于项目管理,每有一个原始类,就得创建一个代理类,如果有100个原始类都需要日志功能,就需要100个代理类,但都是完成日志功能

对于额外功能的维护性差,并且有耦合

二.Spring动态代理开发

1.概念

通过代理类,为原始类添加额外功能

2.搭建开发环境

引入一下三个jar包

注意把这个runtime删除掉

3.开发步骤

创建原始对象

我们就用userservice,在配置文件中进行配置:

提供额外功能

Spring提供了接口:MethodBeforeAdvice

我们就把额外功能写到此接口中

创建一个dynamic包,创建一个Before类,实现该接口并实现其中的before抽象方法:

注意看这个befor方法的参数,第一个参数method代表额外功能所增加给的那个原始方法;第二个参数表示原始方法的参数,第三个参数表示原始类的实例。这些参数提供了但是不一定要使用,就好像Servlet中的request和response提供了但不一定使用

下面我们就书写额外功能并进行文件配置:

注意:这个类是为了提供额外功能,但我可没说要实现原始方法!!!

文件配置:

定义切入点

切入点就是额外功能增加到位置

目的:由程序员根据自己的需要,决定额外功能加到哪里

简单测试:把所有方法都作为切入点,都加额外功能。

配置如下:

<aop:config>标签是告诉Spring哪些方法需要干什么(这个哪些方法与干什么都是在中间的aop标签中定义)

<aop:pointcut>标签是说叫加切入点,其中的expression就是切入点表达式,告诉了Spring哪些方法要作为切入点

组装

将额外功能与切入点进行组装

也就是第2第3步的整合

<aop:advisor>标签,就可以将额外功能与切入点整合。advice-ref就代表了额外功能的实现类(advice表示建议,可以抽象成额外功能,ref表示一个实例对象);pointcut-ref就代表了切入点。

调用

目的:获得Spring工厂创建的动态代理对象并进行调用

如上,使用userService获得的是代理对象(为什么可以用UserService接收?因为代理和实现类都实现了同一个接口),并且在调用原始方法之前调用了Before类的before方法。

debug一下:

看,userService对象的类型是proxy是代理

4.细节分析

Spring创建的动态代理类在哪里?

Spring框架在运行时,通过动态字节码技术,在JVM中创建的,运行在JVM内部,等程序结束后会和JVM一起销毁

什么是动态字节码技术?

以前:java运行一个类,实际上是jvm运行该类的字节码(该类的源文件.java文件编译后就生成了.class字节码文件)

现在的动态字节码技术:由于我们没有手动写代码,也就是没有写.java源文件,所以就没有编译生成.class文件的过程。而是第三方动态字节码框架直接在JVM中生成字节码(这就是生成的动态字节码,对应动态代理类)。

也就是通过第三方动态字节码框架,在jvm中创建对应的类的字节码,进而创建对象,当虚拟机结束时,动态字节码就会跟着消失。

所以说动态代理不需要定义类文件,都是JVM在运行时动态创建的,这就解决了静态代理中静态类文件过多的问题

动态代理编程简化代理的开发

比如现在又想给orderService的方法加上日志功能,你会发现好像只需要创建原始对象即可(就是在配置文件中添加一个bean组件)。而日志额外功能已经在Before类中写过了,切入点也定义好了(就是给所有方法都提供额外功能),额外功能和切入点也已经进行了组装。

所以说,只要加的额外功能一样,在创建其他类的代理对象时,只需要解决原始对象的创建即可

三.动态代理详解

AOP编程,Spring动态代理开发的四步:

1.创建原始对象 2.提供额外功能 3.定义切入点 4.组装

1.额外功能详解

MethodBeforeAdvice

这个方法的参数在上面已经讲解过。

MethodBeforeAdvice的作用:使额外功能运行在原始方法之前(只能是原始方法的前面)

MethodInterceptor(方法拦截器)

它也是为额外功能的定义进行服务的一个接口,只不过,它能使得额外功能既可以运行在原始方法之前,又能运行在原始方法之后,还能前后都运行,也能在原始方法抛出异常时运行。

我们下面就详细看看它的使用:

首先在dynamic包中创建一个Around类并实现MethodInterceptor接口

注意是第一个包

1.invoke方法的作用:书写额外功能

2.要确定原始方法怎么运行:看参数MethodInvocation,它代表的就是额外功能所增加给的那个原始方法。invocation.proceed()就是让原始方法运行

3.返回值:就是原始方法的返回值

如下:

配置文件

注意一定要把之前写的before这个额外功能类给注释掉,然后将advice-ref改成around。

额外功能在原始方法抛异常时:

methodInterceptor可影响原始方法的返回值,就是接收到ret后可进行再加工

2.切入点表达式详解

对于切入点的定义,暴扣哦了切入点函数(比如execution)和切入点表达式(比如* *(..))。我们先来介绍切入点表达式

方法切入点表达式

我们知道一个方法由五部分组成:public void修饰符、返回值、方法名、参数、异常

之前说了* *(..)代表了所有函数。因为第一个通配符*表示了对方法的修饰符和返回值没有要求,第二个*表示对方法的名称没有要求,括号里两点表示对方法的参数没有要求,所以就代表了一切方法。

那么如何以方法为切入点书写表达式?

1.若只对对方法名有要求

只要经第二个*替换成具体的方法名称即可

比如对login方法定义切入点,则* login(..)

2.若对方法名和参数有要求

再将..替换成参数类型,如下:

* login(String,String)表示参数类型为两个String的login方法

* login(String,..)表示第一个参数必须是String类型,第二个及以后有没有都可以,有几个、参数类型是啥,都没要求

* login(..,String,..)表示这个方法里面只要有String类型的参数就可以,它放在哪里有几个无所谓

类切入点表达式

若指定就给这个类的所有方法加额外功能,不指定包,则如下:

* *.UserServiceImpl.*(..)但注意,这样写的话,第二个*只代表一层包

* *..UserServiceImpl.*(..)这样写,就代表了多级包,或者一层包,或者多级,注意,不能代表没有包。

* UserServiceImpl.*(..)这样才是代表没有包

包切入点表达式

语法一:* basic.convertor.*.*(..)表示在basic包下的子包convertor中的所有类。但是,不能包含convertor包的子包中的类

语法二:* basic.convertor..*.*(..)表示在basic包的子包convertor及其子包中的所有类。这次就包含了convertor包的子包以及子包的子包……

包切入点表达式的实战价值更高,可以将像压迫添加相同额外功能的类都放到一个包下面

以上三种表达式可以搭配使用

3.切入点函数详解

切入点函数的作用:用于执行切入点表达式

execution()

最重要,功能最全面,可执行方法切入点表达式,类切入点表达式,包切入点表达式……

args()

用于函数/方法参数的匹配

例如方法参数必须是String类型,则:

execution(* *(String,String))或者args(String,String)

within()

用于进行类/包切入点表达式的匹配

例如指定类:

execution(* *..Person.*(..))或者within(*..Person)

例如指定包:

execution(* basic..*.*(..))或者within(basic..*)

@annotation

作用:为具有特殊注解的方法定义切入点

下面我们来给UserServiceImpl定义一个注解。首先要自定义注解

选择Annocation

然后加一些注解:

首先这个Target注解的可选值代表了这个注解要用在哪里,用在类上就选Type,用在方法上就选Method

这个注解代表了这个自定义注解要在什么时候用,一般都选Runtime。

然后我们在login方法上面加上这个@Log注解

最后在配置文件中进行配置@annocation()内部就是注解的全限定名

效果如上

切入点函数的逻辑运算

指的是整合多个切入点函数一起配合工作,进而完成更复杂的需求

1.and操作

示例:方法为login,并且参数是两个字符串

原来是execution(* login(String,String)),现在是execution(* login(..)) and args(String,String)

注意:与操作不能用于同种类型的切入点函数

案例:register和login都作为切入点

难道是execution(* register(..)) and execution(* login(..))吗?显然不是,这表示方法名既是register,又是login。这该怎么解决?用下面的或操作

2.or操作

案例:register和login都作为切入点

execution(* register(..)) or execution(* login(..))

四.AOP编程

1.概念

OOP(Object Oriented Programing)面向对象编程

以对象为基本单位的程序开发,通过对象间的协调和相互调用,完成程序的创建

POP(Producer Oriented Programing)面向过程编程

以过程(方法、函数)为基本单位的程序开发,通过过程间的协调和相互调用,完成程序的创建

AOP(Aspect Oriented Programing)面向切面编程

以切面为基本单位的程序开发,通过切面间的协调和相互调用,完成程序的创建

切面=切入点+额外功能;而在Spring配置文件中,就有切入点与额外功能进行整合这一步,即<aop:advisor advice-ref="before" pointcut-ref="pc">。

所以说:Spring动态代理开发就是面向切面编程,即AOP编程

或者说:AOP编程的本质就是Spring动态代理开发,通过代理类为原始类增加额外功能

2.AOP编程的开发步骤

既然AOP编程的本质就是Spring动态代理开发,那么两者的开发步骤就是相同的

1.提供原始对象

2.引入额外功能

3.定义切入点

4.组装切面

五.AOP底层实现原理

1.核心问题

1.AOP如何创建动态代理类?

2.Spring工厂如何加工创建代理对象?

2.动态代理类的创建

JDK的动态代理

创建一个包JDK,创建JDKProxy类,如下:

首先要明确代理类创建的三要素:1.提供原始对象 2.编写额外功能 3.实现相同的接口

我们还是使用UserServiceImpl作为原始对象,然后就是创建动态代理,如何创建呢?Spring提供了一个Proxy类,其中有个newProxyInstance方法,就是在创建代理对象

注意这个方法的三个参数。

先看InvocationHandler,它对应的就是额外功能。它是一个接口,需要我们实现其中的方法,这就可以用到内部类。如下:

要实现invoke方法,里面的proxy参数代表的是代理对象,这个可以忽略;method代表了额外功能所增加给的那个原始方法,args则是原始方法的参数列表。在invoke方法中,我们既要实现额外功能,又要调用原始方法。一个方法的调用必须要有对象和参数,所以在调用原始方法时,就要传入对象和参数。对象其实就是我们上面new出来的UserServiceImpl,而参数就是args。所以这个内部类可写为:

接着来解决newInstance方法中的interfaces,它对应的就是原始类所实现的所有接口,这就是要让代理类实现与原始类相同的接口,所以如下:

最后来看ClassLoader,怎么会有类加载器呢?类加载器的作用就是把对应雷达字节码文件加载到JVM中,再通过字节码文件创建class对象,进而创建这个类的对象。也就是说,要想创建对象,就要到jvm中,所以要将.class文件加载到jvm中并创建对象,这就是classLoader的工作。有了class对象才能new userServiceproxy()

那么如何获得类加载器呢?一般来说,JVM会为每一个类的.class文件自动分配一个类加载器。而对于动态代理,动态字节码技术会直接将字节码写在jvm中,所以就没有了加载对象这一步。那么门钥匙真相将对象从jvm中new出来,就必须要有一个类加载器。

这时就可以借一个类加载器,借哪个类的都可以。

分析到这里,我们就可以着手写代码了。如下:

就是借哪个类的类加载器都可以

这就创建出来UserServiceImpl的代理对象,然后调用login和register方法即可

CGlib的动态代理

与JDK创建动态代理不同,CGlib创建动态代理是为没有实现任何接口的原始类创建代理类。那没有实现共同的接口,怎么去实现共同的方法呢?可以让代理类继承原始类,通过super.login去调用原始方法。如下:

创建一个cglib包,创建一个不实现接口的orderService类

再创建CglibProxy类,继承OrderService类

如上,创建CGlib动态代理的关键类就是Enhancer类。和JDK创建动态代理相同的是都需要classLoader和额外功能,但是不同的是,CGlib不需要接口,而是需要设置父类。

现在我们类仔细看一下额外功能的提供:同样是要实现一个接口,这个接口是MethodInterceptor接口,但是和之前Spring动态代理开发实现的不是一个包中的MethodInterceptor,并且其中的抽象方法也不同。在Spring动态代理开发中,那个抽象方法是invoke方法,参数只有一个是MethodInvocation,通过调用invocation.proceed()就可以使原始方法运行。而在这里,抽象方法是intercept,参数很多,其中,method代表原始方法,objects代表参数,methodProxy就是代理对象。那么如何让原始方法被调用呢?这个调用方式就和JDK动态代理中的handler类似,也是调用method.invoke,传入对象和参数列表

3.Spring工厂如何加工原始对象从而获得代理对象?

回顾beanPostProcessor接口,他就是对对象进行加工的。

AOP编程,通过userService这个id获得的是原始对象。但要想获得代理对象,就需要BeanPostProcessor进行加工,将原始对象加工成代理对象再返回

开发步骤

创建一个Factory包,创建UserService接口以及Impl实现类,创建一个ProxyBeanPostProcessor类,实现BeanPostProcessor接口

最后进行文件配置

测试一下:

调试一下也会发现用id值获得的是代理类对象

六.基于注解的AOP编程

1.开发步骤

还是和AOP编程以及Spring动态代理开发一样

创建原始对象,提供额外功能,第一切入点,组装切面

首先,创建aspect包,并创建原始对象以及接口,进行文件配置:

然后创建Myaspect这个切面类,并且要加一些注解:

如上,@Aspect注解就告诉了Spring这是一个自定义的切面类。然后下面写一个around方法,再加一个@Around注解,此时这个around方法就等同于invoke方法了。

那么这个方法的参数是什么?是ProceedingJoinPoint这个接口,通过这个类中的proceed方法就可以调用原始方法。因为proceed方法有返回值,就是原始方法的返回值,所以我们自定义的around方法也要有返回值:

下面一步就是定义切入点,其实就是在@Around注解内部加上切入点表达式

注意要加双引号

最后进行文件配置

最后还要告诉Spring现在要基于注解进行AOP编程:

效果如下:

2.细节分析

切入点复用

现在已经给login方法加了日志功能,但还想给它加一个事务功能,就还得有一个around方法,还得重复上面的一些代码

我们会发现,这两个额外功能的切入点是相同的,所以我们可以进行如下操作从而对切入点进行复用:

也就是自定义一个函数,该函数必须是public void 无参 无实现,然后加上@Pointcut注解,里面填入切入点表达式。

动态代理的创建方式

debug看userService会发现,它是那个动态代理,但在默认情况下的AOP编程的底层是用的JDK的动态代理创建方式。那如何切换CGlib的鼎泰代理创建方式?

如上,再加一个属性即可

那再回顾传统的aop开发,就是使用了aop:config标签的Spring动态代理开发,它如何切换方式呢?也是加这样的属性,如下:

七.AOP开发中的坑

我们将Aspect类中的切入点编程UserServiceImpl这个类切入点,这样,login方法和register方法就都有额外功能了,然后我们在REgister方法内部调用login方法,如下:

然后再进行测试

发现第二个login方法没有被加上额外功能

这是因为我们register中调用的这个login方法式原始对象的,但真正的设计目的是调用代理对象的login方法。所以关键就是在于在原始类中获得代理对象。那如何获得?难道再创建工厂再getBean?

不行!!!因为Spring工厂是重量级资源,一个应用中应该只有一个工厂,所以我们要拿到测试类中的工厂。如何做到?

让UserServiceImpl事项ApplicationContextAware接口,如下:

然后修改register方法:

这次就都加上额外功能了

总结:

在同一个业务类中,进行业务方法间的相互调用,只有最外层方法是加了额外功能的,方法内部通过普通方式调用,都调用的是原始方法。如果想让内层方法也加入额外功能,就要实现ApplicationContextAware接口,拿到测试类中的工厂,从而创建代理对象

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

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

相关文章

必应搜索广告与谷歌搜索广告对比那个更好?

搜索引擎广告作为企业获取潜在客户的重要渠道之一&#xff0c;其效果直接关系到营销策略的成功与否。两大搜索引擎巨头——谷歌&#xff08;Google&#xff09;和必应&#xff08;Bing&#xff09;各自提供了广告平台&#xff0c;即谷歌广告&#xff08;Google Ads&#xff09;…

MSR是个什么寄存器

MSR 这种寄存器专门用于调试、程序执行跟踪、计算机性能监控、简化软件编程、电源控制等等各种实验性功能。 什么是 MSR MSR 的概念是不易理解&#xff0c;所以这一节只说一些 MSR 的外在&#xff0c;比如形容和指令等&#xff0c;然后展开说说&#xff0c;看完整篇文章你应该…

向媒体投稿有了好方法财政单位信息宣传工作简单又轻松

当我初涉财政单位的信息宣传岗位,肩负起对外展示单位风采、传播政策信息的重要职责时,我深刻体验到了投稿之路的艰辛曲折。初期,对于如何有效对接媒体并成功发表稿件,我感到一片茫然,仿佛置身于浩瀚的信息海洋中,无从下手。 那时,我的工作日常就是广泛搜集各类媒体的联系方式,特…

稀碎从零算法笔记Day56-LeetCode:组合总和 Ⅳ

题型&#xff1a;DP、数组 链接&#xff1a;377. 组合总和 Ⅳ - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给你一个由 不同 整数组成的数组 nums &#xff0c;和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的…

数据结构系列-堆排序

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 昨天我们实现的堆的搭建&#xff0c;我们今天实现以下堆的排序&#xff0c; 堆的排序的最大的优点就是提高的效率&#xff0c;减小了时间复杂度&#xff0c;在这个里面我们有一个…

C++ 并发编程指南(11)原子操作 | 11.5、内存模型

文章目录 一、C 内存模型1、为什么需要内存模型&#xff1f;2、happens-before和synchronize-with两个关键概念2.1、happens-before2.2、synchronize-with2.3、总结 前言 C 11标准中最重要的特性之一&#xff0c;是大多数程序员都不会关注的东西。它并不是新的语法特性&#xf…

系统思考—业务复盘

今日的JSTO——《业务复盘》中&#xff0c;赵海懿老师的分享启发了我深度反思。她提到的两句话特别引人思考&#xff1a; 1、学校里学到的最重要的东西&#xff0c;就是“最重要的东西在学校里学不到”。 2、学习型组织不只是组织学习。 这些话提醒我们&#xff0c;真正的学习…

区块链钱包开发指南: 探究区块链钱包开发涉及

区块链钱包是连接用户与区块链网络的重要工具&#xff0c;它们不仅提供了安全的存储和管理数字资产的功能&#xff0c;还允许用户进行交易和与区块链上的智能合约进行互动。本文将探究区块链钱包开发涉及的关键方面和技术要点。 1. 区块链钱包类型 区块链钱包可以分为以下几种…

Unity中的UI系统之UGUI

目录 概述UGUI基础——六大基础组件六大基础组件概述Canvas画布组件CanvasScaler画布缩放控制器组件必备知识恒定像素模式缩放模式恒定物理模式3D模式 Graphic Raycaster图形射线投射器EventSystem和Standalone Input ModuleRectTransform UGUI基础——三大基础控件Image图像控…

JS - 以工厂模式和原型模式方式建造对象、JS的垃级回收机制、数组的使用

创建对象的方式 使用工厂方法来建造对象 在JS中我们可以通过以下方式进行创建对象&#xff1a; var obj {name:"孙悟空",age:18,gender:"男",sayName:function(){alert(this.name);}};var obj2 {name:"猪八戒",age:28,gender:"男",…

【第4讲】XTuner 微调 LLM:1.8B、多模态、Agent

目录 1 简介2 基础知识2.1 finetune简介2.2 xtuner简介2.2.1 技术架构2.2.2 快速上手xtuner 2.3 8GB显存玩转LLM&#xff08;intern1.8b&#xff09;2.3.1 flash attention vs deepspeed zero2.3.2 相关版本更新和使用 2.4 多模态LLM2.4.1 多模态LLaVA基本原理简介2.4.2 快速上…

Linux的学习之路:18、进程间通信(2)

摘要 本章主要是说一下命名管道和共享内存 目录 摘要 一、命名管道 1、创建一个命名管道 2、匿名管道与命名管道的区别 3、命名管道的打开规则 4、代码实现 二、system V共享内存 1、共享内存 2、共享内存函数 三、代码 四、思维导图 一、命名管道 1、创建一个命…

24年做抖店,如何快速脱离“商家新手期”?

我是王路飞。 这个“新手期”不是你们理解的那种店铺新手期&#xff0c;现在抖店没有新手期这一说了。 主要说的是从商家角度来说&#xff0c;如何在最短时间内从一个没有电商经验的新手&#xff0c;蜕变成一个有经验、有流程、有操作、甚至有一定数据的“老玩家”&#xff1…

CentOS7配置固定ip

一、打开配置文件 vi /etc/sysconfig/network-scripts/ifcfg-ens33 二、更改配置文件的参数 将BOOTPROTO的属性值改为static 或者是直接注销原来的重新写更改为静态的 三、在配置文件中设置ip地址和网关 1、IP地址的前三段需要和主机的 VMnet8 网卡的ip保持一致&#xff08;主…

js 打印网页时没有背景色,window.print打印背景色丢失

页面效果 打印效果 需要在打印的容器里增加下面代码 /*webkit 为Google Chrome、Safari等浏览器内核*/ -webkit-print-color-adjust: exact; /*解决火狐浏览器打印*/ print-color-adjust: exact; color-adjust: exact; 完整写法 我为了方便直接写*&#xff0c;这样所有元素都…

IDEA 2024.1 配置 AspectJ环境

最近Java课设在学习AspectJ&#xff0c;做PPT顺便写一个博客 下载包 首先去AspectJ官网下载一个JAR包并安装 安装完最后可以按照他的建议配置一下 然后找到AspectJ的安装位置的lib目录&#xff0c;把三个包拷到自己项目中的lib目录下 由于最新版的IDEA已经不支持AspectJ了 所…

【Linux】在ubuntu快速搭建部署K8S(1.27)集群

ubuntu快速安装K8s1.27 &#xff08;一&#xff09;环境说明1.硬件环境2.Ubuntu环境设置 &#xff08;二&#xff09;安装配置containerd1.安装2.配置3.启动 &#xff08;三&#xff09;所有节点操作1.安装runc和cni2.节点系统设置、关闭临时分区3.修改内核参数4.安装 kubeadm、…

新手小白,在数学建模的过程中应该怎么分工?

大家知道&#xff0c;数学建模竞赛是需要一个团队的三个人在三天或四天的时间内&#xff0c;完成模型建立&#xff0c;编程实现和论文写作的任务&#xff0c;对许多第一次参加建模或者建模经验比较欠缺的团队来说&#xff0c;是时间紧任务重的&#xff0c;那么怎么办呢&#xf…

EelasticSearch的介绍和基于docker安装

1.概述 Elasticsearch 是一个基于 Apache Lucene 构建的开源分布式搜索引擎和分析引擎。它专为云计算环境设计&#xff0c;提供了一个分布式的、高可用的实时分析和搜索平台。Elasticsearch 可以处理大量数据&#xff0c;并且具备横向扩展能力&#xff0c;能够通过增加更多的硬…

程序设计语言—Python几种语言区别的总结

程序设计语言篇—Python&几种语言区别的总结 文章目录 程序设计语言篇—Python&几种语言区别的总结一、Python介绍&理解1.1 Python基础1.2 Python规范 二、标识符&变量&常量三、数据类型&运算符和表达式3.1 数据类型3.2 运算符&表达式 四、常用的函…