SpringBoot:異步任務基礎與源碼剖析

?????????官網文檔:How To Do @Async in Spring | Baeldung。

@Async注解

Spring框架基于@Async注解提供了對異步執行流程的支持。

最簡單的例子是:使用@Async注解修飾一個方法,那么這個方法將在一個單獨的線程中被執行,即:從同步執行流程轉換為異步執行流程。?

此外,Spring框架中,事件Event也是支持異步處理操作的。

?@EnableAsync注解|核心接口

通過在配置類上添加@EnableAsync注解,可以為Spring應用程序啟用異步執行流程的支持

@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }

? ? ? ? 該注解提供了一些可配置屬性,

?annotation:默認情況下,@EnableAsync會告訴Spring程序去探查所有被@Async注解修飾的、以及@javax.ejb.Asynchronous.注解;

mode:指定異步流程生效的方式:JDK Proxy還是AspectJ;

proxtTargetClass:只有在mode為AdviceMode.PROXY(JDK動態代理)時才會生效,用于指定要使用的動態代理類型:CGLIB或者JDK;

order:設置AsyncAnnotationBeanPostProcessor執行異步調用的順序。

mode可選值
AsyncAnnotationBeanPostProcessor-執行異步流程的調用

? ? ? ? 而在AsyncAnnotationBeanPostProcessor類的內部,則是通過TaskExecutor提供了一個線程池,來更加具體的負責執行某一個異步流程的。

? ? ? ? 再往深入查看,就會發現,該接口的父接口Executor,與之相關的就是我們經常談論的和并發編程相關的Executor框架了。

? ? ? ? 再看Spring框架內部提供的TaskExecutor接口的繼承結構,如下圖所示,

? ? ? ? ?因此,要使用@EnableAsync注解開啟異步流程執行的支持,可能就需要去對TaskExecutor接口實例的線程池參數進行配置。

<task:executor id="myexecutor" pool-size="5" ?/>
<task:annotation-driven executor="myexecutor"/>

SpringBoot默認線程池配置

?根據以上解讀,不難發現:其實SpringBoot內部是通過維護線程池的方式去執行異步任務的,那么,這個默認的線程池對應于Exector框架的哪一個實現子類?相關的配置參數又是什么呢?

? ? ? ? 要解決上述疑惑,需要先去找到TaskExecutionAutoConfiguration類,該類定義了默認注入的線程池實例及其配置參數。

默認線程池類型:ThreadPoolTaskExecutor

默認線程池配置參數:TaskExecutionProperties.Pool

? ? ? ? 詳細參數信息,對應于一個靜態內部類Pool,源碼如下,

public static class Pool {/*** Queue capacity. An unbounded capacity does not increase the pool and therefore* ignores the "max-size" property.*/private int queueCapacity = Integer.MAX_VALUE;/*** Core number of threads.*/private int coreSize = 8;/*** Maximum allowed number of threads. If tasks are filling up the queue, the pool* can expand up to that size to accommodate the load. Ignored if the queue is* unbounded.*/private int maxSize = Integer.MAX_VALUE;/*** Whether core threads are allowed to time out. This enables dynamic growing and* shrinking of the pool.*/private boolean allowCoreThreadTimeout = true;/*** Time limit for which threads may remain idle before being terminated.*/private Duration keepAlive = Duration.ofSeconds(60);public int getQueueCapacity() {return this.queueCapacity;}public void setQueueCapacity(int queueCapacity) {this.queueCapacity = queueCapacity;}public int getCoreSize() {return this.coreSize;}public void setCoreSize(int coreSize) {this.coreSize = coreSize;}public int getMaxSize() {return this.maxSize;}public void setMaxSize(int maxSize) {this.maxSize = maxSize;}public boolean isAllowCoreThreadTimeout() {return this.allowCoreThreadTimeout;}public void setAllowCoreThreadTimeout(boolean allowCoreThreadTimeout) {this.allowCoreThreadTimeout = allowCoreThreadTimeout;}public Duration getKeepAlive() {return this.keepAlive;}public void setKeepAlive(Duration keepAlive) {this.keepAlive = keepAlive;}}

從中可以找到默認的配置參數,

默認線程池配置參數

@Async注解使用

使用時的兩個限制

1.它只能應用于公共方法

2.從同一個類中調用異步方法將不起作用(會繞過代理,而直接去調用底層方法本身)

注解修飾對象

查看@Async注解的源碼,可看到:它用于修飾class、interface、method,并且提供了一個value屬性,用于在@Autowired和@@Qualifier注解組合自動裝配時,指定要使用哪一個線程池。因為原則上來講,我們是可以通過@Bean注解,在一個Spring容器中注入多個線程池實例的。

package org.springframework.scheduling.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** Annotation that marks a method as a candidate for <i>asynchronous</i> execution.** <p>Can also be used at the type level, in which case all the type's methods are* considered as asynchronous. Note, however, that {@code @Async} is not supported* on methods declared within a* {@link org.springframework.context.annotation.Configuration @Configuration} class.** <p>In terms of target method signatures, any parameter types are supported.* However, the return type is constrained to either {@code void} or* {@link java.util.concurrent.Future}. In the latter case, you may declare the* more specific {@link org.springframework.util.concurrent.ListenableFuture} or* {@link java.util.concurrent.CompletableFuture} types which allow for richer* interaction with the asynchronous task and for immediate composition with* further processing steps.** <p>A {@code Future} handle returned from the proxy will be an actual asynchronous* {@code Future} that can be used to track the result of the asynchronous method* execution. However, since the target method needs to implement the same signature,* it will have to return a temporary {@code Future} handle that just passes a value* through: e.g. Spring's {@link AsyncResult}, EJB 3.1's {@link javax.ejb.AsyncResult},* or {@link java.util.concurrent.CompletableFuture#completedFuture(Object)}.*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {/*** A qualifier value for the specified asynchronous operation(s).* <p>May be used to determine the target executor to be used when executing* the asynchronous operation(s), matching the qualifier value (or the bean* name) of a specific {@link java.util.concurrent.Executor Executor} or* {@link org.springframework.core.task.TaskExecutor TaskExecutor}* bean definition.* <p>When specified on a class-level {@code @Async} annotation, indicates that the* given executor should be used for all methods within the class. Method-level use* of {@code Async#value} always overrides any value set at the class level.* @since 3.1.2*/String value() default "";}

使用方式1:修飾方法method

根據上面的使用限制,被@Async注解修飾的方法,和主調方法不能位于同一個類中,并且也必須是public類型的公共方法。

package com.example.soiladmin;import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;//@Async注解測試類
@Component
public class AsyncTestMethod {@Asyncpublic void asyncTest(){try {System.out.println(String.format("start:%s",Thread.currentThread().getName()));Thread.sleep(1500);System.out.println(String.format("end:%s",Thread.currentThread().getName()));} catch (InterruptedException e) {e.printStackTrace();}}
}

? ? ? ? 調用方法,

注意到:被@Async修飾的方法線程睡眠了1.5s,如果它是異步執行的,那么就不會阻塞后面for循環的執行。

    @Autowiredprivate AsyncTestMethod asyncTestMethod;@Testpublic void asyncTestMethod_1(){asyncTestClass.asyncTest();for (int i = 0; i < 10; i++) {try {Thread.sleep(300);System.out.println(String.format("i=%d\n",i));} catch (InterruptedException e) {e.printStackTrace();}}}

? ? ? ? 打印結果,

使用方式2:修飾類class

?為了方便,我們直接將使用方式1中的類拷貝一份,然后使用@Async注解修飾class類,而非Method方法。

package com.example.soiladmin;import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;@Component
@Async
public class AsyncTestClass {public void asyncTest(){try {System.out.println(String.format("start:%s",Thread.currentThread().getName()));Thread.sleep(1500);System.out.println(String.format("end:%s",Thread.currentThread().getName()));} catch (InterruptedException e) {e.printStackTrace();}}
}

? ? ? ? 主調方法,

    @Autowiredprivate AsyncTestClass asyncTestClass;@Testpublic void asyncTestClass_2(){asyncTestClass.asyncTest();for (int i = 0; i < 10; i++) {try {Thread.sleep(300);System.out.println(String.format("i=%d\n",i));} catch (InterruptedException e) {e.printStackTrace();}}}

? ? ? ? 打印結果,

使用方式3:帶返回值的方法

以上測試案例都是不帶返回值的,但是一般情況下,我們可能還希望獲取異步執行的結果,然后對結果進行合并、分析等,那么就可以為@Async注解修飾的方法聲明一java.util.concurrent接口類型的返回類型。

但是這里有一個注意點:就是要Future是一個接口,我們沒辦法直接去new一個接口,所以還要找到Future接口的實現子類。

比較常用的是Spring框架提供的實現子類AsyncResult,?

? ? ? ? 我們繼續簡單看一下AsyncResult實現子類的基本結構,

基本上提供了獲取異步執行結果的get方法、對成功/失敗情況進行處理的回調函數addCallBack、將返回結果繼續進行封裝為AsyncResult類型值的forValue,簡單來講,就是對jdk原生的concurrent包下的Future接口進行了功能拓展和增強。

? ? ? ? ?異步方法如下,

/*** 數列求和: An = 2 ^ n(n>=0),求累加和S(n)---這里為了測試效果(出于增加耗時考慮),不直接使用求和公式* @param n* @return*/@Asyncpublic ListenableFuture<Double> asyncSequenceSum(int n){Double sum = 0.0;for (int i = 1; i <= n; i++) {sum += Math.pow(2,i);}return new AsyncResult<>(sum);}

? ? ? ? 測試方法如下,

以下兩種方式都可以拿到執行結果,調用回調函數。

官網給的是第二種寫法,

@Testpublic void asyncTestMethodWithReturns_nor(){System.out.println("開始執行...");ListenableFuture<Double> asyncResult = asyncTestMethod.asyncSequenceSum(10);//添加回調函數asyncResult.addCallback(new SuccessCallback<Double>() {@Overridepublic void onSuccess(Double result) {System.out.println("執行成功:" + result.doubleValue());}},new FailureCallback() {@Overridepublic void onFailure(Throwable ex) {System.out.println("執行失敗:"+ex.getMessage());}});//直接嘗試獲取結果-[只能拿到結果,如果執行出錯,就會拋出異常]try {Double aDouble = asyncResult.get();System.out.println("計算結果:"+aDouble.doubleValue());} catch (ExecutionException | InterruptedException e) {e.printStackTrace();}}@Testpublic void asyncTestMethodWithReturns_normal(){System.out.println("開始執行...");ListenableFuture<Double> asyncResult = asyncTestMethod.asyncSequenceSum(10);//等待執行結果while (true){if (asyncResult.isDone()){//直接嘗試獲取結果-[只能拿到結果,如果執行出錯,就會拋出異常]try {Double aDouble = asyncResult.get();System.out.println("計算結果:"+aDouble.doubleValue());//添加回調函數asyncResult.addCallback(new SuccessCallback<Double>() {@Overridepublic void onSuccess(Double result) {System.out.println("執行成功:" + result.doubleValue());}},new FailureCallback() {@Overridepublic void onFailure(Throwable ex) {System.out.println("執行失敗:"+ex.getMessage());}});} catch (ExecutionException | InterruptedException e) {e.printStackTrace();}//終止循環break;}System.out.println("Continue doing something else. ");}}

? ? ? ? 執行結果,

自定義線程池配置參數

上面提到,SpringBoot內置了1個ThreadPoolTaskExecutor線程池實例,在實際開發中,根據需要,我們也可以結合@Configuration注解自定義新的線程池,也可以通過通過實現AsyncConfigurer接口直接替換掉原有的線程池。

定義新的線程池

這種情況下,Spring容器就會出現多個線程池實例,所以在使用@Async注解時,要通過value屬性指定具體要使用哪一個線程池實例。

@Configuration
@EnableAsync
public class SpringAsyncConfig {@Bean(name = "threadPoolTaskExecutor")public Executor threadPoolTaskExecutor() {return new ThreadPoolTaskExecutor();}
}

? ? ? ? 使用示例,

@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {System.out.println("Execute method with configured executor - "+ Thread.currentThread().getName());
}

替換默認線程池?

替換默認線程池需要實現AsyncConfigurer接口,通過重寫getAsyncExecutor()?,從而讓自定義的線程池變為Spring框架默認使用的線程池。

? ? ? ? 示例代碼如下,

@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();threadPoolTaskExecutor.initialize();return threadPoolTaskExecutor;}
}

配置線程池參數

除了上述自定義新的線程池的方法,也可以通過SpringBoot配置文件,重新對默認線程池的參數進行修改。

?

異步處理流程的異常處理

內置異常處理類:SimpleAsyncUncaughtExceptionHandler

SpringBoot框架內置的異常處理類為SimpleAsyncUncaughtExceptionHandler,僅僅是對異

常信息進行了打印處理。

package org.springframework.aop.interceptor;import java.lang.reflect.Method;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;/*** A default {@link AsyncUncaughtExceptionHandler} that simply logs the exception.** @author Stephane Nicoll* @author Juergen Hoeller* @since 4.1*/
public class SimpleAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {private static final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class);@Overridepublic void handleUncaughtException(Throwable ex, Method method, Object... params) {if (logger.isErrorEnabled()) {logger.error("Unexpected exception occurred invoking async method: " + method, ex);}}}

當我們不做任何處理時,默認就是上述異常處理類在起作用。

繼續向上扒拉源碼,會發現它的父接口AsyncUncaughtExceptionHandler,其作用就是:指定異步方法執行過程中,拋出異常時的因對策略。

?

自定義異常處理類|配置

我們也可以通過實現接口AsyncUncaughtExceptionHandler,來自定義異常處理邏輯。

如下所示,為自定義的異常處理類:CustomAsyncExceptionHandler。

package com.example.soilcommon.core.async;import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;/*** 異步處理流程異常處理類:* [1]對于返回值為Future類型的異步執行方法,異常會被拋出給主調方法* [2]對于返回值為void類型的異步執行方法,異常不會被拋出,即:在主調方法中沒辦法通過try...catch捕獲到異常信息* 當前配置類針對情況[2]進行統一的異常處理*/
@Component("customAsyncExceptionHandler")
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {/*** Handle the given uncaught exception thrown from an asynchronous method.* @param throwable the exception thrown from the asynchronous method* @param method the asynchronous method* @param params the parameters used to invoke the method*/@Overridepublic void handleUncaughtException(Throwable throwable, Method method, Object... params) {System.out.println("Exception message - " + throwable.getMessage());System.out.println("Method name - " + method.getName());for (Object param : params) {System.out.println("Parameter value - " + param);}}
}

接下來我們對其進行配置,使其生效,需要重寫AsyncConfigurer接口getAsyncUncaughtExceptionHandler方法,

package com.example.soilcommon.core.async;import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {/*** 指定要使用哪一個具體的異常處理類* 原因:SpringBoot框架默認使用內置的SimpleAsyncUncaughtExceptionHandler進行異常處理*/@Autowired@Qualifier("customAsyncExceptionHandler")private AsyncUncaughtExceptionHandler asyncUncaughtExceptionHandler;/*** The {@link AsyncUncaughtExceptionHandler} instance to be used* when an exception is thrown during an asynchronous method execution* with {@code void} return type.*/@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return this.asyncUncaughtExceptionHandler;}
}

? ? ? ? 最終異步方法執行拋出異常時,打印的信息就是我們自定義的了,

????????參考文章:How To Do @Async in Spring | Baeldung

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/167626.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/167626.shtml
英文地址,請注明出處:http://en.pswp.cn/news/167626.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

系列六、Spring整合單元測試

一、概述 Spring中獲取bean最常見的方式是通過ClassPathXmlApplicationContext 或者 AnnotationConfigApplicationContext的getBean()方式獲取bean&#xff0c;那么在Spring中如何像在SpringBoot中直接一個類上添加個SpringBootTest注解&#xff0c;即可在類中注入自己想要測試…

java反序列化漏洞詳解

java反序列化漏洞 文章目錄 java反序列化漏洞漏洞原理漏洞評級漏洞危害漏洞驗證漏洞防御典型案例 漏洞原理 由于java開發人員在編寫代碼時重寫了 readObject 方法&#xff0c;在重寫的 readObject 方法中調用其他函數實現鏈式調用最終調用到了危險函數&#xff0c;從而形成反序…

【C++】泛型編程 ? ( 類模板示例 - 數組類模板 | 自定義類中持有指針成員變量 )

文章目錄 一、支持 數組類模板 存儲的 自定義類1、可拷貝和可打印的自定義類2、改進方向3、改進方向 - 構造函數4、改進方向 - 析構函數5、改進方向 - 重載左移運算符6、改進方向 - 重載拷貝構造函數 和 等號運算符 二、代碼示例1、Array.h 頭文件2、Array.cpp 代碼文件3、Test…

[網鼎杯 2020 朱雀組]phpweb

看一下源碼 應該是輸入的date 作為函數&#xff0c;value作為內部參數的值&#xff0c;將date()函數返回的結果顯示在頁面上 回去看的時候&#xff0c;意外發現頁面有了新的跳轉&#xff0c;觀察一下發現&#xff0c;頁面每隔五秒就會發生一次跳轉 所以就抓包看看 抓包發現po…

GEE:kNN(k-最近鄰)分類教程(樣本制作、特征添加、訓練、精度、最優參數、統計面積)

作者:CSDN @ _養樂多_ 本文將介紹在Google Earth Engine (GEE)平臺上進行kNN(k-最近鄰)分類的方法和代碼,其中包括制作樣本點教程(本地、在線和本地在線混合制作樣本點,合并樣本點等),加入特征變量(各種指數、紋理特征、時間序列特征、物候特征等),運行kNN(k-最近…

Linux中,查看Tomcat版本、如何查看Tomcat版本

方法 在tomcat的bin目錄下&#xff0c;執行version.sh命令即可 結果

python每日一題——3最長連續序列

題目 給定一個未排序的整數數組 nums &#xff0c;找出數字連續的最長序列&#xff08;不要求序列元素在原數組中連續&#xff09;的長度。 請你設計并實現時間復雜度為 O(n) 的算法解決此問題。 示例 1&#xff1a; 輸入&#xff1a;nums [100,4,200,1,3,2] 輸出&#xf…

RpcServiceContext上下文

消費者: web 提供者: buss-service 同一服務器: 192.168.100.228 RpcServiceContext serviceContext RpcContext.getServiceContext(); //web->buss-serviceLOGGER.warn("getRequest->{}", JsonUtil.toJson(serviceContext.getRequest())); //getRequest-…

ElementUI table+dialog實現一個簡單的可編輯的表格

table組件如何實現可編輯呢&#xff1f; 我的需求是把table組件那樣的表格&#xff0c;實現它點擊可以彈出一個框&#xff0c;然后在這個框里面輸入你的東西&#xff0c;然后將他回顯回去&#xff0c;當然&#xff0c;輸入的有可能是時間啥的。 為什么要彈出彈層不在框上直接…

最近iphone手機的交管12123閃退,打不開的解決辦法?

蘋果手機系統和新版軟件不配&#xff0c;終極決絕辦法&#xff1a;升級IOS系統就好 可能是手機的內存不足了&#xff0c;因為在使用APP時&#xff0c;需要占用手機的內存&#xff0c;如果手機內存不足以支持軟件允許&#xff0c;軟件就會閃退。車主可以清理一下手機的內存&…

彈窗msvcp140_1.dll丟失的解決方法,超簡單的方法分享

在計算機使用過程中&#xff0c;我們經常會遇到一些錯誤提示&#xff0c;其中最常見的就是缺少某個文件的錯誤。最近&#xff0c;我在使用某些軟件時&#xff0c;遇到了一個名為“msvcp140_1.dll”的錯誤提示。這個錯誤通常出現在運行某些程序時&#xff0c;由于缺少了msvcp140…

項目總結報告(案例模板)

軟件項目總結報告模板套用&#xff1a; 項目概要項目工作分析經驗與教訓改進建議可納入的項目過程資產 --------進主頁獲取更多資料-------

2023年【汽車駕駛員(中級)】最新解析及汽車駕駛員(中級)試題及解析

題庫來源&#xff1a;安全生產模擬考試一點通公眾號小程序 2023年汽車駕駛員&#xff08;中級&#xff09;最新解析為正在備考汽車駕駛員&#xff08;中級&#xff09;操作證的學員準備的理論考試專題&#xff0c;每個月更新的汽車駕駛員&#xff08;中級&#xff09;試題及解…

Doris中的物化視圖-查詢(十九)

物化視圖創建完成后&#xff0c;用戶的查詢會根據規則自動匹配到最優的物化視圖。 比如我們有一張銷售記錄明細表&#xff0c;并且在這個明細表上創建了三張物化視圖。一個存儲了不同時間不同銷售員的售賣量&#xff0c;一個存儲了不同時間不同門店的銷售量&#xff0c;以及每…

C#,《小白學程序》第二課:數組,循環與排序

1 什么是數組&#xff1f; 數組 Array 是一組數值&#xff08;數 或 值&#xff09;。 int[] a; int[,] b; int[][] c; Anything[] d; 都是數組。 2 排序 排序就是按大小、名字、拼音或你指定的信息進行比較后排隊。 排序是數組最基本的功能需求。 3 文本格式 /// <summa…

《數據結構、算法與應用C++語言描述》-代碼實現散列表(線性探查與鏈式散列)

散列表 完整可編譯運行代碼&#xff1a;Github:Data-Structures-Algorithms-and-Applications/_22hash/ 定義 字典的另一種表示方法是散列&#xff08;hashing&#xff09;。它用一個散列函數&#xff08;也稱哈希函數&#xff09;把字典的數對映射到一個散列表&#xff08…

spring-webflux的一些概念的理解

Spring5的webflux可以支持高吞吐量&#xff0c;使用相同的資源可以處理更加多的請求&#xff0c;它將會成為未來技術的趨勢&#xff0c;但是相對于學習其他的框架相比&#xff0c;它的學習曲線很高&#xff0c;綜合了很多現有的技術&#xff0c;即使按照教程學習能編寫代碼&…

requests庫的學習(詳細篇)

一、request庫的安裝 requests屬于第三方庫&#xff0c;Python不內置&#xff0c;因此需要我們手動安裝。 pip3 install requests

HTML5新特性

HTML5新特性 前言語義化標簽常用語義化標簽優點 新增input屬性新增type屬性值內容其他新增input屬性 video&#xff08;視頻&#xff09;與audio&#xff08;音頻&#xff09;標簽 前言 本文主要講解HTML5中新增了哪些內容。 語義化標簽 HTML5新增了語義化標簽這個特性&…

第一類曲線積分@對弧長的曲線積分

文章目錄 abstract對弧長的曲線積分曲線形構件的質量第一類曲線積分曲線積分存在性利用曲線積分的定義描述曲線形構件質量問題推廣曲線積分可加性閉曲線積分 曲線積分性質曲線積分的計算方法證明(部分推導) 小結曲線弧顯函數形式方程下的曲線積分公式推廣例例例 abstract 在積…