目录
概述
@EnableAsync 分析
ProxyAsyncConfiguration 分析
AsyncAnnotationBeanPostProcessor 分析
AsyncAnnotationAdvisor 分析
AnnotationAsyncExecutionInterceptor 分析
AsyncTaskExecutor 查找
Callable 任务封装
doSubmit 异步执行方法
总结
概述
基于 Spring Framework v5.2.6.RELEASE
Spring 终有一种非常简便的方法使 Bean 中的一个方法变成异步执行的方法,那就是在方法上标记 @Async 注解,想要开启这一特性,需要在一个配置类上标记 @EnableAsync 注解。
本文将通过源码分析 @EnableAsync 注解是如何开启这一特性的。
@EnableAsync 分析
@EnableAsync 注解的源码如下。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
Class annotation() default Annotation.class;
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
注解的每一个属性都指定了默认值,后续的分析也会基于默认的属性值进行分析。除此之外,注解上的 @Import 元注解引入了 AsyncConfigurationSelector 类。
从它的类关系中可以看出,AsyncConfigurationSelector 实现了 ImportSelector 接口,因此,当 Spring 扫描到配置类后,会执行它的 selectImports 方法,获取一个包含配置类名称的数组,用于加载对应的配置。
AsyncConfigurationSelector 虽然也包含了selectImports
方法,但是从参数类型中可以看出它不是接口中的selectImports
方法的实现方法,要找到接口中的实现方法,我们需要去 AsyncConfigurationSelector 的父类 AdviceModeImportSelector 中。
@Override
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (attributes == null) {
throw new IllegalArgumentException(String.format(
"@%s is not present on importing class '%s' as expected",
annType.getSimpleName(), importingClassMetadata.getClassName()));
}
AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
String[] imports = selectImports(adviceMode);
if (imports == null) {
throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
}
return imports;
}
这个方法中,主要是从 @EnableAsync 注解获取各项属性的值,然后使用adviceMode
属性,调用另一个selectImports
方法获取最终的结果。
此处被调用的selectImports
方法,就是 AsyncConfigurationSelector 中的 selectImports
方法。
@Override
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
在 @EnableAsync 注解中,mode的默认值是AdviceMode.PROXY
,因此,这里引入的配置类是 ProxyAsyncConfiguration。
接下来分析 ProxyAsyncConfiguration 类。
ProxyAsyncConfiguration 分析
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
bpp.configure(this.executor, this.exceptionHandler);
Class customAsyncAnnotation = this.enableAsync.getClass("annotation");
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.getNumber("order"));
return bpp;
}
}
在 ProxyAsyncConfiguration 中,只有一个 Bean 配置,类型是 AsyncAnnotationBeanPostProcessor,由此可以知道,@EnableAsync 所开启的功能,是通过 Bean 的后处理器来实现的。
上述的方法体中,通过构造方法创建了 AsyncAnnotationBeanPostProcessor 对象。
public AsyncAnnotationBeanPostProcessor() {
setBeforeExistingAdvisors(true);
}
构造方法中设置了一个属性值,这个属性是是beforeExistingAdvisors
,定义在父类 AbstractAdvisingBeanPostProcessor 中,这个属性的默认值是false,当它的值为true时,会将新的增强逻辑添加到增强逻辑列表的开头而不是最后。
也就是说,@EnableAsync 提供的异步执行特性,是基于 AOP 特性来实现的。
接着往下看,在创建了 AsyncAnnotationBeanPostProcessor 对象之后,为其配置了一些属性,有一些属性的值是从 @EnableAsync 属性值获取的,还有两个属性值需要留意,就是this.executor
和this.exceptionHandler
,这两个成员变量的值是从哪儿来的呢?
我们可以找到 ProxyAsyncConfiguration 的父类 AbstractAsyncConfiguration,其中有一个标记了 @Autowired 注解的方法。
// org.springframework.scheduling.annotation.AbstractAsyncConfiguration#setConfigurers
@Autowired(required = false)
void setConfigurers(Collection 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;
}
如果我们自己配置了线程池和异常处理器,则会在这里执行配置,这样,我们配置的线程池和异常处理器就会被添加到 AsyncAnnotationBeanPostProcessor 中。
接下来,我们再分析 AsyncAnnotationBeanPostProcessor 后处理器是如何工作的。
AsyncAnnotationBeanPostProcessor 分析
从它的类继承关系中可以看出,它是一个基于 AOP 特性来为 Bean 中的方法提供异步执行功能的 Bean 后处理器。
AsyncAnnotationBeanPostProcessor 同时实现了 BeanFactoryAware 接口,在它的setBeanFactory
方法中,完成了 Advisor 的创建。
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
if (this.asyncAnnotationType != null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
advisor.setBeanFactory(beanFactory);
this.advisor = advisor;
}
这里创建的 Advisor 类型是 AsyncAnnotationAdvisor,创建完之后,它被复制给了advisor
成员变量,这个成员变量定义在 AsyncAnnotationBeanPostProcessor 的父类 AbstractBeanFactoryAwareAdvisingPostProcessor 中。
这个advisor
成员变量就是处理增强逻辑的对象。
AsyncAnnotationAdvisor 分析
关于 Spring 是如何在后处理器中为 Bean 创建代理对象以及如何向代理对象中加入增强逻辑的,我之前的文章有很详细的分析,可以阅读之前关于 AOP 原理的分析文章来了解。下面我们直接分析 AsyncAnnotationAdvisor,它是完成方法异步执行的核心。
一个 Advisor 通常有两个非常重要的部分,一个是 Pointcut,用于匹配需要增强的方法,另一个是 Advice 也就是具体的增强逻辑。对于 AsyncAnnotationAdvisor 来说,这两个部分都是在它的构造方法中构建的。
public AsyncAnnotationAdvisor(
@Nullable Supplier executor, @Nullable Supplier exceptionHandler) {
Set> asyncAnnotationTypes = new LinkedHashSet<>(2);
asyncAnnotationTypes.add(Async.class);
try {
asyncAnnotationTypes.add((Class)
ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
// If EJB 3.1 API not present, simply ignore.
}
this.advice = buildAdvice(executor, exceptionHandler);
this.pointcut = buildPointcut(asyncAnnotationTypes);
}
其中可以看到两行关键的代码,他们分别完成了advice
和pointcut
成员变量的构建。
this.advice = buildAdvice(executor, exceptionHandler);
this.pointcut = buildPointcut(asyncAnnotationTypes);
下面分别来看这两部分。
Advice 构建
先看buildAdvice
方法。
// org.springframework.scheduling.annotation.AsyncAnnotationAdvisor#buildAdvice
protected Advice buildAdvice(
@Nullable Supplier executor, @Nullable Supplier exceptionHandler) {
AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
interceptor.configure(executor, exceptionHandler);
return interceptor;
}
Advice 的构建比较简单,这里可以看到,最终构建的 Advice 是一个 AnnotationAsyncExecutionInterceptor 类型的拦截器,除了调用构造方法创建之外,还配置了executor
和exceptionHandler
,这个拦截器应该就是完成 AOP 增强逻辑的拦截器,我们放到后文中分析。
Pointcut 构建
下面再看buildPointcut
方法。
// org.springframework.scheduling.annotation.AsyncAnnotationAdvisor#buildPointcut
protected Pointcut buildPointcut(Set> asyncAnnotationTypes) {
ComposablePointcut result = null;
for (Class asyncAnnotationType : asyncAnnotationTypes) {
Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
Pointcut mpc = new AnnotationMatchingPointcut(null, asyncAnnotationType, true);
if (result == null) {
result = new ComposablePointcut(cpc);
}
else {
result.union(cpc);
}
result = result.union(mpc);
}
return (result != null ? result : Pointcut.TRUE);
}
这个方法的逻辑比较简单,首先创建了两个 Pointcut 对象,cpc
用于匹配类型,mpc
用于匹配方法,他们的逻辑都很简单,就是看类或者方法的定义是否包含 @Async 注解。
最后再将两者合并为一个 ComposablePointcut 对象返回,ComposablePointcut 的作用就是将多个 Pointcut 对象合并成一个。
AnnotationAsyncExecutionInterceptor 分析
了解完上面的内容,接下来就开始分析 AnnotationAsyncExecutionInterceptor 拦截器。它是一个包含 AOP 增强逻辑的拦截器,也是完成方法异步调用的核心逻辑。
AnnotationAsyncExecutionInterceptor 要完成它的任务,有两个比较核心的功能,一个是目标方法的匹配,另一个就是拦截器的逻辑。目标方法的匹配逻辑,我们在上文中已经介绍过了,以下主要分析其拦截器逻辑,也就是它的invoke
方法。
以上是 AnnotationAsyncExecutionInterceptor 的类关系图,它实现了 MethodInterceptor 接口,invoke
方法的实现在父类 AsyncExecutionInterceptor 中。
// 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);
final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
if (executor == null) {
throw new IllegalStateException(
"No executor specified and no default executor set on AsyncExecutionInterceptor either");
}
Callable 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;
};
return doSubmit(task, executor, invocation.getMethod().getReturnType());
}
从上面的源码中可以看到三个关键的步骤:
找到目标方法,并根据目标方法获取到执行它的 AsyncTaskExecutor。
将目标方法的调用,封装到一个 Callable 异步任务task
当中。
通过doSubmit方法来异步调用上一步封装的task
。
下面我们详细分析这三个步骤。
AsyncTaskExecutor 查找
AsyncTaskExecutor 在determineAsyncExecutor
方法中完成。
@Nullable
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
AsyncTaskExecutor executor = this.executors.get(method);
if (executor == null) {
Executor targetExecutor;
String qualifier = getExecutorQualifier(method);
if (StringUtils.hasLength(qualifier)) {
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
}
else {
targetExecutor = this.defaultExecutor.get();
}
if (targetExecutor == null) {
return null;
}
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
this.executors.put(method, executor);
}
return executor;
}
首先会从executors
中根据方法获取对应的 AsyncTaskExecutor,executors
是一个用来缓存 Executor 的成员变量。
private final Map executors = new ConcurrentHashMap<>(16);
当第一次进入这个方法的时候,executors
肯定是空的,因此会进入if
语句的逻辑获取 Executor 然后再将其添加到executors
中。在if语句中,首先会通过getExecutorQualifier
方法获取一个qualifier
,我们进入方法查看获取的过程。
// org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor#getExecutorQualifier
@Override
@Nullable
protected String getExecutorQualifier(Method method) {
// Maintainer's note: changes made here should also be made in
// AnnotationAsyncExecutionAspect#getExecutorQualifier
Async async = AnnotatedElementUtils.findMergedAnnotation(method, Async.class);
if (async == null) {
async = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Async.class);
}
return (async != null ? async.value() : null);
}
这个方法会从目标方法或者其所在的类型上的 @Async 注解的value
属性,作为方法的返回值复制给qualifier
。这个qualifier
的值是一个 Executor 的 Bean 名称,也就是说,我们可以通过 @Async 的value
属性指定执行异步任务的 Executor 的 Bean 名称。
如果qualifier
不是空的,那么,就会通过findQualifiedExecutor方法从 Spring 容器中获取对应的 Executor 实例。
// org.springframework.aop.interceptor.AsyncExecutionAspectSupport#findQualifiedExecutor
@Nullable
protected Executor findQualifiedExecutor(@Nullable BeanFactory beanFactory, String qualifier) {
if (beanFactory == null) {
throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() +
" to access qualified executor '" + qualifier + "'");
}
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, Executor.class, qualifier);
}
如果qualifier
是空的,那么就会通过this.defaultExecutor.get()
获取默认的 Executor,那么,默认的 Executor 是什么呢?我们需要在去 AsyncAnnotationAdvisor 的buildAdvice
方法中,回顾一下 AnnotationAsyncExecutionInterceptor 创建的过程。
AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
以上是 AnnotationAsyncExecutionInterceptor 创建的语句,从这里找到对应的构造方法。
public AnnotationAsyncExecutionInterceptor(@Nullable Executor defaultExecutor) {
super(defaultExecutor);
}
构造方法需要提供一个默认的 Executor,也就是defaultExecutor
参数,这里提供了null
,不过我们可以继续查看父类的构造方法。
public AsyncExecutionAspectSupport(@Nullable Executor defaultExecutor) {
this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory));
this.exceptionHandler = SingletonSupplier.of(SimpleAsyncUncaughtExceptionHandler::new);
}
在被调用的 AsyncExecutionAspectSupport 的构造方法中,通过getDefaultExecutor
方法,提供了默认的 Executor。
// org.springframework.aop.interceptor.AsyncExecutionInterceptor#getDefaultExecutor
@Override
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
}
这里看到,默认的 Executor 是一个 SimpleAsyncTaskExecutor,也就是说,如果我们没有在项目中配置线程池,则默认使用 SimpleAsyncTaskExecutor 来执行异步任务。
Callable 任务封装
得到 Executor 之后,就是任务的封装,这一步很简单,就是将目标方法的调用放到一个 Callable 类型的任务的call
方法中。
doSubmit 异步执行方法
最后一步就是任务的提交,通过doSubmit
方法完成。
// org.springframework.aop.interceptor.AsyncExecutionAspectSupport#doSubmit
@Nullable
protected Object doSubmit(Callable task, AsyncTaskExecutor executor, Class returnType) {
if (CompletableFuture.class.isAssignableFrom(returnType)) {
return CompletableFuture.supplyAsync(() -> {
try {
return task.call();
}
catch (Throwable ex) {
throw new CompletionException(ex);
}
}, executor);
}
else if (ListenableFuture.class.isAssignableFrom(returnType)) {
return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
}
else if (Future.class.isAssignableFrom(returnType)) {
return executor.submit(task);
}
else {
executor.submit(task);
return null;
}
}
其实就是调用了 Executor 的submit
异步执行了任务。
不过这里有一点要说明,虽然在我们没有配置 Excutor 的情况下 ,Spring 会使用默认的 SimpleAsyncTaskExecutor 来执行异步任务,但是 SimpleAsyncTaskExecutor 会为每一个任务创建一个新的线程,而不是使用线程池来完成,很容易导致内存溢出,因此,在实践中最好为异步任务配置合适的线程池。
总结
本文以 @EnableAsync 作为切入点,分析了 Spring 开启基于注解的异步任务特性的原理,更多关于Spring EnableAsync注解的资料请关注脚本之家其它相关文章!
你可能感兴趣的:(Spring EnableAsync注解异步执行源码解析)
HarmonyNext实战:基于ArkTS的分布式实时消息队列系统开发
harmonyos-next
HarmonyNext实战:基于ArkTS的分布式实时消息队列系统开发引言在HarmonyNext生态系统中,消息队列是实现分布式系统通信和异步处理的核心组件。本文将深入探讨如何使用ArkTS语言开发一个分布式实时消息队列系统,重点介绍消息的生产、消费、路由以及负载均衡等核心功能的实现。我们将通过一个完整的实战案例,展示如何利用HarmonyNext的分布式能力和ArkTS的高效性能,构建一个高效
Linux报 “permission denied” 异常的原因以及解决办法
为了前进而后退,为了走直路而走弯路
记录一些踩过的坑 linux 服务器 运维
Linux报“permissiondenied”异常的原因以及解决办法Linux报permissiondenied(权限被拒绝)错误通常是由于当前用户缺少执行该文件或文件夹的权限而导致的。这是Linux系统的安全机制之一,可以保证系统的安全性和稳定性。下面来详细介绍一下Linux报permissiondenied的原因和解决办法。原因文件或文件夹的权限不够:Linux系统中,每个文件都有不同的权限
解释型语言和编译型语言
難釋懷
开发语言
在计算机编程的世界里,根据执行方式的不同,编程语言大致可以分为两大类:解释型语言和编译型语言。了解这两种语言类型的特点及其差异对于选择合适的编程语言来完成特定任务至关重要。本文将深入探讨解释型语言和编译型语言的概念、特点及应用场景。一、解释型语言定义解释型语言是指程序在运行时由解释器逐行读取、翻译并执行的语言。这种语言不需要事先进行完整的编译过程,而是边解释边执行,因此具有较高的灵活性。特点即时性
达梦数据库备份
huazhixuthink
数据库 oracle sql
达梦数据库联机在线备份操作指南一、基础条件与准备开启归档模式:联机备份必须处于归档模式下,否则无法执行。需通过disql工具执行以下操作:alterdatabasemount;alterdatabaseARCHIVELOG;例子:[dmdba@server~]$cd/opt/dmdbms/bin[dmdba@serverbin]$./disqlSYSDBA/'"Dameng@123"':5236
Spring Boot 异步编程
珠峰日记
spring boot java 后端
文章目录一、异步方法的使用1.开启异步支持2.定义异步方法3.调用异步方法踩坑记录心得体会二、线程池配置1.自定义线程池2.使用自定义线程池踩坑记录心得体会三、异步任务的监控与管理1.日志记录2.异常处理3.线程池监控踩坑记录心得体会在现代应用程序开发中,异步编程是提升系统性能和响应能力的重要手段。SpringBoot提供了便捷的方式来实现异步编程,下面将详细介绍异步方法的使用、线程池配置以及异步
SpringBoot缓存实践
珠峰日记
spring boot 缓存 后端
文章目录一、引言二、SpringCache抽象(一)核心概念与原理(二)优势与局限性三、集成常用缓存(一)集成Redis缓存1.集成步骤2.踩坑记录与心得体会(二)集成Ehcache缓存1.集成步骤2.踩坑记录与心得体会四、缓存注解的使用(一)`@Cacheable`(二)`@CachePut`(三)`@CacheEvict`(四)`@Caching`(五)踩坑记录与心得体会五、总结一、引言在当今
Springboot应用json数据传给前端首字母大写变小写的问题
珠峰日记
css3 css spring jquery html
对接.net数据,属性首字母大写传给前端后自动转成了小写。解决思路网上有三种:1)如果没有使用fastjson(阿里巴巴出品),使用@JsonProperty("XXXX")的注解方式可以解决问题,XXXX就是转换成Json的属性名,但注意,只有放在getter上才是有效的,直接在属性上加没效果.(这说明spring默认的jackson类库处理是基于访问器。2)如果使用了fastjson,就用@J
Spring测试框架中的@ContextConfiguration继承机制
t0_54program
spring java 后端 个人开发
在Spring框架中,@ContextConfiguration注解是一个非常重要的工具,它用于指定测试类的上下文配置来源。然而,很多人可能并不清楚,这个注解还提供了inheritLocations和inheritInitializers属性,用于控制是否继承父类的配置位置和上下文初始化器。本文将通过具体的代码示例,深入探讨这两个属性的作用。默认行为:继承配置位置和初始化器在默认情况下,inher
Spring Boot——消息队列集成RabbitMQ详细步骤大全
程序员阿皓
SpringBoot java-rabbitmq spring boot rabbitmq
SpringBoot提供了与多种消息队列系统集成的支持,其中最常见的是集成RabbitMQ或Kafka。以RabbitMQ为例集成的详细步骤如下:1.添加RabbitMQ的依赖首先,在SpringBoot项目的pom.xml文件中添加RabbitMQ的依赖:org.springframework.bootspring-boot-starter-amqp2.配置RabbitMQ的连接信息在appli
03.05 随手记
喜刷刷1999
javascript
1.什么是事件流?页面触发一个事件时,会按照一定的顺序来响应事件,事件的响应过程为事件流2.什么是作用域?规定变量和函数的可使用范围称作作用域每个函数都有一个作用域链,查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作作用域链。3.什么是执行栈,什么是执行上下文?执行上下文分为:全局执行上下文创建一个全局的window对象,并规定this指向window,执行js的时候
windows 上删除 node_modules
默默@85
使用工具 visual studio code windows
在Windows11上,你可以通过命令行来删除node_modules文件夹并清除npm缓存。以下是具体步骤:删除node_modules打开命令提示符(CommandPrompt)或终端(PowerShell)。导航到项目目录。你可以使用cd命令。例如:cdpath\to\your\project执行以下命令来删除node_modules文件夹:rd/s/qnode_modules或者如果你使用
PHP 的运行模式到底是干什么的?使用场景是什么?底层原理是什么?
快点好好学习吧
PHP php android 开发语言
1.PHP的运行模式到底是什么?1.1简单解释PHP的运行模式是指PHP脚本在服务器上执行的方式。不同的运行模式适用于不同的场景,决定了PHP如何与Web服务器交互。1.2常见的PHP运行模式CGI模式:PHP作为独立的CGI程序运行,每次请求都会启动一个新的进程。FastCGI模式:使用FastCGI协议,PHP-FPM(FastCGIProcessManager)管理多个PHP进程,提高性能。
SpringBoot(2,arm架构和x86架构区别
m0_64205716
程序员 面试 java 后端
//gradle自身会用到的相关设置buildscript{//仓库repositories{//本地mavenLocal()//中央仓库mavenCentral()//grandle插件maven{url‘https://plugins.gradle.org/m2/’}}//子模块会用到的变量ext{springBootVersion=‘2.4.4’}}//插件plugins{id‘java’i
CSnakes vs Python.NET:跨语言集成的巅峰对决与架构解密
领码科技
AI应用 技能篇 实战篇 跨语言集成 .NET Python 性能优化 双向互操作
摘要本文深入剖析了.NET生态中两大Python集成方案——CSnakes与Python.NET的技术差异。从底层架构、类型系统、执行性能到应用场景,我们进行了全方位对比。CSnakes凭借源生成器与PythonC-API直连,实现了微秒级调用延迟;而Python.NET则通过动态类型系统构建了双向互操作桥梁。通过实测数据与典型场景推演,本文为开发者提供了精准的技术选型指南。关键词:跨语言集成、.
LogBack 动态修改日志级别
敏君宝爸
日志 log spring boot java
由于线上运行的项目,出现问题想看下sql的参数,或者一些框架的debug日志。需要把日志界别从info-->debug。需要动态实时的修改日志界别。只要类:LoggersEndpointmaven依赖:org.springframework.bootspring-boot-starter-actuatorspringboot1.x===================yml配置==========
RHCE---第二部分
Chen_l916
linux 服务器 运维
第二部分shell脚本编程一、shell基本知识shell程序的特点:1、简单易学2、解释性语言,不需要编译即可执行Shell又称命令解释器,它能识别用户输入的各种命令,并传递给操作系统。它的作用类似于Windows操作系统中的命令行,但是,Shell的功能远比命令行强大的多。在UNIX或者localhost中,Shell既是用户交互的界面,也是控制系统的脚本语言。shell的分类·BourneS
springboot 适配ARM 架构
敏君宝爸
spring boot 架构 java
下载对应的mavenhttps://hub.docker.com/_/maven/tags?page=&page_size=&ordering=&name=3.5.3-alpinedockerpullmaven:3.5.3-alpine@sha256:4c4e266aacf8ea6976b52df8467134b9f628cfed347c2f6aaf9e6aff832f7c452、下载对应的jdk
MySQL中SQL语句的执行过程详解
DevDiary
数据库 mysql sql
1.客户端连接和请求客户端连接在MySQL中,客户端连接和请求过程是执行SQL语句的第一步。该步骤主要涉及客户端如何连接到MySQL服务器,以及如何维护和管理客户端与服务器之间的会话。客户端连接:连接器(Connector):连接器是MySQL中负责处理客户端连接请求的组件。它处理TCP/IP协议的连接,管理用户的认证和授权。连接过程:建立连接:客户端通过网络(通常是TCP/IP)向MySQL服务
一些高阶SQL的技巧
迷路的小犀牛
sql
高阶SQL技巧在日常的SQL操作中,很多简单的查询我们都可以通过基础的SQL语法来完成。然而,在面对复杂数据集、性能优化或者高效数据提取时,高阶SQL技巧就显得尤为重要。本文将介绍一些高阶的SQL技巧,帮助你提升SQL查询的效率和可读性。1.使用窗口函数(WindowFunctions)窗口函数是SQL中非常强大的功能,它允许我们在查询结果集的每一行上执行计算,同时不需要将数据分组。常用的窗口函数
linux执行ll报异常Segmentation fault
迷路的小犀牛
linux 运维 服务器 centos java
linux执行ll报异常Segmentationfault在Linux系统中,当你执行ll命令时出现Segmentationfault异常,表示程序访问了它不允许访问的内存区域,这通常是由于程序的错误、内存损坏或者系统环境问题引起的。ll命令通常是ls-l的别名,它用于列出目录内容并显示详细信息。出现Segmentationfault错误可能的原因有很多,下面是一些常见的排查方法和解决方案:1.检
sql执行超时怎么办
Msura
sql 数据库 mysql 服务器 运维
如果SQL查询执行超时,有几种常见的解决方案:优化SQL查询语句。这可能包括使用索引来加速查询,删除无用的列或表,或者使用更高效的查询方法。增加服务器资源。如果服务器的CPU、内存或存储不足,可能会导致查询执行缓慢。在这种情况下,可以尝试增加服务器的资源,以提升查询的执行速度。调整超时时间。如果SQL查询执行超时,可以尝试增加执行超时时间的限制,以让查询有更多的时间完成。分析慢查询日志。如果您使用
mysql语句设置超时_mysql语句执行超时设置
钢的琴琴琴琴
mysql语句设置超时
对于Saas系统来说,设置语句执行超时时间是必要的,原因有二:(1)避免一些有性能问题的语句长时间执行占用大量资源,影响其他用户的使用;(2)避免请求都被中断了服务端还在长时间的执行SQL语句,无谓的消耗资源;有两种方式来设置执行超时,任选一种:1.客户端代码中设置例如:使用mysql的.net驱动MysqlCommand.CommandTimeout=xxx(秒)驱动的实现原理:(1)超时时间到
Gin框架深度解剖:中间件的实现原理
魔法小匠
Go语言深度探索与实战 golang Gin gin框架解析 gin框架原理 gin框架源码 中间件实现原理
引言Gin是一个用Go语言编写的高性能Web框架,以其简洁的API和卓越的性能著称。在Gin中,中间件(Middleware)是一个非常重要的概念,它允许我们在请求处理的前后执行一些通用的逻辑,比如日志记录、身份验证、错误处理等。本文将深入探讨Gin框架中中间件的实现原理,帮助读者更好地理解和使用Gin框架。什么是中间件?中间件是一种在HTTP请求到达目标处理函数之前或之后执行的函数。它可以用来处
Spring Boot整合Resilience4j教程
嘵奇
提升自己 spring boot java
精心整理了最新的面试资料和简历模板,有需要的可以自行获取点击前往百度网盘获取点击前往夸克网盘获取以下是将SpringBoot与Resilience4j整合的详细教程,包含基础配置和核心功能示例:SpringBoot整合Resilience4j教程Resilience4j提供容错机制(断路器、重试、限流等),帮助构建弹性微服务。一、环境准备创建项目使用SpringInitializr生成项目,选择:
基于SpringBoot的电影售票系统
Computer程序设计
Java计算机程序设计 spring boot 后端 java vue.js spring java-ee tomcat
引言 在当今信息化高速发展的时代,互联网已经成为人们日常生活不可或缺的一部分。电影作为一种重要的文化娱乐形式,其售票方式也逐渐从传统的线下购票转向线上购票。基于SpringBoot和Vue的电影售票系统,正是为了迎合这一趋势而设计的。该系统不仅为用户提供了便捷、高效的购票渠道,还为影院管理者提供了强大的后台管理功能。通过该系统,用户可以轻松浏览影片信息、选择座位、在线支付,完成购票全过程;而影院
Azkaban其一,介绍、体系架构和安装
出发行进
# Azkaban Azkaban linux
目录一、简介二、Azkaban的体系结构三、Azkaban的安装步骤1、上传,解压2、生成mysql的元数据3、配置web-server4、配置exec-server5、修改所有的.sh的执行权限一、简介遇到了什么问题才会使用Azkaban?比如:想启动hadoop集群先启动zk集群,再启动hdfs,再启动yarn,再启动日志系统工作过程中总会遇到多个脚本执行的时候有顺序。任务可以有一个编排的工具
实时数据推送:Spring Boot 中两种 SSE 实战方案
微特尔普拉斯
java SpringBoot html5 前端 服务器 spring http 交互
在Web开发中,实时数据交互变得越来越普遍。无论是股票价格的波动、比赛比分的更新,还是聊天消息的传递,都需要服务器能够及时地将数据推送给客户端。传统的HTTP请求-响应模式在处理这类需求时显得力不从心,而服务器推送事件(Server-SentEvents,SSE)为我们提供了一种轻量级且高效的解决方案。本文将介绍两种基于SpringBoot实现SSE的方案,并结合代码示例,帮助你快速掌握实时数据推
Java阻塞队列深度解析:高并发场景下的安全卫士
没什么技术
java 阻塞队列
一、阻塞队列的核心价值在电商秒杀系统中,瞬时涌入的10万请求如果直接冲击数据库,必然导致系统崩溃。阻塞队列如同一个智能缓冲带,通过流量削峰和异步解耦两大核心能力,成为高并发系统的核心组件。二、Java阻塞队列实现类对比队列实现类数据结构锁机制适用场景吞吐量ArrayBlockingQueue数组单锁ReentrantLock固定容量场景中LinkedBlockingQueue链表双锁分离高吞吐量生
深入源码分析spring AOP
萌新coder
Java基础知识 spring java 后端
深入源码分析springAOP一、SpringAOP核心概念AOP(面向切面编程)是Spring框架的核心功能之一,它通过动态代理技术,在不修改源代码的情况下,为业务逻辑横向添加通用功能(如日志、事务、权限等)。其核心思想是将业务代码与非业务代码解耦,例如://业务代码publicvoidtransferMoney(){//转账逻辑...}//非业务代码(日志记录)publicvoidlog(){
Spring Boot 实战:生成条形码的高效方案
墨夶
Java学习资料1 spring boot java 后端
嘿,小伙伴们!今天我们要来动手实践一个非常实用的功能——使用SpringBoot生成条形码。如果你是一名对后端开发感兴趣的开发者,并且希望在项目中集成条形码生成功能,那么这篇文章绝对不容错过!条形码广泛应用于物流、零售、库存管理等多个领域,能够极大地提高数据处理效率和准确性。通过本文,我们将从零开始创建一个简单的SpringBoot应用程序,涵盖以下内容:项目初始化引入依赖生成条形码提供RESTA
JVM StackMapTable 属性的作用及理解
lijingyao8206
jvm 字节码 Class文件 StackMapTable
在Java 6版本之后JVM引入了栈图(Stack Map Table)概念。为了提高验证过程的效率,在字节码规范中添加了Stack Map Table属性,以下简称栈图,其方法的code属性中存储了局部变量和操作数的类型验证以及字节码的偏移量。也就是一个method需要且仅对应一个Stack Map Table。在Java 7版
回调函数调用方法
百合不是茶
java
最近在看大神写的代码时,.发现其中使用了很多的回调 ,以前只是在学习的时候经常用到 ,现在写个笔记 记录一下
代码很简单:
MainDemo :调用方法 得到方法的返回结果
[时间机器]制造时间机器需要一些材料
comsci
制造
根据我的计算和推测,要完全实现制造一台时间机器,需要某些我们这个世界不存在的物质
和材料...
甚至可以这样说,这种材料和物质,我们在反应堆中也无法获得......
开口埋怨不如闭口做事
邓集海
邓集海 做人 做事 工作
“开口埋怨,不如闭口做事。”不是名人名言,而是一个普通父亲对儿子的训导。但是,因为这句训导,这位普通父亲却造就了一个名人儿子。这位普通父亲造就的名人儿子,叫张明正。 张明正出身贫寒,读书时成绩差,常挨老师批评。高中毕业,张明正连普通大学的分数线都没上。高考成绩出来后,平时开口怨这怨那的张明正,不从自身找原因,而是不停地埋怨自己家庭条件不好、埋怨父母没有给他创造良好的学习环境。
jQuery插件开发全解析,类级别与对象级别开发
IT独行者
jquery 开发 插件 函数
jQuery插件的开发包括两种: 一种是类级别的插件开发,即给
jQuery添加新的全局函数,相当于给
jQuery类本身添加方法。
jQuery的全局函数就是属于
jQuery命名空间的函数,另一种是对象级别的插件开发,即给
jQuery对象添加方法。下面就两种函数的开发做详细的说明。
1
、类级别的插件开发 类级别的插件开发最直接的理解就是给jQuer
Rome解析Rss
413277409
Rome解析Rss
import java.net.URL;
import java.util.List;
import org.junit.Test;
import com.sun.syndication.feed.synd.SyndCategory;
import com.sun.syndication.feed.synd.S
RSA加密解密
无量
加密 解密 rsa
RSA加密解密代码
代码有待整理
package com.tongbanjie.commons.util;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerat
linux 软件安装遇到的问题
aichenglong
linux 遇到的问题 ftp
1 ftp配置中遇到的问题
500 OOPS: cannot change directory
出现该问题的原因:是SELinux安装机制的问题.只要disable SELinux就可以了
修改方法:1 修改/etc/selinux/config 中SELINUX=disabled
2 source /etc
面试心得
alafqq
面试
最近面试了好几家公司。记录下;
支付宝,面试我的人胖胖的,看着人挺好的;博彦外包的职位,面试失败;
阿里金融,面试官人也挺和善,只不过我让他吐血了。。。
由于印象比较深,记录下;
1,自我介绍
2,说下八种基本类型;(算上string。楼主才答了3种,哈哈,string其实不是基本类型,是引用类型)
3,什么是包装类,包装类的优点;
4,平时看过什么书?NND,什么书都没看过。。照样
java的多态性探讨
百合不是茶
java
java的多态性是指main方法在调用属性的时候类可以对这一属性做出反应的情况
//package 1;
class A{
public void test(){
System.out.println("A");
}
}
class D extends A{
public void test(){
S
网络编程基础篇之JavaScript-学习笔记
bijian1013
JavaScript
1.documentWrite
<html>
<head>
<script language="JavaScript">
document.write("这是电脑网络学校");
document.close();
</script>
</h
探索JUnit4扩展:深入Rule
bijian1013
JUnit Rule 单元测试
本文将进一步探究Rule的应用,展示如何使用Rule来替代@BeforeClass,@AfterClass,@Before和@After的功能。
在上一篇中提到,可以使用Rule替代现有的大部分Runner扩展,而且也不提倡对Runner中的withBefores(),withAfte
[CSS]CSS浮动十五条规则
bit1129
css
这些浮动规则,主要是参考CSS权威指南关于浮动规则的总结,然后添加一些简单的例子以验证和理解这些规则。
1. 所有的页面元素都可以浮动 2. 一个元素浮动后,会成为块级元素,比如<span>,a, strong等都会变成块级元素 3.一个元素左浮动,会向最近的块级父元素的左上角移动,直到浮动元素的左外边界碰到块级父元素的左内边界;如果这个块级父元素已经有浮动元素停靠了
【Kafka六】Kafka Producer和Consumer多Broker、多Partition场景
bit1129
partition
0.Kafka服务器配置
3个broker
1个topic,6个partition,副本因子是2
2个consumer,每个consumer三个线程并发读取
1. Producer
package kafka.examples.multibrokers.producers;
import java.util.Properties;
import java.util.
zabbix_agentd.conf配置文件详解
ronin47
zabbix 配置文件
Aliaskey的别名,例如 Alias=ttlsa.userid:vfs.file.regexp[/etc/passwd,^ttlsa:.:([0-9]+),,,,\1], 或者ttlsa的用户ID。你可以使用key:vfs.file.regexp[/etc/passwd,^ttlsa:.: ([0-9]+),,,,\1],也可以使用ttlsa.userid。备注: 别名不能重复,但是可以有多个
java--19.用矩阵求Fibonacci数列的第N项
bylijinnan
fibonacci
参考了网上的思路,写了个Java版的:
public class Fibonacci {
final static int[] A={1,1,1,0};
public static void main(String[] args) {
int n=7;
for(int i=0;i<=n;i++){
int f=fibonac
Netty源码学习-LengthFieldBasedFrameDecoder
bylijinnan
java netty
先看看LengthFieldBasedFrameDecoder的官方API
http://docs.jboss.org/netty/3.1/api/org/jboss/netty/handler/codec/frame/LengthFieldBasedFrameDecoder.html
API举例说明了LengthFieldBasedFrameDecoder的解析机制,如下:
实
AES加密解密
chicony
加密解密
AES加解密算法,使用Base64做转码以及辅助加密:
package com.wintv.common;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decod
文件编码格式转换
ctrain
编码格式
package com.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
mysql 在linux客户端插入数据中文乱码
daizj
mysql 中文乱码
1、查看系统客户端,数据库,连接层的编码
查看方法: http://daizj.iteye.com/blog/2174993
进入mysql,通过如下命令查看数据库编码方式: mysql> show variables like 'character_set_%'; +--------------------------+------
好代码是廉价的代码
dcj3sjt126com
程序员 读书
长久以来我一直主张:好代码是廉价的代码。
当我跟做开发的同事说出这话时,他们的第一反应是一种惊愕,然后是将近一个星期的嘲笑,把它当作一个笑话来讲。 当他们走近看我的表情、知道我是认真的时,才收敛一点。
当最初的惊愕消退后,他们会用一些这样的话来反驳: “好代码不廉价,好代码是采用经过数十年计算机科学研究和积累得出的最佳实践设计模式和方法论建立起来的精心制作的程序代码。”
我只
Android网络请求库——android-async-http
dcj3sjt126com
android
在iOS开发中有大名鼎鼎的ASIHttpRequest库,用来处理网络请求操作,今天要介绍的是一个在Android上同样强大的网络请求库android-async-http,目前非常火的应用Instagram和Pinterest的Android版就是用的这个网络请求库。这个网络请求库是基于Apache HttpClient库之上的一个异步网络请求处理库,网络处理均基于Android的非UI线程,通
ORACLE 复习笔记之SQL语句的优化
eksliang
SQL优化 Oracle sql语句优化 SQL语句的优化
转载请出自出处:http://eksliang.iteye.com/blog/2097999
SQL语句的优化总结如下
sql语句的优化可以按照如下六个步骤进行:
合理使用索引
避免或者简化排序
消除对大表的扫描
避免复杂的通配符匹配
调整子查询的性能
EXISTS和IN运算符
下面我就按照上面这六个步骤分别进行总结:
浅析:Android 嵌套滑动机制(NestedScrolling)
gg163
android 移动开发 滑动机制 嵌套
谷歌在发布安卓 Lollipop版本之后,为了更好的用户体验,Google为Android的滑动机制提供了NestedScrolling特性
NestedScrolling的特性可以体现在哪里呢?<!--[if !supportLineBreakNewLine]--><!--[endif]-->
比如你使用了Toolbar,下面一个ScrollView,向上滚
使用hovertree菜单作为后台导航
hvt
JavaScript jquery .net hovertree asp.net
hovertree是一个jquery菜单插件,官方网址:http://keleyi.com/jq/hovertree/ ,可以登录该网址体验效果。
0.1.3版本:http://keleyi.com/jq/hovertree/demo/demo.0.1.3.htm
hovertree插件包含文件:
http://keleyi.com/jq/hovertree/css
SVG 教程 (二)矩形
天梯梦
svg
SVG <rect> SVG Shapes
SVG有一些预定义的形状元素,可被开发者使用和操作:
矩形 <rect>
圆形 <circle>
椭圆 <ellipse>
线 <line>
折线 <polyline>
多边形 <polygon>
路径 <path>
一个简单的队列
luyulong
java 数据结构 队列
public class MyQueue {
private long[] arr;
private int front;
private int end;
// 有效数据的大小
private int elements;
public MyQueue() {
arr = new long[10];
elements = 0;
front
基础数据结构和算法九:Binary Search Tree
sunwinner
Algorithm
A binary search tree (BST) is a binary tree where each node has a Comparable key (and an associated value) and satisfies the restriction that the key in any node is larger than the keys in all
项目出现的一些问题和体会
Steven-Walker
DAO Web servlet
第一篇博客不知道要写点什么,就先来点近阶段的感悟吧。
这几天学了servlet和数据库等知识,就参照老方的视频写了一个简单的增删改查的,完成了最简单的一些功能,使用了三层架构。
dao层完成的是对数据库具体的功能实现,service层调用了dao层的实现方法,具体对servlet提供支持。
&
高手问答:Java老A带你全面提升Java单兵作战能力!
ITeye管理员
java
本期特邀《Java特种兵》作者:谢宇,CSDN论坛ID: xieyuooo 针对JAVA问题给予大家解答,欢迎网友积极提问,与专家一起讨论!
作者简介:
淘宝网资深Java工程师,CSDN超人气博主,人称“胖哥”。
CSDN博客地址:
http://blog.csdn.net/xieyuooo
作者在进入大学前是一个不折不扣的计算机白痴,曾经被人笑话过不懂鼠标是什么,