Java Academy · leksjon

Dynamiske proxyer med InvocationHandler

Opprett dynamiske JDK-proxyer for å fange opp kall til grensesnittmetoder for logging, hurtigbufring og nye forsøk.

Leksjon 3 av 413 trinn

Dynamiske proxyer med InvocationHandler er en gratis leksjon i Java Academy på CoddyKit. Dette er leksjon 3 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Java Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Java Academy inneholder totalt 4 leksjoner.

Hva er en dynamisk proxy?

En dynamisk JDK-proxy er et objekt som opprettes under kjøring og implementerer ett eller flere grensesnitt. Alle metodekall videresendes til en InvocationHandler som du oppgir.

Opprette en proxy med Proxy.newProxyInstance

Kall Proxy.newProxyInstance(classLoader, interfaces[], handler) for å opprette en proxy. Proxyen implementerer alle de oppførte grensesnittene.

UserService proxy = (UserService) Proxy.newProxyInstance(
    UserService.class.getClassLoader(),
    new Class[]{UserService.class},
    new LoggingHandler(realService)
);
proxy.findById(42L); // intercepted by LoggingHandler

Grensesnittet InvocationHandler

Implementer InvocationHandler for å håndtere hvert metodekall. Handleren mottar proxyobjektet, metoden som ble kalt, og argumentene.

public class LoggingHandler implements InvocationHandler {
    private final Object target;
    public LoggingHandler(Object target) { this.target = target; }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">> " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("<< " + method.getName());
        return result;
    }
}

Proxy for bufring

En InvocationHandler kan bufre resultater: Hvis cachen inneholder et resultat for argumentene, returneres det. Ellers kalles den faktiske metoden, og resultatet lagres.

public class CachingHandler implements InvocationHandler {
    private final Object target;
    private final Map<String, Object> cache = new HashMap<>();
    public CachingHandler(Object target) { this.target = target; }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String key = method.getName() + Arrays.toString(args);
        return cache.computeIfAbsent(key, k -> {
            try { return method.invoke(target, args); }
            catch (Exception e) { throw new RuntimeException(e); }
        });
    }
}

Proxy for logikk for nye forsøk

Pakk metodekall inn i en løkke for nye forsøk. Hvis det oppstår et unntak, gjør nye forsøk opptil N ganger før unntaket kastes videre.

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    for (int attempt = 1; attempt <= 3; attempt++) {
        try { return method.invoke(target, args); }
        catch (InvocationTargetException e) {
            if (attempt == 3) throw e.getCause();
            Thread.sleep(1000L * attempt);
        }
    }
    throw new RuntimeException("unreachable");
}

Proxy for transaksjonshåndtering

Spring bruker @Transactional en dynamisk proxy til å starte en transaksjon før et metodekall og utføre commit/rollback etterpå – uten at den som kaller, vet det.

Kontrollere om et objekt er en proxy

Bruk Proxy.isProxyClass(obj.getClass()) for å oppdage proxyer, og Proxy.getInvocationHandler(proxy) for å hente handleren.

if (Proxy.isProxyClass(service.getClass())) {
    InvocationHandler h = Proxy.getInvocationHandler(service);
    System.out.println("Handler: " + h.getClass().getSimpleName());
}

Begrensninger ved dynamiske JDK-proxyer

Dynamiske JDK-proxyer fungerer bare med grensesnitt – de kan ikke opprette proxyer for konkrete klasser. For proxying av klasser kan du bruke CGLIB, som Spring også bruker internt for @Configuration-klasser.

CGLIB som et alternativ

CGLIB genererer en subklasse av målklassen under kjøring og overskriver alle ikke-final-metoder. Spring bruker det når ingen grensesnitt finnes. Ingen annotasjon kreves – det skjer transparent.

Proxyer i Hibernate

Hibernate laster entitetsrelasjoner med lazy loading ved hjelp av CGLIB-/Byte Buddy-proxyer. Når en lazy-samling åpnes, utløser proxyen en SQL-spørring.

// Hibernate creates a proxy for:
@ManyToOne(fetch = FetchType.LAZY)
private Department department;
// Accessing dept triggers a DB query via the proxy

Proxyenes ytelse

Metode-dispatch via dynamisk proxy er omtrent 5–10× tregere enn et direkte kall. For svært varme kodebaner kan du vurdere kodegenerering ved kompilering (APT) eller MethodHandles i stedet for dynamiske proxyer.

Hurtigsjekk

Hva er den viktigste begrensningen ved dynamiske JDK-proxyer?

Oppsummering

Dynamiske JDK-proxyer avskjærer metodekall på grensesnitt via InvocationHandler. Bruk dem til logging, bufring, nye forsøk og transaksjonshåndtering. For proxying av klasser kan du bruke CGLIB. Bufre opprettelsen av proxyer – den bruker refleksjon og er ikke kostnadsfri.

Gratis å komme i gang

Lær deg Java med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
104
Leksjoner
374

Ofte stilte spørsmål

Er leksjonen «Dynamiske proxyer med InvocationHandler» gratis?

Ja – hele teksten i «Dynamiske proxyer med InvocationHandler» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Java Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Java Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Dynamiske proxyer med InvocationHandler»?

Opprett dynamiske JDK-proxyer for å fange opp kall til grensesnittmetoder for logging, hurtigbufring og nye forsøk. Du øver på Java Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Java Academy?

Ingen tidligere erfaring er nødvendig. Java Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.

Hvor lang tid tar leksjonen «Dynamiske proxyer med InvocationHandler»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Java Academy-leksjonen?

Ja. Alle Java Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Klasseinspeksjon med refleksjon
  2. Kalle metoder og få tilgang til felt
  3. Dynamiske proxyer med InvocationHandler
  4. Refleksjonsytelse og alternativer
← Tilbake til Java Academy