Skip to main content

spring_boot_starter_cache 0.5.0 copy "spring_boot_starter_cache: ^0.5.0" to clipboard
spring_boot_starter_cache: ^0.5.0 copied to clipboard

Cache starter for spring_boot_dart: @Cacheable, @CachePut, @CacheEvict and @Caching on bean methods, SpEL keys, a CacheManager with TTL and size limits.

example/example.dart

// Caching in a Spring Boot application: annotate bean methods, the
// generator writes the proxies caching runs on and picks the starter up
// (example.spring.dart, from `dart run build_runner build`).
//
//   dart run example/example.dart

import 'dart:convert';

import 'package:spring_boot_starter_cache/spring_boot_starter_cache.dart';

import 'example.spring.dart';

class Book {
  const Book(this.isbn, this.title);
  final String isbn;
  final String title;
}

/// The slow source behind the cache (a remote API in a real app); counts
/// its loads.
@Component()
class CatalogApi {
  int loads = 0;

  Future<Book> load(String isbn) async {
    loads++;
    return Book(isbn, 'Dune');
  }

  Future<void> remove(String isbn) async {}
}

@Service()
@CacheConfig(cacheNames: ['books'])
class Books {
  Books(this.api);
  final CatalogApi api;

  /// Loaded once per ISBN; later calls return the cached book.
  @Cacheable.of()
  Future<Book> find(String isbn) => api.load(isbn);

  /// Concurrent calls for the same ISBN share one computation.
  @Cacheable('prices', sync: true)
  Future<double> price(String isbn) async => 12.5;

  /// The entry is removed once the book is gone.
  @CacheEvict.of(key: '#isbn')
  Future<void> remove(String isbn) => api.remove(isbn);

  /// Everything is reloaded: the cache is cleared first.
  @CacheEvict.of(allEntries: true, beforeInvocation: true)
  void reloadAll() {}
}

// --- Your own cache -------------------------------------------------------

/// Any synchronous key-value storage: a file, a key-value database, ...
abstract interface class KeyValueStore {
  String? read(String key);
  void write(String key, String value);
  void delete(String key);
  void deleteAll({required String prefix});
}

/// A store in a map (stands in for a real one here).
final class MapStore implements KeyValueStore {
  final _data = <String, String>{};

  @override
  String? read(String key) => _data[key];

  @override
  void write(String key, String value) => _data[key] = value;

  @override
  void delete(String key) => _data.remove(key);

  @override
  void deleteAll({required String prefix}) =>
      _data.removeWhere((key, _) => key.startsWith(prefix));

  /// The stored keys.
  Iterable<String> get keys => _data.keys;
}

/// A cache kept in a [KeyValueStore] as JSON. `CacheDefaults` adds
/// putIfAbsent, evictIfPresent and invalidate; only four methods are left.
final class StoreCache with CacheDefaults implements Cache {
  StoreCache(this.name, this.store);

  @override
  final String name;
  final KeyValueStore store;

  String _key(Object? key) => '$name:$key';

  @override
  ValueWrapper? get(Object? key) => switch (store.read(_key(key))) {
    null => null,
    final json => ValueWrapper(jsonDecode(json)),
  };

  @override
  void put(Object? key, Object? value) =>
      store.write(_key(key), jsonEncode(value));

  @override
  void evict(Object? key) => store.delete(_key(key));

  @override
  void clear() => store.deleteAll(prefix: '$name:');
}

/// A `CacheManager` bean replaces the starter's in-memory manager.
@Configuration()
class CacheSetup {
  @Bean()
  KeyValueStore store() => MapStore();

  @Bean()
  CacheManager cacheManager(KeyValueStore store) => SimpleCacheManager([
    InMemoryCache('books'), // in memory
    StoreCache('prices', store), // in the store, as JSON
  ]);
}

/// Uses the cache and logs what it saw; `@Log` gives it the `log`
/// getter.
@Component()
@Log(topic: 'example')
class CacheTour implements CommandLineRunner {
  CacheTour(this.books, this.api, this.store);

  final Books books;
  final CatalogApi api;
  final KeyValueStore store;

  @override
  Future<void> run(List<String> args) async {
    await books.find('978-0');
    await books.find('978-0');
    log.info('loads: ${api.loads}'); // loads: 1 (the second call was cached)

    await books.remove('978-0');
    await books.find('978-0');
    log.info('loads after evict: ${api.loads}'); // loads after evict: 2

    // The prices cache is the StoreCache of CacheSetup: entries are JSON in
    // the store, so they would survive a restart with a persistent store.
    final price = await books.price('978-0');
    log
      ..info('price: $price') // price: 12.5
      ..info('stored: ${(store as MapStore).keys}'); // stored: (prices:978-0)
  }
}

@SpringBootApplication()
Future<void> main(List<String> args) async {
  final context = await springApplication.runAsync(args);
  context.close();
}
0
likes
0
points
702
downloads

Publisher

verified publisherfarum-azula.org

Weekly Downloads

Cache starter for spring_boot_dart: @Cacheable, @CachePut, @CacheEvict and @Caching on bean methods, SpEL keys, a CacheManager with TTL and size limits.

Repository (GitHub)
View/report issues

Topics

#cache #caching #spring

License

unknown (license)

Dependencies

spring_boot_dart, spring_boot_starter_logging

More

Packages that depend on spring_boot_starter_cache