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

Spring实例化源码解析之Bean的实例化(十二)

前言

本章开始分析finishBeanFactoryInitialization(beanFactory)方法,直译过来就是完成Bean工厂的初始化,这中间就是非lazy单例Bean的实例化流程。ConversionService在第十章已经提前分析了。重点就是最后一句,我们的bean实例化分析就从这里开始。

本章主要是实例化流程的分析,不会太深入到细节

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// Initialize conversion service for this context.// ConversionService(转换服务)在spring框架中用于处理类型转换的任务。它提供了一种统一的方式来执行各种类型之间的转换操作,// 包括字符串到其他类型的转换、日期和时间的转换、数字类型的转换等。if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));}// Register a default embedded value resolver if no BeanFactoryPostProcessor// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:// at this point, primarily for resolution in annotation attribute values.if (!beanFactory.hasEmbeddedValueResolver()) {beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));}// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);for (String weaverAwareName : weaverAwareNames) {getBean(weaverAwareName);}// Stop using the temporary ClassLoader for type matching.beanFactory.setTempClassLoader(null);// Allow for caching all bean definition metadata, not expecting further changes.beanFactory.freezeConfiguration();// Instantiate all remaining (non-lazy-init) singletons.beanFactory.preInstantiateSingletons();}

预处理单例bean

preInstantiateSingletons() 是 Spring 容器的一个方法,它用于预实例化所有的单例(Singleton)Bean。

@Overridepublic void preInstantiateSingletons() throws BeansException {if (logger.isTraceEnabled()) {logger.trace("Pre-instantiating singletons in " + this);}// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...for (String beanName : beanNames) {// 通过bean的名称去获取bean的定义信息RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);// 不是抽象类&&是单例的&&不是懒加载if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {if (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}}else {getBean(beanName);}}}// Trigger post-initialization callback for all applicable beans...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize").tag("beanName", beanName);SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());}else {smartSingleton.afterSingletonsInstantiated();}smartInitialize.end();}}}

这个预处理单例bean的实例化其实非常简单,我们简要分析一下整体流程:

  • 获取到当前所有的beanDefinitionNames,用于迭代。
  • 循环所有的beanDefinitionNames,也就是beanNamess
  • 获取beanName的定义信息,用于后续判断
  • 如果是抽象类,或者不是单例或者是懒加载bean就不处理。
  • 其他情况下会进入getBean逻辑,这个方法我们经常用来从 BeanFactory 中获取一个 Bean,而初始化的过程也封装到了这个方法里。

getBean/doGetBean

Spring源码中很多时候真正做事的是do开头的,个人经验。

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

transformedBeanName

获取beanName,如果是FactoryBean是会以&开头,也就是说这里返回的就是单纯的beanName

String beanName = transformedBeanName(name);public static String transformedBeanName(String name) {Assert.notNull(name, "'name' must not be null");if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {return name;}return transformedBeanNameCache.computeIfAbsent(name, beanName -> {do {beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());}while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));return beanName;});}

getSingleton

这里就是常说的三级缓存。在实例化的时候先要去三级缓存中查看是否存在了,需不需要走后续的实例化流程。

Object sharedInstance = getSingleton(beanName);@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lockObject singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton locksingletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}

从上述代码中,我们知道有三个Map对象,也就是我们常说的三级缓存,实际上就是以下这三个Map。

/** Cache of singleton objects: bean name to bean instance. */private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/** Cache of singleton factories: bean name to ObjectFactory. */private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);/** Cache of early singleton objects: bean name to bean instance. */private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  • 一级缓存:singletonObjects缓存的是单例对象,key是beanName value是bean的实例。
  • 二级缓存:earlySingletonObjects缓存的是早期的单例对象,key是beanName value是bean的实例。
  • 三级缓存:singletonFactories缓存的是ObjectFactory,key是beanName value是ObjectFactory。

回到getSingleton方法,首先spring尝试通过beanName从一级缓存中获取,如果一级缓存中不存在,并且当前bean并不是正在创建中的话直接返回null。只有当bean正在创建中的时候才回去从二级缓存中获取,如果二级缓存中获取不到再从三级缓存中去获取。此处我们先大概有这么一个概念,后续会在循环依赖章节详细描述。

如果当前获取到的sharedInstance不为空且不是FacotyBean的话就直接返回回去了。如果为空继续走后续的逻辑。

dependsOn

根据BeanDefinition获取是否有依赖的bean,如果有的话先把其他bean实例化完成。@DependsOn的注解的作用就在这里可以体现。

String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}registerDependentBean(dep, beanName);try {getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}

singletonsCurrentlyInCreation

如果是单例的,就会调用 getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法。

  • 第一个参数就是beanName
  • 第二个参数是一个FunctionalInterface
if (mbd.isSingleton()) {// 先执行getSingleton方法,然后在代码中会调用createBean方法sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}

执行getSingleton方法,代码如下

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}// 对象是否正在创建中 singletonsCurrentlyInCreation,避免循环依赖的一种操作beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {// 在这里调用外出的createBean返回的就是springbean,然后后续执行缓存的相关操作singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}afterSingletonCreation(beanName);}if (newSingleton) {addSingleton(beanName, singletonObject);}}return singletonObject;}}

首先这个放回加了一个synchronized的锁,锁的是一级缓存对象,也就是说对象的实例化是线程安全的,不让其在实例化的时候出现重复创建。获取锁之后会再从缓存中去获取当前beanName是否存在实例,如果没有的话会执行beforeSingletonCreation(beanName)。

protected void beforeSingletonCreation(String beanName) {if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}}

此处的核心就是singletonsCurrentlyInCreation集合的add方法,就是提前曝光的地方。

private final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<>(16));

直接就是会调用传入的FunctionalInterface,也就是执行createBean方法。

		try {// 在这里调用外出的createBean返回的就是springbean,然后后续执行缓存的相关操作singletonObject = singletonFactory.getObject();newSingleton = true;}

createBean/doCreateBean

在执行doCreateBean的时候会调用构造方法

if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}

然后判断beanDefinition信息是单例的,并且是允许循环依赖的(默认为允许)并且当前的bean是正在创建中就会进入if的逻辑。

addSingletonFactory,从名字开发猜测是否和我们的三级缓存有关联。

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}

addSingletonFactory方法的参数如下:

  • beanName,bean的名称
  • ObjectFactory,函数式接口,之前看getSingleton(name)方法的时候也知道,会调用这个getObject方法来获取对象放入二级缓存中。

一级缓存中不存在就将这个ObjectFactory放入三级缓存,同时清理掉二级缓存和加入已注册的有序集合中。

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}

populateBean

填充bean,也就是属性注入。这里会去解析bean的依赖,从而继续去走getBean的逻辑来让当前bean实例化完整。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {if (bw == null) {if (mbd.hasPropertyValues()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}else {// property mean 属性// Skip property population phase for null instance.return;}}// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the// state of the bean before properties are set. This can be used, for example,// to support styles of field injection.// 在属性填充之前给任意InstantiationAwareBeanPostProcessors一个机会去修改bean的状态// 后置处理器可以被使用在例如:支持字段注入的样式.if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);int resolvedAutowireMode = mbd.getResolvedAutowireMode();if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// Add property values based on autowire by name if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// Add property values based on autowire by type if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);PropertyDescriptor[] filteredPds = null;if (hasInstAwareBpps) {if (pvs == null) {pvs = mbd.getPropertyValues();}// 实例化感知的BeanPostProcessorfor (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {// 默认的情况下resolvedAutowireMode是为0的,所以默认是AutoWiredAnnotationBeanPostProcessor进行自动装配PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}// 在这里面回去校验属性的版本信息(BeanNotOfRequiredTypeException)/***Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:* Error creating bean with name 'b': Unsatisfied dependency expressed through field 'a';* nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException:* Bean named 'a' is expected to be of type 'com.qhyu.cloud.circlarRefrence.A' but was actually of type 'com.sun.proxy.$Proxy34'*/pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}pvs = pvsToUse;}}if (needsDepCheck) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}checkDependencies(beanName, mbd, filteredPds, pvs);}if (pvs != null) {// 属性填充的最后回对spel进行解析applyPropertyValues(beanName, mbd, bw, pvs);}}

initializeBean

如果 bean 实现了 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 接口,回调invokeAwareMethods(beanName, bean)

处理 bean 中定义的 init-method,或者如果 bean 实现了 InitializingBean 接口,调用 afterPropertiesSet() 方法

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());}else {invokeAwareMethods(beanName, bean);}Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {// 先执行beanpostProcessor的before// 通过BeanPostProcessor在bean初始化之前做点事情wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {// 再执行initmethods// 调用bean的初始化方法进行初始化invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}if (mbd == null || !mbd.isSynthetic()) {// 再执行BeanPostProcessor的After方法// 通过BeanPostProcessor在bean初始化之后做点事情// aop的切入点wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;}

getSingleton

如果创建没有任何问题,newSingleton = true,继续执行 addSingleton(beanName, singletonObject)方法

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}// 对象是否正在创建中 singletonsCurrentlyInCreation,避免循环依赖的一种操作beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {// 在这里调用外出的createBean返回的就是springbean,然后后续执行缓存的相关操作singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}afterSingletonCreation(beanName);}if (newSingleton) {addSingleton(beanName, singletonObject);}}return singletonObject;}
}

将创建好的bean放入一级缓存,移除一二级缓存。

protected void addSingleton(String beanName, Object singletonObject) {// 线程安全的synchronized (this.singletonObjects) {// 放入一级缓存this.singletonObjects.put(beanName, singletonObject);// 移除三级缓存this.singletonFactories.remove(beanName);// 移除二级缓存this.earlySingletonObjects.remove(beanName);// 已注册的map中把新初始化的bean放入this.registeredSingletons.add(beanName);}}

总结

整个bean的初始化流程就如下图所示,下面的这个图是一个最简单的单例bean的实例化流程,不涉及到循环依赖问题,循环依赖和三级缓存将在下一章详细分析。本章主要了解bean实例化的整体流程,了解和熟悉spring工作模式。

在这里插入图片描述

相关文章:

Spring实例化源码解析之Bean的实例化(十二)

前言 本章开始分析finishBeanFactoryInitialization(beanFactory)方法&#xff0c;直译过来就是完成Bean工厂的初始化&#xff0c;这中间就是非lazy单例Bean的实例化流程。ConversionService在第十章已经提前分析了。重点就是最后一句&#xff0c;我们的bean实例化分析就从这里…...

git常用的几条命令介绍

必须了解的命令整理 1&#xff0c;git init 初始化一个新的Git仓库。 这将在当前目录中创建一个名为".git"的子目录&#xff0c;Git会将所有仓库的元数据存储在其中。 2&#xff0c;git clone 克隆一个已存在的仓库。 这会创建一个本地仓库的副本&#xff0c;包…...

使用VisualSVN在Windows系统上设置SVN服务器,并结合内网穿透实现公网访问

文章目录 前言1. VisualSVN安装与配置2. VisualSVN Server管理界面配置3. 安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4. 固定公网地址访问 前言 SVN 是 subversion 的缩写&#xff0c;是一个开放源代码的版本控制系统…...

第18章 SpringCloud生态(三)

18.21 Nacos能存储什么样格式的数据(配置中心) 难度:★ 重点:★ 白话解析 看下面这副Nacos控制台的截图就明白了 参考答案 六种格式数据:Text、JSON、XML、Yaml、HTML和Properties格式。 18.22 Nacos是如何实现配置动态更新的(配置中心) 难度:★★ 重点:★★★ 白话…...

leetcode:2347. 最好的扑克手牌(python3解法)

难度&#xff1a;简单 给你一个整数数组 ranks 和一个字符数组 suit 。你有 5 张扑克牌&#xff0c;第 i 张牌大小为 ranks[i] &#xff0c;花色为 suits[i] 。 下述是从好到坏你可能持有的 手牌类型 &#xff1a; "Flush"&#xff1a;同花&#xff0c;五张相同花色的…...

2007-2022 年上市公司国内外专利授权情况数据

2007-2022 年上市公司国内外专利授权情况 1、来源&#xff1a;国家知识产权局 2、时间&#xff1a;2007-2022 年 3、范围&#xff1a;上市公司 4、指标&#xff1a; 证券代码、年份、省份、城市、行业代码、授权地区、申请类型、专利、发明专利、实用新型、外观设计 5、…...

安全渗透测试网络基础知识之路由技术

#1.静态路由技术 ##1.1路由技术种类: 静态路由技术、动态路由技术 ##1.2静态路由原理 静态路由是网络中一种手动配置的路由方式,用于指定数据包在网络中的传输路径。与动态路由协议不同,静态路由需要管理员手动配置路由表,指定目的网络和下一跳路由器的关联关系。 比较适合…...

【大数据】Kafka 实战教程(二)

Kafka 实战教程&#xff08;二&#xff09; 1.下载2.安装3.配置4.运行4.1 启动 Zookeeper4.2 启动 Kafka 5.第一个消息5.1 创建一个 Topic5.2 创建一个消息消费者5.3 创建一个消息生产者 1.下载 你可以在 Kafka 官网&#xff1a;http://kafka.apache.org/downloads&#xff0c…...

React 框架

1、React 框架简介 1.1、介绍 CS 与 BS结合&#xff1a;像 React&#xff0c;Vue 此类框架&#xff0c;转移了部分服务器的功能到客户端。将CS 和 BS 加以结合。客户端只用请求一次服务器&#xff0c;服务器就将所有js代码返回给客户端&#xff0c;所有交互类操作都不再依赖服…...

数据结构与算法之图: Leetcode 133. 克隆图 (Typescript版)

克隆图 https://leetcode.cn/problems/clone-graph/description/ 描述 给你无向 连通 图中一个节点的引用&#xff0c;请你返回该图的 深拷贝&#xff08;克隆&#xff09;。 图中的每个节点都包含它的值 val&#xff08;int&#xff09; 和其邻居的列表&#xff08;list[No…...

illuminate/database 使用 一

illuminate/database 是完整的php数据库工具包&#xff0c;即ORM&#xff08;Object-Relational Mapping&#xff09;类库。 提供丰富的查询构造器&#xff0c;和多个驱动的服务。作为Laravel的数据库层使用&#xff0c;也可以单独使用。 一 使用 加载composer之后&#xff…...

前端koa搭建服务器(保姆级教程)——part1

目录 koa简介前端项目搭建koa环境第一步&#xff1a;新建项目第二步&#xff1a;环境初始化&#xff0c;安装依赖初始化项目&#xff0c;生成package.json文件安装koa依赖安装koa-router 路由管理依赖安装dotenv 环境变量依赖安装nodemon 热启动依赖 第三步&#xff1a;代码调用…...

js逆向第一课 密码学介绍

什么是密码学&#xff1f; 密码学&#xff08;Cryptology&#xff09;是一种用来混淆的技术,它希望将正常的、可识别的信息转变为无法识别的信息。 目前密码学的研究&#xff0c;一种是偏应用&#xff0c;把现有的&#xff0c;别人研究出来的密码学算法&#xff0c;放在一个合…...

Dynamic DataSource 多数据源配置【 Springboot + DataSource + MyBatis Plus + Druid】

一、前言 MybatisPlus多数据源配置主要解决的是多数据库连接和切换的问题。在一些大型应用中&#xff0c;由于数据量的增长或者业务模块的增多&#xff0c;可能需要访问多个数据库。这时&#xff0c;就需要配置多个数据源。 二、Springboot MyBatis Plus 数据源配置 2.1、单数…...

MyBatis:配置文件

MyBatis 前言全局配置文件映射配置文件注 前言 在 MyBatis 中&#xff0c;配置文件分为 全局配置文件&#xff08;核心配置文件&#xff09; 和 映射配置文件 。通过这两个配置文件&#xff0c;MyBatis 可以根据需要动态地生成 SQL 语句并执行&#xff0c;同时将结果集转换成 …...

ARM,基础、寄存器

1.认识ARM 1)是一家公司 2)做RISC处理器内核 3)不生产芯片 2.ARM处理器的最新发展(重要) 高端产品线: cortex-A9 主要做音视频开发&#xff0c;例如&#xff1a;手机 平板..... 中端产品线&#xff1a;cortex-R 主要做实时性要求比较高的系统 例如&#…...

FC-TSGAS-1624 CP451-10 MVI56E-MNETC IC697CMM742

FC-TSGAS-1624 CP451-10 MVI56E-MNETC IC697CMM742. Variscite的DART-MX8M-PLUS和VAR-SOM-MX8M-PLUS基于恩智浦i.MX 8M Plus SoC&#xff0c;集成人工智能能力高达每秒2.3万亿次运算(TOPS)。这些产品&#xff0c;结合海螺-8 AI处理器提供多达26个top&#xff0c;显著优于市场…...

异或运算.

相同为0&#xff0c;不同为1。 1 ^ 10 0 ^ 00 1 ^ 01 0 ^ 11性质&#xff1a; 0 ^ N N N ^ N 0交换、结合 a ^ b b ^ a&#xff1b; (a ^ b) ^ c a ^ (b ^ c)&#xff1b; 因此异或全部的元素的结果就是那个只出现1次的元素。 实现两个值的交换&#xff0c;而不必使…...

NewStarCTF2023week4-逃(反序列化字符串逃逸)

打开链接&#xff0c;大致审一下php代码&#xff0c;是反序列化相关的&#xff1b; 结合题目提示&#xff0c;很典型的字符串逃逸&#xff1b; 并且属于替换修改后导致序列化字符串变长的类型&#xff1b; 看似加了一个waf函数对我们提交的内容进行了过滤替换&#xff0c;实…...

PyTorch Tensor 形状

查看张量形状 有两种方法查看张量形状: 通过属性查看 Tensor.shape通过方法查看 Tensor.size() 两种方式的结果都是一个 torch.Size 类型(元组的子类)的对象 >>> t torch.empty(3, 4) >>> t.size() torch.Size([3, 4]) # 获取 dim1 维度的 size >>…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...