最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringCache框架加載/攔截原理詳解

 更新時(shí)間:2019年04月28日 09:26:17   作者:莊杰森  
這篇文章主要介紹了SpringCache框架加載/攔截原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

官網(wǎng)文檔

背景

項(xiàng)目A中需要多數(shù)據(jù)源的實(shí)現(xiàn),比如UserDao.getAllUserList() 需要從readonly庫(kù)中讀取,但是UserDao.insert() 需要插入主(寫)庫(kù)

就需要在dao層的方法調(diào)用上面添加注解!

了解后知道-接口通過(guò)jdk代理(mybatis的mapper接口就是通過(guò)jdk代理動(dòng)態(tài)生成的-> MapperFactoryBean.class )的,沒辦法被aop的攔截(注解配置的攔截)

//dao
  @Pointcut("@annotation(com.kaola.cs.data.common.aspect.DataSourceSelect)")
  public void dao() {
  }

然后碰巧接觸了項(xiàng)目B,使用了SpringCache模塊,但是Spring的Cache模塊居然能夠攔截(spring-cache也是通過(guò)注解攔截?。。?

引起了我的興趣,就把源碼翻了一遍

SpringCache的用途

與 mybatis 對(duì)比

1.  spring-cache 是基于spring的方法級(jí)別的,也就是說(shuō)你方法做了啥不關(guān)心,它只負(fù)責(zé)緩存方法結(jié)果

mybatis 的緩存(CachingExecutor / BaseExecutor) 是基于數(shù)據(jù)庫(kù)查詢結(jié)果的緩存

 2.  spring-cache 可以配置各種類型的緩存介質(zhì)(redis , ehcache , hashmap, 甚至db等等) -> 它僅僅是提供接口和默認(rèn)實(shí)現(xiàn),可以自己拓展

mybatis 的緩存是hashmap,單一?。owb

SpringCache 的配置

1.注解(spring-boot) 2.xml配置

這里只講注解,但是初始化的類都是一樣的!?。?br />

定義 CacheConfigure.java 就能直接使用

@EnableCaching
@Configuration
public class CacheConfigure extends CachingConfigurerSupport {
  @Override
  @Bean
  public CacheManager cacheManager() {
    SimpleCacheManager result = new SimpleCacheManager();
    List<Cache> caches = new ArrayList<>();
    caches.add(new ConcurrentMapCache("testCache"));
    result.setCaches(caches);
    return result;
  }

  @Override
  @Bean
  public CacheErrorHandler errorHandler() {
    return new SimpleCacheErrorHandler();
  }

}

通過(guò) @EnableCaching 注解可以找到 Spring-Cache 初始化的核心類

ProxyCachingConfiguration.java

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

 @Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
 BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
 advisor.setCacheOperationSource(cacheOperationSource());
 advisor.setAdvice(cacheInterceptor());
 if (this.enableCaching != null) {
  advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
 }
 return advisor;
 }

 @Bean
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 public CacheOperationSource cacheOperationSource() {
 return new AnnotationCacheOperationSource();
 }

 @Bean
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 public CacheInterceptor cacheInterceptor() {
 CacheInterceptor interceptor = new CacheInterceptor();
 interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
 interceptor.setCacheOperationSource(cacheOperationSource());
 return interceptor;
 }

}

通過(guò)注解,把3個(gè)類的bean 實(shí)例化: BeanFactoryCacheOperationSourceAdvisor 、CacheOperationSource 、CacheInterceptor
說(shuō)一下這3個(gè)類的作用

BeanFactoryCacheOperationSourceAdvisor.java

/*
 BeanFactoryCacheOperationSourceAdvisor 繼承了 AbstractBeanFactoryPointcutAdvisor
 在spring 中的效果就是,在每個(gè)bean的初始化時(shí) (每個(gè)bean都會(huì)被加載成 advised 對(duì)象 -> 有 targetSource 和 Advisor[] 數(shù)組)
 每個(gè)bean被調(diào)用方法的時(shí)候都是先遍歷advisor的方法,然后在調(diào)用原生bean(也就是targetSource)的方法,實(shí)現(xiàn)了aop的效果

 bean 加載的時(shí)候 BeanFactoryCacheOperationSourceAdvisor 的 getPointcut()-> 也就是 CacheOperationSourcePointcut 就會(huì)被獲取,然后調(diào)用 
 CacheOperationSourcePointcut.matches()方法, 用來(lái)匹配對(duì)應(yīng)的bean 
 假設(shè)bean 在 BeanFactoryCacheOperationSourceAdvisor 的掃描中 matchs() 方法返回了true
 結(jié)果就是 
  在每個(gè)bean的方法被調(diào)用的時(shí)候 CacheInterceptor 中的 invoke() 方法就會(huì)被調(diào)用 

 總結(jié):
  spring-cache 也完成了aop一樣的實(shí)現(xiàn)(spring-aop也是這樣做的)

 重點(diǎn)就是在 CacheOperationSourcePointcut.matchs() 方法中,怎么匹配接口的了 這里先不說(shuō)后面具體介紹!?。。?

*/
public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

 @Nullable
 private CacheOperationSource cacheOperationSource;

 private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
 @Override
 @Nullable
 protected CacheOperationSource getCacheOperationSource() {
  return cacheOperationSource;
 }
 };


 /**
 * Set the cache operation attribute source which is used to find cache
 * attributes. This should usually be identical to the source reference
 * set on the cache interceptor itself.
 */
 public void setCacheOperationSource(CacheOperationSource cacheOperationSource) {
 this.cacheOperationSource = cacheOperationSource;
 }

 /**
 * Set the {@link ClassFilter} to use for this pointcut.
 * Default is {@link ClassFilter#TRUE}.
 */
 public void setClassFilter(ClassFilter classFilter) {
 this.pointcut.setClassFilter(classFilter);
 }

 @Override
 public Pointcut getPointcut() {
 return this.pointcut;
 }

}

CacheOperationSource.java 是個(gè)接口

實(shí)現(xiàn)類是 -> AnnotationCacheOperationSource.java 重點(diǎn)是父類 -> AbstractFallbackCacheOperationSource.java

講解一下:

代碼量很少,主要是 attributeCache 的封裝使用,通過(guò)把 method - CacheOperation

然后在 CacheInterceptor.invoke() 的時(shí)候通過(guò)invocation 獲取到 method-class 然后調(diào)用CacheOperationSource.getCacheOperations() 獲取到 CacheOperation
CacheOperation 其實(shí)就是觸發(fā)對(duì)應(yīng)spring-cache 注解的操作-獲取緩存的實(shí)現(xiàn)了

public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {

 /**
 * Canonical value held in cache to indicate no caching attribute was
 * found for this method and we don't need to look again.
 */
 private static final Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();


 /**
 * Logger available to subclasses.
 * <p>As this base class is not marked Serializable, the logger will be recreated
 * after serialization - provided that the concrete subclass is Serializable.
 */
 protected final Log logger = LogFactory.getLog(getClass());

 /**
 * Cache of CacheOperations, keyed by method on a specific target class.
 * <p>As this base class is not marked Serializable, the cache will be recreated
 * after serialization - provided that the concrete subclass is Serializable.
 */
 private final Map<Object, Collection<CacheOperation>> attributeCache = new ConcurrentHashMap<>(1024);


 /**
 * Determine the caching attribute for this method invocation.
 * <p>Defaults to the class's caching attribute if no method attribute is found.
 * @param method the method for the current invocation (never {@code null})
 * @param targetClass the target class for this invocation (may be {@code null})
 * @return {@link CacheOperation} for this method, or {@code null} if the method
 * is not cacheable
 */
 @Override
 @Nullable
 public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
 if (method.getDeclaringClass() == Object.class) {
  return null;
 }

 Object cacheKey = getCacheKey(method, targetClass);
 Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);

 if (cached != null) {
  return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
 }
 else {
  Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
  if (cacheOps != null) {
  if (logger.isTraceEnabled()) {
   logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
  }
  this.attributeCache.put(cacheKey, cacheOps);
  }
  else {
  this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
  }
  return cacheOps;
 }
 }

 /**
 * Determine a cache key for the given method and target class.
 * <p>Must not produce same key for overloaded methods.
 * Must produce same key for different instances of the same method.
 * @param method the method (never {@code null})
 * @param targetClass the target class (may be {@code null})
 * @return the cache key (never {@code null})
 */
 protected Object getCacheKey(Method method, @Nullable Class<?> targetClass) {
 return new MethodClassKey(method, targetClass);
 }

 @Nullable
 private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
 // Don't allow no-public methods as required.
 if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
  return null;
 }

 // The method may be on an interface, but we need attributes from the target class.
 // If the target class is null, the method will be unchanged.
 Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

 // First try is the method in the target class.
 Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
 if (opDef != null) {
  return opDef;
 }

 // Second try is the caching operation on the target class.
 opDef = findCacheOperations(specificMethod.getDeclaringClass());
 if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
  return opDef;
 }

 if (specificMethod != method) {
  // Fallback is to look at the original method.
  opDef = findCacheOperations(method);
  if (opDef != null) {
  return opDef;
  }
  // Last fallback is the class of the original method.
  opDef = findCacheOperations(method.getDeclaringClass());
  if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
  return opDef;
  }
 }

 return null;
 }


 /**
 * Subclasses need to implement this to return the caching attribute for the
 * given class, if any.
 * @param clazz the class to retrieve the attribute for
 * @return all caching attribute associated with this class, or {@code null} if none
 */
 @Nullable
 protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);

 /**
 * Subclasses need to implement this to return the caching attribute for the
 * given method, if any.
 * @param method the method to retrieve the attribute for
 * @return all caching attribute associated with this method, or {@code null} if none
 */
 @Nullable
 protected abstract Collection<CacheOperation> findCacheOperations(Method method);

 /**
 * Should only public methods be allowed to have caching semantics?
 * <p>The default implementation returns {@code false}.
 */
 protected boolean allowPublicMethodsOnly() {
 return false;
 }

}

!?。?!  CacheOperationSourcePointcut.java 的 matchs() 方法

用來(lái)判斷類是不是符合spring-cache 攔截條件 也就是 @Cachable @CachePut 等等的注解怎么識(shí)別的地方

經(jīng)過(guò)跟蹤代碼發(fā)現(xiàn)是 AnnotationCacheOperationSource.findCacheOperations() 調(diào)用的

省略部分代碼....

public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {


 private final Set<CacheAnnotationParser> annotationParsers;


 @Override
 @Nullable
 protected Collection<CacheOperation> findCacheOperations(Class<?> clazz) {
 return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
 }

 @Override
 @Nullable
 protected Collection<CacheOperation> findCacheOperations(Method method) {
 return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
 }

 /**
 * Determine the cache operation(s) for the given {@link CacheOperationProvider}.
 * <p>This implementation delegates to configured
 * {@link CacheAnnotationParser CacheAnnotationParsers}
 * for parsing known annotations into Spring's metadata attribute class.
 * <p>Can be overridden to support custom annotations that carry caching metadata.
 * @param provider the cache operation provider to use
 * @return the configured caching operations, or {@code null} if none found
 */
 @Nullable
 protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
 Collection<CacheOperation> ops = null;
 for (CacheAnnotationParser annotationParser : this.annotationParsers) {
  Collection<CacheOperation> annOps = provider.getCacheOperations(annotationParser);
  if (annOps != null) {
  if (ops == null) {
   ops = annOps;
  }
  else {
   Collection<CacheOperation> combined = new ArrayList<>(ops.size() + annOps.size());
   combined.addAll(ops);
   combined.addAll(annOps);
   ops = combined;
  }
  }
 }
 return ops;
 }
}

然后就是注解的解析方法 SpringCacheAnnotationParser.java

代碼很簡(jiǎn)單-就不多說(shuō)了

@Nullable
 private Collection<CacheOperation> parseCacheAnnotations(
  DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {

 Collection<? extends Annotation> anns = (localOnly ?
  AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
  AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
 if (anns.isEmpty()) {
  return null;
 }

 final Collection<CacheOperation> ops = new ArrayList<>(1);
 anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
  ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
 anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
  ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
 anns.stream().filter(ann -> ann instanceof CachePut).forEach(
  ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
 anns.stream().filter(ann -> ann instanceof Caching).forEach(
  ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
 return ops;
 }
 

總結(jié)

1.spring-cache 實(shí)現(xiàn)了 AbstractBeanFactoryPointcutAdvisor 提供 CacheOperationSourcePointcut (PointCut) 作切點(diǎn)判斷,提供 CacheInterceptor (MethodInterceptor) 作方法攔截

2.spring-cache 提供 CacheOperationSource 作為 method 對(duì)應(yīng) CacheOperation(緩存操作) 的查詢和加載

3.spring-cache 通過(guò) SpringCacheAnnotationParser 來(lái)解析自己定義的 @Cacheable @CacheEvict @Caching 等注解類
所以 spring-cache 不使用 aspectj 的方式,通過(guò) CacheOperationSource.getCacheOperations() 方式可以使jdk代理的類也能匹配到

jdk代理的類的匹配

代碼類在 CacheOperationSource.getCacheOperations()

重點(diǎn)在于 targetClass 和 method ,如果是對(duì)應(yīng)的 dao.xxx() 就能matchs() 并且攔截

CacheInterceptor -> CacheAspectSupport.execute() 方法

// 代碼自己看吧。也很簡(jiǎn)單 -> 結(jié)果就是spring-cache 也可以攔截到mybatis的dao層接口,進(jìn)行緩存

 @Nullable
 protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
 // Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
 if (this.initialized) {
  Class<?> targetClass = getTargetClass(target);
  CacheOperationSource cacheOperationSource = getCacheOperationSource();
  if (cacheOperationSource != null) {
  Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
  if (!CollectionUtils.isEmpty(operations)) {
   return execute(invoker, method,
    new CacheOperationContexts(operations, method, args, target, targetClass));
  }
  }
 }

 return invoker.invoke();
 }

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • idea中如何使用(Undo Commit...)

    idea中如何使用(Undo Commit...)

    這篇文章主要介紹了idea中如何使用(Undo Commit...)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • java調(diào)用shell命令并獲取執(zhí)行結(jié)果的示例

    java調(diào)用shell命令并獲取執(zhí)行結(jié)果的示例

    今天小編就為大家分享一篇java調(diào)用shell命令并獲取執(zhí)行結(jié)果的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • Java中的TreeSet源碼解讀

    Java中的TreeSet源碼解讀

    這篇文章主要介紹了Java中的TreeSet源碼解讀,TreeSet 是一個(gè) 有序集合,它擴(kuò)展了 AbstractSet 類并實(shí)現(xiàn)了 NavigableSet 接口,對(duì)象根據(jù)其自然順序以升序排序和存儲(chǔ),該 TreeSet 中使用 平衡樹,更具體的一個(gè) 紅黑樹,需要的朋友可以參考下
    2023-09-09
  • 解決Maven的pom.xml中設(shè)置repository不起作用問(wèn)題

    解決Maven的pom.xml中設(shè)置repository不起作用問(wèn)題

    這篇文章主要介紹了解決Maven的pom.xml中設(shè)置repository不起作用問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Java數(shù)據(jù)結(jié)構(gòu)和算法之冒泡,選擇和插入排序算法

    Java數(shù)據(jù)結(jié)構(gòu)和算法之冒泡,選擇和插入排序算法

    這篇文章主要為大家介紹了Java冒泡,選擇和插入排序算法 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-01-01
  • java實(shí)現(xiàn)單鏈表倒轉(zhuǎn)的方法

    java實(shí)現(xiàn)單鏈表倒轉(zhuǎn)的方法

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)單鏈表倒轉(zhuǎn)的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Java was started but returned exit code=13問(wèn)題解決案例詳解

    Java was started but returned exit code=13問(wèn)題解決案例詳解

    這篇文章主要介紹了Java was started but returned exit code=13問(wèn)題解決案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • java編程Reference核心原理示例源碼分析

    java編程Reference核心原理示例源碼分析

    這篇文章主要為大家介紹了java編程Reference的核心原理以及示例源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-01-01
  • Spring BeanDefinition使用介紹

    Spring BeanDefinition使用介紹

    BeanDefinition是Spring框架中非常核心的概念,BeanDefinition是定義Bean的配置元信息接口,Spring根據(jù)BeanDefinition來(lái)定義Bean對(duì)象,簡(jiǎn)單說(shuō)就是對(duì)Bean信息的定義
    2023-01-01
  • java中json-diff簡(jiǎn)單使用及對(duì)象是否一致詳解

    java中json-diff簡(jiǎn)單使用及對(duì)象是否一致詳解

    這篇文章主要為大家介紹了java中json-diff簡(jiǎn)單使用及對(duì)象是否一致對(duì)比詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03

最新評(píng)論

宜丰县| 赤城县| 米泉市| 深水埗区| 宁乡县| 弥渡县| 正定县| 宜黄县| 车险| 通化市| 康保县| 永州市| 永兴县| 枣强县| 丹巴县| 邹平县| 通渭县| 罗江县| 澄江县| 和田市| 平阴县| 东城区| 清丰县| 彩票| 卢氏县| 舟曲县| 白山市| 渑池县| 汽车| 临西县| 满洲里市| 琼中| 汤阴县| 永寿县| 英超| 中阳县| 云浮市| 鄂伦春自治旗| 庐江县| 大姚县| 辉县市|