Introduktion til cglib
1. Oversigt
I denne artikel vil vi se på cglib (Code Generation Library) bibliotek. Det er et byteinstrumentationsbibliotek, der bruges i mange Java-rammer som f.eks Dvale eller Forår. Bytecode-instrumenteringen tillader manipulation eller oprettelse af klasser efter kompileringsfasen af et program.
2. Maven-afhængighed
At bruge cglib i dit projekt skal du blot tilføje en Maven-afhængighed (den nyeste version kan findes her):
cglib cglib 3.2.4
3. Cglib
Klasser i Java indlæses dynamisk ved kørsel. Cglib bruger denne funktion af Java-sprog for at gøre det muligt at tilføje nye klasser til et allerede kørende Java-program.
Dvale bruger cglib til generering af dynamiske proxyer. For eksempel vil det ikke returnere det fulde objekt, der er gemt i en database, men det vil returnere en instrumenteret version af den lagrede klasse, der let laver værdier fra databasen efter behov.
Populære mocking rammer, ligesom Mockito, brug cglib til spottemetoder. Mock er en instrumenteret klasse, hvor metoder erstattes af tomme implementeringer.
Vi vil se på de mest nyttige konstruktioner fra cglib.
4. Implementering af proxy ved hjælp af cglib
Lad os sige, at vi har en PersonService klasse, der har to metoder:
offentlig klasse PersonService {public String sayHello (String name) {return "Hello" + name; } public Integer lengthOfName (String name) {return name.length (); }}
Bemærk, at den første metode vender tilbage Snor og den anden Heltal.
4.1. Returnering af samme værdi
Vi ønsker at oprette en simpel proxy-klasse, der opfanger et opkald til en sig hej() metode. Det Enhancer klasse giver os mulighed for at oprette en proxy ved dynamisk at udvide en PersonService klasse ved hjælp af en setSuperclass () metode fra Enhancer klasse:
Enhancer enhancer = ny Enhancer (); enhancer.setSuperclass (PersonService.class); enhancer.setCallback ((FixedValue) () -> "Hej Tom!"); PersonService proxy = (PersonService) enhancer.create (); Streng res = proxy.sayHello (null); assertEquals ("Hej Tom!", res);
Det Fast værdi er en tilbagekaldsgrænseflade, der simpelthen returnerer værdien fra den nærliggende metode. Udfører sig hej() metode på en proxy returnerede en værdi, der er angivet i en proxymetode.
4.2. Returneringsværdi afhængigt af metodesignatur
Den første version af vores proxy har nogle ulemper, fordi vi ikke er i stand til at beslutte, hvilken metode en proxy skal opfange, og hvilken metode der skal påberåbes fra en superklasse. Vi kan bruge en Metode Interceptor interface til at opfange alle opkald til proxyen og beslutte, om du vil foretage et specifikt opkald eller udføre en metode fra en superklasse:
Enhancer enhancer = ny Enhancer (); enhancer.setSuperclass (PersonService.class); enhancer.setCallback ((MethodInterceptor) (obj, method, args, proxy) -> {if (method.getDeclaringClass ()! = Object.class && method.getReturnType () == String.class) {return "Hej Tom!" ;} ellers {returner proxy.invokeSuper (obj, args);}}); PersonService proxy = (PersonService) enhancer.create (); assertEquals ("Hej Tom!", proxy.sayHello (null)); int lengthOfName = proxy.lengthOfName ("Mary"); assertEquals (4, lengthOfName);
I dette eksempel opfanger vi alle opkald, når metodesignatur ikke er fra Objekt klasse, hvilket betyder at dvs. toString () eller hashCode () metoder bliver ikke opfanget. Derudover opfanger vi kun metoder fra a PersonService der returnerer en Snor. Ring til en lengthOfName () metoden bliver ikke opfanget, fordi dens returtype er en Heltal.
5. Bean Creator
En anden nyttig konstruktion fra cglib er en BeanGenerator klasse. Det giver os mulighed for dynamisk at skabe bønner og tilføje felter sammen med setter- og gettermetoder. Det kan bruges af kodegenereringsværktøjer til at generere enkle POJO-objekter:
BeanGenerator beanGenerator = ny BeanGenerator (); beanGenerator.addProperty ("navn", String.class); Objekt myBean = beanGenerator.create (); Metodesetter = myBean.getClass (). GetMethod ("setName", String.class); setter.invoke (myBean, "en eller anden strengværdi indstillet af en cglib"); Metode getter = myBean.getClass (). GetMethod ("getName"); assertEquals ("nogle strengværdier indstillet af en cglib", getter.invoke (myBean));
6. Oprettelse af Mixin
EN mixin er en konstruktion, der gør det muligt at kombinere flere objekter i et. Vi kan inkludere en adfærd i et par klasser og udsætte denne adfærd som en enkelt klasse eller grænseflade. Det cglib Mixins tillader kombinationen af flere objekter i et enkelt objekt. For at gøre det skal alle objekter, der er inkluderet i et mixin, imidlertid understøttes af grænseflader.
Lad os sige, at vi vil skabe et mixin af to grænseflader. Vi er nødt til at definere begge grænseflader og deres implementeringer:
offentlig grænseflade Interface1 {Streng først (); } offentlig grænseflade Interface2 {Streng sekund (); } public class Class1 implementerer Interface1 {@Override public String first () {return "first behavior"; }} public class Class2 implementerer Interface2 {@Override public String second () {return "second behavior"; }}
At komponere implementeringer af Grænseflade 1 og Interface2 vi er nødt til at oprette en grænseflade, der udvider dem begge:
offentlig grænseflade MixinInterface udvider Interface1, Interface2 {}
Ved at bruge en skab() metode fra Mixin klasse, vi kan inkludere adfærd fra Klasse1 og Klasse 2 ind i en MixinInterface:
Mixin mixin = Mixin.create (ny klasse [] {Interface1.class, Interface2.class, MixinInterface.class}, nyt objekt [] {new Class1 (), ny Class2 ()}); MixinInterface mixinDelegate = (MixinInterface) mixin; assertEquals ("første adfærd", mixinDelegate.first ()); assertEquals ("anden adfærd", mixinDelegate.second ());
Opkaldsmetoder på mixinDelegate vil påberåbe sig implementeringer fra Klasse1 og Klasse 2.
7. Konklusion
I denne artikel så vi på cglib og dens mest nyttige konstruktioner. Vi oprettede en proxy ved hjælp af en Enhancer klasse. Vi brugte en BeanCreator og til sidst oprettede vi en Mixin der omfattede adfærd fra andre klasser.
Cglib bruges i vid udstrækning af Spring-rammen. Et eksempel på brug af en cglib-proxy fra Spring er at tilføje sikkerhedsbegrænsninger til metodeopkald. I stedet for at kalde en metode direkte, vil Spring-sikkerhed først kontrollere (via proxy), hvis en bestemt sikkerhedskontrol passerer og kun delegere til den aktuelle metode, hvis denne verifikation var vellykket. I denne artikel så vi, hvordan man opretter en sådan proxy til vores eget formål.
Implementeringen af alle disse eksempler og kodestykker findes i GitHub-projektet - dette er et Maven-projekt, så det skal være let at importere og køre, som det er.