【博学谷学习记录】超强总结,用心分享 | 架构师 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流程源码剖析
* 问题1:Spring和SpringMVC整合使用时,会创建一个容器还是两个容器(父子容器?)
答:会创建两个容器对象,并且是有父子容器关系,对于Spring容器主要管理业务层/持久层/事务层对象,对于SpringMVC容器主要负责Web层对象的维护,SpringMVC容器为子容器,Spring容器为父容器(其实就是设置了SpringMVC的一个parent属性)* 问题2:DispatcherServlet初始化过程中做了什么?
答:在DispatcherServlet初始化init方法时,首先构建了子容器对象,并且根据spring-mvc.xml进行文件解析,这里关注<mvc:annotation-driven>标签(开启注解模式驱动),它向当前的BeanDefinitionMap中注册了一些Bean定义,其中有RequestMappingHandlerMapping、RequestMappingHandlerAdapter、ExceptionHandlerExceptionResolver,以支持对使用了@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:RequestMappingInfo)
RequestMappingHandlerAdapter:在它的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应用部署初始化
流程执行图:
可以发现,在tomcat
下web应用
的初始化流程是,先初始化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);
}
该接口只有两个方法contextInitialized
和contextDestroyed
,这里采用的是观察者模式,也称为为订阅-发布模式,实现了该接口的listener
会向发布者进行订阅,当Web应用
初始化或销毁时会分别调用上述两个方法。
继续看ContextLoaderListener
,该listener
实现了ServletContextListener
接口,因此在Web应用
初始化时会调用该方法,该方法的具体实现如下:
/*** Initialize the root web application context.*/@Overridepublic void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext());}
ContextLoaderListener
的contextInitialized()
方法直接调用了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附带了大量命令,每个命令都是唯一的,并在特定情况下使用。Linux timeout命令的一个属性是时间限制。可以为任何命令设置时间限制。如果时间到期,命令将停止执行。 如何使用timeout命令 我们将解释如何使用Linux timeout命令 timeout […...
西湖论剑初赛web wp
Node Magical Login 简单的js代码审计。 Flag分成了两部分。 第一部分: 这里就简单的判断了一下user是否等于admin,直接绕过。 第二部分: checkcode ! “aGr5AtSp55dRacer”,让其为真,利用数组绕过。 Flag为&#x…...
【YOLOv8/YOLOv7/YOLOv5系列算法改进NO.55】融入美团最新QARepVGG
文章目录 前言一、解决问题二、基本原理三、添加方法四、总结前言 作为当前先进的深度学习目标检测算法YOLOv8,已经集合了大量的trick,但是还是有提高和改进的空间,针对具体应用场景下的检测难点,可以不同的改进方法。此后的系列文章,将重点对YOLOv8的如何改进进行详细…...
Flutter Windows端打包并生成可安装文件流程
Windows打包 1.首先安装visual Studio 下载地址:https://visualstudio.microsoft.com/zh-hans/ 下载成功后按照下图勾选桌面应用和移动应用下的使用C的桌面开发,勾选右侧安装详细信息中的windows 11/10 sdk 中的任意一个完成安装即可 2.打包Windows …...
凸优化学习:PART3凸优化问题(持续更新)
凸优化问题 凸优化问题的广义定义: 目标函数为凸函数约束集合为凸集 一、优化问题 基本用语 一般优化问题的描述: minimizef0(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中制作了一个美术材质之后,引擎本身会为我们做很多事情,它会把结点翻译为hlsl,生成多个shader变体,并在多个mesh pass中去选择性的调用所需的shader,其中一个重要的过程就是获取shader绑定的数据。 本文将主…...
Rust语言之迭代器
文章目录Rust迭代器Rust迭代器的实现Iterator特型IntoIterator特型for循环与迭代器迭代器类型再看for循环实现自定义迭代器方式一方式二相关参考Rust迭代器 Rust语言内置了迭代器模式,用于实现对一个项的序列进行特定的处理,通常配合for循环使用。当我们…...
TreeSet 与 TreeMap And HashSet 与 HashMap
目录 Map TreeMap put()方法 : get()方法 : Set> entrySet() (重) : foreach遍历 : Set 哈希表 哈希冲突 : 冲突避免 : 冲突解决 ---- > 比散列(开放地址法) : 开散列 (链地址法 . 开链法) 简介 : 在Java中 , TreeSet 与 TreeMap 利用搜索树实现 Ma…...
Java围棋游戏的设计与实现
技术:Java等摘要:围棋作为一个棋类竞技运动,在民间十分流行,为了熟悉五子棋规则及技巧,以及研究简单的人工智能,决定用Java开发五子棋游戏。主要完成了人机对战和玩家之间联网对战2个功能。网络连接部分为S…...
第七十三章 使用 irisstat 实用程序监控 IRIS - 使用选项运行 irisstat
文章目录第七十三章 使用 irisstat 实用程序监控 IRIS - 使用选项运行 irisstat使用选项运行 irisstatirisstat Options第七十三章 使用 irisstat 实用程序监控 IRIS - 使用选项运行 irisstat 使用选项运行 irisstat 不带选项运行 irisstat 会生成基本报告。通常,…...
【博客619】PromQL如何实现Left joins以及不同metrics之间的复杂联合查询
PromQL如何实现Left joins以及不同metrics之间的复杂联合查询 1、场景 我们需要在PromQL中实现类似SQL中的连接查询: SELECT a.value*b.value, * FROM a, b2、不同metrics之间的复杂联合查询 瞬时向量与瞬时向量之间进行数学运算: 例如:根…...
Win11自定义电脑右下角时间显示格式
Win11自定义电脑右下角时间显示格式 一、进入附加设置菜单 1、进入控制面板,选择日期和时间 2、选择修改日期和时间 3、选择修改日历设置 4、选择附加设置 二、自定义时间显示出秒 1、在选项卡中,选时间选项卡 2、在Short time的输入框中输入H:m…...
TrueNas篇-trueNas Scale安装
安装TrueNAS Scale 在尝试trueNas core时发下可以成功安装,但是一直无法成功启动,而且国内对我遇见的错误几乎没有案例,所以舍弃掉了,而且trueNas core是基于Linux的,对Linux的生态好了很多,还可以可以在t…...
element表单搜索框与表格高度自适应
一般在后台管理系统中,表单搜索框和表格的搭配是非常常见的,如下所示: 在该图中,搜索框有五个,分为了两行排列。但根据大多数的UI标准,搜索框默认只显示一行,多余的需要进行隐藏。此时的页面被…...
MySQL使用技巧整理
title: MySQL使用技巧整理 date: 2021-04-11 00:00:00 tags: MySQL categories:数据库 重建索引 索引可能因为删除,或者页分裂等原因,导致数据页有空洞,重建索引的过程会创建一个新的索引,把数据按顺序插入,这样页面…...
七大设计原则之里氏替换原则应用
目录1 里氏替换原则2 里氏替换原则应用1 里氏替换原则 里氏替换原则(Liskov Substitution Principle,LSP)是指如果对每一个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都替换成 o2 时,程序 P…...
1行Python代码去除图片水印,网友:一干二净
大家好,这里是程序员晚枫。 最近小明在开淘宝店(店名:爱吃火锅的少女),需要给自己的原创图片加水印,于是我上次给她开发了增加水印的功能:图片加水印,保护原创图片,一行…...
Connext DDS属性配置参考大全(2)
DDSSecure安全com.rti.servcom.rti.serv.load_plugin...
一起Talk Android吧(第四百九十二回:精简版动画)
文章目录概念介绍使用方法示例代码经验总结各位看官们大家好,上一回中咱们说的例子是"动画集合:AnimatorSetBuilder",这一回中咱们说的例子是" 精简版动画"。闲话休提,言归正转,让我们一起Talk Android吧&…...
seata源码-全局事务回滚服务端源码
这篇博客来记录在发起全局事务回滚时,服务端接收到netty请求是如何处理的 1. 发起全局事务回滚请求 在前面的博客中,有说到过,事务发起者在发现分支事务执行异常之后,会提交全局事务回滚的请求到netty服务端,这里是发…...
【Vue3源码】第一章 effect和reactive
文章目录【Vue3源码】第一章 effect和reactive前言1、实现effect函数2、封装track函数(依赖收集)3、封装reactive函数4、封装trigger函数(依赖触发)5、单元测试【Vue3源码】第一章 effect和reactive 前言 今天就正式开始Vue3源码…...
C函数指针
函数指针是指向函数的指针变量。通常我们说的指针变量是指向一个整型、数组或字符型等变量,而函数指针是指向函数。函数指针可以像一般函数一样,用于调用函数、传递参数。函数指针变量的声明:typedef int (*fun_ptr)(int,int); // 声明一个指…...
2023同等学力申请硕士计算机综合国考
同等学力国考报名要开始了 2023年2月15日,中国教育考试网和“全国同等学力人员申请硕士学位管理工作信息平台”(https://tdxl.chsi.com.cn,联系服务电话:010-67410388)公布报名工作通知。考生须按照通知要求进行注册或…...
英语基础-并列句概述
什么是并列句?并列句就是用连词把独立的句子连接起来,使得句子之间产生并列的逻辑。 1. 并列句中的逻辑 1. 小明步行上学,小红骑自行车上班。 Ming goes to school on foot,and Hong goes to work by bike. 平行逻辑 2. 小红经常玩手机…...
大数据框架之Hadoop:HDFS(一)HDFS概述
1.1HDFS产出背景及定义 HDFS 产生背景 随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件…...
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 遍历字典 容器通用功能总览 字典的定义 使用{},不过存储的元素是一个个的:键值对&#…...
傻白探索Chiplet,文献阅读笔记汇总(十二)
Summary(方便分类管理) Article(文献出处) 方便再次搜索 Data(文献数据) 总结归纳,方便理解 Comments(对文献的想法)/Why(为什么看这篇文献)强…...
#电子电气架构——Vector工具常见问题解决三板斧
我是穿拖鞋的汉子,魔都中一位坚持长期主义的工科男。 今天在与母亲聊天时,得到老家隔壁邻居一位大姐年初去世的消息,挺让自己感到伤感!岁月如流水,想抓都抓不住。想起平时自己加班的那个程度,可能后续也要自己注意身体啦。 老规矩,分享一段喜欢的文字,避免自己成为高知…...
wordpress单页面代码/google关键词指数
作者简介王峰。曾就职于北京拓尔思,任山东区技术总监,山东米迦勒联合创始人,现就职于中安威士。拥有多年数据治理、数据安全相关工作经验。免责声明:本公众号发布的文章均转载自互联网或经作者投稿授权的原创,文末已注…...
网站域名哪里买/便宜的seo网络营销推广
点击上方"蓝字"关注我们,享更多干货!前段时间,墨天轮邀请数据库资深专家 李真旭(Roger) 老师分享了《Oracle中为什么没有Double Write?Oracle支持原子写吗?》,在这里我们将…...
网站建设问题大全/平原县网站seo优化排名
今天给大家分享一个最新开发的Vue3自定义模拟滚动条组件v3scroll。V3Scroll 基于vue3.0开发的轻量级PC端虚拟滚动条组件。支持是否自动隐藏、自定义大小、颜色及层叠等功能。开发灵感来自于之前的vue2版自定义滚动条组件。借鉴了ElementPlus滚动条设计。vue2.x自定义桌面端滚动…...
wordpress用户权限管理/sem优化
目前市面上管理BUG的平台很多,如 QC(Quality Center) 国际顶级,功能强大但收费。 Bugzilla 开源免费,功能还不错,但界面丑陋,配置繁琐。 EasyBUG 在线式,无需配置,但免费版的有10个人的人数…...
软件工程的八个步骤/网站seo具体怎么做
ESP8266 Arduino开发之路(2)— 连接到无线WiFi路由器 一、前言 ESP8266可以通过WiFi连接到无线路由器,这种方式和手机通过WiFi连接无线路由器的模式是相同的,我们称该模式为无线终端模式(Wireless Station),即STA工作…...
建网站申请/百度投放平台
服务提供者暴露一个服务的详细过程 首先ServiceConfig类拿到对外提供服务的实际类ref(如:HelloServiceImpl),然后通过ProxyFactory类的getInvoker方法使用ref生成一个AbstractProxyInvoker实例,到这一步就完成具体服务到Invoker的转化。接下来就是Invoke…...