Spring事務管理(三)-PlatformmTransactionManager解析和事務傳播方式原理

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

Spring在事務管理時,對事務的處理做了極致的抽象,即PlatformTransactionManager。對事務的操作,簡單地來說,只有三步操作:獲取事務,提交事務,回滾事務。

public interface PlatformTransactionManager {// 獲取事務TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;// 提交事務void commit(TransactionStatus status) throws TransactionException;// 回滾事務void rollback(TransactionStatus status) throws TransactionException;}

當然Spring不會僅僅只提供一個接口,同時會有一個抽象模版類,實現了事務管理的具體骨架。AbstractPlatformTransactionManager類可以說是Spring事務管理的控制臺,決定事務如何創建,提交和回滾。

在Spring事務管理(二)-TransactionProxyFactoryBean原理中,分析TransactionInterceptor增強時,在invoke方法中最重要的三個操作:

  1. 創建事務 createTransactionIfNecessary
  2. 異常后事務處理 completeTransactionAfterThrowing
  3. 方法執行成功后事務提交 commitTransactionAfterReturning

在具體操作中,最后都是通過事務管理器PlatformTransactionManager的接口實現來執行的,其實也就是上面列出的三個接口方法。我們分別介紹這三個方法的實現,并以DataSourceTransactionManager為實現類觀察JDBC方式事務的具體實現。

1. 獲取事務

getTransaction方法根據事務定義來獲取事務狀態,事務狀態中記錄了事務定義,事務對象及事務相關的資源信息。對于事務的獲取,除了調用事務管理器的實現來獲取事務對象本身外,另外的很重要的一點是處理了事務的傳播方式。

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {// 1.獲取事務對象Object transaction = doGetTransaction();// Cache debug flag to avoid repeated checks.boolean debugEnabled = logger.isDebugEnabled();if (definition == null) {// Use defaults if no transaction definition given.definition = new DefaultTransactionDefinition();}// 2.如果已存在事務,根據不同的事務傳播方式處理獲取事務if (isExistingTransaction(transaction)) {// Existing transaction found -> check propagation behavior to find out how to behave.return handleExistingTransaction(definition, transaction, debugEnabled);}// Check definition settings for new transaction.if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());}// 3. 如果當前沒有事務,不同的事務傳播方式不同處理方式// 3.1 事務傳播方式為mandatory(強制必須有事務),則拋出異常// No existing transaction found -> check propagation behavior to find out how to proceed.if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}// 3.2 事務傳播方式為required或required_new或nested(嵌套),創建一個新的事務狀態else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {SuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);}try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// 創建新的事務狀態對象DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// 事務初始化doBegin(transaction, definition);// 準備其他同步操作prepareSynchronization(status, definition);return status;}catch (RuntimeException | Error ex) {resume(null, suspendedResources);throw ex;}}// 3.3 其他事務傳播方式,返回一個事務對象為null的事務狀態對象else {// Create "empty" transaction: no actual transaction, but potentially synchronization.if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {logger.warn("Custom isolation level specified but no actual transaction initiated; " +"isolation level will effectively be ignored: " + definition);}boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);}
}

獲取事務的方法主要做兩件事情:

  1. 獲取事務對象
  2. 根據事務傳播方式返回事務狀態對象

獲取事務對象,在DataSourceTransactionManager的實現中,返回一個DataSourceTransactionObject對象

protected Object doGetTransaction() {DataSourceTransactionObject txObject = new DataSourceTransactionObject();txObject.setSavepointAllowed(isNestedTransactionAllowed());ConnectionHolder conHolder =(ConnectionHolder) // 從事務同步管理器中根據DataSource獲取數據庫連接資源		TransactionSynchronizationManager.getResource(obtainDataSource());txObject.setConnectionHolder(conHolder, false);return txObject;
}

每次執行doGetTransaction方法,即會創建一個DataSourceTransactionObject對象txObject,并從事務同步管理器中根據DataSource獲取數據庫連接持有對象ConnectionHolder,然后存入txObject中。**事務同步管理類持有一個ThreadLocal級別的resources對象,存儲DataSource和ConnectionHolder的映射關系。**因此返回的txObject中持有的ConnectionHolder可能有值,也可能為空。而不同的事務傳播方式下,事務管理的處理根據txObejct中是否存在事務有不同的處理方式。

關于關注事務傳播方式的實現,很多人對事務傳播方式都是一知半解,只是因為沒有了解源碼的實現。現在就來看看具體的實現。事務傳播方式的實現分為兩種情況,事務不存在和事務已經存在。isExistingTransaction方法判斷事務是否存在,默認在AbstractPlatformTransactionManager抽象類中返回false,而在DataSourceTransactionManager實現中,則根據是否有數據庫連接來決定。

protected boolean isExistingTransaction(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}

當前無事務

如果當前沒有事務,則不同事務傳播方式的處理如下:

  1. 事務傳播方式為mandatory(強制必須有事務),當前沒有事務,即拋出異常。
  2. 事務傳播方式為required或required_new或nested(嵌套),當前沒有事務,即會創建一個新的事務狀態。
  3. 其他事務傳播方式時,直接返回事務對象為null的事務狀態對象,即不在事務中執行。

如何創建一個新的事務狀態

// 1. 新建事務狀態,返回DefaultTransactionStatus對象
DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 2. 事務初始化
doBegin(transaction, definition);
// 3. 準備同步操作
prepareSynchronization(status, definition);
return status;

第一步,新建事務狀態,就是構建一個DefaultTransactionStatus對象

protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {boolean actualNewSynchronization = newSynchronization &&!TransactionSynchronizationManager.isSynchronizationActive();return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization,definition.isReadOnly(), debug, suspendedResources);
}

第二步,事務初始化,AbstractPlatformTransactionManager沒有實現,來看DataSourceTransactionManager的實現:獲取一個新的數據庫連接并開啟事務,完成事務的基本屬性設置。

protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {// 如果當前沒有事務,由DataSource獲取一個新的數據庫連接,并賦予txObjectif (!txObject.hasConnectionHolder() ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {Connection newCon = obtainDataSource().getConnection();if (logger.isDebugEnabled()) {logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");}txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}// 設置數據庫連接與事務同步txObject.getConnectionHolder().setSynchronizedWithTransaction(true);con = txObject.getConnectionHolder().getConnection();// 設置事務隔離級別Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,// so we don't want to do it unnecessarily (for example if we've explicitly// configured the connection pool to set it already).// 非常重要的一點,JDBC通過設置自動提交為false,開啟一個新的事務if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}con.setAutoCommit(false);}// 如果設置事務只讀屬性,執行Statement設置只讀prepareTransactionalConnection(con, definition);// 激活事務狀態txObject.getConnectionHolder().setTransactionActive(true);// 設置超時時間int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// Bind the connection holder to the thread.// 如果為新連接,綁定DataSource和數據庫連接持有者的映射關系if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());}}catch (Throwable ex) {if (txObject.isNewConnectionHolder()) {DataSourceUtils.releaseConnection(con, obtainDataSource());txObject.setConnectionHolder(null, false);}throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);}
}

第三步:準備同步操作,如果事務狀態開啟同步,則在事務同步管理器中設置事務基礎屬性

protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {if (status.isNewSynchronization()) {TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?definition.getIsolationLevel() : null);TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());TransactionSynchronizationManager.initSynchronization();}
}

當前有事務

如果當前已經有事務存在,由handleExistingTransaction方法完成事務操作。

  1. 傳播方式為never(不允許事務),拋出異常
  2. 傳播方式為not_supported(不支持),則掛起當前事務,以無事務方式運行
  3. 傳播方式為required_new,掛起原有事務,并開啟新的事務
  4. 傳播方式為nested(嵌套),創建嵌套事務。這里一般方式都是通過savepoint保存點的完成嵌套,但Spring對JTA事務單獨做了另一種處理。
  5. 傳播方式為supports或required,返回當前事務
private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled)throws TransactionException {// 傳播方式為never(不允許事務),拋出異常if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");}// 傳播方式為not_supported(不支持),則掛起當前事務,以無事務方式運行if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {if (debugEnabled) {logger.debug("Suspending current transaction");}Object suspendedResources = suspend(transaction);boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);}// 傳播方式為required_new,掛起原有事務,并開啟新的事務if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {if (debugEnabled) {logger.debug("Suspending current transaction, creating new transaction with name [" +definition.getName() + "]");}SuspendedResourcesHolder suspendedResources = suspend(transaction);try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}catch (RuntimeException | Error beginEx) {resumeAfterBeginException(transaction, suspendedResources, beginEx);throw beginEx;}}// 傳播方式為nested(嵌套),創建嵌套事務if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {if (!isNestedTransactionAllowed()) {throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - " +"specify 'nestedTransactionAllowed' property with value 'true'");}if (debugEnabled) {logger.debug("Creating nested transaction with name [" + definition.getName() + "]");}// 使用保存點支持嵌套事務if (useSavepointForNestedTransaction()) {// Create savepoint within existing Spring-managed transaction,// through the SavepointManager API implemented by TransactionStatus.// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);status.createAndHoldSavepoint();return status;}// 只適用于JTA事務:通過嵌套的begin和commit/rollback創建嵌套事務else {// Nested transaction through nested begin and commit/rollback calls.// Usually only for JTA: Spring synchronization might get activated here// in case of a pre-existing JTA transaction.boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}}// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.// 傳播方式為supports或required,返回當前事務if (debugEnabled) {logger.debug("Participating in existing transaction");}if (isValidateExistingTransaction()) {if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {Constants isoConstants = DefaultTransactionDefinition.constants;throw new IllegalTransactionStateException("Participating transaction with definition [" +definition + "] specifies isolation level which is incompatible with existing transaction: " +(currentIsolationLevel != null ?isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :"(unknown)"));}}if (!definition.isReadOnly()) {if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {throw new IllegalTransactionStateException("Participating transaction with definition [" +definition + "] is not marked as read-only but existing transaction is");}}}boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}

這里關注兩個點

第一是事務的掛起,Spring并不是真的對數據庫連接做了什么掛起操作,而是在邏輯上由事務同步管理器做了事務信息和狀態的重置,并將原事務信息和狀態返回,并記錄在新的事務狀態對象中,從而形成一種鏈式結構。

protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {if (TransactionSynchronizationManager.isSynchronizationActive()) {List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();try {Object suspendedResources = null;if (transaction != null) {suspendedResources = doSuspend(transaction);}String name = TransactionSynchronizationManager.getCurrentTransactionName();TransactionSynchronizationManager.setCurrentTransactionName(null);boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();TransactionSynchronizationManager.setActualTransactionActive(false);return new SuspendedResourcesHolder(suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);}catch (RuntimeException | Error ex) {// doSuspend failed - original transaction is still active...doResumeSynchronization(suspendedSynchronizations);throw ex;}}else if (transaction != null) {// Transaction active but no synchronization active.Object suspendedResources = doSuspend(transaction);return new SuspendedResourcesHolder(suspendedResources);}else {// Neither transaction nor synchronization active.return null;}
}

第二是嵌套事務設置保存點,通常由JDBC3.0支持的savepoint API完成,然后將保存點記錄在事務狀態中。

DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;

至此,完成了獲取事務

2.提交事務

首先要說的,commit方法并不是一定提交事務,也可能回滾。

public final void commit(TransactionStatus status) throws TransactionException {// 事務已經提交,再次提交拋出異常if (status.isCompleted()) {throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");}DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;// 如果事務狀態設置了回滾標識,則執行回滾if (defStatus.isLocalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Transactional code has requested rollback");}processRollback(defStatus, false);return;}// 設置全局回滾標識為true,則執行回滾if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");}processRollback(defStatus, true);return;}// 提交事務processCommit(defStatus);
}

processCommit執行事務的提交,但事務的提交也分為幾種情況:

  1. 存在保存點,即嵌套事務,則釋放保存點
  2. 如果事務是由當前事務狀態開啟的,即事務傳播的第一層,執行事務提交
  3. 其他情況(比如事務是繼承自上一層),則不做任何操作

且在processCommit方法中,不同時候設置了不同狀態的觸發監控,用來提示事務同步相關資源,觸發需要的操作。

private void processCommit(DefaultTransactionStatus status) throws TransactionException {try {boolean beforeCompletionInvoked = false;try {boolean unexpectedRollback = false;prepareForCommit(status);// 提交前提示triggerBeforeCommit(status);// 完成前提示triggerBeforeCompletion(status);beforeCompletionInvoked = true;if (status.hasSavepoint()) {if (status.isDebug()) {logger.debug("Releasing transaction savepoint");}unexpectedRollback = status.isGlobalRollbackOnly();status.releaseHeldSavepoint();}else if (status.isNewTransaction()) {if (status.isDebug()) {logger.debug("Initiating transaction commit");}unexpectedRollback = status.isGlobalRollbackOnly();// 執行事務提交doCommit(status);}else if (isFailEarlyOnGlobalRollbackOnly()) {unexpectedRollback = status.isGlobalRollbackOnly();}// Throw UnexpectedRollbackException if we have a global rollback-only// marker but still didn't get a corresponding exception from commit.if (unexpectedRollback) {throw new UnexpectedRollbackException("Transaction silently rolled back because it has been marked as rollback-only");}}catch (UnexpectedRollbackException ex) {// can only be caused by doCommit// 回滾完成提示triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);throw ex;}catch (TransactionException ex) {// can only be caused by doCommitif (isRollbackOnCommitFailure()) {doRollbackOnCommitException(status, ex);}else {// 未知狀態完成提示triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);}throw ex;}catch (RuntimeException | Error ex) {if (!beforeCompletionInvoked) {triggerBeforeCompletion(status);}doRollbackOnCommitException(status, ex);throw ex;}// Trigger afterCommit callbacks, with an exception thrown there// propagated to callers but the transaction still considered as committed.try {// 事務提交完成提示triggerAfterCommit(status);}finally {// 操作完成完成提示triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);}}finally {// 完成后清理cleanupAfterCompletion(status);}
}

DataSourceTransactionManager對doCommit的實現,就是執行數據庫連接的提交

protected void doCommit(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();if (status.isDebug()) {logger.debug("Committing JDBC transaction on Connection [" + con + "]");}try {// 提交事務con.commit();}catch (SQLException ex) {throw new TransactionSystemException("Could not commit JDBC transaction", ex);}
}

3.回滾事務

回滾事務時也分為幾種情況:

  1. 存在保存點(嵌套事務),則回滾到保存點
  2. 如果事務是由當前事務狀態開啟的,則執行回滾操作
  3. 其他情況下,如果事務狀態設置了回滾標識,則設置事務對象的狀態也為回滾,否則不做任何操作
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {try {boolean unexpectedRollback = unexpected;try {triggerBeforeCompletion(status);if (status.hasSavepoint()) {if (status.isDebug()) {logger.debug("Rolling back transaction to savepoint");}// 回滾保存點status.rollbackToHeldSavepoint();}else if (status.isNewTransaction()) {if (status.isDebug()) {logger.debug("Initiating transaction rollback");}// 回滾事務doRollback(status);}else {// Participating in larger transactionif (status.hasTransaction()) {if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {if (status.isDebug()) {logger.debug("Participating transaction failed - marking existing transaction as rollback-only");}doSetRollbackOnly(status);}else {if (status.isDebug()) {logger.debug("Participating transaction failed - letting transaction originator decide on rollback");}}}else {logger.debug("Should roll back transaction but cannot - no transaction available");}// Unexpected rollback only matters here if we're asked to fail earlyif (!isFailEarlyOnGlobalRollbackOnly()) {unexpectedRollback = false;}}}catch (RuntimeException | Error ex) {triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);throw ex;}triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);// Raise UnexpectedRollbackException if we had a global rollback-only markerif (unexpectedRollback) {throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");}}finally {cleanupAfterCompletion(status);}
}

對于DataSourceTransactionManager實現,回滾保存點和回滾事務都由JDBC的API來完成。

至此,事務管理器對事務的三種操作就簡單地介紹完了,但其中事務同步資源的控制十分精妙,這里就不做詳細的介紹。有興趣的自己去研究源碼。于我而言,任何強大的機制都是由一行行地源碼精妙地組建出來的,深入進去,一切都將真相大白。

轉載于:https://my.oschina.net/u/2377110/blog/1614198

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

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

相關文章

div方框彎曲邊樣式_使用彎曲樣式編輯文本

div方框彎曲邊樣式Would you like a new Notepad replacement that incorporates the latest technologies while staying slim and fast? Text editors are usually bland, boring programs, but here’s a new one that makes your text come to life beautifully. 您是否想…

分布式鎖的幾種實現原理

分布式鎖主流有三種模式&#xff1a; 實現方式 功能要求 實現難度 學習成本 運維成本 MySQL 的方案借助表鎖/行鎖實現 滿足基本要求 不難 熟悉 小量OK、大量影響現有業務、1主多從架構&#xff0c;不方便擴容 通過 ZK 創建數據節點的方式實現 滿足要求 熟悉 ZK API 即可 需要學…

如何破解您忘記的Windows密碼

Here at How-To Geek, we’ve covered many different ways to reset your password for Windows—but what if you can’t reset your password? Or what if you’re using drive encryption that would wipe out your files if you changed the password? It’s time to cr…

sql語句練習50題(Mysql版)

表名和字段–1.學生表Student(s_id,s_name,s_birth,s_sex) –學生編號,學生姓名, 出生年月,學生性別–2.課程表Course(c_id,c_name,t_id) – –課程編號, 課程名稱, 教師編號–3.教師表Teacher(t_id,t_name) –教師編號,教師姓名–4.成績表Score(s_id,c_id,s_score) –學生編號…

OpenCV3 識別圖中表格-JAVA 實現

2019獨角獸企業重金招聘Python工程師標準>>> 關于 JAVA 學習 OpenCV 的內容&#xff0c;函數講解。內容我均整理在 GitHubd的OpenCV3-Study-JAVA OpenCV 3 識別圖中表格-Java 實現 1. 說明 網上大部分資料&#xff0c;都是針對 C的&#xff0c;python、java 的例子太…

內存泄露 體現在哪個數字上_Microsoft剛剛泄漏了一個新的開始菜單。 你喜歡哪個?...

內存泄露 體現在哪個數字上NTAuthority on TwitterTwitter上的NTAuthorityMicrosoft messed up today, releasing an internal build of Windows 10 to Windows Insiders. This build was never meant to see the light of day, but it features a new Start menu design—with…

簡述 Spring Cloud 是什么

很多同學都了解了Spring &#xff0c;了解了 Spring Boot, 但對于 Spring Cloud 是什么還是比較懵逼的。 本文帶你簡單的了解下&#xff0c;什么是Spring Cloud。 Spring Cloud 是什么 從字面理解&#xff0c;Spring Cloud 就是致力于分布式系統、云服務的框架。 Spring Cloud …

js DOM節點

元素節點 4種方式獲取 var oDiv document.getElementById("box");        //通過Id獲取元素var oDiv document.getElementsByClassName("div")[0];   //通過類名獲取元素  --》[ 0 ] 必須寫var oDiv document.getElementsByTagName("…

python web scraping

2019獨角獸企業重金招聘Python工程師標準>>> 最近在看《Web Scraping with Python》&#xff0c;借此來熟悉Python2.7如何開始編程。 發現書上主要使用的 http://example.webscraping.com/ 網站有部分變化&#xff0c;書中的代碼有點無法對照使用&#xff0c;因此稍…

傅里葉變換的物理意義

用三角函數表示周期函數 傅里葉的相關理論始于下面假設&#xff1a;對于周期為1的信號$f(t)$&#xff0c;可以由不同頻率的三角函數組成&#xff0c; $f(t) \frac{a_0}{2}\displaystyle{\sum^{\infty}_{k1}}(a_kcos(2\pi kt)b_ksin(2\pi kt))$ 組成的基礎波形為一個信號對&…

天貓年度總結

2019獨角獸企業重金招聘Python工程師標準>>> 魯大師天貓工作總結 時間&#xff1a;2017年10月22日-1月30日 1、對代理商進行6大區域的劃分管理&#xff0c;有專門的客服指導。 2、加班費申請和車費報銷制度。 3、簡化了特權訂金2階段改成1階段&#xff0c;極大的方便…

因特網使用期限_Internet死亡時使用PC的其他方式

因特網使用期限Nothing is more annoying than getting your Internet connection shut down, due to weather, or perhaps forgetting to pay your bill. Let’s take a look at some ways you can be productive and entertained without the Internet. 沒有什么比由于天氣原…

【基礎操作】線性基詳解

線性基是一個奇妙的集合&#xff08;我摘的原話&#xff09; 這里以非 $OI$ 的角度介紹了線性基 基礎部分 模板題 給你 $n$ 個數的集合&#xff0c;讓你選出任意多個不重復的數&#xff0c;使得它們的異或和最大。 線性基是什么 我們稱集合 $B$ 是集合 $S$ 的線性基&#xff0c…

節省大量教科書的三種潛在風險方法

Photo by Sultry 攝影&#xff1a; Sultry You can always save money on textbooks by buying online, going ebook, or renting what you need. But there are riskier ways to save a buck that just may yield even greater payoff, such as getting the international or …

解決內網搭建本地yum倉庫。

2019獨角獸企業重金招聘Python工程師標準>>> 一、使用iso鏡像搭建本地yum倉庫&#xff1b; 1、掛載鏡像到/mnt目錄下&#xff1a; [rootDasoncheng ~]# mount /dev/cdrom /mnt mount: /dev/sr0 is write-protected, mounting read-only2、備份配置文件&#xff0c;并…

通過用 .NET 生成自定義窗體設計器來定制應用程序

本文討論&#xff1a; ? 設計時環境基本原理 ? 窗體設計器體系結構 ? Visual Studio .NET 中窗體設計器的實現 ? 為自己的應用程序編寫窗體設計器而需要實現的服務 在很多年中&#xff0c;MFC 一直是生成基于 Windows? 的應用程序的流行框架。MFC 包含一個可以使窗體生成、…

airdrop 是 藍牙嗎_您可以在Windows PC或Android手機上使用AirDrop嗎?

airdrop 是 藍牙嗎Aleksey Khilko/Shutterstock.comAleksey Khilko / Shutterstock.comApple’s AirDrop is a convenient way to send photos, files, links, and other data between devices. AirDrop only works on Macs, iPhones, and iPads, but similar solutions are av…

vue加百度統計代碼(親測有效)

申請百度統計后&#xff0c;會得到一段JS代碼&#xff0c;需要插入到每個網頁中去&#xff0c;在Vue.js項目首先想到的可能就是&#xff0c;把統計代碼插入到index.html入口文件中&#xff0c;這樣就全局插入&#xff0c;每個頁面就都有了;這樣做就涉及到一個問題&#xff0c;V…

如何將Rant變成生產力電動工具

Ranting doesn’t have to be a waste of breathe and time. You can turn a rant into a powerful tool for productivity. Learn how to transform your sense of victim hood and irritability to self-empowerment and mental clarity. 狂歡不必浪費呼吸和時間。 您可以將r…

linux 下使用 curl post

命令&#xff1a; curl -X POST -d /etc/lazada/lazada_tracking.txt http://localhost:8080/booking/rs/LazadaService/post --header "Content-Type:application/json" -d 后臺 / &#xff1a; post 的 body 體 &#xff45;&#xff47;&#xff1a; {"a…