java的spring循环依赖、Bean作用域等深入理解

news2024/11/24 20:04:50

前言

通过之前的几篇文章将Spring基于XML配置的IOC原理分析完成,但其中还有一些比较重要的细节没有分析总结,比如循环依赖的解决、作用域的实现原理、BeanPostProcessor的执行时机以及SpringBoot零配置实现原理(@ComponentScan、@Import、@ImportSource、@Bean注解的使用和解析)等等。下面就先来看看循环依赖是怎么解决的,在此之前一定要熟悉整个Bean的实例化过程,本篇只会贴出关键性代码。

正文

循环依赖

首先来看几个问题:

  • 什么是循环依赖?
  • 在熟悉了Bean实例化原理后,你会怎么解决循环依赖的问题?
  • Spring怎么解决循环依赖?有哪些循环依赖可以被解决?哪些又不能?

什么是循环依赖?

这个概念很容易理解,简单说就是两个类相互依赖,类似线程死锁的问题,也就是当创建A对象时需要注入B的依赖对象,但B同时也依赖A,那到底该先创建A还是先创建B呢?

Spring是如何解决循环依赖的?

探究Spring的解决方法之前,我们首先得搞清楚Spring Bean有几种依赖注入的方式:

  • 通过构造函数
  • 通过属性
  • 通过方法(不一定是setter方法,只要在方法上加上了@Autowired,都会进行依赖注入)

其次,Spring作用域有singletonprototyperequestsession等等,但在非单例模式下发生循环依赖是会直接抛出异常的,下面这个代码不知道你还有没有印象,在AbstractBeanFactory.doGetBean中有这个判断:

if (isPrototypeCurrentlyInCreation(beanName)) {
	throw new BeanCurrentlyInCreationException(beanName);
}

为什么这么设计呢?反过来想,如果不这么设计,你怎么知道循环依赖到底是依赖的哪个对象呢?搞清楚了这个再来看哪些依赖注入的方式发生循环依赖是可以解决,而那些又不能。结论是构造函数方式没办法解决循环依赖,其它两种都可以。
我们先来看看为什么通过属性注入方法注入可以解决。回忆一下Bean的实例化过程:

	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			//创建实例
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {

					// Bean实例化完成后收集类中的注解(@PostConstruct,@PreDestroy,@Resource, @Autowired,@Value)
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		// 单例bean提前暴露
		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");
			}
			//这里着重理解,对理解循环依赖帮助非常大,重要程度 5   添加三级缓存
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//ioc di,依赖注入的核心方法,该方法必须看
			populateBean(beanName, mbd, instanceWrapper);

			//bean 实例化+ioc依赖注入完以后的调用,非常重要
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			//注册bean销毁时的类DisposableBeanAdapter
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

仔细看这个过程其实不难理解,首先Spring会通过无参构造实例化一个空的A对象,实例化完成后会调用addSingletonFactory存入到三级缓存中(注意这里存入的是singletonFactory对象):

	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			// 一级缓存
			if (!this.singletonObjects.containsKey(beanName)) {
				System.out.println("========set value to 3 level cache->beanName->" + beanName + "->value->" + singletonFactory);
				// 三级缓存
				this.singletonFactories.put(beanName, singletonFactory);
				// 二级缓存
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

然后才会去依赖注入触发类B的实例化,所以这时缓存中已经存在了一个空的A对象;同样B也是通过无参构造实例化,B依赖注入又调用getBean获取A的实例,而在创建对象之前,先是从缓存中获取对象:

	//从缓存中拿实例
	Object sharedInstance = getSingleton(beanName);

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//根据beanName从缓存中拿实例
		//先从一级缓存拿
		Object singletonObject = this.singletonObjects.get(beanName);
		//如果bean还正在创建,还没创建完成,其实就是堆内存有了,属性还没有DI依赖注入
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				//从二级缓存中拿
				singletonObject = this.earlySingletonObjects.get(beanName);

				//如果还拿不到,并且允许bean提前暴露
				if (singletonObject == null && allowEarlyReference) {
					//从三级缓存中拿到对象工厂
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						//从工厂中拿到对象
						singletonObject = singletonFactory.getObject();
						//升级到二级缓存
						System.out.println("======get instance from 3 level cache->beanName->" + beanName + "->value->" + singletonObject );
						this.earlySingletonObjects.put(beanName, singletonObject);
						//删除三级缓存
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

很明显,会从三级缓存中拿到singletonFactory对象并调用getObject方法,这是一个Lambda表达式,在表达式中又调用了getEarlyBeanReference方法:

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

这里你点进去看会发现都是返回之前我们创建的空的A对象,因此B对象能够依赖注入完成并存入到一级缓存中,接着A对象继续未完成的依赖注入自然是可以成功的,也存入到一级缓存(创建完成的对象放在一级缓存中)中。Spring就是这样通过缓存解决了循环依赖,但是不知道你注意到没有在上面的getSingleton方法中,从三级缓存中拿到对象后,会添加到二级缓存并删除三级缓存,这是为什么呢?这个二级缓存有什么用呢?
其实也很简单,就是为了提高效率的,因为在getEarlyBeanReference方法中是循环调用BeanPostProcessor类的方法的,当只有一对一的依赖时没有什么问题,但是当A和B相互依赖,A又和C相互依赖,A在注入完B触发C的依赖注入时,这个循环还有必要么?读者们可以自行推演一下整个过程。
至此,Spring是如何解决循环依赖的相信你也很清楚了,现在再来看通过构造函数依赖注入为什么不能解决循环依赖是不是也很清晰了?因为通过构造函数(空参的构造函数)实例化并依赖注入是没办法缓存一个实例对象供依赖对象注入的。

作用域实现原理以及如何自定义作用域

作用域实现原理

在Spring中主要有reqest、session、singleton、prototype等等几种作用域,前面我们分析了singleton创建bean的原理,是通过缓存来实现的,那么其它的呢?还是回到AbstractBeanFactory.doGetBean方法中来:

if (mbd.isSingleton()) {
	sharedInstance = getSingleton(beanName, () -> {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// Explicitly remove instance from singleton cache: It might have been put there
			// eagerly by the creation process, to allow for circular reference resolution.
			// Also remove any beans that received a temporary reference to the bean.
			destroySingleton(beanName);
			throw ex;
		}
	});
	// 该方法是FactoryBean接口的调用入口
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
	// It's a prototype -> create a new instance.
	Object prototypeInstance = null;
	try {
		beforePrototypeCreation(beanName);
		prototypeInstance = createBean(beanName, mbd, args);
	}
	finally {
		afterPrototypeCreation(beanName);
	}
	// 该方法是FactoryBean接口的调用入口
	bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
	String scopeName = mbd.getScope();
	final Scope scope = this.scopes.get(scopeName);
	if (scope == null) {
		throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
	}
	try {
		Object scopedInstance = scope.get(beanName, () -> {
			beforePrototypeCreation(beanName);
			try {
				return createBean(beanName, mbd, args);
			}
			finally {
				afterPrototypeCreation(beanName);
			}
		});
		// 该方法是FactoryBean接口的调用入口
		bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
	}
}

在singleton作用域下,会调用getSingleton方法,然后回调createBean创建对象,最终在getSingleton中完成缓存;而当scope为prototype时,可以看到是直接调用了createBean方法并返回,没有任何的缓存操作,因此每次调用getBean都会创建新的对象,即使是同一个线程;除此之外都会进入到else片段中。
这个代码也很简单,首先通过我们配置的scopeNamescopes中拿到对应的Scope对象,如SessionScope和RequestScope(但这两个只会在Web环境中被加载,在WebApplicationContextUtils.registerWebApplicationScopes可以看到注册操作),然后调用对应的get方法存到对应的request或session对象中去。代码很简单,这里就不分析了。

自定义Scope

通过以上分析,不难发现我们是很容易实现一个自己的Scope的,首先实现Scope接口,然后将我们类的实例添加到scopes缓存中来,关键是怎么添加呢?在AbstractBeanFactory类中有一个registerScope方法就是干这个事的,因此我们只要拿到一个BeanFactory对象就行了,那要怎么拿?还记得在refresh中调用的invokeBeanFactoryPostProcessors方法么?因此我们只需要实现BeanFactoryPostProcessor接口就可以了,是不是So Easy!

BeanPostProcessor的执行时机

BeanPostProcessor执行点很多,根据其接口类型在不同的位置进行调用,只有熟记其执行时机,才能更好的进行扩展,这里以一张时序图来总结:
在这里插入图片描述

SpringBoot零配置实现原理浅析

在SpringBoot项目中,省去了大量繁杂的xml配置,只需要使用@ComponentScan、@Configuration以及@Bean注解就可以达到和使用xml配置的相同效果,大大简化了我们的开发,那这个实现原理是怎样的呢?熟悉了xml解析原理,相信对于这种注解的方式基本上也能猜个大概。
首先我们进入到AnnotationConfigApplicationContext类,这个就是注解方式的IOC容器:

	public AnnotationConfigApplicationContext(String... basePackages) {
		this();
		scan(basePackages);
		refresh();
	}

	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

这里ClassPathBeanDefinitionScanner在解析xml时出现过,就是用来扫描包找到合格的资源的;同时还创建了一个AnnotatedBeanDefinitionReader对象对应XmlBeanDefinitionReader,用来解析注解:

	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		if (beanFactory != null) {
			if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
				beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
			}
			if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
				beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
			}
		}

		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
		if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
		if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition();
			try {
				def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
						AnnotationConfigUtils.class.getClassLoader()));
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
			}
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
		}

		return beanDefs;
	}

AnnotatedBeanDefinitionReader构造方法中可以看到调用了registerAnnotationConfigProcessors注册一些列注解解析的Processor类,重点关注ConfigurationClassPostProcessor类,该类是BeanDefinitionRegistryPostProcessor的子类,所以会在refresh中调用,该类又会委托ConfigurationClassParser去解析@Configuration、@Bean、@ComponentScan等注解,所以这两个类就是SpringBoot实现零配置的关键类,实现和之前分析的注解解析流程差不多,所以具体的实现逻辑读者请自行分析。
回头看当解析器扫描器创建好后,同样是调用scan方法扫描包,然后refresh启动容器,所以实现逻辑都是一样的,殊途同归,只不过通过父子容器的构造方式使得我们可以很方便的扩展Spring。

总结

本篇是关于IOC实现的一些补充,最重要的是要理解循环依赖的解决办法,其次SpringBoot零配置实现原理虽然这里只是简单起了个头,但需要好好阅读源码分析。另外还有很多细节,不可能全都讲到,需要我们自己反复琢磨,尤其是Bean实例化那一块,这将是后面我们理解AOP的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1607494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

推荐一款websocket接口测试工具

网址&#xff1a;Websocket在线测试-Websocket接口测试-Websocket模拟请求工具 http://www.jsons.cn/websocket/ 很简单输入以ws开后的网址就可以了 这个网址是你后台设置的 如果连接成功会砸提示框内显示相关字样&#xff0c;反之则不行

python爬虫之爬取携程景点评价(5)

一、景点部分评价爬取 【携程攻略】携程旅游攻略,自助游,自驾游,出游,自由行攻略指南 (ctrip.com) import requests from bs4 import BeautifulSoupif __name__ __main__:url https://m.ctrip.com/webapp/you/commentWeb/commentList?seo0&businessId22176&busines…

U.2 NVMe全闪存储阵列在高性能计算环境中的表现

用户利用高性能计算 (HPC) 先进的计算技术来执行大规模的复杂计算任务。这有助于短时间内解决复杂问题&#xff0c;与传统计算方法相比遥遥领先。Infortrend 存储解决方案专门针对密集型 HPC 工作负载进行了优化。新推出的U.2 NVMe全闪存储阵列GS 5024UE在0.3毫秒的延迟下提供1…

镭速助力企业集成OIDC实现安全高效的大文件数据传输

在当今数字化时代&#xff0c;企业尤其是科研机构、研究所和实验室等&#xff0c;对于大量敏感数据的传输安全和效率有着日益增长的需求。面对这一挑战&#xff0c;企业需要一种既能保障数据传输安全&#xff0c;又能提高传输效率的解决方案。镭速&#xff0c;作为一款面向企业…

【C++学习】C++4种类型转换详解

这里写目录标题 &#x1f680;C语言中的类型转换&#x1f680;为什么C需要四种类型转换&#x1f680;C强制类型转换&#x1f680;static_cast&#x1f680;**reinterpret_cast**&#x1f680;const_cast与volatile&#x1f680;dynamic_cast &#x1f680;C语言中的类型转换 在…

buuctf——[ZJCTF 2019]NiZhuanSiWei

buuctf——[ZJCTF 2019]NiZhuanSiWei 1.绕过file_get_contents()函数 file_get_contents函数介绍 定义和用法 file_get_contents() 把整个文件读入一个字符串中。 该函数是用于把文件的内容读入到一个字符串中的首选方法。如果服务器操作系统支持&#xff0c;还会使用内存映射…

【opencv】示例-videocapture_starter.cpp 从视频文件、图像序列或连接到计算机的摄像头中捕获帧...

/** * file videocapture_starter.cpp * brief 一个使用OpenCV的VideoCapture与捕获设备&#xff0c;视频文件或图像序列的入门示例 * 就像CV_PI一样简单&#xff0c;对吧&#xff1f; * * 创建于: 2010年11月23日 * 作者: Ethan Rublee * * 修改于: 2013年4月17日 * …

【笔记】十分钟学会正确的github工作流,和开源作者们使用同一套流程

对视频十分钟学会正确的github工作流&#xff0c;和开源作者们使用同一套流程的记录&#xff0c;方便自己回顾和使用。 注1&#xff1a;一个分支 只有一个人在进行 注2&#xff1a; main和master是不同时期对主分支的命名&#xff0c;两者是同一个东西。如果项目已经有了&#…

将MySQL数据库导入到EA模型的教程

将MySQL数据库导入到EA 1.下载安装mysql-connector-odbc2.在管理工具中新增ODBC数据源3.在EA中新建项目4.链接MYSQL数据源4.1 安装64位的ODBC驱动可能出现”在连接ODBC 时发生错误&#xff0c;请相关检查设置“的提示&#xff0c;卸载后重新安装32位ODBC驱动后可以正常执行 5.导…

【1577】java网吧收费管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java 网吧收费管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0…

唠一唠,氮化镓和普通快充的不同

充电器,对于手机、电脑、平板等电子产品来说,就是“生命之源”。没有它,这些设备就像鱼儿离开了水。 过去数十年,充电器市场一直在不断变革。从最早的座插式,到后来的一体式、万能充、分离式直充、快充、无线充,再到现在的氮化镓充电器,技术的更新换代快得让人眼花缭乱。 那么…

SQL Server 2022 安装及使用

SQL Server 2022 前言一、安装SQL Server 2022下载SQL Server 2022安装SQL Server 2022配置SQL Server 2022 二、安装SQL Server Management Studio下载SQL Server Management Studio安装SSMS-Setup-CHS 三、使用SQL Server 2022四、解决连接到服务器报错问题 前言 SQL Serve…

Jmeter 性能-内存溢出问题定位分析

1、堆内存溢出 ①稳定性压测一段时间后&#xff0c;Jmeter报错&#xff0c;日志报&#xff1a; java.lang.OutOfMemoryError.Java heap space ②用jmap -histo pid命令dump堆内存使用情况&#xff0c;查看堆内存排名前20个对象。 看是否有自己应用程序的方法&#xff0c;从…

C++Primer3.2 标准类型string

文章目录 初始化string对象读写string对象string的empty和size操作不同string对象的比较string的加法处理string对象的字符遍历string中的每个字符 初始化string对象 //string的初始化 void test01() { string s1; // 默认初始化&am…

YOLOv8改进 | Conv篇 | CVPR2024最新DynamicConv替换下采样(包含C2f创新改进,解决低FLOPs陷阱)

一、本文介绍 本文给大家带来的改进机制是CVPR2024的最新改进机制DynamicConv其是CVPR2024的最新改进机制,这个论文中介绍了一个名为ParameterNet的新型设计原则,它旨在在大规模视觉预训练模型中增加参数数量,同时尽量不增加浮点运算(FLOPs),所以本文的DynamicConv被提出…

AIoT人工智能物联网之deepstream

1.deepstream介绍安装 deepstream是一个很强大的工具集,能够执行数据收集、数据预处理、视频追踪、编码等功能 (1)deepstream docker 版本查询 网页查询 https://catalog.ngc.nvidia.com/containers (2)下载 deepstream docker 对应 版本 https://catalog.ngc.nvidia.c…

【微信公众平台】扫码登陆

文章目录 前置准备测试号接口配置 带参数二维码登陆获取access token获取Ticket拼装二维码Url编写接口返回二维码接收扫描带参数二维码事件编写登陆轮训接口测试页面 网页授权二维码登陆生成ticket生成授权地址获取QR码静态文件支持编写获取QR码的接口 接收重定向参数轮训登陆接…

正确解决:关于Lattic Diamond和Radiant License冲突问题(无法破解问题)

一、问题 今天工作&#xff0c;搞16nm Avant E系列FPGA&#xff0c;需要用到莱迪思的Radiant 2023.2软件&#xff08;按这个博主的安装流程Lattice Radiant 2023.1 软件安装教程&#xff09;。 安装好之后&#xff0c;设置环境变量&#xff0c;导入License.dat就是破解不了&…

从零开始学习Linux(3)----权限

1.Linux权限的概念 Linux用户&#xff1a;1.root&#xff0c;超级管理员 2.非root&#xff0c;XXX&#xff0c;普通用户 命令&#xff1a;su[用户名] 功能&#xff1a;切换用户。 su -&#xff1a;是指以root的身份重新登录一次。 普通用户切换root需要输入密码&#xff0c;…

【Java开发指南 | 第二篇】标识符、Java关键字及注释

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 标识符Java关键字Java注释 标识符 Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。 所有的标识符都应该以字母&#xff08;A-Z 或者 a-z&#xff09;,美元符&#xff08;$&#xff0…