当前位置: 首页 > news >正文

mini-spring源码分析

IOC模块

关键解释

beanFactory:beanFactory是一个hashMap, key为beanName, Value为 beanDefination

beanDefination:

BeanDefinitionRegistry,BeanDefinition注册表接口,定义注册BeanDefinition的方法

beanReference:增加BeanReference类,包装一个bean对另一个bean的引用。实例化beanA后填充属性时,若PropertyValue#value为BeanReference,引用beanB,则先去实例化beanB。 由于不想增加代码的复杂度提高理解难度,暂时不支持循环依赖,后面会在高级篇中解决该问题

Resource:

BeanFactoryPostProcessor和BeanPostProcessor是spring框架中具有重量级地位的两个接口,理解了这两个接口的作用,基本就理解spring的核心原理了。为了降低理解难度分两个小节实现。

BeanFactoryPostProcessor是spring提供的容器扩展机制,允许我们在bean实例化之前修改bean的定义信息即BeanDefinition的信息。其重要的实现类有PropertyPlaceholderConfigurer和CustomEditorConfigurer,PropertyPlaceholderConfigurer的作用是用properties文件的配置值替换xml文件中的占位符,CustomEditorConfigurer的作用是实现类型转换。BeanFactoryPostProcessor的实现比较简单,看单元测试BeanFactoryPostProcessorAndBeanPostProcessorTest#testBeanFactoryPostProcessor追下代码。

BeanPostProcessor也是spring提供的容器扩展机制,不同于BeanFactoryPostProcessor的是,BeanPostProcessor在bean实例化后修改bean或替换bean。BeanPostProcessor是后面实现AOP的关键

//在bean实例化之前,执行BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);

然后重写beanFactory方法,对入参beanFactory进行interface 重写。

应用上下文ApplicationContext是spring中较之于BeanFactory更为先进的IOC容器,ApplicationContext除了拥有BeanFactory的所有功能外,还支持特殊类型bean如上一节中的BeanFactoryPostProcessor和BeanPostProcessor的自动识别、资源加载、容器事件和监听器、国际化支持、单例bean自动初始化等。

BeanFactory是spring的基础设施,面向spring本身;而ApplicationContext面向spring的使用者,应用场合使用ApplicationContext。

具体实现查看AbstractApplicationContext#refresh方法即可。注意BeanFactoryPostProcessor和BeanPostProcessor的自动识别,这样就可以在xml文件中配置二者而不需要像上一节一样手动添加到容器中了。

ApplicationContext把使用者的步骤打包了

getBeanOfType 

@Override
public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {Map<String, T> result = new HashMap<>();beanDefinitionMap.forEach((beanName, beanDefinition) -> {Class beanClass = beanDefinition.getBeanClass();if (type.isAssignableFrom(beanClass)) {T bean = (T) getBean(beanName);result.put(beanName, bean);}});return result;
}

ApplicationContext容器提供了完善的事件发布和事件监听功能。

ApplicationEventMulticaster接口是注册监听器和发布事件的抽象接口,AbstractApplicationContext包含其实现类实例作为其属性,使得ApplicationContext容器具有注册监听器和发布事件的能力。在AbstractApplicationContext#refresh方法中,会实例化ApplicationEventMulticaster、注册监听器并发布容器刷新事件ContextRefreshedEvent;在AbstractApplicationContext#doClose方法中,发布容器关闭事件ContextClosedEvent。

support 比较实现类的CustomEvent

这是一种很好的泛型写法,可以运行时反射到(调用方发现是ApplicatonListerner,  使用方可以通过CustomEvent告诉兴趣事件。)

AOP引入

关键概念

Joinpoint,织入点,指需要执行代理操作的某个类的某个方法(仅支持方法级别的JoinPoint);Pointcut是JoinPoint的表述方式,能捕获JoinPoint。

最常用的切点表达式是AspectJ的切点表达式。需要匹配类,定义ClassFilter接口;匹配方法,定义MethodMatcher接口。PointCut需要同时匹配类和方法,包含ClassFilter和MethodMatcher,AspectJExpressionPointcut是支持AspectJ切点表达式的PointCut实现,简单实现仅支持execution函数。

AopProxy是获取代理对象的抽象接口,JdkDynamicAopProxy的基于JDK动态代理的具体实现。TargetSource,被代理对象的封装。MethodInterceptor,方法拦截器,是AOP Alliance的"公民",顾名思义,可以拦截方法,可在被代理执行的方法前后增加代理行为。

JDK实现

Advisor是Pointcut和Advice的组合
public class DynamicProxyTest {@Testpublic void testJdkDynamicProxy() throws Exception {WorldService worldService = new WorldServiceImpl();AdvisedSupport advisedSupport = new AdvisedSupport();TargetSource targetSource = new TargetSource(worldService);WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor();MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* org.springframework.test.service.WorldService.explode(..))").getMethodMatcher();advisedSupport.setTargetSource(targetSource);advisedSupport.setMethodInterceptor(methodInterceptor);advisedSupport.setMethodMatcher(methodMatcher);WorldService proxy = (WorldService) new JdkDynamicAopProxy(advisedSupport).getProxy();proxy.explode();}
}

三级缓存singletonFactories里的对象里面有getObject里面会调用wrapifnessary,在这里会创建新的代理对象,如果二级缓存的话,会存在引用不相等的问题。

	protected Object wrapIfNecessary(Object bean, String beanName) {//避免死循环if (isInfrastructureClass(bean.getClass())) {return bean;}Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();try {ProxyFactory proxyFactory = new ProxyFactory();for (AspectJExpressionPointcutAdvisor advisor : advisors) {ClassFilter classFilter = advisor.getPointcut().getClassFilter();if (classFilter.matches(bean.getClass())) {TargetSource targetSource = new TargetSource(bean);proxyFactory.setTargetSource(targetSource);proxyFactory.addAdvisor(advisor);proxyFactory.setMethodMatcher(advisor.getPointcut().getMethodMatcher());}}if (!proxyFactory.getAdvisors().isEmpty()) {return proxyFactory.getProxy();}} catch (Exception ex) {throw new BeansException("Error create proxy bean for: " + beanName, ex);}return bean;}

基于CGLIB的动态代理实现逻辑也比较简单,查看CglibAopProxy。与基于JDK的动态代理在运行期间为接口生成对象的代理对象不同,基于CGLIB的动态代理能在运行期间动态构建字节码的class文件,为类生成子类,因此被代理类不需要继承自任何接口。

public class DynamicProxyTest {private AdvisedSupport advisedSupport;@Beforepublic void setup() {WorldService worldService = new WorldServiceImpl();advisedSupport = new AdvisedSupport();TargetSource targetSource = new TargetSource(worldService);WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor();MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* org.springframework.test.service.WorldService.explode(..))").getMethodMatcher();advisedSupport.setTargetSource(targetSource);advisedSupport.setMethodInterceptor(methodInterceptor);advisedSupport.setMethodMatcher(methodMatcher);}@Testpublic void testCglibDynamicProxy() throws Exception {WorldService proxy = (WorldService) new CglibAopProxy(advisedSupport).getProxy();proxy.explode();}
}
		// 使用JDK动态代理advisedSupport.setProxyTargetClass(false);WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();proxy.explode();// 使用CGLIB动态代理advisedSupport.setProxyTargetClass(true);proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();proxy.explode();

增加AOP代理工厂ProxyFactory,由AdvisedSupport#proxyTargetClass属性决定使用JDK动态代理还是CGLIB动态代理(引用还是一样的)

如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP

每层缓存的意义

第三级缓存是个lamdba表达式值捕获bean引用 ,进行advisor判断,然后进行代理升级。

getBeansByType

Application refresh

	@Overridepublic void refresh() throws BeansException {//创建BeanFactory,并加载BeanDefinitionrefreshBeanFactory();ConfigurableListableBeanFactory beanFactory = getBeanFactory();//添加ApplicationContextAwareProcessor,让继承自ApplicationContextAware的bean能感知beanbeanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));//在bean实例化之前,执行BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactory);//BeanPostProcessor需要提前与其他bean实例化之前注册registerBeanPostProcessors(beanFactory);//初始化事件发布者initApplicationEventMulticaster();//注册事件监听器registerListeners();//注册类型转换器和提前实例化单例beanfinishBeanFactoryInitialization(beanFactory);//发布容器刷新完成事件finishRefresh();}

//在bean实例化之前,执行BeanFactoryPostProcessor invokeBeanFactoryPostProcessors(beanFactory);

	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {// 接口的妙用,用接口判断,然后传入beanMap,接口进行处理,和上面泛型接口刚好服务方和调用者做了一个翻转。beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);}}

拓展篇

TODO三级缓存,和

类型转换服务bean

spring在org.springframework.core.convert.converter包中定义了三种类型转换器接口:Converter、ConverterFactory、GenericConverter。

Converter<S,T>接口适合一对一的类型转换,如果要将String类型转换为Ineger/Long/Float/Double/Decimal等类型,就要实现一系列的StringToInteger/StringToLongConverter/StringToFloatConverter转换器,非常不优雅。

ConverterFactory接口则适合一对多的类型转换,可以将一种类型转换为另一种类型及其子类。比如将String类型转换为Ineger/Long/Float/Double/Decimal等Number类型时,只需定义一个ConverterFactory转换器:

做成一个服务的factoryBean

为了方便使用,提供了创建ConversionService的FactoryBean——ConversionServiceFactoryBean。

如果有定义ConversionService,在AbstractApplicationContext#finishBeanFactoryInitialization方法中设置到容器中。

  • 为bean填充属性时,见AbstractAutowireCapableBeanFactory#applyPropertyValues
  • 处理@Value注解时,见AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues

多切面AOP

组装advisor key可以进行缓存,但最好用method作为key

MethodInvocation只是简单的将拦截器链的所有拦截器一一执行,最后再触发当前的method方法。这是很简单高效的方法,但问题是我们希望某些增强比如AfterReturningAdvice能够在方法执行完才被执行,这就涉及到不同增强的执行顺序的问题了。而MethodInvocation显然没有考虑顺序的问题,一个AfterReturningAdvice很可能在BeforeAdvice之前被调用。那么该如何保证顺序问题呢?

答案是,控制增强的调用顺序其实由每个拦截器负责,所以我们需要分析MethodBeforeAdviceInterceptorAfterReturningAdviceInterceptor

但是代理的顺序不同还是会影响返回值,但是相对于一次的方法位置是符合预期的。

三级缓存实现

三级缓存解决问题是解决三个转态转换的状态机(用于单例bean) 1.属性填充完成 2.代理完成对象 3.bean定义(lamdba表达式)

也是一种可重入的判断

也是共享引用的思想

解决有代理对象时的循环依赖问题,需要提前暴露代理对象的引用,而不是暴露实例化后的bean的引用(这是上节的遗留问题的原因,应该提前暴露A的代理对象的引用)。

spring中用singletonFactories(一般称第三级缓存)解决有代理对象时的循环依赖问题。在实例化后提前暴露代理对象的引用(见AbstractAutowireCapableBeanFactory#doCreateBean方法第6行)。

getBean()时依次检查一级缓存singletonObjects、二级缓存earlySingletonObjects和三级缓存singletonFactories中是否包含该bean。如果三级缓存中包含该bean,则挪至二级缓存中,然后直接返回该bean。见AbstractBeanFactory#getBean方法第1行。

protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {Object bean;try {bean = createBeanInstance(beanDefinition);//为解决循环依赖问题,将实例化后的bean放进缓存中提前暴露if (beanDefinition.isSingleton()) {Object finalBean = bean;addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {return getEarlyBeanReference(beanName, beanDefinition, finalBean);}});}//实例化bean之后执行boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);if (!continueWithPropertyPopulation) {return bean;}//在设置bean属性之前,允许BeanPostProcessor修改属性值applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);//为bean填充属性applyPropertyValues(beanName, bean, beanDefinition);//执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法bean = initializeBean(beanName, bean, beanDefinition);} catch (Exception e) {throw new BeansException("Instantiation of bean failed", e);}//注册有销毁方法的beanregisterDisposableBeanIfNecessary(beanName, bean, beanDefinition);Object exposedObject = bean;if (beanDefinition.isSingleton()) {//如果有代理对象,此处获取代理对象exposedObject = getSingleton(beanName);addSingleton(beanName, exposedObject);}return exposedObject;}

相关文章:

mini-spring源码分析

IOC模块 关键解释 beanFactory&#xff1a;beanFactory是一个hashMap, key为beanName, Value为 beanDefination beanDefination: BeanDefinitionRegistry&#xff0c;BeanDefinition注册表接口&#xff0c;定义注册BeanDefinition的方法 beanReference&#xff1a;增加Bean…...

黑马程序员Java项目实战《苍穹外卖》Day01

苍穹外卖-day01 课程内容 软件开发整体介绍苍穹外卖项目介绍开发环境搭建导入接口文档Swagger 项目整体效果展示&#xff1a; ​ 管理端-外卖商家使用 ​ 用户端-点餐用户使用 当我们完成该项目的学习&#xff0c;可以培养以下能力&#xff1a; 1. 软件开发整体介绍 作为一…...

uniapp开发支付宝小程序自定义tabbar样式异常

解决方案&#xff1a; 这个问题应该是支付宝基础库的问题&#xff0c;除了依赖于官方更新之外&#xff0c;开发者可以利用《自定义 tabBar》曲线救国 也就是创建一个空内容的自定义tabBar&#xff0c;这样即使 tabBar 被渲染出来&#xff0c;但从视觉上也不会有问题 1.官方文…...

python+django5.1+docker实现CICD自动化部署springboot 项目前后端分离vue-element

一、开发环境搭建和配置 # channels是一个用于在Django中实现WebSocket、HTTP/2和其他异步协议的库。 pip install channels#channels-redis是一个用于在Django Channels中使用Redis作为后台存储的库。它可以用于处理#WebSocket连接的持久化和消息传递。 pip install channels…...

python代码示例(读取excel文件,自动播放音频)

目录 python 操作excel 表结构 安装第三方库 代码 自动播放音频 介绍 安装第三方库 代码 python 操作excel 表结构 求出100班同学的平均分 安装第三方库 因为这里的表结构是.xlsx文件,需要使用openpyxl库 如果是.xls格式文件,需要使用xlrd库 pip install openpyxl /…...

【第十课】Rust并发编程(一)

目录 前言 Fork和Join 前言 本节会介绍Rust中的并发编程&#xff0c;并发编程在编程中是提升cpu使用率的一大利器&#xff0c;通过多线程技术提升效率&#xff0c;Rust的并发和其他编程语言的并发不同的地方在于&#xff0c;Rust号称无畏并发。更重要的一点是安全。Rust中所有…...

图形渲染性能优化

variable rate shading conditional render 设置可见性等&#xff0c; 不需要重新build command buffer indirect draw glMultiDraw* - 直接支持多次绘制glMultiDrawIndirect - 间接多次绘制multithreading 多线程录制 实例化渲染 lod texture array 小对象剔除 投影到…...

elasticsearch的索引模版使用方法

5 索引模版⭐️⭐️⭐️⭐️⭐️ 索引模板就是创建索引时要遵循的模板规则索引模板仅对新创建的索引有效&#xff0c;已经创建的索引并不受索引模板的影响 5.1 索引模版的基本使用 1.查看所有的索引模板 GET 10.0.0.91:9200/_index_template2.创建自定义索引模板 xixi &…...

论文学习——进化动态约束多目标优化:测试集和算法

论文题目&#xff1a;Evolutionary Dynamic Constrained Multiobjective Optimization: Test Suite and Algorithm 进化动态约束多目标优化&#xff1a;测试集和算法&#xff08;Guoyu Chen ,YinanGuo , Member, IEEE, Yong Wang , Senior Member, IEEE, Jing Liang , Senior …...

C++中的volatile关键字

作用&#xff1a; 1.它用于修饰变量&#xff0c;告知编译器该变量的值可能会在程序的外部被改变&#xff0c;编译器不能对这个变量的访问进行优化。这是因为编译器通常会对代码进行优化&#xff0c;例如把变量的值缓存到寄存器中&#xff0c;但对于 volatile 变量&#xff0c;…...

linux桌面qt应用程序UI自动化实现之dogtail

1. 前言 Dogtail适用于Linux 系统上进行 GUI 自动化测试,利用 Accessibility 技术与桌面程序通信;Dogtail 包含一个名为 sniff 的组件,这是一个嗅探器,用于 GUI 程序追踪; 源码下载:​​dogtail PyPI 可通过sudo python setup.py install安装或sudo pip install dogt…...

Hello World C#

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System; 引入了System命名空间&#xff0c;基本输入输出。一般只用这个&#xff0c;后面的不用 using System.Collections.Generic; 包含了定…...

SAP开发语言ABAP开发入门

1. 了解ABAP开发环境和基础知识 - ABAP简介 - ABAP&#xff08;Advanced Business Application Programming&#xff09;是SAP系统中的编程语言&#xff0c;主要用于开发企业级的业务应用程序&#xff0c;如财务、物流、人力资源等模块的定制开发。 - 开发环境搭建 - 首先需…...

应急响应靶机——easy溯源

载入虚拟机&#xff0c;开启虚拟机&#xff1a; &#xff08;账户密码&#xff1a;zgsfsys/zgsfsys&#xff09; 解题程序.exe是额外下载解压得到的&#xff1a; 1. 攻击者内网跳板机IP地址 2. 攻击者服务器地址 3. 存在漏洞的服务(提示:7个字符) 4. 攻击者留下的flag(格式…...

【前端】vscode报错: 无法加载文件 D:\nodejs\node_global\yarn.ps1,因为在此系统上禁止运行脚本。

vscode运行前端代码时候&#xff0c;执行yarn install时候报错 问题&#xff1a; 无法加载文件 D:\nodejs\node_global\yarn.ps1&#xff0c;因为在此系统上禁止运行脚本。 解决方式&#xff1a; 首先用管理员身份运行vscode 查看 get-ExecutionPolicy&#xff0c;Restrict…...

Spring Web MVC(详解中)

文章目录 Spring MVC&#xff08;中&#xff09;RESTFul风格设计RESTFul风格概述RESTFul风格特点RESTFul风格设计规范RESTFul风格好处RESTFul风格实战需求分析RESTFul风格接口设计后台接口实现 基于RESTFul风格练习&#xff08;前后端分离模式&#xff09;案例功能和接口分析功…...

Flutter:encrypt插件 AES加密处理

1、pubspec.yaml导入插件 cupertino_icons: ^1.0.8 # 密码加密 encrypt: 5.0.3encrypt封装 import package:encrypt/encrypt.dart; /// 加密类 class EncryptUtil {static final EncryptUtil _instance EncryptUtil._internal();factory EncryptUtil() > _instance;Encrypt…...

Python bytes类型及用法

在Python中&#xff0c;bytes类型是一种不可变的字节序列&#xff0c;用于存储原始的二进制数据。bytes对象通常用于处理文件、网络通信和其他需要处理原始字节数据的场景。 以下是bytes类型的一些基本用法和特性&#xff1a; 1. 创建bytes对象 可以通过多种方式创建bytes对…...

阅读《基于蒙特卡洛法的破片打击无人机易损性分析》_笔记

目录 基本信息 1 引言 1.1 主要研究内容 1.2 研究必要性&#xff08;为什么要研究&#xff09; 1.3 该领域研究现状&#xff08;别人做了什么/怎么做的&#xff09; 2 主要研究过程&#xff08;我们做了什么&#xff09; 2.1 建立目标仿真模型 2.2 确定毁伤依据 2.3 无…...

【vim】vim怎么把某一列内容复制到另一列

1. vim 怎么把某一列内容复制到另一列 移动光标到你想复制的列的第一个字符上。按下 ctrlv 进入选择模式。按下方向键选择多行。按下 h 或 j 或 k 或 l&#xff0c;选择整列。按下 y 复制所选择的列。移动光标到你想粘贴内容的列的第一个字符上。按下 p 粘贴内容。...

IP划分(笔记)

IPv4 32位4字节 IPv6 128位16字节 IPv4转IPv6 前10字节全0,2字节全1&#xff0c;再加ipv4的4个字节 IPv4划分笔记&#xff08;有类域&#xff09;&#xff1a; A类&#xff1a;首字节必为0b0xxxxxxx 0.0.0.0/8-127.255.255.255/8 实际范围&#xff1a;1.0.0.1/8-126.25…...

【ChatGPT大模型开发调用】如何获得 OpenAl API Key?

如何获取 OpenAI API Key 获取 OpenAI API Key 主要有以下三种途径&#xff1a; OpenAI 官方平台 (推荐): 开发者用户可以直接在 OpenAI 官方网站 (platform.openai.com) 注册并申请 API Key。 通常&#xff0c;您可以在账户设置或开发者平台的相关页面找到申请入口。 Azure…...

人证合一开启安全认证新时代、C#人证合一接口集成、人脸识别

在数字化转型加速推进的今天&#xff0c;确保用户身份的真实性和唯一性成为了各行各业关注的重点。从金融交易到在线教育&#xff0c;从远程办公到智慧医疗&#xff0c;如何高效准确地验证“你是你”变得至关重要。正是在这种背景下&#xff0c;翔云“人证合一”接口应运而生&a…...

EBS 中 Oracle Payables (AP) 模块的相关集成

Oracle E-Business Suite (EBS) 中的 Oracle Payables (AP) 模块是一个全面的应付账款管理系统&#xff0c;它不仅提供了丰富的功能来管理与供应商的财务交易&#xff0c;还通过与其他模块的紧密集成&#xff0c;实现了企业内部各个业务流程的无缝衔接。以下是 Oracle Payables…...

Flask项目入门—会话技术Cookie和Session

Session和Cookie都是用于跟踪用户会话的技术、它们可以存储用户信息&#xff0c;以便在用户与网站进行交互时提供个性化的体验。然而&#xff0c;这两者在存储位置、使用方式、安全性等方面存在显著的差异。 工作原理/流程 Cookie 1、用户第一次访问时候服务器生成 Cookie&a…...

通达OA down存在信息泄露漏洞

免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...

Python中的简单爬虫

文章目录 一. 基于FastAPI之Web站点开发1. 基于FastAPI搭建Web服务器2. Web服务器和浏览器的通讯流程3. 浏览器访问Web服务器的通讯流程4. 加载图片资源代码 二. 基于Web请求的FastAPI通用配置1. 目前Web服务器存在问题2. 基于Web请求的FastAPI通用配置 三. Python爬虫介绍1. 什…...

Scala入门基础(20)数据集复习拓展

一.Stack栈二.Queue 队列 一.Stack栈 Stack:栈&#xff0c;特殊的结构。它对元素的操作是在头部&#xff1a;栈顶 先进后出的队列。pop表示取出&#xff0c;push表示在栈中添加元素 二.Queue 队列 Queue 队列;先进先出.enqueue入队&#xff0c;dequeue出队。...

leetcode 二叉树的最大深度

104. 二叉树的最大深度 已解答 简单 相关标签 相关企业 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3…...

elasticsearch集群部署及加密通讯

原文地址&#xff1a;elasticsearch集群部署及加密通讯 – 无敌牛 欢迎参观我的个人博客&#xff1a;无敌牛 – 技术/著作/典籍/分享等 第零步&#xff0c;准备 给各台设备配置虚拟主机名&#xff0c;这样集群不依赖IP&#xff0c;即使IP变动&#xff0c;改动也更方便。参考…...

Hdi做指数网站/网络整合营销4i原则是指

今天我们来画折线图 效果图以下为模拟数据[{"time":19,"text":"入\n院\n19\n时\n11\n分","position":42,"cellMin":29.0,"cellSplit":0.2,"type":"text","color":"red",…...

正规购物网站建设/成都网站建设公司排名

JVM中的堆一般分为三大部分&#xff1a;新生代、老年代、永久代&#xff0c;其大致的占比如下&#xff1a; 一、新生代 新生代主要用来存放新生的对象。一般占据堆空间的1/3。在新生代中&#xff0c;保存着大量的刚刚创建的对象&#xff0c;但是大部分的对象都是朝生夕死&#…...

olam wordpress/北京关键词优化报价

1、链接的onclick事件被先执行&#xff0c;href的事件后执行 2、就今天遇到的问题说一下&#xff0c;通过a标签的onclick事件实现点击后定位到指定的div&#xff0c;或者是执行其他函数&#xff08;比如弹出artdialog等&#xff09;&#xff0c;页面如果有滚动条&#xff0c;那…...

wordpress标签化关键词/百度免费

QT 常用事件及QPainter QWidget提供的事件处理函数 重载eventbool event(QEvent *e)返回true处理下一个事件返回false本事件的下一个处理函数以及一些常用事件QEvent提供的属性及函数 QEvent 子类QInputEvent 示例&#xff08;针对按键事件的两种方式 event函数 需要事件类型…...

wordpress前台可发表文章/上海百度推广排名

图示 如上图所示红框部分&#xff0c;本人在做稳定性测试的时候&#xff0c;当flume运行几天后&#xff0c;我发现这个counter值逐渐变大&#xff0c;到一定值后&#xff0c;又变小了&#xff0c;有一个循环的过程&#xff0c;故而对此产生研究的欲望&#xff0c;下面来看看&am…...

网站开发助理工程师/郑州seo顾问培训

1. 前言 Druid 的目标是提供一个能够在大数据集上做实时数据摄入与查询的平台&#xff0c;然而对于大多数系统而言&#xff0c;提供数据的快速摄入与提供快速查询是难以同时实现的两个指标。例如对于普通的RDBMS,如果想要获取更快的查询速度&#xff0c;就会因为创建索引而牺牲…...