1. 程式人生 > >MyBatis的初始化,別名,事務,外掛,快取和執行原理總結

MyBatis的初始化,別名,事務,外掛,快取和執行原理總結

MyBatis的初始化的過程其實就是解析配置檔案和初始化Configuration的過程。

1.MyBatis的通用執行流程
參照GOF提出的23種設計模式,可以看出MyBatis的執行流程算是種通用的模板模式,其實現過程也簡單:
首先通過相關的資原始檔配置的載入得到對應的InputStream;
然後通過SqlSessionFactoryBuilder的build(InputStream)方法來構建SqlSessionFactory;
接著通過SqlSessionFactory的openSession方法來得到SqlSession;
接下來通過SqlSession來執行對應SQL語句實現;
最後封裝底層JDBC返回的執行結果,然後返回到業務端使用。

2.SqlSessionFactory的構建
MyBatis對資源的載入是通過Resources類來處理的,通過Resources類可以得到檔案的Properties,InputStream和Reader等。究其底層實現原理,都是通過urlString或者resourceString來得到對應的URL,然後通過URL來獲取對應的InputStream,最後進行封裝返回結果。SqlSessionFactory的建立比較簡單,其實現原始碼如下所示:
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
   XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
   return build(parser.parse()); // 此處構建返回的是DefaultSqlSessionFactory
}

3.Configuration的初始化
Configuration的初始化是通過XMLConfigBuilder的parse來實現的,
public Configuration parse() {
   // 通過XPathParser來解析,將XML中資料封裝成XNode,便於後續解析
   parseConfiguration(parser.evalNode("/configuration"));
   return configuration;
}

private void parseConfiguration(XNode root) {
   propertiesElement(root.evalNode("properties"));
   Properties settings = settingsAsProperties(root.evalNode("settings"));
   loadCustomVfs(settings);
   typeAliasesElement(root.evalNode("typeAliases"));
   pluginElement(root.evalNode("plugins"));
   objectFactoryElement(root.evalNode("objectFactory"));
   objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
   reflectorFactoryElement(root.evalNode("reflectorFactory"));
   settingsElement(settings);
   environmentsElement(root.evalNode("environments"));
   databaseIdProviderElement(root.evalNode("databaseIdProvider"));
   typeHandlerElement(root.evalNode("typeHandlers"));
   mapperElement(root.evalNode("mappers"));

在生成物件XMLConfigBuilder時會進行Configuration的初始化,在Configuration的初始化中會看到進行TypeAliasRegistry和LanguageDriverRegistry的註冊實現。在依次解析配置中的節點內容時會進豐富Configuration的引數,例如在解析environments節點內容時會根據transactionManager的配置來建立事務管理器,以及根據dataSource的配置來建立DataSource物件。

總結:關於XML檔案本身是通過XPath和Dom4來解析的,對於其中XML內容的解析則是通過PropertyParser,GenericTokenParser和TokenHandler來實現的。PropertyParser用來替換掉#{}和${}相關格式的字串,GenericTokenParser和TokenHandler用來處理相關的引數。在Map中查不到對應的key【XML中的id】時,或者重複新增對映時【xml中的id重複】時,或者繫結的Mapper中某個方法出錯時會丟擲此BindingException異常。

MyBatis的執行流程通常可分為兩種:
(1)直接執行已對映的SQL語句
   String statement = "com.XXX.dao.UserDao.getById";
   User user = session.selectOne(statement, 1);
(2)執行更清晰和型別安全的程式碼
   UserDao userDao = session.getMapper(UserDao.class);   
   User user = userDao.getById(1);   
在MyBatis中是通過MapperProxy動態代理Mapper的,對Mapper中的方法的處理是通過MapperProxy實現的。

1. MapperProxy的獲取
在DefaultSqlSession的原始碼中可以看出Mapper的獲取是通過Configuration來實現的:
public <T> T getMapper(Class<T> type) {
  return configuration.<T>getMapper(type, this);
}
在Configuration的原始碼中可以看出Mapper的獲取是通過MapperRegistry來實現的:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}
MyBatis的MapperRegistry類例項在應用啟動時建立,在MapperRegistry中通過knownMappers屬性來獲取MapperProxyFactory,然後通過MapperProxyFactory來獲取MapperProxy,其實現原始碼如下所示:
Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  return mapperProxyFactory.newInstance(sqlSession);
}
public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },
mapperProxy);
}

新增或獲取Mapper都在MapperRegistry類中實現,Mapper的註冊必須是介面且不能重複,Mapper先註冊knownMappers中,然後通過MapperAnnotationBuilder來進行解析,如果解析異常則從knownMappers中移除當前Mapper。由MapperProxyFactory來獲取Mapper例項是通過JDK自帶的動態代理實現的。通過代理後所有Mapper的方法呼叫時都會呼叫MapperProxy的invoke方法,但是並不是任何方法都需要執行呼叫代理物件來進行執行,例如Object中通用的方法就無需執行。

2. MapperMethod的執行
由MapperProxy原始碼可知invoke方法的執行是MapperMethod實現的,先從快取中獲取MapperMethod,獲取不到再重新生成:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  if (Object.class.equals(method.getDeclaringClass())) {
    return method.invoke(this, args);
  } else if (isDefaultMethod(method)) {
    return invokeDefaultMethod(proxy, method, args);
  }
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

MapperMethod中的execute執行方法:
public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  switch (command.getType()) {
    case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      break;
    }
    case UPDATE: {
      result = rowCountResult(sqlSession.update(command.getName(), param));
    }
    case DELETE: {
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    }
    case SELECT:
      if (method.returnsVoid() && method.hasResultHandler()) {
    executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else if (method.returnsCursor()) {
        result = executeForCursor(sqlSession, args);
      } else {
        result = sqlSession.selectOne(command.getName(), param);
      }
    case FLUSH:
      result = sqlSession.flushStatements();
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }
  return result;
}

MapperMethod中存在SqlCommand靜態內部類,通過SqlCommand來判斷是CRUD型別的哪種,然後根據型別來呼叫SqlSession中的執行方法。由原始碼分析可知無論選擇MyBatis的哪種執行流程,其底層實現都是由SqlSession來完成。

MyBatis中的別名是用來簡寫輸入輸出型別的全路徑的定義,存在兩種方式來註冊別名:
1)XML方式:<typeAlias alias="User" type="XXX.User"/>                                                                                                  2)Annotation方式:@Alias("user")public class User{ }

1. TypeAlias的解析註冊
別名的處理是通過XMLConfigBuilder中的typeAliasesElement(root.evalNode("typeAliases"))來實現的,其實現原始碼如下所示:
private void typeAliasesElement(XNode parent) {
  for (XNode child : parent.getChildren()) {
    if ("package".equals(child.getName())) {
      String typeAliasPackage = child.getStringAttribute("name");
      configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
    } else {
      if (alias == null) {
         typeAliasRegistry.registerAlias(clazz);
      } else {
         typeAliasRegistry.registerAlias(alias, clazz);
      }
    }
  }
}

別名的解析最終都是註冊到TypeAliasRegistry的Map<String, Class<?>> TYPE_ALIASES = new HashMap<>():
public void registerAlias(Class<?> type) {
  String alias = type.getSimpleName();
  Alias aliasAnnotation = type.getAnnotation(Alias.class);
  if (aliasAnnotation != null) {
    alias = aliasAnnotation.value();
  } 
  registerAlias(alias, type); // TYPE_ALIASES.put(alias.toLowerCase(Locale.ENGLISH), value);
}
由原始碼可知如果不存在別名則取類名為別名,如果存在Alias註解則取Alias註解的Value值,即上文的user值。在Configuration和
TypeAliasRegistry中都有預設的別名註冊實現,可直接在專案中使用:
public TypeAliasRegistry() {
  registerAlias("string", String.class);
  // ......
}

2. TypeHandler的解析註冊
型別解析器是通過XMLConfigBuilder中typeHandlerElement(root.evalNode("typeHandlers"))實現的,其實現原始碼如下所示:  
private void typeHandlerElement(XNode parent) throws Exception {
  for (XNode child : parent.getChildren()) {
    if ("package".equals(child.getName())) {
      String typeHandlerPackage = child.getStringAttribute("name");
      typeHandlerRegistry.register(typeHandlerPackage); // 忽略內部類,介面和抽象類以及package-info.java類
      // Java類的註解:typeHandlerClass.getAnnotation(MappedTypes.class); 
    } else {
      if (javaTypeClass != null) {
        if (jdbcType == null) {
          typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
      // JDBC類的註解:typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
        } else {
          typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
        }
      } else {
       typeHandlerRegistry.register(typeHandlerClass);
      }
    }
  }
}

型別解析器的解析方式多種,最終都是註冊到TypeHandlerRegistry的對應的Map中:
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<>();
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<>();
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
在TypeHandlerRegistry中有預設的型別解析器註冊實現,可直接在專案中使用:
public TypeHandlerRegistry() {
  register(Boolean.class, new BooleanTypeHandler());
  // ......
}

3. 總結
MyBatis提供的TypeHandler<T>介面存在4個方法:引數的設定,從儲存過程中取值,根據索引號或名稱從ResultSet結果集中取值。可通過Collections.unmodifiableCollection(ALL_TYPE_HANDLERS_MAP.values())來確保TypeHandler的獲取是執行緒安全的。在實際運用中可以使用自定義TypeHandler來處理列舉值的轉換或者狀態值的轉換,其實現只需要繼承BaseTypeHandler類,實現其對應方法即可。MyBatis對於SqlRunner的執行是通過JDK底層的JDBC來實現的,但是Parameters引數的設定或者結果集ResultSet的獲取都是通過TypeHandler來實現的。

所有的TypeAlias都需要註冊到註冊機TYPE_ALIASES中,對於TypeAlias的解析首先將key轉成小寫,然後從TYPE_ALIASES中取得對應的Class,如果獲取不到則將key直接轉成Class,這也是為什麼使用java.lang.Integer或int都可以的邏輯。實際運用中如果需要處理頻繁獲取列舉值的操作,可以在類初始化時將對應的列舉值key-value存進靜態私有變數HashMap中,從記憶體HashMap獲取值可以提高效能。

1. 資料來源工廠 
MyBatis有提供3種內建的資料來源型別:UNPOOLED,POOLED和JNDI。
public interface DataSourceFactory {
  // 設定屬性,被XMLConfigBuilder所呼叫
  void setProperties(Properties props);
  // 生產資料來源,直接得到javax.sql.DataSource
  DataSource getDataSource();
}

實際應用中,通常是使用C3P0和Druid等第三方的連線池工具包來管理資料庫的連線,很少使用MyBatis自帶的實現。如果使用也通常是使用POOLED的方式來進行連線的管理,如果是使用UNPOOLED和JNDI的話,需要注意其屬性值的配置分別是以env.和driver.開頭才行。

2. MyBatis連線池實現
MyBatis有提供自己的POOLED連線池實現,通過結合PoolState類中的idleConnections和activeConnections屬性,以及對其中的方法通過使用synchronized,wait和notifyAll來進行執行緒安全性互動處理的。其對Connection連線的有效性判斷是通過statement.executeQuery("NO PING QUERY SET")是否存在異常來判斷的。

3. MyBatis的事務處理
MyBatis的Transaction介面包裝了資料庫的連線,並提供commit,rollback和close方法。MyBatis中有提供兩種事務型別管理器,其型別是:TYEP=[JDBC|MANAGED],通常單純的select是不存在事務處理的【除非引入Spring框架,強行補上事務】。

JdbcTransaction和ManagedTransaction【託管事務通常交由Spring框架處理】的成員變數都包括:TransactionIsolationLevel,Connection和DataSource。JdbcTransaction還擁有成員變數autoCommmit來表示是否自動提交事務,ManagedTransaction擁有closeConnection來表示是否關閉連線。MyBatis有提供JdbcTransactionFactory和ManagedTransactionFactory來獲取配置資訊和生成相關事務物件。

4. 事務隔離級別:
  NONE(Connection.TRANSACTION_NONE),
  READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
  READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
  REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
  SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);

Jdbc事務是直接利用JDBC的commit和rollback;它依賴於從資料來源得到的連線來管理事務範圍;直到getConnection()被呼叫才會產生連線,這是種延遲載入;如果autocommit的狀態是on,那麼commit和rollback會被直接忽略。ManagedTransaction託管事務是將事務交給容器來管理,它從不提交或者回滾連線,它會讓容器來管理整個事務的生命週期,例如Spring等。預設情況下連線是關閉的。

總結:MyBatis對資料來源,連線和事務實現都是比較簡單粗暴的,實際運用通常是通過Spring+Druid來進行精細化處理的。

1. MyBatis的快取概述

MyBatis會為每個名稱空間建立快取例項,每個快取的實現的建構函式必須接收String型別的cache id,Mybatis也會將名稱空間作為Id傳遞給建構函式,MyBatis也有提供SPI給cache提供者。MyBatis在CacheKey中通過List來儲存所需要更新的物件,但實際上List中儲存的是物件的雜湊碼,如果在同時將多個物件加入列表中時發生了雜湊碰撞,此時會根據equals方法來嚴格區分每個物件是否一致。

MyBatis有提供多種簡單的快取實現方案,差不多都是通過decorators模式裝飾後使用,其資料結構基本是List和Map。在眾多的快取實現中,TransactionalCache是值得關注的,TransactionalCache是第二級快取事務緩衝區,它會儲存會話期間要新增到第二級快取中的所有快取項。當呼叫commit或回滾會話時,將把Entries傳送到快取。增加了對阻塞快取的支援,因此任何返回快取丟失的get()後面都跟著put(),這樣與key相關聯的任何鎖都可以被釋放。MyBatis有提供簡單事務快取管理器來管理事務快取,最終被CachingExecutor使用。

在Session包中提供了本地快取機制來防止迴圈引用和加速重複巢狀查詢。本地快取機制預設值為SESSION,通常這種情況下會快取一個會話中執行的所有查詢。若將本地快取機制設定值為STATEMENT,本地會話僅用在語句執行上,對相同SqlSession的不同調用將不會共享資料。MyBatis提供兩種快取,預設開啟的SqlSession級別的一級快取和Namespace Mapper級別的二級快取。

SqlSession快取中存在SESSION【預設】和STATEMENT兩個選項,SESSION在MyBatis會話中執行的所有語句時共享這個快取,STATEMENT只快取對當前執行的這個Statement有效。SqlSession快取的作用域僅限當前SqlSession,SqlSession結束後會將快取Clear,在SqlSession中首次查詢資料時會將結果快取,在進行Update,Delete,Insert操作後會將快取清空。

在SqlSession是用Executor執行查詢操作時,會將結果儲存到Executor的快取PerpetualCache localCache中,原始碼如下所示:
private <E> List<E> queryFromDatabase(查詢所需入參) throws SQLException {
   list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
   localCache.putObject(key, list); // 將查詢到的結果快取,當前key是CacheKey
   localOutputParameterCache.putObject(key, parameter); // 儲存過程的處理
   return list;
}   //  實際上在PerpetualCache之前,Cache會被裝飾處理,得到最終的包裝物件

Namespace Mapper快取需要設定全域性引數cacheEnabled value=true;也需要在當前Namespace中設定cacheType來開啟當前Namespace的二級快取,在當前Namespace中使用useCache=false來禁用二級快取,預設flushCache=true重新整理清空快取。其作用域在當前Namespace,執行方式同SqlSession快取類似。二級快取的開啟是通過CachingExecutor代理包裝Executor來實現的,對於資料的獲取首先在CachingExecutor中查詢,如果獲取不到再通過Executor delegate在SqlSession中查詢。

其實現原理如下所示:
public <E> List<E> query(ms, parameterObject, rowBounds, resultHandler, CacheKey key, boundSql){
  Cache cache = ms.getCache();  // Cache是全域性變數,會被多個SqlSession共享
  if (cache != null) { // 簡單流程:二級快取》SqlSession快取》資料庫操作
    if (ms.isUseCache() && resultHandler == null) {
      List<E> list = (List<E>) tcm.getObject(cache, key); // 注意TransactionalCache中的裝飾順序
      // SynchronizedCache -> LoggingCache -> SerializedCache -> LruCache -> PerpetualCache
      if (list == null) { // 快取獲取不到值,通過SqlSession查詢後儲存到二級快取
        list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); // tcm:TransactionalCacheManager
      }
      return list;   //  二級快取中獲取到值就直接返回,在分散式環境下很有可能是髒資料
    }
  }
  return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

Mapper配置檔案中的cache節點會被解析到XMLMapperBuilder例項中的builderAssistant屬性中的currentCache值裡。Cache物件中的eviction引數中的演算法:
(1)Least Recently Used【LRU】最近最少使用演算法:如果快取中的容量已滿,將快取中最近最少被使用的快取記錄清除掉,然後新增新記錄,注意同Least Frequently Used【LFU,MyBatis未提供的演算法】的區別;
(2)First in first out【FIFO】先進先出演算法:如果快取中的容量已滿,將最先進入快取中的資料清除掉;
(3)Scheduled指定時間間隔清空演算法:該演算法會以指定的某一個時間間隔將Cache快取中的資料清空;
(4)Soft軟引用:JVM處理,移除基於垃圾回收器狀態和軟引用規則的物件;
(5)Weak弱引用:JVM處理,更積極地移除基於垃圾收集器狀態和弱引用規則的物件。

2. MyBatis的外掛概述

MyBatis的外掛是在執行時進行攔截處理的,Plugin採用了動態代理來尋找需要攔截的方法,呼叫Interceptor.intercept來插入對應的邏輯。實際應用中如果要實現定製的MyBatis功能外掛,只需要實現Mybatis的Intercepter介面方法即可。在InterceptorChain類中存在攔截器鏈List<Interceptor> interceptors = new ArrayList<Interceptor>(),通過pluginAll(target)方法來迴圈呼叫每個Interceptor.plugin方法來實現外掛功能。

在Plugin中獲取到的要改變行為的類通常是:ParameterHandler,ResultSetHandler,StatementHandler和Executor。由原始碼可知道在session包中的Configuration類中,在建立ParameterHandler,ResultSetHandler,StatementHandler和Executor【注意不同型別的執行器的建立】時,會發現如下所示程式碼interceptorChain.pluginAll(XXX)來觸發外掛的執行。如果要讓其它的攔截也生效,可以通過覆蓋Plugin.wrap方法來達到攔截其它類的功能。

總結:在SqlSession執行底層資料的操作時,會通過Configuration來建立ParameterHandler,ResultSetHandler等物件來進行輔助處理,在實際運用中使用最多的外掛應該是PageHelper分頁外掛。MyBatis的快取的實現都比較簡單,都是對HashMap的簡單運用,MyBatis本身提供的多種快取實現運用了裝飾者模式,對於自定義快取的實現只需要實現MyBatis提供的Cache介面即可。為保證資料的實時有效性,避免引起髒資料尤其是在分散式的環境下,通常情況下不會開啟二級快取,亦或是結合第三方快取Ehcache或者Redis來實現。個人感覺MyBatis和JPA的快取在網際網路高併發和分散式的場景下都顯得很雞肋,就本人而言覺得應該儘量避免使用,可能技術有限理解不到位。

相關推薦

MyBatis初始別名事務外掛快取執行原理總結

MyBatis的初始化的過程其實就是解析配置檔案和初始化Configuration的過程。 1.MyBatis的通用執行流程 參照GOF提出的23種設計模式,可以看出MyBatis的執行流程算是種通用的模板模式,其實現過程也簡單: 首先通過相關的資原始檔配置的載入得到對應的

spring容器初始bean之後或銷燬bean之前能做的操作

通過 <bean> 標籤 init-method  初始化bean之後呼叫的方法 destroy-method  銷燬bean之前呼叫的操作方法 <bean id="initQuartzJob" class="com.upinc

建構函式的初始初始列表還是大括號裡好那種效率高

complex (double r = 0, double i = 0): re (r), im (i){}: re (r), im (i)  初始化列表   放在大括號中實現  re和im的初始化  也可,只是效率差

MyBatis初始

provider nic catch 上大 src session tca parsec idata 1. 準備工作 為了看清楚MyBatis的整個初始化過程,先創建一個簡單的Java項目,目錄結構如下圖所示: 1.1 Product 產品實體類 public clas

MyBatis初始過程解析----廣西11選5平臺出租源碼解析

solver 原本 file code 1.3 lds elements ret variables 準備工作 為了看清楚廣西11選5平臺出租的 Q1446595067 整個初始化過程,先創建一個簡單的Java項目,目錄結構如下圖所示: 1.1 Product 產品實體類

大資料之scala(二) --- 對映元組簡單類內部類物件ObjectIdea中安裝scala外掛trait特質[介面]包的匯入

一、對映<Map> ----------------------------------------------------- 1.建立一個不可變的對映Map<k,v> ==> Map(k -> v) scala> val map

Mybatis原始碼解析之Mybatis初始過程

一、搭建一個簡單的Mybatis工程 為了瞭解Mybatis的初始化過程,這裡需要搭建一個簡單的Mybatis工程操作資料庫,工程結構如下: 一個UserBean.java private int id; private String user

MySql資料庫在表中新增新欄位設定主鍵設定外來鍵欄位移動位置以及修改資料庫後如何進行部署維護的總結

1,為當前已有的表新增新的欄位 alter table student add studentName varchar(20) not null; 2,為當前已有的表中的欄位設定為主鍵自增 alter table student add constraint PK_studentId primaryKe

springcloud系列—Hystrix—第3章-3: Hystrix 服務降級(fallback)與異常處理Hystrix依賴隔離(命令名稱-分組執行緒池)、請求快取與清除快取、斷路器

資料參考:《Spring Cloud 微服務實戰》 目錄 服務降級 在HystrixCommand中可以通過過載getFallback()方法來實現服務降級邏輯。 在 HystrixObservableCommand 實現得 Hystrix 命令中,我們可以通過過載 resumenW

細談遞迴備忘錄遞迴動態規劃三種演算法思想執行原理

大家都知道,數值稍大的遞迴執行時間對於開發者來說就是場災難,我們總是想方設法在優化遞迴,或者說不用遞迴,此文中從空間時間角度詳細剖析以上三種演算法的區別,以及執行原理,以斐波那契數為例, 程式語言java 此處為程式碼 package test

《深入理解mybatis原理Mybatis初始機制詳解 侵立刪

對於任何框架而言,在使用前都要進行一系列的初始化,MyBatis也不例外。本章將通過以下幾點詳細介紹MyBatis的初始化過程。   1.MyBatis的初始化做了什麼   2. MyBatis基於XML配置檔案建立Configuration物件的過程 &nb

架構框架模式模組、元件、外掛的含義區別

架構、框架、模式、模組、元件、外掛、控制元件、中介軟體的含義和區別。經常看到這些概念,但是有些含糊,花點兒功夫整理一下,結果還是有些地方理解的不透徹,先將整理的內容寫下來,以供交流。左側英文欄中有些單詞被分成了兩半,放到了兩行中,看的時候需要注意。歡迎各路大蝦、大牛、大神

MyEclipse專案使用自帶Git外掛新增Git支援(SSH方式)

本文,主要總結使用MyEclipse自帶的Git外掛,對MyEclipse專案新增Git支援。 同時,登入遠端伺服器,使用的是SSH方式。 主要步驟如下: 一、建立本地的Git倉庫。 1、右鍵專案,依次點選 Team-->Share Project 。 2、有C

MyEclipse 配置maven專案pom.xml設定 上傳專案到nexus私服通過中轉倉庫下載外掛以及nexus的配置

1。安裝官方nexus的開源版本 下載安裝可以參照http://www.th7.cn/system/win/201609/179882.shtml 因為不是專業版本,有好多功能限制,不過個人用用夠了。 2。安裝maven 配置相對簡單:官方下載後解壓到安裝目錄即可。再在

《深入理解mybatis原理2》 Mybatis初始機制詳解

and 文件創建 builder模式 處理器 XML iou info pro 兩種 《深入理解mybatis原理》 Mybatis初始化機制詳解 對於任何框架而言,在使用前都要進行一系列的初始化,MyBatis也不例外。本章將通過以下幾點詳細介紹MyBatis的初始化過

講真這兩個IDE外掛可以讓你寫出質量槓槓的程式碼

昨晚躺在床上看《拯救大兵瑞恩》的時候,不由得感嘆道:“斯皮爾伯格的電影質量真高,片頭真實地還原了二戰的殘酷性。”看完後,我的精神異常的亢奮,就想寫篇文章來幫助大家提高一下程式碼的質量,畢竟二哥也是一個有態度的作者啊,向斯皮爾伯格學習。 程式碼質量的重要性就不用我來贅述了,大家都懂。沒有人喜歡糟糕的

初窺Mybatis初始

# 引言 這篇文章呢,主要是講Mybtais的兩種方式的原始碼剖析:傳統方式以及Mapper代理方式,初次探索Mybatis原始碼,希望大佬勿噴並且指正錯誤,謝謝! 個人部落格:www.fqcoder.cn # 一、Mybatis架構原理 ### 1.架構圖 首先,讓我們來看看下面這張圖: ![i

講真這兩款idea外掛能治癒你英語不好的病

時不時就有小夥伴問我,“二哥,能推薦一款 IDE 嗎?”你看這話問的,現在搞 Java 的不都在用 Intellij IDEA 嗎,還用得著推薦(我已經和 Eclipse 分手了)。然後小夥伴又說,“二哥,IDEA 支援中文嗎?我英語不太好。”你看這話問的,搞程式設計的,英語不好是硬傷啊! 不過,隨著 IDE

答應我用了這個jupyter外掛別再重複造輪子了

# 1 簡介   在使用`Python`、`R`等完成日常任務的過程中,可能會經常書寫同樣或模式相近的同一段程式碼,譬如每次使用`matplotlib`繪製圖像的時候可以在開頭新增下面兩行程式碼來解決中文亂碼等顯示問題: ```Python plt.rcParams['font.sans-serif']