【Spring】Spring源码(一)循环依赖与bean生命周期

2023-11-12

Spring循环依赖



在这里插入图片描述

如图,类之间的相互引用就是循环依赖,spring是允许这样的循环依赖(前提是单例的,非构造方法注入的情况下)

原型模式时每次注入bean(例如B)都会创建一个新的bean实例,创建B的实例又会注入bean(C),再创建C的实例则又要创建A的实例。所以原型模式使用循环依赖会直接抛出BeanCurrentlyInCreationException

Spring解决循环依赖

首先,Spring内部维护了三个Map,也就是我们通常说的三级缓存。

  • singletonObjects 俗称“单例池”“容器”,缓存创建完成单例Bean的地方。
  • earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.
  • singletonFactories 映射创建Bean的原始工厂

在这里插入图片描述

图片来源:https://blog.csdn.net/qq_35190492

所谓的循环依赖其实无非就是属性注入,或者自动注入, 故而搞明白循环依赖就需要去研究spring自动注入的源码;spring的属性注入属于spring bean的生命周期一部分。

理解Spring生命周期:

  1. spring bean——受spring容器管理的对象,可能经过了完整的spring bean生命周期,最终存在spring容器当中;一个bean一定是个对象
  2. 对象——任何符合java语法规则实例化出来的对象,但是一个对象并不一定是spring bean;

Spring生命周期

Class--------》beanDefinition----------》bean 与普通对象不同的是bean对象要经过

首先,Java虚拟机将类的class文件加载到方法区,然后扫描是否有注入bean的操作。 对于要注入的,spring会new一个BeanDefination接口(这个接口包括各种对bean的属性判断方法,例如GenericBeanDefinition)的实现类,然后将实现类对象即bean的各种属性(属性例如:beanClassName=“A”, beanClass=A.class,scope=“singleton”,…)put到BeanDefinitionMap的beanDefinitionMap中去,通过finishiBeanFactoryInitialization(beanFactory)方法获取beanDefinitionMap中的beanName开始验证,验证成功后new一个bean对象放到spring单例池SingletonObjects(map类型)中,【注意:原型对象不是在spring初始化的时候new一个bean,而是在getBean的时候】bean对象到单例池之前还可以调用BeanFactoryPostProcessor接口的实现类进行扩展。

具体步骤如下:

1.扫描:扫描是否有bean注入

例如通过xml方式,JavaConfig方式或注解方式的bean注入。

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
 this();
 register(annotatedClasses);//注册配置类
 refresh();// 真正初始化bean的方法
}
2.解析:解析是不是单例模式,是否懒加载…

在一个for循环中 {

​ new一个BeanDefinition类的子类GenericBeanDefinition对象genericBeanDefinition,这个对象包括setBeanClass,setBeanClassName,setScope…等方法。

​ 定义一个BeanDefinitionMap的beanDefinitionMap,将GenericBeanDefinition对象put到beanDefinitionMap<“xxx”,genericBeanDefinition>中。

​ 此外还会定义一个List的beanNames,beanNames.add(xxx),负责存储beanDefinitionMap中的key,用于遍历GenericBeanDefinition对象。

}

3.调用扩展:查看是否赋予额外的扩展功能

如何扩展:

创建一个类实现BeanFactoryPostProcessor接口,继承postProcessBeanFactory方法,方法会传一个beanFactory(就是Spring工厂,Spring工厂中就包括了上一步定义的beanDefinitionMap),通过beanFactory调用getBeanDefinition获取beanDefinitionMap中的BeanDefinition对象。

// 扩展实现,更改beanClass
@Component	//加上才会扫描到,否则不起作用
public class test implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition bd = beanFactory.getBeanDefinition("A");
        // 这里写一些扩展功能,比如更改beanName,禁止循环依赖...
        bd.setBeanClass(B.Class);
    }
}
// getBeanDefinition内部方法
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
    // 在这里获取map中的beanDefinition对象,beanName未声明则默认为自动驼峰命名。
    BeanDefinition bd = this.beanDefinitionMap.get(beanName);
    if (bd == null) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No bean named '" + beanName + "' found in " + this);
        }
        throw new NoSuchBeanDefinitionException(beanName);
    }
    return bd;
}
4.验证:根据第二步解析获得的属性判断要不要new一个Bean

具体判断bean是不是原型的;装配模式是byType还是byName;构造方法有没有参数,参数合不合理;

合理就实例化Bean:new一个 Bean对象到Spring的单例池SingletonObjects(Map类型)中。

验证代码

finishBeanFactoryInitialization

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 。。。
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}

preInstantiateSingletons

public void preInstantiateSingletons() throws BeansException {
		//。。。
 	// 取出beanDefinitionMap存放的beanNames
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// 遍历并验证beanNames,进行实例化
		for (String beanName : beanNames) {
         // 验证是不是单例、懒加载。。。
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				//。。。
             //实例化方法
              getBean(beanName);
              //。。。
			}
		}
	}

说明

// 实例化所有还剩下的、非懒加载的单例对象
finishiBeanFactoryInitialization(beanFactory);
// 内部继续调用preInstantiateSingletons方法
beanFactort.preInstantiateSingletons();
// 获取第二步存入List的beanNames,进行遍历验证,例如是不是单例,是否懒加载...等,验证之后开始实例化
getBean(beanName);
// 内部调用doGetBean方法
5.实例化:实例化Bean

一级缓存: private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

二级缓存: private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

三级缓存: private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

getBean

public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}

doGetBean

protected <T> T doGetBean(final String name, 。。。) throws BeansException {
	// 验证beanName合法性
final String beanName = transformedBeanName(name);
// 检查单例池中是否已经有了这个bean
Object sharedInstance = getSingleton(beanName);
// 若单例池中没有此bean,
if (sharedInstance != null && args == null) {...}
else {
  // 判断这个类是不是在创建过程中,是则抛出异常
  if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
  }
}

//继续进行一系列验证...

// 如果是单例
if (mbd.isSingleton()) {
  //...
  // bean开始创建
	return createBean(beanName, mbd, args);
}
//...
}

getSingleton //判断单例池中是否已经有了这个bean

/** 单例对象缓存/单例池(beanName->bean实例): bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	Object singletonObject = this.singletonObjects.get(beanName);
	// 如果单例池中没有这个bean,且当前bean正在创建,则去三级缓存中取bean。
 // 循环依赖中,第一次初始化bean时‘正在创建’条件不成立,当循环到第二次初始化bean时,‘正在创建’条件成立
 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     // 条件一旦成立,就会从三级缓存中取出bean,移到二级缓存中
     synchronized (this.singletonObjects) {
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
             ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
             if (singletonFactory != null) {
                 singletonObject = singletonFactory.getObject();
                 this.earlySingletonObjects.put(beanName, singletonObject);
                 this.singletonFactories.remove(beanName);
             }
         }
     }
 }
	return singletonObject;
}

createBean

protected Object createBean(String beanName, RootBeanDefinition mbd, ...)throws BeanCreationException {
//...
RootBeanDefinition mbdToUse = mbd;
//...
// 第一次调用后置处理器,允许用代理对象替换目标对象
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
// ...
// 进入doCreateBean方法创建bean实例
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}

doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {
// 实例化bean
BeanWrapper instanceWrapper = null;
// 单例的,则从bean工厂里移除
if (mbd.isSingleton()) {
  instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
  // 实例化对象,createBeanInstance里第二次调用后置处理器(通过构造方法反射实例化对象)
  instanceWrapper = createBeanInstance(beanName, mbd, args);
  //。。。
  // 第三次后置处理器,修改合并的bean定义
  applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
  //。。。
  // 是否允许循环依赖(单例的&&允许循环依赖&&正在创建)【allowCircularReferences默认为true】
  // 可以通过setAllowCircularReferences(false);设置不支持循环依赖
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
			//。。。
      // 第四次调用后置处理器,判断是否需要AOP。将未完成的bean存入三级缓存。
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
  // 初始化bean实例
  Object exposedObject = bean;
		try {
      // 填充属性,即自动注入。内部完成第五,第六次后置处理器调用
       	//注入属性时就会发现循环依赖的bean没有。
			populateBean(beanName, mbd, instanceWrapper);
      // 初始化Spring。内部完成第七,第八次后置处理器调用
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
}
}

============================


  • Spring是如何默认支持循环依赖的?

    比如说要在A类中注入B,B类中注入A,在Spring生命周期的doGetBean方法会将A放入表示正在创建的集合中。到了doCreateBean方法中,会通过allowCircularReferences属性判断Spring容器是否允许循环依赖,allowCircularReferences属性默认为true,所以Spring容器是默认开启循环依赖的。当判断允许循环依赖时,Spring会将未完成的bean:A存入第三级缓存singletonFactories中。第二次循环创建B注入A时,到doGetBean方法中会调用getSingleton方法判断单例池,也就是第一级缓存singletonObjects中是否有bean:A,如果没有且A正在创建,就会先从二级缓存earlySingletonObjects 中取,二级取不到就从第三级缓存中取出bean移到第二级缓存中,并清除三级缓存中的这个bean。

    • 第一次循环时要注入的bean刚开始创建,到了第二次循环才会判断出这个bean正在创建

    • 可以手动调用初始化spring环境的主要方法refresh(),并在此之前使用setAllowCircularReferences(false)关闭循环依赖。

    • 也可以通过扩展功能,实现BeanFactoryPostProcessor接口,获取BeanDefinition对象,设置属性关闭循环依赖。

    • 二级缓存earlySingletonObjects 的作用:出现循环依赖时,保存暴露的早期对象,防止工厂重复创建对象,提升性能。

      参考:

      https://blog.csdn.net/u012098021/article/details/107352463/

  • @Autowired与@Resource有什么区别?

    @Autowired默认使用byType,匹配不到bean时再使用byName,此时的name是属性的名(xml中的byName是根据set方法名)。可以使用@Qualifier()指定bean的id。默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) 。

    @Resource默认使用byName,当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。可以@Resource(name = / type =)指定name与type。

    @Autowired是由后置处理器AutowiredAnnotationBeanPostProcessor类解析的

    @Resource是由后置处理器CommonAnnotationBeanPostProcessor类解析的

  • Spring原理(IOC与AOP的理解)?

    ​ spring框架最核心的就是ioc和AOP了,其中ioc控制反转是一种设计模式,主要为了降低代码的耦合度,主要使用依赖注入实现。创建对象不再用new,而是通过xml、注解、Javaconfig的方式装配bean到ioc容器中,再通过构造函数方式、setter方法或@Autowired/@Resource注解方式注入到需要依赖的类中,从而实现将类和类之间的依赖关系交给spring容器去管理。

    ​ AOP面向切面编程就是将系统性的服务,比如说日志模块,安全模块,事务管理等通过自定义注解、切面类和反射实现切面化,并用声明的方法将他们应用到他们需要影响的组件中去,从而使业务组件具有更高的内聚性,只需关心自身的业务,而不用为了实现系统性的服务导致代码复杂。

  • Spring Bean的生命周期?

    1. 首先,Java虚拟机将类的class文件加载到方法区,然后扫描是否有注入bean的操作,进行Bean的实例化。对于要注入的,spring会new一个BeanDefination接口(这个接口包括各种对bean的属性判断方法,例如GenericBeanDefinition)的实现类
    2. 实例化后对bean进行属性注入。然后将实现类对象即bean的各种属性(属性例如:beanClassName=“A”, beanClass=A.class,scope=“singleton”,…)put到BeanDefinitionMap的beanDefinitionMap中去,通过finishiBeanFactoryInitialization(beanFactory)方法获取beanDefinitionMap中的beanName开始验证
    3. 初始化之前实现BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口,获取bean的name,类加载器,BeanFactory容器等资源。
    4. 再实现ApplicationContextAware接口,返回当前的ApplicationContext应用上下文对象。
    5. 实现BeanPostProcessor接口,bean初始化之前执行postProcessBeforeInitialization()方法。【bean对象到单例池之前还可以调用BeanFactoryPostProcessor接口的实现类进行扩展。验证成功后new一个bean对象放到spring单例池SingletonObjects(map类型)中】
    6. 实现InitializingBean接口,调用afterPropertiesSet()方法初始化bean。如果bean使用init-method声明了初始化方法,该方法也会被调用
    7. 实现BeanPostProcessor接口,bean初始化之后执行postProcessAfterInitialization()方法。
    8. 此时,Bean已初始化完成,可以被应用程序使用。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
    9. 实现DisposableBean接口,执行destory()方法,销毁bean。如果bean使用了destory-method 声明销毁方法,该方法也会被调用。

    ​ 【注意:原型对象不是在spring初始化的时候new一个bean,而是在getBean的时候】

    ​ 【

    • BeanPostProcessor

    • InstantiationAwareBeanPostProcessor

      Spring扩展中最重要的两个接口!InstantiationAwareBeanPostProcessor作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。】

      【 Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如BeanNameAware可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在初始化阶段之前调用的!】

      参考:

      https://www.jianshu.com/p/1dec08d290c1

      https://www.cnblogs.com/javazhiyin/p/10905294.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Spring】Spring源码(一)循环依赖与bean生命周期 的相关文章

  • 插入最大日期(独立于数据库)

    在我的本地设置中 我使用一个简单的 H2 数据库 托管 解决方案将有另一个 类似但不相同 数据库 我需要将最大可能日期插入到日期时间列中 我尝试使用 Instant MAX 但是 这会导致列中出现 169104626 12 11 20 08
  • Java AES 128 加密方式与 openssl 不同

    我们遇到了一种奇怪的情况 即我们在 Java 中使用的加密方法会向 openssl 生成不同的输出 尽管它们在配置上看起来相同 使用相同的键和 IV 文本 敏捷的棕色狐狸跳过了懒狗 加密为 Base64 字符串 openssl A8cMRI
  • 比较两个文本文件的最快方法是什么,不将移动的行视为不同

    我有两个文件非常大 每个文件有 50000 行 我需要比较这两个文件并识别更改 然而 问题是如果一条线出现在不同的位置 它不应该显示为不同的 例如 考虑这个文件A txt xxxxx yyyyy zzzzz 文件B txt zzzzz xx
  • 画透明圆,外面填充

    我有一个地图视图 我想在其上画一个圆圈以聚焦于给定区域 但我希望圆圈倒转 也就是说 圆的内部不是被填充 而是透明的 其他所有部分都被填充 请参阅这张图片了解我的意思 http i imgur com zxIMZ png 上半部分显示了我可以
  • Java 8 流 - 合并共享相同 ID 的对象集合

    我有一系列发票 class Invoice int month BigDecimal amount 我想合并这些发票 这样我每个月都会收到一张发票 金额是本月发票金额的总和 例如 invoice 1 month 1 amount 1000
  • 具有 java XSLT 扩展的数组

    我正在尝试使用 java 在 XSLT 扩展中使用数组 我收到以下错误 Caused by java lang ClassCastException org apache xpath objects XObject cannot be ca
  • 使用 SQLITE 按最近的纬度和经度坐标排序

    我必须获得一个 SQLite SQL 语句 以便在给定初始位置的情况下按最近的纬度和经度坐标进行排序 这是我在 sqlite 数据库中的表的例句 SELECT id name lat lng FROM items EXAMPLE RESUL
  • 很好地处理数据库约束错误

    再一次 它应该很简单 我的任务是在我们的应用程序的域对象中放置一个具有唯一约束的特定字段 这本身并不是一个很大的挑战 我刚刚做了以下事情 public class Location more fields Column unique tru
  • 使用过滤器@ComponentScan所有包的危险

    我现在正在开发一个概念应用程序 我想使用组件扫描来使用特定的自定义 Spring 元注释来获取类路径上任何位置的所有类 我的注释如下所示 Target value ElementType TYPE Retention value Reten
  • 以编程方式在java的resources/source文件夹中创建文件?

    我有两个资源文件夹 src 这是我的 java 文件 资源 这是我的资源文件 图像 properties 组织在文件夹 包 中 有没有办法以编程方式在该资源文件夹中添加另一个 properties 文件 我尝试过这样的事情 public s
  • 编辑文件名在 JComboBox 中的显示方式,同时保持对文件的访问

    我对 Java 很陌生 对堆栈溢出也很陌生 我正在尝试利用 JMF API 创建一个用 Java 编码的简单媒体播放器 到目前为止 我已经能够设置一个简单的队列 播放列表来使用JComboBox called playListHolder
  • Javafx过滤表视图

    我正在尝试使用文本字段来过滤表视图 我想要一个文本字段 txtSearch 来搜索 nhs 号码 名字 姓氏 和 分类类别 我尝试过在线实施各种解决方案 但没有运气 我对这一切仍然很陌生 所以如果问得不好 我深表歉意 任何帮助将不胜感激 我
  • 欧洲中部时间 14 日 3 月 30 日星期五 00:00:00 至 日/月/年

    我尝试解析格式日期Fri Mar 30 00 00 00 CET 14至 日 月 年 这是我的代码 SimpleDateFormat formatter new SimpleDateFormat dd MM yyyy System out
  • Struts 2 + Sitemesh 3 集成 - FreemarkerDecoratorServlet 中的 NPE

    我将 Struts 2 版本 2 3 14 3 与 Sitemesh 3 版本 3 0 alpha 2 一起使用 并且在某些情况下遇到 NullPointerException 首先 这是我的 web xml 中的 struts2 site
  • 如何在JSTL中调​​用java方法? [复制]

    这个问题在这里已经有答案了 这可能是重复的问题 我只想调用不是 getter 或 setter 方法的方法例如 xyz 类的 makeCall someObj stringvalue Java类 Class XYZ public Strin
  • java.lang.NumberFormatException: Invalid int: "3546504756",这个错误是什么意思?

    我正在创建一个 Android 应用程序 并且正在从文本文件中读取一些坐标 我在用着Integer parseInt xCoordinateStringFromFile 将 X 坐标转换为整数 Y 坐标的转换方法相同 当我运行该应用程序时
  • 测试弱引用

    在 Java 中测试弱引用的正确方法是什么 我最初的想法是执行以下操作 public class WeakReferenceTest public class Target private String value public Targe
  • 游戏内的java.awt.Robot?

    我正在尝试使用下面的代码来模拟击键 当我打开记事本时 它工作正常 但当我打开我想使用它的游戏时 它没有执行任何操作 所以按键似乎不起作用 我尝试模拟鼠标移动和点击 这些动作确实有效 有谁知道如何解决这个问题 我发现这个问题 如何在游戏中使用
  • 将 Azure AD 高级自定义角色与 Spring Security 结合使用以进行基于角色的访问

    我创建了一个演示 Spring Boot 应用程序 我想在其中使用 AD 身份验证和授权 并使用 AD 和 Spring Security 查看 Azure 文档 我执行了以下操作 package com myapp contactdb c
  • 调整添加的绘制组件的大小和奇怪的摆动行为

    这个问题困扰了我好几天 我正在制作一个特殊的绘画程序 我制作了一个 JPanel 并添加了使用 Paint 方法绘制的自定义 jComponent 问题是 每当我调整窗口大小时 所有添加的组件都会 消失 或者只是不绘制 因此我最终会得到一个

随机推荐

  • Codeium的使用

    官网 CodeiumCodeium offers best in class AI code completion search all for free It supports over 40 languages and integrat
  • 连续型随机变量密度函数与累积密度函数

    1 连续性随机变量的概率密度函数 注意 f x 是非负的可积函数 以及在负无穷到正无穷区间内的累积概率为1 累积概率的取值区间是从负无穷到正无穷 但是概率密度函数的取值并不是从负无穷到正无穷 尤其是在实际问题中 比如说报童模型中的报纸订购量
  • Python异常值预警

    coding utf 8 基于3sigma的异常值检测 import numpy as np import pandas as pd import matplotlib pyplot as plt 导入绘图库 n 3 n sigma cat
  • Hive---分区表和分桶表

    分区表和分桶表区别如下 1 分区使用的是表外字段 需要指定字段类型 分桶使用的是表内字段 已经知道字段类型 不需要再指定 2 分区通过关键字partitioned by partition name string 声明 分桶表通过关键字cl
  • 「硬见小百科」10分钟详细图解MOS管的结构原理

    什么是MOS管 MOS管是金属 metal 氧化物 oxide 半导体 semiconductor 场效应晶体管 或者称是金属 绝缘体 insulator 半导体 MOS管的source和drain是可以对调的 他们都是在P型backgat
  • AndroidSweetSheet类库的使用

    普通的弹框多以dialog的形式弹出 这个类库是以布局的形式弹出来的 以下是作者的源类库中的包定义 SweetSheet class 代码中主要使用的类 在初始化时候需要传入依附的父布局 以后弹出的view就是要加入到这个view中的 Sw
  • 微信小程序wx.getLocation()报错以及解决方法

    问题介绍 使用wx getLocation 来获取当前地理位置的经纬度 主要代码如下 wx getLocation success function res 经度 let lat res longitude 纬度 let lng res l
  • RK3568 GPIO 按键事件响应

    目录 adb shell getevent查看事件 设备树添加事件驱动 cat查看事件详细内容 author daisy skye的博客 CSDN博客 嵌入式 Qt Linux领域博主 adb shell getevent查看事件 130
  • 【Java基础】计算机程序语言发展史

    程序语言发展史 第一代 机器语言 指令以二进制代码形式存在 第二代 汇编语言 使用助记符表示一条机器指令 第三代 高级语言 C Pascal Fortran等面向过程的语言 C 面向过程 面向对象 Java 跨平台的纯面向对象的语言 NET
  • nginx配置文件之“location ~ .*\. (js

    nginx里面的location配置语法 location url 选项参数匹配 表示精准匹配 表示uri以某个常规字符串开头 理解为匹配 url路径即可 nginx不对url做编码 因此请求为 static 20 aa 可以被规则 sta
  • Proteus仿真STM32的课设实例4——stm32简易测频率

    本教程是基于STM32的嵌入式仿真大作业 源文件链接 https pan baidu com s 1fU4isp7UXTtUFHwpt76zzw pwd 9x7t 提取码 9x7t 使用 Proteus 仿真32单片机 实现了可以测量正弦波
  • 不想dto套dto可以这样写

    之前都是要新建个dto文件的 偶然看到别人这样写 简单记录一下 Data public class GdtDailyBalanceContent List
  • 编程教育是孩子计算机启蒙的好伙伴

    网络上关于 少儿编程 的利好资讯铺天盖地 印证着这一行业的欣欣向荣 国际上 很多发达国家早已把编程教育纳入小学课程表 重视儿童学习编程成为教育的主流 格物斯坦小坦克认为 由此看来从小就培养孩子学习编程 真的有必要 其实 大多数家长对编程可能
  • 没有公网IP,怎么实现外网访问内网视频监控

    以海康威视监控为例 先在内网部署好监控系统 通过本地浏览器输入监控设备管理地址 登录管理后台 在管理后台可成功查看摄像头视频画面即内网监控系统搭建成功 找到配置界面查看设备端口信息 记下HTTP访问端口80 默认网址访问端口 及服务数据传输
  • devops

    1 环境准备 使用的虚拟机环境centos7 8 Jenkins 10 21 90 111 k8sMaster 10 21 90 113 k8sNode 10 21 90 114 同步主机时区为亚洲 并同步时间 root linux nod
  • React Hooks的history带参数跳转,并获取参数

    跳转页面带参数 history push pathname admin test query id user id name user name 跳转后获取参数 const query props console log query nam
  • esp8266学习笔记(5)——连接wifi、AP、UDP通信

    终于开始接触网络了 基础不行 这个摸索了有点久 还好网上资料多 有些细节还是没有怎么吃透 哈哈 开始联网了 ESP8266有三种模式 station模式 0x01 soft AP模式 0x02 soft AP station模式 0x03
  • spring 在容器中一个bean依赖另一个bean 需要通过ref方式注入进去 通过构造器 或property

    spring 在容器中一个bean依赖另一个bean 需要通过ref方式注入进去 通过构造器 或property
  • 项目管理培训记录

    PMP 十大知识领域 五大过程组 IPD 易经
  • 【Spring】Spring源码(一)循环依赖与bean生命周期

    Spring循环依赖 文章目录 Spring循环依赖 Spring解决循环依赖 Spring生命周期 1 扫描 扫描是否有bean注入 2 解析 解析是不是单例模式 是否懒加载 3 调用扩展 查看是否赋予额外的扩展功能 4 验证 根据第二步