Einführung in cglib

Einführung in cglib

1. Überblick

In diesem Artikel betrachten wir die Bibliothekcglib (Code Generation Library). Es ist eine Byte-Instrumentierungsbibliothek, die in vielen Java-Frameworks wieHibernate oderSpring verwendet wird. Mit der Bytecode-Instrumentierung können Klassen nach der Kompilierungsphase eines Programms bearbeitet oder erstellt werden.

2. Maven-Abhängigkeit

Umcglib in Ihrem Projekt zu verwenden, fügen Sie einfach eine Maven-Abhängigkeit hinzu (die neueste Version finden Sie unterhere):


    cglib
    cglib
    3.2.4

3. Cglib

Klassen in Java werden zur Laufzeit dynamisch geladen. Cglib verwendet diese Funktion der Java-Sprache, um das Hinzufügen neuer Klassen zu einem bereits ausgeführten Java-Programm zu ermöglichen.

Hibernate verwendet cglib zur Generierung dynamischer Proxys. Beispielsweise wird nicht das gesamte in einer Datenbank gespeicherte Objekt zurückgegeben, sondern eine instrumentierte Version der gespeicherten Klasse, mit der Werte bei Bedarf träge aus der Datenbank geladen werden.

Beliebte Verspottungs-Frameworks wieMockito,verwendencglib für Verspottungsmethoden. Der Mock ist eine instrumentierte Klasse, bei der Methoden durch leere Implementierungen ersetzt werden.

Wir werden uns die nützlichsten Konstrukte auscglib. ansehen

4. Proxy mitcglib implementieren

Nehmen wir an, wir haben einePersonService-Klasse mit zwei Methoden:

public class PersonService {
    public String sayHello(String name) {
        return "Hello " + name;
    }

    public Integer lengthOfName(String name) {
        return name.length();
    }
}

Beachten Sie, dass die erste MethodeString und die zweiteInteger. zurückgibt

4.1. Rückgabe des gleichen Wertes

Wir möchten eine einfache Proxy-Klasse erstellen, die einen Aufruf dersayHello()-Methode abfängt. Mit derEnhancer-Klasse können wir einen Proxy erstellen, indem wir einePersonService-Klasse dynamisch erweitern, indem wir einesetSuperclass()-Methode aus derEnhancer-Klasse verwenden:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((FixedValue) () -> "Hello Tom!");
PersonService proxy = (PersonService) enhancer.create();

String res = proxy.sayHello(null);

assertEquals("Hello Tom!", res);

FixedValue ist eine Rückrufschnittstelle, die einfach den Wert von der Proxy-Methode zurückgibt. Das Ausführen der MethodesayHello() auf einem Proxy gab einen in einer Proxy-Methode angegebenen Wert zurück.

4.2. Rückgabewert abhängig von einer Methodensignatur

Die erste Version unseres Proxys hat einige Nachteile, da wir nicht entscheiden können, welche Methode ein Proxy abfängt und welche Methode von einer Superklasse aufgerufen werden soll. Wir können dieMethodInterceptor-Schnittstelle verwenden, um alle Aufrufe an den Proxy abzufangen und zu entscheiden, ob Sie einen bestimmten Aufruf tätigen oder eine Methode aus einer Oberklasse ausführen möchten:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
        return "Hello Tom!";
    } else {
        return proxy.invokeSuper(obj, args);
    }
});

PersonService proxy = (PersonService) enhancer.create();

assertEquals("Hello Tom!", proxy.sayHello(null));
int lengthOfName = proxy.lengthOfName("Mary");

assertEquals(4, lengthOfName);

In diesem Beispiel fangen wir alle Aufrufe ab, wenn die Methodensignatur nicht aus der KlasseObjecttammt, was bedeutet, dass d.h. toString()- oderhashCode()-Methoden werden nicht abgefangen. Außerdem fangen wir nur Methoden von aPersonService ab, die aString zurückgeben. Der Aufruf einerlengthOfName()-Methode wird nicht abgefangen, da der RückgabetypInteger. ist

5. Bean Creator

Ein weiteres nützliches Konstrukt auscglib ist eineBeanGenerator-Klasse. Es ermöglicht uns, dynamisch Beans zu erstellen und Felder zusammen mit Setter- und Getter-Methoden hinzuzufügen. Es kann von Tools zur Codegenerierung verwendet werden, um einfache POJO-Objekte zu generieren:

BeanGenerator beanGenerator = new BeanGenerator();

beanGenerator.addProperty("name", String.class);
Object myBean = beanGenerator.create();
Method setter = myBean.getClass().getMethod("setName", String.class);
setter.invoke(myBean, "some string value set by a cglib");

Method getter = myBean.getClass().getMethod("getName");
assertEquals("some string value set by a cglib", getter.invoke(myBean));

6. Mixin erstellen

Amixin ist ein Konstrukt, mit dem mehrere Objekte zu einem kombiniert werden können. Wir können ein Verhalten von mehreren Klassen einschließen und dieses Verhalten als einzelne Klasse oder Schnittstelle verfügbar machen. Diecglib Mixins ermöglichen die Kombination mehrerer Objekte zu einem einzigen Objekt. Dazu müssen jedoch alle Objekte, die in einem Mixin enthalten sind, durch Schnittstellen gesichert werden.

Nehmen wir an, wir möchten eine Mischung aus zwei Schnittstellen erstellen. Wir müssen beide Schnittstellen und ihre Implementierungen definieren:

public interface Interface1 {
    String first();
}

public interface Interface2 {
    String second();
}

public class Class1 implements Interface1 {
    @Override
    public String first() {
        return "first behaviour";
    }
}

public class Class2 implements Interface2 {
    @Override
    public String second() {
        return "second behaviour";
    }
}

Um Implementierungen vonInterface1 undInterface2 zu erstellen, müssen wir eine Schnittstelle erstellen, die beide erweitert:

public interface MixinInterface extends Interface1, Interface2 { }

Durch Verwendung einercreate()-Methode aus derMixin-Klasse können Verhaltensweisen vonClass1 undClass2 inMixinInterface: eingeschlossen werden

Mixin mixin = Mixin.create(
  new Class[]{ Interface1.class, Interface2.class, MixinInterface.class },
  new Object[]{ new Class1(), new Class2() }
);
MixinInterface mixinDelegate = (MixinInterface) mixin;

assertEquals("first behaviour", mixinDelegate.first());
assertEquals("second behaviour", mixinDelegate.second());

Das Aufrufen von Methoden fürmixinDelegate ruft Implementierungen vonClass1 undClass2. auf

7. Fazit

In diesem Artikel haben wir uns diecglib und ihre nützlichsten Konstrukte angesehen. Wir haben einen Proxy mit einerEnhancer-Klasse erstellt. Wir haben einBeanCreator verwendet und schließlich einMixin erstellt, das Verhaltensweisen anderer Klassen enthält.

Cglib wird in großem Umfang vom Spring-Framework verwendet. Ein Beispiel für die Verwendung eines cglib-Proxys von Spring ist das Hinzufügen von Sicherheitsbeschränkungen zu Methodenaufrufen. Anstatt eine Methode direkt aufzurufen, überprüft Spring Security zunächst (über einen Proxy), ob eine bestimmte Sicherheitsüberprüfung erfolgreich war, und delegiert sie nur dann an die eigentliche Methode, wenn diese Überprüfung erfolgreich war. In diesem Artikel haben wir gesehen, wie ein solcher Proxy für unseren eigenen Zweck erstellt wird.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie inGitHub project - dies ist ein Maven-Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.