SpringCache源码解析(三)——@EnableCaching

news2024/11/13 3:49:16

一、源码阅读

让我们进行源码阅读把。

1.1 阅读源码基础:

@Import(xxx.class)里的类可以有两种类:

  • ImportSelector接口的实现类;
  • ImportBeanDefinitionRegistrar接口的实现类;

两种接口简介:

  • ImportSelector接口:在配置类中被@Import加入到Spring容器中以后。Spring容器就会把ImportSelector接口方法返回的字符串数组中的类new出来对象然后放到工厂中去。

  • ImportBeanDefinitionRegistrar:ImportBeanDefinitionRegistrar接口是也是spring的扩展点之一,它可以支持我们自己写的代码封装成BeanDefinition对象,注册到Spring容器中,功能类似于注解@Service @Component。

Spring框架中ImportBeanDefinitionRegistrar的应用详解

ImportBeanDefinitionRegistrar的作用

1.2 CachingConfigurationSelector

在这里插入图片描述

@EnableCaching导入了CachingConfigurationSelector。
org.springframework.cache.annotation.CachingConfigurationSelector:

@Override
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return getProxyImports();
			case ASPECTJ:
				return getAspectJImports();
			default:
				return null;
		}
	}

getProxyImports方法:

private String[] getProxyImports() {
		List<String> result = new ArrayList<>(3);
		//将AutoProxyRegistrar注入到容器
		result.add(AutoProxyRegistrar.class.getName());
		result.add(ProxyCachingConfiguration.class.getName());
		if (jsr107Present && jcacheImplPresent) {
			result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
		}
		return StringUtils.toStringArray(result);
	}

getAspectJImports方法:

private String[] getAspectJImports() {
		List<String> result = new ArrayList<>(2);
		result.add(CACHE_ASPECT_CONFIGURATION_CLASS_NAME);
		if (jsr107Present && jcacheImplPresent) {
			result.add(JCACHE_ASPECT_CONFIGURATION_CLASS_NAME);
		}
		return StringUtils.toStringArray(result);
	}

1.3 getProxyImports方法内部

1.3.1 AutoProxyRegistrar

  顾名思义,AutoProxyRegistrar从名称上就可以看出其本质是一个ImportBeanDefinitionRegistrar,而ImportBeanDefinitionRegistrar最重要的方法为registerBeanDefinitions(),这个方法的主要作用就是用方法参数中的registry向容器中注入一些BeanDefinition。

源码追踪:

@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean candidateFound = false;
		Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
		//1.遍历类元数据中的所有注解类型
		for (String annType : annTypes) {
			AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
			if (candidate == null) {
				continue;
			}
			Object mode = candidate.get("mode");
			Object proxyTargetClass = candidate.get("proxyTargetClass");
			//2.检查每个注解是否包含mode和proxyTargetClass属性,并判断其类型是否正确。
			if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
					Boolean.class == proxyTargetClass.getClass()) {
				candidateFound = true;
				//3.如果属性类型正确且mode为AdviceMode.PROXY,则根据proxyTargetClass值配置自动代理创建器。
				if (mode == AdviceMode.PROXY) {
					AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
					if ((Boolean) proxyTargetClass) {
						AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
						return;
					}
				}
			}
		}
		if (!candidateFound && logger.isInfoEnabled()) {
			String name = getClass().getSimpleName();
			logger.info(String.format("%s was imported but no annotations were found " +
					"having both 'mode' and 'proxyTargetClass' attributes of type " +
					"AdviceMode and boolean respectively. This means that auto proxy " +
					"creator registration and configuration may not have occurred as " +
					"intended, and components may not be proxied as expected. Check to " +
					"ensure that %s has been @Import'ed on the same class where these " +
					"annotations are declared; otherwise remove the import of %s " +
					"altogether.", name, name, name));
		}
	}

该方法用于注册Bean定义。主要功能如下:

  1. 遍历类元数据中的所有注解类型。
  2. 检查每个注解是否包含mode和proxyTargetClass属性,并判断其类型是否正确。
  3. 如果属性类型正确且mode为AdviceMode.PROXY,则根据proxyTargetClass值配置自动代理创建器。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

下面这是@EnableCaching里的mode值和proxyTargetClass值:
在这里插入图片描述
下面这个是@Configration注解里的mode值和proxyTargetClass的值,可以看到是null
在这里插入图片描述
友友们,这两个值是从哪来的呢?看这里:
在这里插入图片描述
其实就是注解里定义的呀。那么问题来了,这两个值是干嘛的,什么情况下要定义这两个值?

拿@EnableCaching 里的这两个值打个比方。它提供了几个属性来控制缓存的行为:

  1. mode():此属性决定了 Spring AOP(面向切面编程)使用的模式。它可以取两个值:
      AdviceMode.PROXY:表示使用代理模式实现AOP,这是默认值。
      AdviceMode.ASPECTJ:表示使用 AspectJ 模式实现AOP。
  2. proxyTargetClass():此属性仅在 mode 设置为 AdviceMode.PROXY 时有效。它指定了是否使用 CGLIB 代理(true)而不是 >JDK 动态代理(false,默认)。CGLIB 代理可以代理不可代理的类(即没有实现接口的类),而 JDK 动态代理只能代理实现了接口的类。

这些属性帮助开发者根据应用的需求选择合适的代理机制,从而更灵活地管理缓存逻辑。

拓展:AutoProxyRegistrar里关键代码分析

1.3.1.1 AutoProxyRegistrar.registerBeanDefinitions()方法总结

总结:AutoProxyRegistrar.registerBeanDefinitions()往容器中注入了一个类InfrastructureAdvisorAutoProxyCreator。

这段是不是很熟悉,没错,声明式事务中注入的也是这个InfrastructureAdvisorAutoProxyCreator类,用来生成代理对象。

1.3.2 ProxyCachingConfiguration

ProxyCachingConfiguration向Spring容器中注入了三个Bean:

  1. BeanFactoryCacheOperationSourceAdvisor:切面,用来匹配目标方法和目标类,看那些方法需要实现缓存的增强;
  2. CacheOperationSource:用来解析缓存相关的注解;
  3. CacheInterceptor:实现了Advice,是一个通知,实现对目标方法的增强。
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

	@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
		BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
		advisor.setCacheOperationSource(cacheOperationSource());
		advisor.setAdvice(cacheInterceptor());
		if (this.enableCaching != null) {
			advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
		}
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheOperationSource cacheOperationSource() {
		return new AnnotationCacheOperationSource();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheInterceptor cacheInterceptor() {
		CacheInterceptor interceptor = new CacheInterceptor();
		interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
		interceptor.setCacheOperationSource(cacheOperationSource());
		return interceptor;
	}

}

ProxyCachingConfiguration继承自AbstractCachingConfiguration,AbstractCachingConfiguration是抽象基础配置类,为启用Spring的注解驱动的缓存管理功能提供通用结构;代码如下:
在这里插入图片描述

1.3.2.1 BeanFactoryCacheOperationSourceAdvisor

BeanFactoryCacheOperationSourceAdvisor是一个切面,切面需要包括通知和切点:

  1. Advice通知:CacheInterceptor,由ProxyCachingConfiguration注入;
  2. Pointcut切点:内部CacheOperationSourcePointcut类型的属性;
1.3.2.2 CacheOperationSourcePointcut

Pointcut主要包含两部分,对目标类的匹配,对目标方法的匹配。
对目标类的匹配,其实啥也没干,返回true,主要逻辑在对目标方法的匹配上,大部分Pointcut都是如此:

private class CacheOperationSourceClassFilter implements ClassFilter {

	@Override
	public boolean matches(Class<?> clazz) {
		if (CacheManager.class.isAssignableFrom(clazz)) {
			return false;
		}
		// 啥也没干
		CacheOperationSource cas = getCacheOperationSource();
		return (cas == null || cas.isCandidateClass(clazz));
	}
}

对目标方法的匹配:

public boolean matches(Method method, Class<?> targetClass) {
	CacheOperationSource cas = getCacheOperationSource();
	/**
	 * @see AbstractFallbackCacheOperationSource#getCacheOperations(java.lang.reflect.Method, java.lang.Class)
	 */
	// 就是看目标方法上面有没有缓存注解
	return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}

这里会委托给CacheOperationSource来解析缓存注解,这个类是在ProxyCachingConfiguration中注入的。

1.3.3 org.springframework.cache.jcache.config.ProxyJCacheConfiguration

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

	@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
		BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
		advisor.setCacheOperationSource(cacheOperationSource());
		advisor.setAdvice(cacheInterceptor());
		if (this.enableCaching != null) {
			advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
		}
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheOperationSource cacheOperationSource() {
		return new AnnotationCacheOperationSource();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheInterceptor cacheInterceptor() {
		CacheInterceptor interceptor = new CacheInterceptor();
		interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
		interceptor.setCacheOperationSource(cacheOperationSource());
		return interceptor;
	}

}

ProxyCachingConfiguration向Spring容器中注入了三个Bean:

  1. BeanFactoryCacheOperationSourceAdvisor:切面,用来匹配目标方法和目标类,看那些方法需要实现缓存的增强;
  2. CacheOperationSource:用来解析缓存相关的注解;
  3. CacheInterceptor:实现了Advice,是一个通知,实现对目标方法的增强。
1.3.3.1 BeanFactoryCacheOperationSourceAdvisor

BeanFactoryCacheOperationSourceAdvisor是一个切面,切面需要包括通知和切点:

  • Advice通知:CacheInterceptor,由ProxyCachingConfiguration注入;
  • Pointcut切点:内部CacheOperationSourcePointcut类型的属性;
1.3.3.2 CacheOperationSourcePointcut

Pointcut主要包含两部分,对目标类的匹配,对目标方法的匹配。

对目标类的匹配,其实啥也没干,返回true,主要逻辑在对目标方法的匹配上,大部分Pointcut都是如此:
在这里插入图片描述

abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		if (CacheManager.class.isAssignableFrom(targetClass)) {
			return false;
		}
		// 就是看目标方法上面有没有缓存注解
		CacheOperationSource cas = getCacheOperationSource();
		return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
	}
}

这里会委托给CacheOperationSource来解析缓存注解,这个类是在ProxyCachingConfiguration中注入的。

最终会调用到org.springframework.cache.annotation.SpringCacheAnnotationParser#parseCacheAnnotations(org.springframework.cache.annotation.SpringCacheAnnotationParser.DefaultCacheConfig, java.lang.reflect.AnnotatedElement, boolean)

private Collection<CacheOperation> parseCacheAnnotations(
		DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {

	Collection<? extends Annotation> anns = (localOnly ?
			AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
			AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
	if (anns.isEmpty()) {
		return null;
	}

	// @Cacheable解析为CacheableOperation
	// @CacheEvict解析为CacheEvictOperation
	// @CachePut解析为CachePutOperation
	final Collection<CacheOperation> ops = new ArrayList<>(1);
	anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
			ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
	anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
			ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
	anns.stream().filter(ann -> ann instanceof CachePut).forEach(
			ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
	anns.stream().filter(ann -> ann instanceof Caching).forEach(
			ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
	return ops;
}

1.3.3.3 CacheInterceptor

CacheInterceptor是一个Advice,用来实现对目标方法的增强,当调用目标方法时会先进入CacheInterceptor.invoke()方法:

org.springframework.cache.interceptor.CacheInterceptor#invoke

public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {

	@Override
	@Nullable
	public Object invoke(final MethodInvocation invocation) throws Throwable {
		Method method = invocation.getMethod();

		CacheOperationInvoker aopAllianceInvoker = () -> {
			try {
				// 调用目标方法,后面的代码会回调到这里
				return invocation.proceed();
			}
			catch (Throwable ex) {
				throw new CacheOperationInvoker.ThrowableWrapper(ex);
			}
		};

		try {
			return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
		}
		catch (CacheOperationInvoker.ThrowableWrapper th) {
			throw th.getOriginal();
		}
	}

}

org.springframework.cache.interceptor.CacheAspectSupport#execute(org.springframework.cache.interceptor.CacheOperationInvoker, java.lang.reflect.Method, org.springframework.cache.interceptor.CacheAspectSupport.CacheOperationContexts)

private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
	// Special handling of synchronized invocation
	if (contexts.isSynchronized()) { // false不会进入
		CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
		if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
			Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
			Cache cache = context.getCaches().iterator().next();
			try {
				return wrapCacheValue(method, handleSynchronizedGet(invoker, key, cache));
			}
			catch (Cache.ValueRetrievalException ex) {
				// Directly propagate ThrowableWrapper from the invoker,
				// or potentially also an IllegalArgumentException etc.
				ReflectionUtils.rethrowRuntimeException(ex.getCause());
			}
		}
		else {
			// No caching required, only call the underlying method
			return invokeOperation(invoker);
		}
	}


	// Process any early evictions
	// 先调用@CacheEvict注解中的属性beforeInvocation=true的,实际上这个值默认为false,这里一般不会干啥,看后面的调用
	// cache.evict()
	processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
			CacheOperationExpressionEvaluator.NO_RESULT);

	// Check if we have a cached item matching the conditions
	// cache.get()
	Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

	// Collect puts from any @Cacheable miss, if no cached item is found
	List<CachePutRequest> cachePutRequests = new LinkedList<>();
	if (cacheHit == null) {
		collectPutRequests(contexts.get(CacheableOperation.class),
				CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
	}

	Object cacheValue;
	Object returnValue;

	if (cacheHit != null && !hasCachePut(contexts)) {
		// If there are no put requests, just use the cache hit
		cacheValue = cacheHit.get();
		returnValue = wrapCacheValue(method, cacheValue);
	}
	else {
		// Invoke the method if we don't have a cache hit
		// 调用目标方法
		returnValue = invokeOperation(invoker);
		cacheValue = unwrapReturnValue(returnValue);
	}

	// Collect any explicit @CachePuts
	collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

	// Process any collected put requests, either from @CachePut or a @Cacheable miss
	for (CachePutRequest cachePutRequest : cachePutRequests) {
		cachePutRequest.apply(cacheValue);
	}

	// Process any late evictions
	// 调用@CacheEvict注解中的属性beforeInvocation=false的
	processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

	return returnValue;
}

1.4 扩展缓存注解

有时候由于业务需要或者Spring提供的缓存不满足我们的要求,如无法解决缓存雪崩问题,扩展步骤如下:

  1. 实现CacheManager接口或继承AbstractCacheManager,管理自身的cache实例,也可以直接使用内置的SimpleCacheManager;
  2. 实现Cache接口,自定义缓存实现逻辑;
  3. 将自定义的Cache和CacheManager进行关联并注入到Spring容器中。

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

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

相关文章

如何在算家云搭建Open-Sora1.0

一、模型介绍 2024 年 3 月 18 日&#xff0c;Colossal-AI 团队发布了 Open-Sora 1.0 项目&#xff0c;该项目是一个全面开源的视频生成模型项目&#xff0c;项目旨在高效制作高质量视频&#xff0c;并使所有人都能使用其模型、工具和内容。 模型架构 &#xff1a; Open-Sor…

ubuntu20.04 编译vtk 9.3.1+vtkDicom+GDCM 3.0.24

1 下载vtk源码 链接地址如下&#xff1a; Download | VTK 使用cmake-gui编译&#xff08;如何安装使用&#xff0c;查看前两篇文章&#xff09;&#xff0c;运行命令&#xff1a; cmake-gui 如下图所示&#xff0c;选择源码目录和build目录&#xff1a; 勾选 BUILD_SHARED…

基于SpringBoot+Vue+MySQL的志愿服务管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着社会对志愿服务需求的日益增长&#xff0c;传统的志愿服务管理方式已难以满足高效、透明、精准的管理需求。为提升志愿服务组织的运营效率&#xff0c;优化资源配置&#xff0c;增强志愿者参与度和满意度&#xff0c;开发基…

项目启动 | 盘古信息携手晶捷电子,开启数字化生产管理新纪元

随着智能制造技术的不断成熟与普及&#xff0c;一个全新的制造业时代已经到来&#xff0c;智能制造已成为推动全球制造业转型升级的重要引擎。在日新月异的市场环境中&#xff0c;传统制造业更需加快转型升级的步伐&#xff0c;以智能化、精细化的生产模式&#xff0c;实现生产…

昆仑联通8000万补流全砍:曾分红近亿,应收账款周转率连年下滑

《港湾商业观察》廖紫雯 日前&#xff0c;北京昆仑联通科技发展股份有限公司&#xff08;以下简称&#xff1a;昆仑联通&#xff09;拟冲刺北交所&#xff0c;保荐机构为东方证券。 2023年6月29日&#xff0c;昆仑联通于上交所递交招股书&#xff0c;拟冲刺沪市主板&#xff…

混淆矩阵与 ROC 曲线:何时使用哪个进行模型评估

必须在机器学习和数据科学中评估模型性能&#xff0c;才能提出一个可靠、准确和高效的模型来进行任何类型的预测。一些常见的工具是 Confusion Matrix 和 ROC Curve。两者具有不同的用途&#xff0c;确切地知道何时使用它们对于稳健的模型评估至关重要。在这篇博客中&#xff0…

【nginx】转发配置、漏洞整改

转发配置 常见的接口调用配置&#xff1a; location /com_api/ {proxy_set_header X-Real-IP $remote_addr;proxy_set_header Host $http_host;proxy_pass http://后端服务IP:后端服务端口号/; }若转发调不通时&#xff08;常出现在调用第三方系统时&#xff09;&#xff0c;…

大数据 - OLAP与OLTP的区别

前言 联机事务处理OLTP&#xff08;on-line transaction processing&#xff09;和 联机分析处理OLAP&#xff08;On-Line Analytical Processing&#xff09;。 OLTP&#xff0c;主要是面向传统的“增删改查”事务系统&#xff0c;数据大都是以实体对象模型来存储数据&#…

Java设计模式—面向对象设计原则(二) --------> 里氏代换原则 LSP (完整详解,附有代码+案列)

文章目录 里氏代换原则3.2.1 概述3.2.2 改进上述代码 里氏代换原则 3.2.1 概述 里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则&#xff1a;任何基类可以出现的地方&#xff0c;子类一定可以出现。通俗理解&#xff1a;子类可以扩展父类的功能&#xff0c;但不能…

快速同步与问题解决:每日站立会议的实用指南

每日站会不管是在大型企业中&#xff0c;还是在中小型企业中都是每日必备的一种晨会。但并不是所有的企业都能够正确使用每日站会&#xff0c;较多的企业在每日站会中总会偏离每日站会的使用目的&#xff0c;从而变成了一个偏向于教育批评的会议。本篇文章中&#xff0c;让我们…

4WRA6E07-2X/G24N9K4/VL配套HE-SP2比例放大器

比例方向阀由直流比例电磁铁与液压阀两部分组成。通过BEUEC比例放大器对控制信号进行功率放大后&#xff0c;电磁铁产生与电流成比例的力或位移&#xff0c;移动阀芯位置&#xff0c;实现流量和流向的精确控制。比例方向阀能实现流量和方向的连续且精确的控制&#xff0c;尤其是…

企业如何“向内求”, 挖出更多净利润?

未来的财务部门将是一个“主导“部门&#xff0c;成为业务的翻译官&#xff0c;成为业财融合的引领者&#xff0c;引领企业走在最适合自己的财务数智化转型道路上。 作者|思杭 出品|产业家 收入增速放缓、营收下滑、消费意愿降低&#xff0c;这些也许是近半年在各种企业财…

14款用于创建和销售数字产品的工具(专家推荐)

创建和销售数字产品是获得被动收入并向全球观众分享您的专业知识的绝佳方式。但您需要合适的工具来实现这一目标。否则&#xff0c;您可能会在复杂的系统上浪费时间和金钱&#xff0c;最终无法获得预期的效果。 在WPBeginner&#xff0c;我们已经创建数字产品超过十年&#xf…

[FireshellCTF2020]Caas1

知识点&#xff1a; 1.文件包含 2.#include " " 预处理报错 进入页面发现是让我么输入code然后他去处理&#xff0c;那就输一下试试. 它报错了&#xff0c;可以看出这是个C语言的报错&#xff0c;那么传入一下C语言代码. #include <stdio.h>int main() {prin…

力扣最热一百题——二叉树的直径

目录 题目链接&#xff1a;543. 二叉树的直径 - 力扣&#xff08;LeetCode&#xff09; 题目描述 示例 提示&#xff1a; 解法一&#xff1a;深度优先搜索 实现思路&#xff1a; 关键点&#xff1a; Java写法&#xff1a; 运行时间 C写法&#xff1a; 运行时间 总结…

优思学院|精益工程师是做什么的? 前途好吗?

如果你有经常留意招聘网站&#xff0c;你或者会偶尔看到精益工程师这个职位&#xff0c;事实上精益工程师这个岗位不多&#xff0c;但却有很多人会觉得精益工程师前途无限&#xff0c;到底为什么呢&#xff1f; 精益工程师的职责主要围绕着帮助企业减少浪费、优化流程、提升效…

第二证券:股票是谁控制涨跌?股票涨跌如何计算?

股票是谁控制涨跌&#xff1f; 1、上市公司基本面 上市公司基本面包括盈利才干、成长性、财务状况、管理水平、行业远景、商场竞争、工业政策等。这些方面是决定股价长时间趋势的首要要素&#xff0c;一般来说基本面好的公司&#xff0c;其股价会随着成绩的提高而上涨。 2、…

solidworks案例4-20240911

使用到的命令&#xff1a;拉伸&#xff0c;拉伸切除

Linux常用命令笔记

执行查看帮助命令 1.1 Linux命令的格式 命令名称 [命令参数] [命令对象] 命令名称、命令参数、命令对象之间请用空格键分隔命令对象一般是指要处理的文件、目录、用户等资源&#xff0c;而命令参数可以用长格式&#xff08;完整的选项名称&#xff09;&#xff0c;也可以用短…

word文档的读入(6)

上一个方式&#xff0c;虽然能获取到标准答案和所对应的学生答案&#xff0c;但代码不够简单和优雅。这时&#xff0c;可以用另一种方式来实现&#xff1a;遍历索引。 定义 简单来说&#xff0c;enumerate()函数用来遍历一个可遍历对象中的元素&#xff0c;同时通过一个计数器…