class LoadingCache<K, V> extends SimpleProxyCache<K, V> {

    protected Consumer<CacheEvent> eventConsumer;

    protected CacheConfig<K, V> config;

    public LoadingCache(Cache<K, V> cache) {
        super(cache);
        this.config = config();
        eventConsumer = CacheUtil.getAbstractCache(cache)::notify;
    }

    @Override
    public V get(K key) throws CacheInvokeException {
        CacheLoader<K, V> loader = config.getLoader();
        if (loader != null) {
            return AbstractCache.computeIfAbsentImpl(key, loader,
                    config.isCacheNullValue() ,0, null, this);
        } else {
            return cache.get(key);
        }
    }

    protected boolean needUpdate(V loadedValue, CacheLoader<K, V> loader) {
        if (loadedValue == null && !config.isCacheNullValue()) {
            return false;
        }
        if (loader.vetoCacheUpdate()) {
            return false;
        }
        return true;
    }

    @Override
    public Map<K, V> getAll(Set<? extends K> keys) throws CacheInvokeException {<FILL_FUNCTION_BODY>}
}

        CacheLoader<K, V> loader = config.getLoader();
        if (loader != null) {
            MultiGetResult<K, V> r = GET_ALL(keys);
            Map<K, V> kvMap;
            if (r.isSuccess() || r.getResultCode() == CacheResultCode.PART_SUCCESS) {
                kvMap = r.unwrapValues();
            } else {
                kvMap = new HashMap<>();
            }
            Set<K> keysNeedLoad = new LinkedHashSet<>();
            keys.forEach((k) -> {
                if (!kvMap.containsKey(k)) {
                    keysNeedLoad.add(k);
                }
            });
            if (!config.isCachePenetrationProtect()) {
                if (eventConsumer != null) {
                    loader = CacheUtil.createProxyLoader(cache, loader, eventConsumer);
                }
                Map<K, V> loadResult;
                try {
                    loadResult = loader.loadAll(keysNeedLoad);

                    CacheLoader<K, V> theLoader = loader;
                    Map<K, V> updateValues = new HashMap<>();
                    loadResult.forEach((k,v)->{
                        if (needUpdate(v, theLoader)){
                            updateValues.put(k, v);
                        }
                    });
                    // batch put
                    if (!updateValues.isEmpty()) {
                        PUT_ALL(updateValues);
                    }
                } catch (Throwable e) {
                    throw new CacheInvokeException(e);
                }
                kvMap.putAll(loadResult);
            } else {
                AbstractCache<K, V> abstractCache = CacheUtil.getAbstractCache(cache);
                loader = CacheUtil.createProxyLoader(cache, loader, eventConsumer);
                for(K key : keysNeedLoad) {
                    Consumer<V> cacheUpdater = (v) -> {
                        if(needUpdate(v, config.getLoader())) {
                            PUT(key, v);
                        }
                    };
                    V v = AbstractCache.synchronizedLoad(config, abstractCache, key, loader, cacheUpdater);
                    kvMap.put(key, v);
                }
            }
            return kvMap;
        } else {
            return cache.getAll(keys);
        }

    
<methods>public void <init>(Cache<K,V>) ,public CacheGetResult<V> GET(K) ,public MultiGetResult<K,V> GET_ALL(Set<? extends K>) ,public com.alicp.jetcache.CacheResult PUT(K, V) ,public com.alicp.jetcache.CacheResult PUT(K, V, long, java.util.concurrent.TimeUnit) ,public com.alicp.jetcache.CacheResult PUT_ALL(Map<? extends K,? extends V>) ,public com.alicp.jetcache.CacheResult PUT_ALL(Map<? extends K,? extends V>, long, java.util.concurrent.TimeUnit) ,public com.alicp.jetcache.CacheResult PUT_IF_ABSENT(K, V, long, java.util.concurrent.TimeUnit) ,public com.alicp.jetcache.CacheResult REMOVE(K) ,public com.alicp.jetcache.CacheResult REMOVE_ALL(Set<? extends K>) ,public void close() ,public V computeIfAbsent(K, Function<K,V>) ,public V computeIfAbsent(K, Function<K,V>, boolean) ,public V computeIfAbsent(K, Function<K,V>, boolean, long, java.util.concurrent.TimeUnit) ,public CacheConfig<K,V> config() ,public V get(K) ,public Map<K,V> getAll(Set<? extends K>) ,public Cache<K,V> getTargetCache() ,public void put(K, V) ,public void put(K, V, long, java.util.concurrent.TimeUnit) ,public void putAll(Map<? extends K,? extends V>) ,public void putAll(Map<? extends K,? extends V>, long, java.util.concurrent.TimeUnit) ,public boolean putIfAbsent(K, V) ,public boolean remove(K) ,public void removeAll(Set<? extends K>) ,public com.alicp.jetcache.AutoReleaseLock tryLock(K, long, java.util.concurrent.TimeUnit) ,public boolean tryLockAndRun(K, long, java.util.concurrent.TimeUnit, java.lang.Runnable) ,public T unwrap(Class<T>) <variables>protected Cache<K,V> cache