Heim  >  Artikel  >  Java  >  Grundlegendes zum Spring Boot-Cache-Quellcode

Grundlegendes zum Spring Boot-Cache-Quellcode

不言
不言nach vorne
2018-11-16 15:56:572209Durchsuche

Der Inhalt dieses Artikels befasst sich mit dem Verständnis des Spring Boot-Cache-Quellcodes. Er hat einen gewissen Referenzwert. Ich hoffe, dass er für Sie hilfreich ist.

Ich möchte dem Projekt einen Anwendungscache hinzufügen. Ich habe ursprünglich darüber nachgedacht, wie ich ehcache und springboot integrieren kann, und bin bereit, diese und jene Konfiguration zu konfigurieren. Am Ende muss ich nur drei Dinge tun:

pom-Abhängigkeit

Schreiben Sie eine Ehcache-Konfigurationsdatei

Fügen Sie die Anmerkung @EnableCaching zur Boot-Anwendung hinzu
Das ist es nicht magisch.

POM-Abhängigkeit

<dependency>
            <groupid>net.sf.ehcache</groupid>
            <artifactid>ehcache</artifactid>
            <version>2.10.5</version>
</dependency>

Konfigurationsdatei

<?xml  version="1.0" encoding="UTF-8"?>
<ehcache>
    <!-- 设定缓存的默认数据过期策略 -->
    <defaultcache></defaultcache>
</ehcache>

EnableCaching-Anmerkung zur Anwendung hinzufügen

@SpringBootApplication
@EnableCaching
public class EhCacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(EhCacheApplication.class, args);
    }
}

Dann können Sie Cache-Anmerkungen wie folgt im Code verwenden.

@CachePut(value = "fish-ehcache", key = "#person.id")
    public Person save(Person person) {
        System.out.println("为id、key为:" + person.getId() + "数据做了缓存");
        return person;
    }

    @CacheEvict(value = "fish-ehcache")
    public void remove(Long id) {
        System.out.println("删除了id、key为" + id + "的数据缓存");
    }


    @Cacheable(value = "fish-ehcache", key = "#person.id")
    public Person findOne(Person person) {
        findCount.incrementAndGet();
        System.out.println("为id、key为:" + person.getId() + "数据做了缓存");
        return person;
    }

Es ist sehr praktisch, oder? Lassen Sie uns als nächstes etwas tiefer graben und sehen, wie der Frühling es macht. Es ist hauptsächlich in zwei Teile unterteilt: Der erste ist, was beim Starten getan wird, der zweite ist, was beim Ausführen getan wird, und der dritte ist die Anpassung mit Cache-Komponenten von Drittanbietern

Was wird wann getan? beginnend,

Dies beginnt mit dem @EnableCaching-Tag. Bei Verwendung der Caching-Funktion muss die Annotation @EnableCaching zur Startklasse der Springboot-Anwendung hinzugefügt werden. Dieses Tag führt das

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({CachingConfigurationSelector.class})
public @interface EnableCaching {
    boolean proxyTargetClass() default false;
    AdviceMode mode() default AdviceMode.PROXY;
    int order() default 2147483647;
}
und führt die Klasse CachingConfigurationSelector ein. Diese Klasse ermöglicht die Konfiguration der Cache-Funktion. Diese Klasse fügt zwei Klassen hinzu: AutoProxyRegistrar.java und ProxyCachingConfiguration.java.

  • AutoProxyRegistrar: Implementiert die ImportBeanDefinitionRegistrar-Schnittstelle. Ich verstehe das hier nicht, ich muss weiter lernen.

  • ProxyCachingConfiguration: ist eine Konfigurationsklasse, die drei Beans generiert: BeanFactoryCacheOperationSourceAdvisor, CacheOperationSource und CacheInterceptor.

CacheOperationSource kapselt die Analysearbeit von Cache-Methodensignaturanmerkungen und bildet eine Sammlung von CacheOperations. CacheInterceptor verwendet diesen Sammlungsfilter, um die Cache-Verarbeitung durchzuführen. Die Klasse, die Cache-Anmerkungen analysiert, ist SpringCacheAnnotationParser, und ihre Hauptmethoden lauten wie folgt:

/**
由CacheOperationSourcePointcut作为注解切面,会解析
SpringCacheAnnotationParser.java
扫描方法签名,解析被缓存注解修饰的方法,将生成一个CacheOperation的子类并将其保存到一个数组中去
**/
protected Collection<cacheoperation> parseCacheAnnotations(SpringCacheAnnotationParser.DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
        Collection<cacheoperation> ops = null;
        //找@cacheable注解方法
        Collection<cacheable> cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class);
        if (!cacheables.isEmpty()) {
            ops = this.lazyInit(ops);
            Iterator var5 = cacheables.iterator();

            while(var5.hasNext()) {
                Cacheable cacheable = (Cacheable)var5.next();
                ops.add(this.parseCacheableAnnotation(ae, cachingConfig, cacheable));
            }
        }
        //找@cacheEvict注解的方法
        Collection<cacheevict> evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class);
        if (!evicts.isEmpty()) {
            ops = this.lazyInit(ops);
            Iterator var12 = evicts.iterator();

            while(var12.hasNext()) {
                CacheEvict evict = (CacheEvict)var12.next();
                ops.add(this.parseEvictAnnotation(ae, cachingConfig, evict));
            }
        }
        //找@cachePut注解的方法
        Collection<cacheput> puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class);
        if (!puts.isEmpty()) {
            ops = this.lazyInit(ops);
            Iterator var14 = puts.iterator();

            while(var14.hasNext()) {
                CachePut put = (CachePut)var14.next();
                ops.add(this.parsePutAnnotation(ae, cachingConfig, put));
            }
        }
        Collection<caching> cachings = AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class);
        if (!cachings.isEmpty()) {
            ops = this.lazyInit(ops);
            Iterator var16 = cachings.iterator();

            while(var16.hasNext()) {
                Caching caching = (Caching)var16.next();
                Collection<cacheoperation> cachingOps = this.parseCachingAnnotation(ae, cachingConfig, caching);
                if (cachingOps != null) {
                    ops.addAll(cachingOps);
                }
            }
        }
        return ops;
}</cacheoperation></caching></cacheput></cacheevict></cacheable></cacheoperation></cacheoperation>
Die Analysemethoden, die den vier Anmerkungen Cachable, Caching, CachePut und CachEevict entsprechen, werden alle in der Collection gespeichert .

Was geschieht, wenn die Methode ausgeführt wird?

Bei der Ausführung wird hauptsächlich die CacheInterceptor-Klasse verwendet.

public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
    public CacheInterceptor() {
    }

    public Object invoke(final MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() {
            public Object invoke() {
                try {
                    return invocation.proceed();
                } catch (Throwable var2) {
                    throw new ThrowableWrapper(var2);
                }
            }
        };

        try {
            return this.execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
        } catch (ThrowableWrapper var5) {
            throw var5.getOriginal();
        }
    }
}
Dieser Interceptor erbt die CacheAspectSupport-Klasse und die MethodInterceptor-Schnittstelle. Unter anderem kapselt CacheAspectSupport die Hauptlogik. Zum Beispiel der folgende Absatz.

/**
CacheAspectSupport.java
执行@CachaEvict @CachePut @Cacheable的主要逻辑代码
**/

private Object execute(final CacheOperationInvoker invoker, Method method, CacheAspectSupport.CacheOperationContexts contexts) {
        if (contexts.isSynchronized()) {
            CacheAspectSupport.CacheOperationContext context = (CacheAspectSupport.CacheOperationContext)contexts.get(CacheableOperation.class).iterator().next();
            if (this.isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
                Object key = this.generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
                Cache cache = (Cache)context.getCaches().iterator().next();

                try {
                    return this.wrapCacheValue(method, cache.get(key, new Callable<object>() {
                        public Object call() throws Exception {
                            return CacheAspectSupport.this.unwrapReturnValue(CacheAspectSupport.this.invokeOperation(invoker));
                        }
                    }));
                } catch (ValueRetrievalException var10) {
                    throw (ThrowableWrapper)var10.getCause();
                }
            } else {
                return this.invokeOperation(invoker);
            }
        } else {
            /**
            执行@CacheEvict的逻辑,这里是当beforeInvocation为true时清缓存
            **/
            this.processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT);
            //获取命中的缓存对象
            ValueWrapper cacheHit = this.findCachedItem(contexts.get(CacheableOperation.class));
            List<cacheaspectsupport.cacheputrequest> cachePutRequests = new LinkedList();
            if (cacheHit == null) {
                //如果没有命中,则生成一个put的请求
                this.collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
            }


            Object cacheValue;
            Object returnValue;
            /**
                如果没有获得缓存对象,则调用业务方法获得返回对象,hasCachePut会检查exclude的情况
            **/
            if (cacheHit != null && cachePutRequests.isEmpty() && !this.hasCachePut(contexts)) {
                cacheValue = cacheHit.get();
                returnValue = this.wrapCacheValue(method, cacheValue);
            } else {
                
                returnValue = this.invokeOperation(invoker);
                cacheValue = this.unwrapReturnValue(returnValue);
            }

            this.collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
            Iterator var8 = cachePutRequests.iterator();

            while(var8.hasNext()) {
                CacheAspectSupport.CachePutRequest cachePutRequest = (CacheAspectSupport.CachePutRequest)var8.next();
                /**
                执行cachePut请求,将返回对象放到缓存中
                **/
                cachePutRequest.apply(cacheValue);
            }
            /**
            执行@CacheEvict的逻辑,这里是当beforeInvocation为false时清缓存
            **/
            this.processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
            return returnValue;
        }
    }</cacheaspectsupport.cacheputrequest></object>
Die oben genannten Codefragmente sind relativ zentral und stellen den Inhalt des Caches dar. Auf den Quellcode von AOP werde ich hier nicht näher eingehen. Die Hauptklassen und Schnittstellen befinden sich im Spring-Kontext im Paket org.springframework.cache.

Anpassung an Caching-Komponenten von Drittanbietern

Durch die obige Analyse kennen wir die Besonderheiten der Spring-Cache-Funktion. Im Folgenden müssen die Gründe dafür analysiert werden Es ist nur eine Maven-Deklaration erforderlich. Mit nur wenigen Abhängigkeiten kann sich Spring Boot automatisch anpassen.

In der obigen Ausführungsmethode sehen wir

cachePutRequest.apply(cacheValue), das den Cache betreibt. CachePutRequest ist die innere Klasse CacheAspectSupport.

private class CachePutRequest {
        private final CacheAspectSupport.CacheOperationContext context;
        private final Object key;
        public CachePutRequest(CacheAspectSupport.CacheOperationContext context, Object key) {
            this.context = context;
            this.key = key;
        }
        public void apply(Object result) {
            if (this.context.canPutToCache(result)) {
                //从context中获取cache实例,然后执行放入缓存的操作
                Iterator var2 = this.context.getCaches().iterator();
                while(var2.hasNext()) {
                    Cache cache = (Cache)var2.next();
                    CacheAspectSupport.this.doPut(cache, this.key, result);
                }
            }
        }
    }
Cache ist eine Standardschnittstelle, wobei EhCacheCache die Implementierungsklasse von EhCache ist. Dies ist die Verbindung zwischen SpringBoot und Ehcache. Wann wird also die Cache-Liste im Kontext generiert? Die Antwort ist die getCaches-Methode von CacheAspectSupport

protected Collection extends Cache> getCaches(CacheOperationInvocationContext<cacheoperation> context, CacheResolver cacheResolver) {
        Collection extends Cache> caches = cacheResolver.resolveCaches(context);
        if (caches.isEmpty()) {
            throw new IllegalStateException("No cache could be resolved for '" + context.getOperation() + "' using resolver '" + cacheResolver + "'. At least one cache should be provided per cache operation.");
        } else {
            return caches;
        }
    }</cacheoperation>
und das Abrufen des Caches wird jedes Mal ausgeführt, wenn ein Cache-Vorgang ausgeführt wird. Sie können einen Blick auf den Aufrufstapel werfen

Grundlegendes zum Spring Boot-Cache-Quellcode

Es scheint etwas vom Thema abzuweichen, bringen Sie es zurück ... Im Spring-Boot-Autoconfigure-Paket gibt es alle automatisch -Montagebezogene Klassen. Hier gibt es eine EhcacheCacheConfiguration-Klasse, wie unten gezeigt

@Configuration
@ConditionalOnClass({Cache.class, EhCacheCacheManager.class})
@ConditionalOnMissingBean({CacheManager.class})
@Conditional({CacheCondition.class, EhCacheCacheConfiguration.ConfigAvailableCondition.class})
class EhCacheCacheConfiguration {
 ......
 static class ConfigAvailableCondition extends ResourceCondition {
        ConfigAvailableCondition() {
            super("EhCache", "spring.cache.ehcache", "config", new String[]{"classpath:/ehcache.xml"});
        }
    }    
}
Dadurch wird direkt ermittelt, ob sich eine ehcache.xml-Datei im Klassenpfad befindet

Das obige ist der detaillierte Inhalt vonGrundlegendes zum Spring Boot-Cache-Quellcode. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen