spring 随笔 async 1-源码走读

0. 这一块比较简单,还就内个无障碍阅读

不谈,放个调用栈的日志先 …

// 我们自己写的 @Async 注解的方法
simpleTest:31, TestAsyncTestService (cn.angel.project.angelmicroservicesample.test.service)
invoke:-1, TestAsyncTestService$$FastClassBySpringCGLIB$$880884d7 (cn.angel.project.angelmicroservicesample.test.service)

// 从这个方法拦截器的调用顺序(异步 -> 事务)也可以想到:异步中启动事务,事务的作用域并不会带到全局,只是作用在当前线程
// 准备钻入到 我们自己写的方法中(aop的连接点 反射调用)
invoke:218, MethodProxy (org.springframework.cglib.proxy)
invokeJoinpoint:771, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
proceed:163, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:749, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)

proceedWithInvocation:-1, 1094377388 (org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$566)
invokeWithinTransaction:367, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:118, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:175, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:749, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)
invoke:95, ExposeInvocationInterceptor (org.springframework.aop.interceptor)

// 准备钻入到 事务的interceptor
proceed:186, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:749, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)

lambda$invoke$0:115, AsyncExecutionInterceptor (org.springframework.aop.interceptor)
call:-1, 1109445703 (org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda$564)
run$$$capture:266, FutureTask (java.util.concurrent)
run:-1, FutureTask (java.util.concurrent)
 - Async stack trace
<init>:132, FutureTask (java.util.concurrent)
newTaskFor:102, AbstractExecutorService (java.util.concurrent)
submit:133, AbstractExecutorService (java.util.concurrent)
submit:348, ThreadPoolTaskExecutor (org.springframework.scheduling.concurrent)
doSubmit:290, AsyncExecutionAspectSupport (org.springframework.aop.interceptor)
invoke:129, AsyncExecutionInterceptor (org.springframework.aop.interceptor)

// 从这里开始走读源码
// 准备钻入到 异步的Interceptor
proceed:186, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:749, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)

intercept:691, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
simpleTest:-1, TestAsyncTestService$$EnhancerBySpringCGLIB$$72420f26 (cn.angel.project.angelmicroservicesample.test.service)

// 我们自己写的 控制器方法
async:57, TestEndpoint (cn.angel.project.angelmicroservicesample.endpoint)
invoke0:-2, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
doInvoke:190, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:138, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:105, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:878, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:792, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1040, DispatcherServlet (org.springframework.web.servlet)
doService:943, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doPost:909, FrameworkServlet (org.springframework.web.servlet)
service:652, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:733, HttpServlet (javax.servlet.http)
internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:100, RequestContextFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:93, FormContentFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
invoke:202, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:542, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:143, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:343, CoyoteAdapter (org.apache.catalina.connector)
service:374, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:888, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1597, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

1. spring.aop.methodInterceptor 老一套了

	// org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke
	@Override
	@Nullable
	public Object invoke(final MethodInvocation invocation) throws Throwable {
		// 我们自定义方法所在的类(被代理的类)
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
		Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
		
		// step into ...
		// 这里将返回 用于获取异步线程池 的方法
		final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

		// step into ...
		// 显然这里获取异步线程池的方法,内部是有说法的!
		AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
		if (executor == null) {
			throw new IllegalStateException(
					"No executor specified and no default executor set on AsyncExecutionInterceptor either");
		}

		// 这里创建了1个task
		// MethodInvocation 将调用我们自定义的代码
		Callable<Object> task = () -> {
			try {
				Object result = invocation.proceed();
				if (result instanceof Future) {
					return ((Future<?>) result).get();
				}
			}
			catch (ExecutionException ex) {
				handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
			}
			catch (Throwable ex) {
				handleError(ex, userDeclaredMethod, invocation.getArguments());
			}
			return null;
		};

		// step into ...
		return doSubmit(task, executor, invocation.getMethod().getReturnType());
	}
	
-------------

	//  org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor
	@Nullable
	protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
		AsyncTaskExecutor executor = this.executors.get(method);
		if (executor == null) {
			Executor targetExecutor;
			// 参考: org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor#getExecutorQualifier
			// 从@Async中获取Executor.bean.qualifier
			String qualifier = getExecutorQualifier(method);
			if (StringUtils.hasLength(qualifier)) {
				targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
			}
			else {
				// private SingletonSupplier<Executor> defaultExecutor;
				// 这个Supplier函数即我们工厂类初始化缓存下来的Executor获取方法
				targetExecutor = this.defaultExecutor.get();
			}
			if (targetExecutor == null) {
				return null;
			}
			executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
					(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
					
			// private final Map<Method, AsyncTaskExecutor> executors = new ConcurrentHashMap<>(16);
			// 也就是说这一代码路径,第一次调用之后,也就顺带缓存下来了
			this.executors.put(method, executor);
		}
		return executor;
	}
	
--------------

	// org.springframework.aop.interceptor.AsyncExecutionAspectSupport#doSubmit
	@Nullable
	protected Object doSubmit(Callable<Object> task, AsyncTaskExecutor executor, Class<?> returnType) {
		// 根据@Async注解的方法的返回类型 分为不同的代码分支
		// 也就是说 被@Async注解的方法 只支持以下类型
		// public class CompletableFuture<T> implements Future<T>, CompletionStage<T>
		// juc.CompletableFuture 相比 Future 新增组合的能力
		if (CompletableFuture.class.isAssignableFrom(returnType)) {
			return CompletableFuture.supplyAsync(() -> {
				try {
					// 其实 Callable 也可以视作 Supplier函数
					// j.u.c.CompletableFuture.supplyAsync 的语义:该方法将在运行时调用
					return task.call();
				}
				catch (Throwable ex) {
					throw new CompletionException(ex);
				}
			// 这里提交的executor即我们入参传的线程池
			}, executor);
		}
		// spring封装的Future类型,集成了回调函数
		else if (ListenableFuture.class.isAssignableFrom(returnType)) {
			return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
		}
		else if (Future.class.isAssignableFrom(returnType)) {
			return executor.submit(task);
		}
		else {
			// step into ...
			// void
			// 这个看起来比较简单,我们追踪这个
			executor.submit(task);
			return null;
		}
	}
	
---------

	// org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor#submit(java.lang.Runnable)
	@Override
	public Future<?> submit(Runnable task) {
		// private ThreadPoolExecutor threadPoolExecutor;
		// spring异步的工厂类初始化时,会保存 默认Executor的get方法,第一次使用时调用并赋值
		// 可见 ThreadPoolTaskExecutor 只是委托juc的线程池,spring并没有自己实现线程池
		ExecutorService executor = getThreadPoolExecutor();
		try {
			// 从这里开始接入 j.u.c.AbstractExecutorService
			return executor.submit(task);
		}
		catch (RejectedExecutionException ex) {
			throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
		}
	}

2. 小加一餐:默认Executor获取方法的调用

// step into ...
setConfigurers:77, AbstractAsyncConfiguration (org.springframework.scheduling.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
inject:754, AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement (org.springframework.beans.factory.annotation)
inject:119, InjectionMetadata (org.springframework.beans.factory.annotation)
postProcessProperties:399, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
populateBean:1420, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:593, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:516, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:324, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 4073506 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$139)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:322, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:202, AbstractBeanFactory (org.springframework.beans.factory.support)
instantiateUsingFactoryMethod:409, ConstructorResolver (org.springframework.beans.factory.support)
instantiateUsingFactoryMethod:1336, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBeanInstance:1176, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:556, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:516, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:324, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 4073506 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$139)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:322, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:207, AbstractBeanFactory (org.springframework.beans.factory.support)
registerBeanPostProcessors:229, PostProcessorRegistrationDelegate (org.springframework.context.support)
registerBeanPostProcessors:723, AbstractApplicationContext (org.springframework.context.support)
refresh:536, AbstractApplicationContext (org.springframework.context.support)
refresh:143, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:758, SpringApplication (org.springframework.boot)
refresh:750, SpringApplication (org.springframework.boot)
refreshContext:405, SpringApplication (org.springframework.boot)
run:315, SpringApplication (org.springframework.boot)
run:1237, SpringApplication (org.springframework.boot)
run:1226, SpringApplication (org.springframework.boot)
main:10, AngelMicroServiceSampleApplication (cn.angel.project.angelmicroservicesample)

东西并不多

	// org.springframework.scheduling.annotation.AbstractAsyncConfiguration#setConfigurers
	@Autowired(required = false)
	void setConfigurers(Collection<AsyncConfigurer> configurers) {
		if (CollectionUtils.isEmpty(configurers)) {
			return;
		}
		if (configurers.size() > 1) {
			throw new IllegalStateException("Only one AsyncConfigurer may exist");
		}
		AsyncConfigurer configurer = configurers.iterator().next();

		// 熟悉的来咯
		this.executor = configurer::getAsyncExecutor;
		this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler;
	}

3. 走个流程总结一下

除开async,其实spring很多框架都不啥干实事的,底层大多都委托给jdk、第三方(spring.validation=spring封装调用+jsr提出规范+hibernate实现细节)来实现,这也是java语言所倡导的"开放扩展"。


言归正传,补充以下类图

3.1 spring.async.ThreadpoolExecutor

请添加图片描述

3.2 spring.async.Future

在这里插入图片描述

3.3 spring.async.MethodInterceptor

请添加图片描述

4. 想起来了,再加一餐:SimpleAsyncTaskExecutor

  • 当没有指定默认线程池,并且使用@EnableAsync + @Async的时候,会默认使用这个池子

  • 这个池子提供了 节流(Throttle) 方面的实现,是基于同步锁实现的,跟j.u.c.ThreadpoolExecutor的做法(BlockQueue)还不一样,怪不得叫SimpleXxx

package org.springframework.core.task;

@SuppressWarnings("serial")
public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
		implements AsyncListenableTaskExecutor, Serializable {

	@Override
	public void execute(Runnable task, long startTimeout) {
		Assert.notNull(task, "Runnable must not be null");
		Runnable taskToUse = (this.taskDecorator != null ? this.taskDecorator.decorate(task) : task);
		if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {
			// ConcurrencyThrottleAdapter.beforeAccess() 算是伏笔了
			this.concurrencyThrottle.beforeAccess();
			doExecute(new ConcurrencyThrottlingRunnable(taskToUse));
		}
		else {
			doExecute(taskToUse);
		}
	}

	protected void doExecute(Runnable task) {
		Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
		thread.start();
	}

	private final ConcurrencyThrottleAdapter concurrencyThrottle = new ConcurrencyThrottleAdapter();

	public void setConcurrencyLimit(int concurrencyLimit) { this.concurrencyThrottle.setConcurrencyLimit(concurrencyLimit); }
	public final int getConcurrencyLimit() { return this.concurrencyThrottle.getConcurrencyLimit(); }
	public final boolean isThrottleActive() { return this.concurrencyThrottle.isThrottleActive(); }
	
	private static class ConcurrencyThrottleAdapter extends ConcurrencyThrottleSupport {
		// 实现都在 ConcurrencyThrottleSupport 里面了
		@Override
		protected void beforeAccess() { super.beforeAccess(); }
		@Override
		protected void afterAccess() { super.afterAccess(); }
	}

	private class ConcurrencyThrottlingRunnable implements Runnable {
		private final Runnable target;
		public ConcurrencyThrottlingRunnable(Runnable target) { this.target = target; }
		@Override
		public void run() {
			try { this.target.run(); }
			// ConcurrencyThrottleAdapter.afterAccess() 果然还是出现了
			finally { concurrencyThrottle.afterAccess(); }
		}
	}
}

4.1 说白了,这才是 节流 的实现代码

package org.springframework.util;

@SuppressWarnings("serial")
public abstract class ConcurrencyThrottleSupport implements Serializable {

	public static final int UNBOUNDED_CONCURRENCY = -1;
	public static final int NO_CONCURRENCY = 0; // 串行何尝不是一种节流

	// 重点应该就在这个 monitor 了
	private transient Object monitor = new Object();

	private int concurrencyLimit = UNBOUNDED_CONCURRENCY;
	private int concurrencyCount = 0;

	public boolean isThrottleActive() { return (this.concurrencyLimit >= 0); }

	// 任务执行前调用
	protected void beforeAccess() {
		if (this.concurrencyLimit == NO_CONCURRENCY) {
			throw new IllegalStateException(
					"Currently no invocations allowed - concurrency limit set to NO_CONCURRENCY");
		}
		if (this.concurrencyLimit > 0) {
			boolean debug = logger.isDebugEnabled();
			synchronized (this.monitor) {
				boolean interrupted = false;
				// 任务线程的并发数到头了,所以说,节流就是block,哈哈哈
				while (this.concurrencyCount >= this.concurrencyLimit) {
					if (interrupted) {
						throw new IllegalStateException("Thread was interrupted while waiting for invocation access, " +
								"but concurrency limit still does not allow for entering");
					}
					if (debug) {
						logger.debug("Concurrency count " + this.concurrencyCount +
								" has reached limit " + this.concurrencyLimit + " - blocking");
					}
					try {
						// 您的block来辣
						this.monitor.wait();
					}
					catch (InterruptedException ex) {
						// Re-interrupt current thread, to allow other threads to react.
						Thread.currentThread().interrupt();
						interrupted = true;
					}
				}
				if (debug) {
					logger.debug("Entering throttle at concurrency count " + this.concurrencyCount);
				}
				// 这里还处于同步块,但不被block
				// 同步中做了 计数累加
				// 表示这单任务接下了
				this.concurrencyCount++;
			}
		}
	}

	// 任务执行后调用
	protected void afterAccess() {
		if (this.concurrencyLimit >= 0) {
			synchronized (this.monitor) {
				// 表示任务搞定,并发数-1
				this.concurrencyCount--;
				if (logger.isDebugEnabled()) {
					logger.debug("Returning from throttle at concurrency count " + this.concurrencyCount);
				}
				// 唤醒1条被beforeAccess()所block的任务线程
				this.monitor.notify();
			}
		}
	}

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

手撕vector

文章目录一.vector的基本结构二.构造函数调用不明确三.迭代器失效&#xff08;其实是野指针问题&#xff09;a.扩容导致的迭代器失效b.意义不同四.深层次的深浅拷贝五.整体代码实现有了前面模拟实现string的经验&#xff0c;vector对大家来说也不会算很难。vector本质也就是一个…

【ORACLE】极速通关Oracle23c开发者免费版连接

前言 oracle23c开发者免费版已经于2023年4月4日(北京时间)推出&#xff0c;并且官方也公布了安装介质的下载地址&#xff0c;有RPM安装包、VM虚拟机、docker镜像&#xff08;下载链接见文末&#xff09;。 由于最近工作比较忙&#xff0c;暂时无法写一篇内容丰富的测试&#x…

springboot树形结构接口, 懒加载实现

数据库关系有父子id的, 作为菜单栏展示时需要用前端需要用到懒加载, 所谓懒加载就是接口有一个标志位isLeaf, 前端请求后通过该字段判断该节点是否还有子节点数据 创建数据库表 t_company_info结构有id和parentId标识, 用来表示父子关系 /*Navicat Premium Data TransferSourc…

大数据环境-云平台(阿里云)

由于电脑配置原因&#xff0c;无法在本地利用虚拟机搭建环境&#xff0c;因此使用云平台来当做学习的环境。 本节内容参考&#xff1a; 【2023新版黑马程序员大数据入门到实战教程&#xff0c;大数据开发必会的Hadoop、Hive&#xff0c;云平台实战项目全套一网打尽-哔哩哔哩】 …

72-Linux_线程同步

线程同步一.什么是线程同步二.线程同步的方法1.互斥锁(1)什么是互斥锁(2)互斥锁的接口(3)互斥锁的使用(例题)2.信号量(1)什么是信号量(2)信号量的接口(3)信号量的使用(例题)a.循环打印ABCb.:主线程获取用户输入,函数线程将用户输入的数据存储到文件中;3.读写锁(1)读写锁的接口(…

R语言|plot和par函数绘图详解,绘图区域设置 颜色设置 绘图后修改及图像输出

plot()函数 plot()函数是R中最基本的绘图函数&#xff0c;其实最简单、最基础的函数&#xff0c;这也就意味着其具有更多的可操作性。 plot(x,y,...) 在plot函数中&#xff0c;只需指定最基本的x和y轴对应数据即可进行图像的绘制&#xff0c;x和y轴数据分别为两个向量或者是只…

年薪40W测试工程师成长之路,你在哪个阶段?

对任何职业而言&#xff0c;薪资始终都会是众多追求的重要部分。前几年的软件测试行业还是一个风口&#xff0c;随着不断地转行人员以及毕业的大学生疯狂地涌入软件测试行业&#xff0c;目前软件测试行业“缺口”已经基本饱和。当然&#xff0c;我说的是最基础的功能测试的岗位…

WIN10無法再使用 IE 瀏覽器打开网页解决办法

修改 Registry&#xff08;只適用 Win10&#xff09; 微軟已於 2023 年 2 月 14 日永久停用 Internet Explorer&#xff0c;會透過 Edge 的更新讓使用者開啟 IE 時自動導向 Edge&#xff0c;其餘如工作列上的圖示&#xff0c;使用的方法則是透過「品質更新」的 B 更新來達成&am…

NoSQL与Redis五次作业回顾

文章目录1. 作业12. 作业23. 作业34. 作业45. 作业51. 作业1 要求&#xff1a; 在 VM 上安装 CentOS Linux 系统&#xff0c;并在 Linux 上通过 yum 安装 C编译器&#xff0c;对 Redis 进行编译安装。 观察 Redis 目录结构&#xff0c;使用 redis-server 启动服务器&#xff…

Android---Jetpack之DataBinding

DataBinding 的意义 让布局文件承担了部分原本属于页面的工作&#xff0c;使页面与布局耦合度进一步降低。 DataBinding 的应用 使用 dataBinding 需要在 gradle 里添加如下代码 dataBinding{enabled true} 应用实现 activity_main.xml <?xml version"1.0" e…

python argparse的使用,本文基本够用

一、前言 在学习深度学习会发现都比较爱用python这个argparse&#xff0c;虽然基本能理解&#xff0c;但没有仔细自己动手去写&#xff0c;因此这里写下来作为自己本人的学习笔记 argparse是python的一个命令行参数解析包&#xff0c;在代码需要频繁修改参数时&#xff0c;方便…

Shell编程之免交互

目录交互的概念与Linux中的运用Here Document 免交互tee命令重定向输出加标准输出支持变量替换多行注释Expect实例操作免交互预设值修改用户密码创建用户并设置密码实现 ssh 自动登录交互的概念与Linux中的运用 交互&#xff1a;当计算机播放某多媒体程序的时候&#xff0c;编…

步进频雷达的一维距离像matlab仿真

步进频雷达的一维距离像matlab仿真发射与回波信号模型距离高分辨原理仿真分析不进行步进频高分辨一维距离像进行步进频高分辨一维距离像代码发射与回波信号模型 步进频率信号发射得的是一串窄带的相参脉冲&#xff0c;每个脉冲的载频之间是均匀线性步进的&#xff0c;经过相参本…

Spring依赖注入详解

1.set注入 启动容器后看看到底能不能拿到teacherService的值。可以看到拿到了值。我们具体来分析怎么注入的 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean 发现pvs里面有一个我们自己set的值 直接进行属性赋值。 org.springf…

【创作赢红包】Python第3章 流程控制

这里写目录标题【本章导读】真值测试比较运算成员运算for循环while循环项目实训1项目实训2项目实训3项目实训4&#xff1a;项目实训5&#xff1a;项目实训6&#xff1a;项目实训7&#xff1a;项目实训8项目实训9&#xff1a;项目实训10:项目实训11&#xff1a;项目实训12&#…

【Redis7】Redis7 十大数据类型

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍Redis7 十大数据类型。 后续会继续分享Redis7和其他重要知识点总结&#xff0c;如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下吧】 上一篇文章&#xff1a;《【Redis7】Redis7概述、安装…

PCB生产工艺流程五:PCB生产工艺流程的第3步,钻孔的分类及目的

PCB生产工艺流程五&#xff1a;PCB生产工艺流程的第3步&#xff0c;钻孔的分类及目的 今天第五期的内容就是详细讲述PCB工艺流程第三步——钻孔&#xff0c;忘记第二步的小伙伴看这里 PCB工艺流程第2步&#xff0c;你了解多少&#xff1f; 钻孔的目的 在板面上钻出层与层之间…

【Java项目】SpringBoot实现一个请求同时上传多个文件和类并附上代码实例

文章目录前言文件多线程兼多文件上传RequestParam和RequestPart的区别前言 项目在做二手市场&#xff0c;然后商品的提交我们希望对商品的描述和商品的照片能一起传递到同一个接口来统一处理&#xff0c;而不是分发到两个接口中去处理&#xff0c;因为如果分到两个接口那么会特…

大语言模型带来的一些启发

仅代表个人看法&#xff0c;不喜勿喷。 The limits of my language means the limits of my world. (Ludwig Wittgenstein) 我的语言的极限意味着我的世界的极限。——维特根斯坦 大语言模型解决的不仅是处理文本相关问题&#xff0c;它带来的是人对世界的理解&#xff0c;或者…

华为网络篇 单臂路由-17

实验难度 2实验复杂度2目录 一、实验原理 二、实验拓扑 三、实验步骤 四、实验过程 总结 一、实验原理 单臂路由&#xff08;router-on-a-stick&#xff09;是指在路由器的一个接口上通过配置子接口&#xff08;或“逻辑接口”&#xff0c;并不存在真正物理接口&…