spring源码分析bean的生命周期(上)

news2024/9/21 11:52:27

bean扫描生成BeanDefinition的过程:

创建非懒加载的单例bean的过程:

spring容器初始化好之后,首先要进行bean的扫描,然后再进行bean的创建和管理 

一、扫描生成BeanDefinition

public int scan(String... basePackages) {
        // 扫描开始前有多少BeanDefinition
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		doScan(basePackages);

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

        // 扫描结束后有多少BeanDefinition
		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

scan()方法解释如下:

  1. 获取扫描前BeanDefinition的个数
  2. 获取调用doScan()方法扫描的BeanDefinition个数
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
            // 查找BeanDefinition
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
                // 解析类上的@Scope的value属性
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                // 给BeanDefinition设置Scope属性
				candidate.setScope(scopeMetadata.getScopeName());
                // 根据@Component上指定名称 如果没有调用Introspector.decapitalize(shortClassName)返回
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
                    // 给BeanDefinition设置默认值
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
                    // 解析@Lazy、@Primary、@DependsOn、@Role、@Description
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
                    // 通过beanName判断是否已存在BeanDefinition
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    // 添加到Set集合中
					beanDefinitions.add(definitionHolder);
                    // 注册BeanDefinition
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        // 会对spring.xml文件中对符合includeFilters返回BeanDefinition
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
            // 包路径扫描
			return scanCandidateComponents(basePackage);
		}
	}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			// 获取basePackage下所有的文件资源
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
            // 资源加载
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
            // 遍历资源文件
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
                // 资源是否可读
				if (resource.isReadable()) {
					try {
                        // 元数据读取器读取资源文件
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						// excludeFilters、includeFilters判断
						if (isCandidateComponent(metadataReader)) { // @Component-->includeFilters判断
                            // 创建BeanDefinition设置beanClass属性为类名称
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setSource(resource);
                            // 是否为顶级类&&(不是抽象类或接口||是抽象类并且有方法上加了@Lookup注解)
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
                                // 添加到集合中
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
						else {
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because not readable: " + resource);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}
// 根据beanDefinition返回beanName
@Override
	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		if (definition instanceof AnnotatedBeanDefinition) {
			// 获取注解所指定的beanName
			String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
			if (StringUtils.hasText(beanName)) {
				// Explicit bean name found.
				return beanName;
			}
		}
		// Fallback: generate a unique default bean name.
		return buildDefaultBeanName(definition, registry);
	}
@Nullable
	protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
		AnnotationMetadata amd = annotatedDef.getMetadata();
		Set<String> types = amd.getAnnotationTypes();
		String beanName = null;
		for (String type : types) {
			AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
			if (attributes != null) {
				Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {
					Set<String> result = amd.getMetaAnnotationTypes(key);
					return (result.isEmpty() ? Collections.emptySet() : result);
				});
				if (isStereotypeWithNameValue(type, metaTypes, attributes)) {
                    // 获取org.springframework.stereotype.Component注解类的value属性
					Object value = attributes.get("value");
					if (value instanceof String) {
                        // 强转成string
						String strVal = (String) value;
						if (StringUtils.hasLength(strVal)) {
							if (beanName != null && !strVal.equals(beanName)) {
								throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
										"component names: '" + beanName + "' versus '" + strVal + "'");
							}
                            // 赋值
							beanName = strVal;
						}
					}
				}
			}
		}
        // 最终返回
		return beanName;
	}
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
        // beanName已经注册过BeanDefinition返回true
		if (!this.registry.containsBeanDefinition(beanName)) {
			return true;
		}
		BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
		BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
		if (originatingDef != null) {
			existingDef = originatingDef;
		}
		// 是否兼容,如果兼容返回false表示不会重新注册到Spring容器中,如果不冲突则会抛异常。
		if (isCompatible(beanDefinition, existingDef)) {
			return false;
		}
        // 一个beanName存在其它类@Component指定的名称 就是说两个类用同一个beanName
		throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
				"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
				"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
	}
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
	}

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
        // 获取BeanDefinition注册到spring容器中
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if (existingDefinition != null) {
			// 默认是允许BeanDefinition覆盖的
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (logger.isInfoEnabled()) {
					logger.info("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(existingDefinition)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
            // 添加beanDefinition到beanDefinitionMap中
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
                // 添加beanName到beanDefinitionNames中
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (existingDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
		else if (isConfigurationFrozen()) {
			clearByTypeCache();
		}
	}
  1. 首先,通过ResourcePatternResolver获得指定包路径下的所有.class文件(Spring源码中将此文件包装成了Resource对象)
  2. 遍历每个Resource对象
  3. 利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory,MetadataReader的具体实现类为SimpleMetadataReader)
  4. 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选(条件注解并不能理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。)
  5. 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition
  6. 再基于metadataReader判断是不是对应的类是不是接口或抽象类
  7. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集

MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,功能有

  1. 获取类的名字、
  2. 获取父类的名字
  3. 获取所实现的所有接口名
  4. 获取所有内部类的名字
  5. 判断是不是抽象类
  6. 判断是不是接口
  7. 判断是不是一个注解
  8. 获取拥有某个注解的方法集合
  9. 获取类上添加的所有注解信息
  10. 获取类上添加的所有注解类型集合

值得注意的是,CachingMetadataReaderFactory解析某个.class文件得到MetadataReader对象是利用的ASM技术,并没有加载这个类到JVM。并且,最终得到的ScannedGenericBeanDefinition对象,beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object,它即可以存储类的名字,也可以存储class对象)

最后,上面是说的通过扫描得到BeanDefinition对象,我们还可以通过直接定义BeanDefinition,或解析spring.xml文件的<bean/>,或者@Bean注解得到BeanDefinition对象。(后续课程会分析@Bean注解是怎么生成BeanDefinition的)。

二、合并成RootBeanDefinition

父子BeanDefinition,如果子BeanDefinition有指定属性就按照子BeanDefinition设定,没有指定就继承父BeanDefinition的

<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/>
<bean id="child" class="com.zhouyu.service.Child" parent="parent"/>
<!-- 但是这么定义的情况下,child就是原型Bean了。 -->

合并获取bean的源码 

@Override
	public 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) {
			// 获取合并后的BeanDefinition
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					// 获取FactoryBean对象
					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) {
							// isEagerInit()返回true 创建真正的Bean对象(getObject()返回的对象)
							getBean(beanName);
						}
					}
				}
				else {
					// 创建Bean对象
					getBean(beanName);
				}
			}
		}

		// 所有的非懒加载单例Bean都创建完了后
		// 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 {
                    // 实现了SmartInitializingSingleton的非懒加载的单例bean 执行afterSingletonsInstantiated()方法
					smartSingleton.afterSingletonsInstantiated();
				}
				smartInitialize.end();
			}
		}
	}
// 合并BeanDefinition
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
		// Quick check on the concurrent map first, with minimal locking.
		RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
		if (mbd != null && !mbd.stale) {
			return mbd;
		}
		return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
	}
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
			throws BeanDefinitionStoreException {

		return getMergedBeanDefinition(beanName, bd, null);
	}

protected RootBeanDefinition getMergedBeanDefinition(
			String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
			throws BeanDefinitionStoreException {

		synchronized (this.mergedBeanDefinitions) {
			RootBeanDefinition mbd = null;
			RootBeanDefinition previous = null;

			// Check with full lock now in order to enforce the same merged instance.
			if (containingBd == null) {
				mbd = this.mergedBeanDefinitions.get(beanName);
			}

			if (mbd == null || mbd.stale) {
				previous = mbd;
                // 如果当前BeanDefinition没有父级BeanDefinition 
				if (bd.getParentName() == null) {
					// Use copy of given root bean definition.
					if (bd instanceof RootBeanDefinition) {
                        // 当前BeanDefinition如果实现了RootBeanDefinition 克隆
						mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
					}
					else {
                        // 创建一个RootBeanDefinition
						mbd = new RootBeanDefinition(bd);
					}
				}
				else {
					// Child bean definition: needs to be merged with parent.
					// pbd表示parentBeanDefinition
					BeanDefinition pbd;
					try {
						String parentBeanName = transformedBeanName(bd.getParentName());
                        // BeanDefinition的parent还有parent 所以要递归合并
						if (!beanName.equals(parentBeanName)) {
							pbd = getMergedBeanDefinition(parentBeanName);
						}
						else {
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							else {
								throw new NoSuchBeanDefinitionException(parentBeanName,
										"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
												"': cannot be resolved without a ConfigurableBeanFactory parent");
							}
						}
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
								"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
					}

					// Deep copy with overridden values.
					// 子BeanDefinition的属性覆盖父BeanDefinition的属性,这就是合并
					mbd = new RootBeanDefinition(pbd);
					mbd.overrideFrom(bd);
				}

				// Set default singleton scope, if not configured before.
				if (!StringUtils.hasLength(mbd.getScope())) {
					mbd.setScope(SCOPE_SINGLETON);
				}

				// A bean contained in a non-singleton bean cannot be a singleton itself.
				// Let's correct this on the fly here, since this might be the result of
				// parent-child merging for the outer bean, in which case the original inner bean
				// definition will not have inherited the merged outer bean's singleton status.
				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
					mbd.setScope(containingBd.getScope());
				}

				// Cache the merged bean definition for the time being
				// (it might still get re-merged later on in order to pick up metadata changes)
				if (containingBd == null && isCacheBeanMetadata()) {
					this.mergedBeanDefinitions.put(beanName, mbd);
				}
			}
			if (previous != null) {
				copyRelevantMergedBeanDefinitionCaches(previous, mbd);
			}
			return mbd;
		}
	}

1、抽象的BeanDefinition

抽象的BeanDefinition不会实例化生成bean,可以用作公共的BeanDefinition,作为其它Definition的parent,定义一个规范

<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype" abstract="true"/>
<bean id="child" class="com.zhouyu.service.Child" parent="parent"/>

2、合并BeanDefinition为什么要创建一个新的BeanDefinition? 

因为任何BeanDefinition都有可能是其它BeanDefinition的parent,所以只能新建一个,不能在原来的BeanDefinition上修改

3、SmartFactoryBean接口

@Component
public class TestFactoryBean implements SmartFactoryBean {

	@Override
	public boolean isEagerInit() {
		return true;
	}

	@Override
	public Object getObject() throws Exception {
		UserService userService = new UserService();

		return userService;
	}

	@Override
	public Class<?> getObjectType() {
		return UserService.class;
	}
}

 SmartFactoryBean继承了FactoryBean,当isEagerInit()返回true时,在创建非懒加载的单例bean的TestFactoryBean时就会调用getObject()方法创建userService对象

4、SmartInitializingSingleton接口

所有非懒加载的单例bean创建完之后,再遍历所有beanNames,通过beanName从单例池中拿出单例bean判断是否实现了SmartInitializingSingleton接口,如果是会调用afterSingletonsInstantiated()方法

@Component
public class UserService implements SmartInitializingSingleton {

	@Override
	public void afterSingletonsInstantiated() {
        // 创建完所有的非懒加载的单例bean时会来调这个方法
		System.out.println("afterSingletonsInstantiated()");
	}
}

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

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

相关文章

[Go版]算法通关村第十关黄金——归并排序

目录 归并排序&#xff08;mergeSort&#xff09;思路分析&#xff1a;二分分割 合并两个数组 递归遍历时处理元素的过程图&#xff1a;递归遍历时栈内的数据图&#xff1a;复杂度&#xff1a;时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)、空间复杂度 O ( n ) O(n) O(n)Go…

innodb的锁

一致性锁定读和一致性非锁定读 Read Committed和Repetable Read级别下采用MVCC 实现非锁定读 但在一些情况下&#xff0c;要使用加锁来保障数据的逻辑一致性 自增列 锁的算法 唯一值 MySQL 中关于gap lock / next-key lock 的一个问题_呜呜呜啦啦啦的博客-CSDN博客 RR可以通过…

为什么一些程序员很傲慢?懂点编程了不起吗?

Perl 语言之父 Larry Wall 说过&#xff0c;好的程序员有 3 种美德&#xff1a;懒惰、急躁和傲慢&#xff08;Laziness, Impatience and hubris&#xff09;。 在日常工作中&#xff0c;程序员的傲慢可以说是被吐槽的最多的&#xff0c;之前还有人特地开了帖子&#xff0c;发…

九耶丨阁瑞钛伦特-HashCode是什么

HashCode是一种用于快速查找和比较对象的方法。它是一个整数值&#xff0c;由对象的内容计算得出。HashCode通常用于数据结构中的散列函数&#xff0c;如哈希表、散列表等。 HashCode的作用有以下几点&#xff1a; 在哈希表中快速查找对象&#xff1a;哈希表根据对象的HashCod…

SQL Server数据库无法连接

问题如下&#xff1a; 原因&#xff1a;sql server服务器未开启 解决方法&#xff1a;以管理员身份打开cmd&#xff0c;输入&#xff1a;net start mssqlserver。

[Go版]算法通关村第十二关白银——字符串经典基础面试题

目录 反转专题题目&#xff1a;反转字符串思路分析&#xff1a;左右双指针 对向交换复杂度&#xff1a;时间复杂度 O ( n ) O(n) O(n)、空间复杂度 O ( 1 ) O(1) O(1)Go代码 题目&#xff1a;反转字符串 II思路分析&#xff1a;K个一组反转思想&#xff08;找到每组的首尾索引…

实在没货,简历(软件测试)咋写?

简历咋写&#xff0c;这是很多没有【软件测试实际工作经验】的同学们非常头疼的事情。 简历咋写&#xff1f;首先你要知道简历的作用。 简历的作用是啥呢&#xff1f;一句话就是&#xff1a;让HR小姐姐约你。 如何让HR看你一眼&#xff0c;便相中你的简历&#xff0c;实现在众…

AcrelEMS-IDC数据中心综合能效管理系统解决方案-安科瑞黄安南

1 概述 安科瑞电气紧跟数据中心发展形式&#xff0c;推出AcrelEMS-IDC数据中心综合能效管理解决方案&#xff0c;包含有电力监控、动环监控、消防监控、能耗统计分析、智能照明控制以及新能源监测几个子系统。集成了变配电监测、电源备自投、电气接点测温、智能照明控制、电能…

vue 实现图片懒加载

一&#xff1a;懒加载的目的 有些页面可能展示的是大量的图片&#xff0c;如果我们一次性加载所有图片就会浪费性能&#xff0c;影响用户体验&#xff0c;所以我们就会懒加载这些图片。即可视区域之外的图片不加载&#xff0c;随着页面的滚动&#xff0c;图片进入可视区域&…

小程序swiper一个轮播显示一个半内容且实现无缝滚动

效果图&#xff1a; wxml&#xff08;无缝滚动&#xff1a;circular"true"&#xff09;&#xff1a; <!--components/tool_version/tool_version.wxml--> <view class"tool-version"><swiper class"tool-version-swiper" circul…

基于IMX6ULLmini的linux裸机开发系列二:使用C语言和SDK点亮LED

引入sdk头文件 sudo chown -R gec /opt 用这条命令给gec赋权限&#xff0c;否则访问权限不够&#xff0c;无法读取&#xff0c;如下图成功 目的&#xff1a;解决寄存器地址难查难设置 devices/MCIMX6Y2/MCIMX6Y2.h 记录外设寄存器及其相关操作 devices/MCIMX6Y2/drivers/fsl_…

分班情况通知如何下发?使用这个技术源一键搞定

在下发分班情况通知之前&#xff0c;我们需要制作好分班查询页面&#xff0c;我们先来看看利用易查分快速制作分班查询页面的教程&#xff0c;后面会分享具体的【分班情况通知范文】【分班情况通知下发方式】&#xff01; 分班查询页面制作教程 在制作分班查询系统前&#xff…

《Go 语言第一课》课程学习笔记(四)

构建模式&#xff1a;Go Module 的 6 类常规操作 为当前 module 添加一个依赖 我们如何为一个 Go Module 添加一个新的依赖包呢&#xff1f; 如果我们要为项目增加一个新依赖&#xff1a;github.com/google/uuid&#xff0c;我们首先会更新源码&#xff1a;package mainimpor…

Docker 三要素

文章目录 Docker 简介Docker客户端Docker服务器Docker 镜像Docker 容器 Docker 简介 学习完容器的相关概念&#xff0c;开始学习docker的核心组件分别是Docker客户端、Docker服务器、Docker镜像、Docker容器、仓库。 学习之前&#xff0c;我们先思考一个问题&#xff0c;目前…

如何关闭“若要接收后续google chrome更新,您需使用windows10或更高版本”

Windows Registry Editor Version 5.00[HKEY_CURRENT_USER\Software\Policies\Google\Chrome] "SuppressUnsupportedOSWarning"dword:00000001 如何关闭“若要接收后续 google chrome 更新,您需使用 windows 10 或更高版本” - 知乎

影视公司技术流程设计之服务器搭建

在影视公司&#xff0c;硬件的投入占相当大的比例&#xff0c; 大到存储&#xff0c; 服务器&#xff0c;工作站&#xff0c; 小到主机CPU&#xff0c;内存&#xff0c;显卡&#xff0c;手绘板。 而存储又是硬件上的大头&#xff0c;一套合理的存储解决方案&#xff0c;优为关键…

render和h函数的使用

// 如果没有配置项&#xff0c;则可以省略不写 {}h("div", [h(h-tooltip, // 在tooltip中进行改造// ----- h-tooltip 的配置项 -----Start{props: {placement: "top-start",// content: 提示内容,transfer: true},style: {overflow: hidden,text-overflow…

信安通用基础知识

文章目录 密码学经典误区PGP优良保密协议信安经典其它安全手段XSS与CSRF cross site request forgeryCSRF的利用逻辑CSRF示例CSRF防范检查Referer字段添加校验token XSS cross site scripting common weakness enumeration常见密码api误用&#xff08;摘自毕设参考文献&#xf…

Java面试题目汇总

一、面向对象的三个基本特征 2、方法重载和方法重写的概念和区别 3、接口和内部类、抽象类的特性 4、文件读写的基本类 **5、串行化的注意事项以及如何实现串行化 6、线程的基本概念、线程的基本状态以及状态之间的关系 7、线程的同步、如何实现线程的同步 8、几种常用的数据结…

如何仿写简易tomcat 实现思路+代码详细讲解

仿写之前&#xff0c;我们要搞清楚都要用到哪些技术 自定义注解&#xff0c;比如Tomcat使用的是Servlet&#xff0c;我们可以定义一个自己的MyServlet构造请求体和返回体&#xff0c;比如tomcat使用HttpRequest&#xff0c;我们可以自己定义myHttpRequestjava去遍历一个指定目…