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

【博学谷学习记录】超强总结,用心分享 | 架构师 Spring源码学习总结

文章目录

  • Spring的循环依赖
    • 1.循环依赖的定义&&原因
    • 2.循环依赖的场景
      • 1.构造器注入引起循环依赖
      • 2.Field属性setter注入的循环依赖
      • 3.循环依赖解决思路
      • 4.三级缓存
      • 5.面试题[三级缓存]
  • AOP源码深度剖析
    • 概述
    • Spring AOP的前世今生
    • 实现机制
      • **JDK 动态代理**
      • **CGLIB 代理**
    • 流程
    • 总结
  • MVC流程源码剖析
    • Servlet生命周期
    • DispatcherServlet 类图
    • 源码剖析-根容器初始化【父容器】
      • Web应用部署初始化过程 (Web Application Deployement)
      • ContextLoaderListener的初始化过程
      • ServletContextListener接口源码:
    • 源码剖析-DispatcherServlet初始化【子容器&9大组件】
      • DispatcherServlet类图
      • 为什么需要多个IOC容器呢?
      • DispatcherServlet初始化

1.Spring中核心组件
2.IOC流程:bean对象是如何创建出来的
3.Bean生命周期
4.循环依赖:循坏依赖为什么要通过三级缓存来解决
5.AOP
6.事务
7.MVC

Spring的循环依赖

1.循环依赖的定义&&原因

定义:一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了一个环形调用(闭环)

在这里插入图片描述

在Spring中,一个对象并不是简单new出来的,而是会经过一系列的Bean的生命周期,就是因为Bean的生命周期,所以才出现了循环依赖的问题

在Bean生命周期的属性赋值阶段,A依赖了B,从单例池中找B对象,没有则创建,创建B的途中依赖了A,此处形成了环形调用。

2.循环依赖的场景

1.构造器注入引起循环依赖

在这里插入图片描述

2.Field属性setter注入的循环依赖

在这里插入图片描述

对于以上两种场景的循环依赖,Spring下测试效果:

1.构造器注入引起的循环依赖(不能解决

2.单例Bean的Setter注入产生的循环依赖(能解决

补充:多例Bean的Setter注入产生的循环依赖(不能解决

3.循环依赖解决思路

Spring的循环依赖的理论依据是:基于Java的引用传递

即:可以先实例化对象,实例化对象之后,在内存中就有了该对象的内存地址,我们就可以先从内存中获取到该对象而对象的具体属性,是可以延后设置的

核心:创建和属性赋值分开,达到对象的提前暴露效果

A创建时,先创建A原始对象放入缓存中

因为依赖于B,在创建B时需要A,从缓存中取A,缓存中有A则B创建完成,赋值给A

4.三级缓存

一级缓存

存放完整的Bean

Bean执行了一系列生命周期,存入单例池SingletonObjects中

二级缓存

存放半成品Bean

类似于A:{b=null},实例化但未赋值的状态,存入earlySingletonObjects

三级缓存

存放ObjectFactory对象

将Bean进行包装成ObjectFactory,存入singletonFactories

取对象时,可能取A的原始对象/A的代理对象

bean将A的地址传入进来,如果现在针对A进行AOP配置的话,A对象的方法增强,则在三级缓存中取A的时候,会提前生成A对象的代理对象proxy,如果没有配置则返回A的原始对象。

当有AOP配置时,Spring根据beanName在已注册的Advisor集合中找匹配到的拦截面,生成代理对象,把生成的代理对象存入二级缓存,并删除三级缓存: this.singletonFactories.remove(beanName)

5.面试题[三级缓存]

1.构造器注入引起的循环依赖能够解决吗?

不能。因为创建A对象时候是通过A的有参构造去构建的——new A(B),这个对象的创建和属性赋值是没有分开来的,所以没有办法进行A对象的提前暴露。

2.多例Bean对象setter注入产生的循环依赖能够解决吗?

不能。多例Bean的生命周期不由Spring管理

3.循环依赖,只有一级缓存,能够解决循环依赖吗?

单从循环依赖的角度,能解决,但使用过程会有问题,因为此时成品对象、半成品对象都存到一个map中,如果另一个请求需要调用A对象的B属性的方法,他并不知道此时A对象是成品对象还是半成品对象,如果是半成品对象,调用B属性方法(此时B为null),则会报空指针异常。

4.循环依赖,只有一级缓存、二级缓存,能够解决循环依赖吗?

如果不存在AOP代理的情况,是可以解决的。

如果对A进行了AOP配置,要生成A的代理对象,A在初始化通过BeanPostProcessor#after方法生成proxy代理对象并存储缓存,但B的A属性还是指向A的原始对象,这里就产生了问题。

针对以上问题解决思路:在A放缓存之前,判断是否需要生成代理对象,如果需要,则基于原始对象生成代理对象,再把代理对象存入缓存中(等同于违背了Spring的设计原则,无论Bean是否产生循环依赖问题,所有Bean对象产生代理对象的时机都提前了(所有的Bean都得多一个判断))

5.循环依赖,只有一级缓存、三级缓存,能够解决循环依赖吗?

简单的A、B循环依赖没问题,但如果此时出现复杂的循环依赖会出现问题:比如A、B、C相互依赖场景

在这里插入图片描述

B依赖于A,生成了A的代理对象Proxy1,C依赖于A,又生成了A的代理对象Proxy2,此时B、C引用了不同的A对象地址。

通过二级缓存解决这种现象:Spring先查二级缓存,发现有A对象代理生成,则不会再去生成新的A对象代理。

生成代理对象的时候有判断:该bean之前是否生成过代理,不重复生成,通过集合判断

6.三级缓存为什么存ObjectFactory?为什么需要三级缓存解决循环依赖问题?

本身违背了Spring设计原则,Spring很多拓展点设计都无法应用,只有产生循环依赖,才会把产生循环依赖的对象要生成的代理对象做一个提前操作,否则就把没有循环依赖,但需要产生代理对象按照Spring的设计规范生成

总结

三级缓存作用:在没有循环依赖的情况下,能够包装bean的初始化的最后阶段再生成代理对象,遵循Spring设计原则。

个人理解:三级缓存就是判断对象是否有AOP配置生成proxy对象,将生成代理对象提前了,但Spring找缓存对象时是按照顺序:一级缓存 -> 二级缓存 -> 三级缓存 查找的,不一定每个Bean都走到三级缓存,所以不违背Spring设计原则。

AOP源码深度剖析

概述

AOP(Aspect Orient Programming):面向切面编程;

用途:用于系统中的横切关注点,比如日志管理,事务管理;

实现:利用代理模式,通过代理对象对被代理的对象增加功能。

所以,关键在于AOP框架自动创建AOP代理对象,代理模式分为静态代理和动态代理;

框架:

​ AspectJ使用静态代理,编译时增强,在编译期生成代理对象;

​ SpringAOP使用动态代理,运行时增强,在运行时,动态生成代理对象;

Spring AOP的前世今生

目前 Spring AOP 一共有三种配置方式,Spring 做到了很好地向下兼容,所以可以放心使用。

  • Spring 1.2 基于接口的配置:最早的 Spring AOP 是完全基于几个接口的
  • Spring 2.0 schema-based 配置:Spring 2.0 以后使用 XML 的方式来配置,使用 命名空间
  • Spring 2.0 @AspectJ 配置:使用注解的方式来配置,这种方式感觉是最方便的,还有,这里虽然叫做 @AspectJ,但是这个和 AspectJ 其实没啥关系。

要说明的是,这里介绍的 Spring AOP 是纯的 Spring 代码,和 AspectJ 没什么关系,但是 Spring 延用了 AspectJ 中的概念,包括使用了 AspectJ 提供的 jar 包中的注解,但是不依赖于其实现功能。

如 @Aspect、@Pointcut、@Before、@After 等注解都是来自于 AspectJ,但是功能的实现是纯 Spring AOP 自己实现的。

实现机制

Spring AOP 底层实现机制目前有两种:JDK 动态代理、CGLIB 动态字节码生成。在阅读源码前对这两种机制的使用有个认识,有利于更好的理解源码。

JDK 动态代理

在这里插入图片描述

Proxy.newProxyInstance()第三个参数,实现InvocationHandler接口重写invoke方法,在每次调用方法时,都会进入invoke方法实现增强。

Spring为Bean对象创建代理时,会判断当前Bean对象有没有实现接口,如果实现接口,使用JDK动态代理生成对象,否则使用CGLIB代理。

如果proxy-target-class=“true”,则再判断当前对象是否为接口对象,是接口的话依然使用JDK动态代理,不是接口使用CGLIB;如果proxy-target-class不设置或者为false,则默认为JDK动态代理

CGLIB 代理

在这里插入图片描述

new Enhance对象,设置属性,调用create()方法创建proxy对象,每次调用方法时,会进入intercept方法,可以在proxy.invokeSuper()前后进行逻辑增强。

流程

Spring对标签<aop:aspectj-autoproxy @EnableAspectJAutoProxy/>的解析(作用):

​ 注册AnnotationAwareAspectJAutoProxyCreator后置处理器

AnnotationAwareAspectJAutoProxyCreator类图:实现了BeanPostProcessor

在这里插入图片描述

AnnotationAwareAspectJAutoProxyCreator 实现了几个重要的扩展接口(可能是在父类中实现):
1)实现了 BeanPostProcessor 接口:实现了 postProcessAfterInitialization 方法。
2)实现了 InstantiationAwareBeanPostProcessor 接口:实现了 postProcessBeforeInstantiation 方法。
3)实现了 SmartInstantiationAwareBeanPostProcessor 接口:实现了 predictBeanType 方法、getEarlyBeanReference 方法。
4)实现了 BeanFactoryAware 接口,实现了 setBeanFactory 方法。
对于 AOP 来说,postProcessAfterInitialization 是我们重点分析的内容,因为在该方法中,会对 bean 进行代理,该方法由父类 AbstractAutoProxyCreator 实现。

AbstractAutoProxyCreator#postProcessAfterInitialization

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);// 1.判断当前bean是否需要被代理,如果需要则进行封装if (!this.earlyProxyReferences.contains(cacheKey)) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}

AnnotationAwareAspectJAutoProxyCreator的BeanPostProcessor#after方法执行的时候,经过wrapIfNecessary方法来判断是否需要针对当前类生成代理对象;

wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 1.判断当前bean是否在targetSourcedBeans缓存中存在(已经处理过),如果存在,则直接返回当前beanif (beanName != null && this.targetSourcedBeans.contains(beanName)) {return bean;}// 2.在advisedBeans缓存中存在,并且value为false,则代表无需处理if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}// 3.bean的类是aop基础设施类 || bean应该跳过,则标记为无需处理,并返回if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.// 4.获取当前bean的Advices和AdvisorsObject[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);// 5.如果存在增强器则创建代理if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);// 5.1 创建代理对象:这边SingletonTargetSource的target属性存放的就是我们原来的bean实例(也就是被代理对象),// 用于最后增加逻辑执行完毕后,通过反射执行我们真正的方法时使用(method.invoke(bean, args))Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));// 5.2 创建完代理后,将cacheKey -> 代理类的class放到缓存this.proxyTypes.put(cacheKey, proxy.getClass());// 返回代理对象return proxy;}// 6.标记为无需处理this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;
}

getAdvicesAndAdvisorsForBean

@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {// 1.找到符合条件的AdvisorList<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {// 2.如果没有符合条件的Advisor,则返回nullreturn DO_NOT_PROXY;}return advisors.toArray();
}

getAdvicesAndAdvisorsForBean针对当前程序中所有切面类@Aspect进行解析,找到切面类中@Before/@After/@Around注解标注的方法,把这些方法创建成Advice对象,并且针对@Before(“pointcut()”)中的切点信息进行解析成Pointcut对象,将Advice对象和Pointcut对象进行封装,形成Advisor对象。

总结

1.标签<aop:aspectj-autoproxy @EnableAspectJAutoProxy/> 注册了一个AnnotationAwareAspectJAutoProxyCreator后置处理器,当代理对象生成的时候是调用AnnotationAwareAspectJAutoProxyCreator后置处理器的after方法生成。

2.创建代理对象时,after方法对于当前Bean对象去找对应的Advisor,如果有对应的Advisor,则生成代理对象(判断对象有没有实现接口,如果实现接口,采用JDK动态代理,否则采用CGLIB动态代理);当代理对象调用接口中任意方法时,都是执行底层的invoke方法,invoke方法执行时涉及拦截器链的依次执行(如果是后置通知/最终通知:先放行,再在方法返回时执行对应逻辑)。

MVC流程源码剖析

* 问题1SpringSpringMVC整合使用时,会创建一个容器还是两个容器(父子容器?)
答:会创建两个容器对象,并且是有父子容器关系,对于Spring容器主要管理业务层/持久层/事务层对象,对于SpringMVC容器主要负责Web层对象的维护,SpringMVC容器为子容器,Spring容器为父容器(其实就是设置了SpringMVC的一个parent属性)* 问题2DispatcherServlet初始化过程中做了什么?
答:在DispatcherServlet初始化init方法时,首先构建了子容器对象,并且根据spring-mvc.xml进行文件解析,这里关注<mvc:annotation-driven>标签(开启注解模式驱动),它向当前的BeanDefinitionMap中注册了一些Bean定义,其中有RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver,以支持对使用了@RequestMapping@ExceptionHandler及其他注解的控制器方法的请求处理。RequestMappingHandlerMapping:生命周期中,父类实现了initializingBean#AfterPropertiesSet,这里进行了映射关系的注册:1.获取容器中所有的BeanName,根据BeanName获得对应的BeanType,判断有没有@Controller/@RequestMapping注解,如果有 说明是处理器(Handler)对象,将这些Bean封装到Map集合中(key:Method,value:RequestMappingInfo)(RequestMappingInfo是针对@RequestMapping注解的属性解析封装)2.根据Map注册映射关系(key:RequestMappingInfo,value:HandlerMethod)(这里HandlerMethod是上一步Method的封装,比如还封装了这个Method的所在类的对象)3.RequestMappingInfo再封装到urlLookup中(key:url,value:RequestMappingInfoRequestMappingHandlerAdapter:在它的initializingBean#AfterPropertiesSet方法中:初始化了参数解析器、返回值处理器* 问题3:请求的执行流程是怎么样的?
答:1.根据请求地址,定位到Controller中的方法,可能有拦截器拦截,所以返回的是执行器链,根据顺序:拦截器目标方法、拦截器后置方法执行;2.(参数如何绑定)通过不同的参数解析器完成参数值的解析,再反射调用方法3.(不同返回值如何处理)通过不同的返回值处理器将结果变为ModelAndView对象,再进行视图渲染,最终以转发的形式完成视图的跳转

SpringMVC是基于Servlet和Spring容器设计的Web框架

Servlet生命周期

1</> //将随着服务的启动进行实例化,并调用init方法且只调用一次。

在这里插入图片描述

ServletConfig 是一个和 Servlet 配置相关的接口:

在配置 Spring MVC 的 DispatcherServlet 时,会通过 ServletConfig 将配置文件的位置告知 DispatcherServlet。

例:

<servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param>
</servlet>

如上,标签内的配置信息最终会被放入 ServletConfig 实现类对象中。DispatcherServlet 通过 ServletConfig 接口中的方法,就能获取到 contextConfigLocation 对应的值。

DispatcherServlet 类图

在这里插入图片描述

请求入口:DispatcherServlet类的dpDispatch()方法 (找service()方法一步一步找到的核心方法)

源码剖析-根容器初始化【父容器】

Web应用部署初始化过程 (Web Application Deployement)

Web应用部署初始化流程执行图:

在这里插入图片描述

可以发现,在tomcatweb应用的初始化流程是,先初始化listener接着初始化filter最后初始化servlet,当我们清楚认识到Web应用部署到容器后的初始化过程后,就可以进一步深入探讨SpringMVC的启动过程。

ContextLoaderListener的初始化过程

首先定义了<context-param>标签,用于配置一个全局变量,<context-param>标签的内容读取后会被放进application中,做为Web应用的全局变量使用,接下来创建listener时会使用到这个全局变量,因此,Web应用在容器中部署后,进行初始化时会先读取这个全局变量,之后再进行上述讲解的初始化启动过程。

接着定义了一个ContextLoaderListener类listener。查看ContextLoaderListener的类声明源码如下图:

在这里插入图片描述

ServletContextListener接口源码:

public interface ServletContextListener extends java.util.EventListener {void contextInitialized(javax.servlet.ServletContextEvent servletContextEvent);void contextDestroyed(javax.servlet.ServletContextEvent servletContextEvent);
}

该接口只有两个方法contextInitializedcontextDestroyed,这里采用的是观察者模式,也称为为订阅-发布模式,实现了该接口的listener会向发布者进行订阅,当Web应用初始化或销毁时会分别调用上述两个方法。

继续看ContextLoaderListener,该listener实现了ServletContextListener接口,因此在Web应用初始化时会调用该方法,该方法的具体实现如下:

  /*** Initialize the root web application context.*/@Overridepublic void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext());}

ContextLoaderListenercontextInitialized()方法直接调用了initWebApplicationContext()方法,这个方法是继承自ContextLoader类,通过函数名可以知道,该方法是用于初始化Web应用上下文,即IoC容器,这里使用的是代理模式

源码剖析-DispatcherServlet初始化【子容器&9大组件】

DispatcherServlet类图

Web应用启动的最后一个步骤就是创建和初始化相关Servlet,我们配置了DispatcherServlet类前端控制器,前端控制器作为中央控制器是整个Web应用的核心,用于获取分发用户请求并返回响应。

其类图如下所示:

在这里插入图片描述

通过类图可以看出DispatcherServlet类的间接父类实现了Servlet接口,因此其本质上依旧是一个Servlet

为什么需要多个IOC容器呢?

答:父子容器类似于类的继承关系,子类可以访问父类中的成员变量,而父类不可访问子类的成员变量,同样的,子容器可以访问父容器中定义的Bean,但父容器无法访问子容器定义的Bean。

根IoC容器做为全局共享的IoC容器放入Web应用需要共享的Bean,而子IoC容器根据需求的不同,放入不同的Bean,这样能够做到隔离,保证系统的安全性。

DispatcherServlet类的子IoC容器创建过程,如果当前Servlet存在一个IoC容器则为其设置根IoC容器作为其父类,并配置刷新该容器,用于构造其定义的Bean,这里的方法与前文讲述的根IoC容器类似,同样会读取用户在web.xml中配置的中的值,用于查找相关的xml配置文件用于构造定义的Bean,这里不再赘述了。如果当前Servlet不存在一个子IoC容器就去查找一个,如果仍然没有查找到则调用 createWebApplicationContext()方法去创建一个,查看该方法的源码如下图所示:

protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {return createWebApplicationContext((ApplicationContext) parent);
}protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {Class<?> contextClass = getContextClass();if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Fatal initialization error in servlet with name '" + getServletName() +"': custom WebApplicationContext class [" + contextClass.getName() +"] is not of type ConfigurableWebApplicationContext");}ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());wac.setParent(parent);String configLocation = getContextConfigLocation();if (configLocation != null) {wac.setConfigLocation(configLocation);}configureAndRefreshWebApplicationContext(wac);return wac;
}

该方法用于创建一个子IoC容器并将根IoC容器做为其父容器,接着进行配置和刷新操作用于构造相关的Bean。至此,根IoC容器以及相关Servlet的子IoC容器已经配置完成,子容器中管理的Bean一般只被该Servlet使用,因此,其中管理的Bean一般是“局部”的,如SpringMVC中需要的各种重要组件,包括Controller、Interceptor、Converter、ExceptionResolver等。

DispatcherServlet初始化

DispatcherServlet的内置组件及其作用。

在这里插入图片描述

如 @Aspect、@Pointcut、@Before、@After 等注解都是来自于 AspectJ,但是功能的实现是纯 Spring AOP 自己实现的。

颜色

颜色

颜色

颜色

颜色

颜色

颜色

颜色

颜色

颜色

相关文章:

【博学谷学习记录】超强总结,用心分享 | 架构师 Spring源码学习总结

文章目录Spring的循环依赖1.循环依赖的定义&&原因2.循环依赖的场景1.构造器注入引起循环依赖2.Field属性setter注入的循环依赖3.循环依赖解决思路4.三级缓存5.面试题[三级缓存]AOP源码深度剖析概述Spring AOP的前世今生实现机制**JDK 动态代理****CGLIB 代理**流程总结…...

Linux C/C++ timeout命令实现(运行具有时间限制)

Linux附带了大量命令&#xff0c;每个命令都是唯一的&#xff0c;并在特定情况下使用。Linux timeout命令的一个属性是时间限制。可以为任何命令设置时间限制。如果时间到期&#xff0c;命令将停止执行。 如何使用timeout命令 我们将解释如何使用Linux timeout命令 timeout […...

西湖论剑初赛web wp

Node Magical Login 简单的js代码审计。 Flag分成了两部分。 第一部分&#xff1a; 这里就简单的判断了一下user是否等于admin&#xff0c;直接绕过。 第二部分&#xff1a; checkcode ! “aGr5AtSp55dRacer”&#xff0c;让其为真&#xff0c;利用数组绕过。 Flag为&#x…...

【YOLOv8/YOLOv7/YOLOv5系列算法改进NO.55】融入美团最新QARepVGG

文章目录 前言一、解决问题二、基本原理三、​添加方法四、总结前言 作为当前先进的深度学习目标检测算法YOLOv8,已经集合了大量的trick,但是还是有提高和改进的空间,针对具体应用场景下的检测难点,可以不同的改进方法。此后的系列文章,将重点对YOLOv8的如何改进进行详细…...

Flutter Windows端打包并生成可安装文件流程

Windows打包 1.首先安装visual Studio 下载地址&#xff1a;https://visualstudio.microsoft.com/zh-hans/ 下载成功后按照下图勾选桌面应用和移动应用下的使用C的桌面开发&#xff0c;勾选右侧安装详细信息中的windows 11/10 sdk 中的任意一个完成安装即可 2.打包Windows …...

凸优化学习:PART3凸优化问题(持续更新)

凸优化问题 凸优化问题的广义定义&#xff1a; 目标函数为凸函数约束集合为凸集 一、优化问题 基本用语 一般优化问题的描述&#xff1a; minimize⁡f0(x)subject to fi(x)⩽0,i1,⋯,mhi(x)0,i1,⋯,p(1)\begin{array}{ll} \operatorname{minimize} & f_0(x) \\ \text { s…...

[ue4] 着色器绑定(Shader Binding)

当我们在ue4中制作了一个美术材质之后&#xff0c;引擎本身会为我们做很多事情&#xff0c;它会把结点翻译为hlsl&#xff0c;生成多个shader变体&#xff0c;并在多个mesh pass中去选择性的调用所需的shader&#xff0c;其中一个重要的过程就是获取shader绑定的数据。 本文将主…...

Rust语言之迭代器

文章目录Rust迭代器Rust迭代器的实现Iterator特型IntoIterator特型for循环与迭代器迭代器类型再看for循环实现自定义迭代器方式一方式二相关参考Rust迭代器 Rust语言内置了迭代器模式&#xff0c;用于实现对一个项的序列进行特定的处理&#xff0c;通常配合for循环使用。当我们…...

TreeSet 与 TreeMap And HashSet 与 HashMap

目录 Map TreeMap put()方法 : get()方法 : Set> entrySet() (重) : foreach遍历 : Set 哈希表 哈希冲突 : 冲突避免 : 冲突解决 ---- > 比散列(开放地址法) : 开散列 (链地址法 . 开链法) 简介 : 在Java中 , TreeSet 与 TreeMap 利用搜索树实现 Ma…...

Java围棋游戏的设计与实现

技术&#xff1a;Java等摘要&#xff1a;围棋作为一个棋类竞技运动&#xff0c;在民间十分流行&#xff0c;为了熟悉五子棋规则及技巧&#xff0c;以及研究简单的人工智能&#xff0c;决定用Java开发五子棋游戏。主要完成了人机对战和玩家之间联网对战2个功能。网络连接部分为S…...

第七十三章 使用 irisstat 实用程序监控 IRIS - 使用选项运行 irisstat

文章目录第七十三章 使用 irisstat 实用程序监控 IRIS - 使用选项运行 irisstat使用选项运行 irisstatirisstat Options第七十三章 使用 irisstat 实用程序监控 IRIS - 使用选项运行 irisstat 使用选项运行 irisstat 不带选项运行 irisstat 会生成基本报告。通常&#xff0c;…...

【博客619】PromQL如何实现Left joins以及不同metrics之间的复杂联合查询

PromQL如何实现Left joins以及不同metrics之间的复杂联合查询 1、场景 我们需要在PromQL中实现类似SQL中的连接查询&#xff1a; SELECT a.value*b.value, * FROM a, b2、不同metrics之间的复杂联合查询 瞬时向量与瞬时向量之间进行数学运算&#xff1a; 例如&#xff1a;根…...

Win11自定义电脑右下角时间显示格式

Win11自定义电脑右下角时间显示格式 一、进入附加设置菜单 1、进入控制面板&#xff0c;选择日期和时间 2、选择修改日期和时间 3、选择修改日历设置 4、选择附加设置 二、自定义时间显示出秒 1、在选项卡中&#xff0c;选时间选项卡 2、在Short time的输入框中输入H:m…...

TrueNas篇-trueNas Scale安装

安装TrueNAS Scale 在尝试trueNas core时发下可以成功安装&#xff0c;但是一直无法成功启动&#xff0c;而且国内对我遇见的错误几乎没有案例&#xff0c;所以舍弃掉了&#xff0c;而且trueNas core是基于Linux的&#xff0c;对Linux的生态好了很多&#xff0c;还可以可以在t…...

element表单搜索框与表格高度自适应

一般在后台管理系统中&#xff0c;表单搜索框和表格的搭配是非常常见的&#xff0c;如下所示&#xff1a; 在该图中&#xff0c;搜索框有五个&#xff0c;分为了两行排列。但根据大多数的UI标准&#xff0c;搜索框默认只显示一行&#xff0c;多余的需要进行隐藏。此时的页面被…...

MySQL使用技巧整理

title: MySQL使用技巧整理 date: 2021-04-11 00:00:00 tags: MySQL categories:数据库 重建索引 索引可能因为删除&#xff0c;或者页分裂等原因&#xff0c;导致数据页有空洞&#xff0c;重建索引的过程会创建一个新的索引&#xff0c;把数据按顺序插入&#xff0c;这样页面…...

七大设计原则之里氏替换原则应用

目录1 里氏替换原则2 里氏替换原则应用1 里氏替换原则 里氏替换原则&#xff08;Liskov Substitution Principle,LSP&#xff09;是指如果对每一个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都替换成 o2 时&#xff0c;程序 P…...

1行Python代码去除图片水印,网友:一干二净

大家好&#xff0c;这里是程序员晚枫。 最近小明在开淘宝店&#xff08;店名&#xff1a;爱吃火锅的少女&#xff09;&#xff0c;需要给自己的原创图片加水印&#xff0c;于是我上次给她开发了增加水印的功能&#xff1a;图片加水印&#xff0c;保护原创图片&#xff0c;一行…...

Connext DDS属性配置参考大全(2)

DDSSecure安全com.rti.servcom.rti.serv.load_plugin...

一起Talk Android吧(第四百九十二回:精简版动画)

文章目录概念介绍使用方法示例代码经验总结各位看官们大家好&#xff0c;上一回中咱们说的例子是"动画集合&#xff1a;AnimatorSetBuilder",这一回中咱们说的例子是" 精简版动画"。闲话休提&#xff0c;言归正转&#xff0c;让我们一起Talk Android吧&…...

seata源码-全局事务回滚服务端源码

这篇博客来记录在发起全局事务回滚时&#xff0c;服务端接收到netty请求是如何处理的 1. 发起全局事务回滚请求 在前面的博客中&#xff0c;有说到过&#xff0c;事务发起者在发现分支事务执行异常之后&#xff0c;会提交全局事务回滚的请求到netty服务端&#xff0c;这里是发…...

【Vue3源码】第一章 effect和reactive

文章目录【Vue3源码】第一章 effect和reactive前言1、实现effect函数2、封装track函数&#xff08;依赖收集&#xff09;3、封装reactive函数4、封装trigger函数&#xff08;依赖触发&#xff09;5、单元测试【Vue3源码】第一章 effect和reactive 前言 今天就正式开始Vue3源码…...

C函数指针

函数指针是指向函数的指针变量。通常我们说的指针变量是指向一个整型、数组或字符型等变量&#xff0c;而函数指针是指向函数。函数指针可以像一般函数一样&#xff0c;用于调用函数、传递参数。函数指针变量的声明&#xff1a;typedef int (*fun_ptr)(int,int); // 声明一个指…...

2023同等学力申请硕士计算机综合国考

同等学力国考报名要开始了 2023年2月15日&#xff0c;中国教育考试网和“全国同等学力人员申请硕士学位管理工作信息平台”&#xff08;https://tdxl.chsi.com.cn&#xff0c;联系服务电话&#xff1a;010-67410388&#xff09;公布报名工作通知。考生须按照通知要求进行注册或…...

英语基础-并列句概述

什么是并列句&#xff1f;并列句就是用连词把独立的句子连接起来&#xff0c;使得句子之间产生并列的逻辑。 1. 并列句中的逻辑 1. 小明步行上学&#xff0c;小红骑自行车上班。 Ming goes to school on foot,and Hong goes to work by bike. 平行逻辑 2. 小红经常玩手机…...

大数据框架之Hadoop:HDFS(一)HDFS概述

1.1HDFS产出背景及定义 HDFS 产生背景 随着数据量越来越大&#xff0c;在一个操作系统存不下所有的数据&#xff0c;那么就分配到更多的操作系统管理的磁盘中&#xff0c;但是不方便管理和维护&#xff0c;迫切需要一种系统来管理多台机器上的文件&#xff0c;这就是分布式文件…...

20230210组会论文总结

目录 【Ultra-High-Definition Low-Light Image Enhancement: A Benchmark and Transformer-Based Method】 【ShuffleMixer: An Efficient ConvNet for Image Super-Resolution】 【A Close Look at Spatial Modeling: From Attention to Convolution 】 【DEA-Net: Single i…...

Python - 数据容器dict(字典)

目录 字典的定义 字典数据的获取 字典的嵌套 字典的各种操作 新增与更新元素 [Key] Value 删除元素 pop和del 清空字典 clear 获取全部的键 keys 遍历字典 容器通用功能总览 字典的定义 使用{}&#xff0c;不过存储的元素是一个个的&#xff1a;键值对&#…...

傻白探索Chiplet,文献阅读笔记汇总(十二)

Summary&#xff08;方便分类管理&#xff09; Article&#xff08;文献出处&#xff09; 方便再次搜索 Data&#xff08;文献数据&#xff09; 总结归纳&#xff0c;方便理解 Comments&#xff08;对文献的想法&#xff09;/Why&#xff08;为什么看这篇文献&#xff09;强…...

#电子电气架构——Vector工具常见问题解决三板斧

我是穿拖鞋的汉子,魔都中一位坚持长期主义的工科男。 今天在与母亲聊天时,得到老家隔壁邻居一位大姐年初去世的消息,挺让自己感到伤感!岁月如流水,想抓都抓不住。想起平时自己加班的那个程度,可能后续也要自己注意身体啦。 老规矩,分享一段喜欢的文字,避免自己成为高知…...

wordpress单页面代码/google关键词指数

作者简介王峰。曾就职于北京拓尔思&#xff0c;任山东区技术总监&#xff0c;山东米迦勒联合创始人&#xff0c;现就职于中安威士。拥有多年数据治理、数据安全相关工作经验。免责声明&#xff1a;本公众号发布的文章均转载自互联网或经作者投稿授权的原创&#xff0c;文末已注…...

网站域名哪里买/便宜的seo网络营销推广

点击上方"蓝字"关注我们&#xff0c;享更多干货&#xff01;前段时间&#xff0c;墨天轮邀请数据库资深专家 李真旭&#xff08;Roger&#xff09; 老师分享了《Oracle中为什么没有Double Write&#xff1f;Oracle支持原子写吗&#xff1f;》&#xff0c;在这里我们将…...

网站建设问题大全/平原县网站seo优化排名

今天给大家分享一个最新开发的Vue3自定义模拟滚动条组件v3scroll。V3Scroll 基于vue3.0开发的轻量级PC端虚拟滚动条组件。支持是否自动隐藏、自定义大小、颜色及层叠等功能。开发灵感来自于之前的vue2版自定义滚动条组件。借鉴了ElementPlus滚动条设计。vue2.x自定义桌面端滚动…...

wordpress用户权限管理/sem优化

目前市面上管理BUG的平台很多&#xff0c;如 QC(Quality Center) 国际顶级&#xff0c;功能强大但收费。 Bugzilla 开源免费&#xff0c;功能还不错&#xff0c;但界面丑陋&#xff0c;配置繁琐。 EasyBUG 在线式&#xff0c;无需配置&#xff0c;但免费版的有10个人的人数…...

软件工程的八个步骤/网站seo具体怎么做

ESP8266 Arduino开发之路&#xff08;2&#xff09;— 连接到无线WiFi路由器 一、前言 ESP8266可以通过WiFi连接到无线路由器&#xff0c;这种方式和手机通过WiFi连接无线路由器的模式是相同的&#xff0c;我们称该模式为无线终端模式(Wireless Station)&#xff0c;即STA工作…...

建网站申请/百度投放平台

服务提供者暴露一个服务的详细过程 首先ServiceConfig类拿到对外提供服务的实际类ref(如&#xff1a;HelloServiceImpl),然后通过ProxyFactory类的getInvoker方法使用ref生成一个AbstractProxyInvoker实例&#xff0c;到这一步就完成具体服务到Invoker的转化。接下来就是Invoke…...