Zum Inhalt springen
Anjunar/ DOCS

Caching und Identität

TypeResolver hält eine ResolvedClass pro Type für die Lebensdauer des Programms. Auflösen ist nach dem ersten Mal billig, und das Modell wächst faul mit der Nutzung.

Identität

Derselbe Typ, dieselbe Instanz

resolve sucht den Type zuerst in einer Map. Zwei ResolvedClasses sind gleich, wenn ihre underlying-Typen gleich sind.

scala
val first = TypeResolver.resolve(classOf[User]) val second = TypeResolver.resolve(classOf[User]) println(first eq second) // true
Faulheit

Bezahlt beim ersten Zugriff

hierarchy, fields, methods, constructors und annotations sind lazy vals. Der erste Aufruf liest die Reflection-Daten der JVM, jeder spätere liefert die zwischengespeicherten Arrays.

Gut zu wissen
Der Cache wird nie geleert; aufgelöste Typen leben so lange wie der Class Loader.
companionClass hält einen eigenen Cache, Fehlschläge eingeschlossen.
BeanIntrospector und AnnotationIntrospector speichern ein Modell pro Klasse (und Annotation).
Scala-3-lazy-vals werden einmal initialisiert, auch wenn mehrere Threads gleichzeitig fragen.
Hot Deploys mit neuem Class Loader bekommen neue Types und damit neue Einträge.
Threads

Die Cache-Map ist nicht synchronisiert

In 1.0.4 sind Typ-Cache, Companion-Cache und die Caches der Introspektoren einfache Hash-Maps ohne Synchronisierung. Gleichzeitiges erstes Auflösen verschiedener Typen kann darauf kollidieren.

Den Cache auf einem Thread füllen

Löse in einem Server die Typen, die du mappst, beim Start auf, bevor Anfragen kommen, oder serialisiere das erste Auflösen selbst. Das Lesen bereits zwischengespeicherter Typen ist sicher, solange nicht gleichzeitig geschrieben wird.

scala
// at startup, before the HTTP server accepts requests Seq(classOf[User], classOf[Article], classOf[Comment]).foreach { clazz => val resolved = TypeResolver.resolve(clazz) resolved.fields; resolved.methods // touch the lazy parts once AnnotationIntrospector.create(resolved, classOf[JsonbProperty]).properties }