【博学谷学习记录】超强总结,用心分享 | 架构师 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吧&…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...
渗透实战PortSwigger Labs指南:自定义标签XSS和SVG XSS利用
阻止除自定义标签之外的所有标签 先输入一些标签测试,说是全部标签都被禁了 除了自定义的 自定义<my-tag onmouseoveralert(xss)> <my-tag idx onfocusalert(document.cookie) tabindex1> onfocus 当元素获得焦点时(如通过点击或键盘导航&…...
