Avatar billede kernelx Juniormester
14. maj 2011 - 22:27 Der er 18 kommentarer og
1 løsning

fra instance med new til method interceptor via cglig

Hi,

jeg har lavet en lille method interceptor for cglib.

=================
public class TestMethodInterceptor {
    public Object intercept(Object o, Method m, Object[] params, MethodProxy mp) {
        return "interceptor";
    }
}
=================

Nu har jeg en test class
=================
public class Test {
    public String fooBar() {
        return "method";
    }
}
=================

Hvis jeg laver
=================
public class Main {
    public static void main(String[] args) {
        Enhancer e = new Enhancer();
        e.setSuperclass(Test.class);
        MethodInterceptor mi = new TestMethodInterceptor();
        e.setCallback(mi);
        Object o = e.create();
        Test t = (Test)o;
        String v = t.fooBar()
        System.out.println(v);
    }
}
=================
så er min output: interceptor

Er det muligt at jeg har en instance og bagefter adder en interceptor?
=================
public class Main {
    public static void main(String[] args) {
        Test t = new Test(); // en instance med new
        Enhancer e = new Enhancer();
        MethodInterceptor mi = new TestMethodInterceptor();
        e.setCallback(mi);
        e.setMethodInterceptorToInstance(t); // noget i denne stil?
        String v = t.fooBar()
        System.out.println(v); // output: interceptor
    }
}
=================
Avatar billede arne_v Ekspert
14. maj 2011 - 23:15 #1
Avatar billede kernelx Juniormester
19. maj 2011 - 20:37 #2
jeg kender den "wrappede" class type ikke i forvejen:

<T> T getProxyInstance(T instanceToWrap) {
    Class type = instanceToWrap.getClass();
    Enhancer e = new Enhancer();
    MethodInterceptor mi = new TestMethodInterceptor();
    e.setCallback(mi);
    e.setSuperclass(Test.class);

    // jeg ved ikke, hvordan jeg kan wrappe en class,
    // hvis jeg ikke laver det eksplicit i forvejen 
    e.setWrapperInstance(instanceToWrap);
    Object o = e.create();
    T wrappedInstance = (T)o;

    return wrappedInstance;
}
Avatar billede arne_v Ekspert
21. maj 2011 - 20:57 #3
Det er hvad linket forklarer.


public class Test {
    public String fooBar() {
        return "method";
    }
}



import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class TestMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method m, Object[] params, MethodProxy mp) {
        return "interceptor";
    }
}



public class WrappedTest extends Test{
    public String fooBar() {
        return "wrapped";
    }
}



import java.lang.reflect.Method;
import java.util.Arrays;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class Wrapper {
    @SuppressWarnings("unchecked")
    public static <S,W> W createWrapper(final S source, final Class<W> wrapperClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(wrapperClass);
        enhancer.setInterfaces(wrapperClass.getInterfaces());
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                if (Arrays.asList(wrapperClass.getDeclaredMethods()).contains(method)) {
                    return methodProxy.invokeSuper(proxy, args);
                }
                return methodProxy.invoke(source, args);
            }
        });
        return (W)enhancer.create();
    }
}



import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {
        Test o1 = new Test();
        System.out.println(o1.fooBar());
        Enhancer e = new Enhancer();
        e.setSuperclass(Test.class);
        e.setCallback(new TestMethodInterceptor());
        Test o2 = (Test)e.create();
        System.out.println(o2.fooBar());
        Test o3 = new Test();
        System.out.println(o3.fooBar());
        Test o4 = Wrapper.createWrapper(o3, WrappedTest.class);
        System.out.println(o4.fooBar());
    }
}
Avatar billede arne_v Ekspert
21. maj 2011 - 20:58 #4
Jeg droppede Wrapper interfacet og getSource metoden.

Du kan putte det tilbage hvis du har brug for det.
Avatar billede kernelx Juniormester
21. maj 2011 - 22:31 #5
=========================================
public class WrappedTest extends Test{
    public String fooBar() {
        return "wrapped";
    }
}
=========================================

her wrapper jeg eksplicit "Test".
Mit problem er, at jeg i forvejen ikke ved hvad for en class der skal laves en interceptor til.

Så ovenstående kode skal på en eller anden måde genereres dynamisk.

public static <T> wrap(T instance) {
    // jeg ved ikke om "instance" er Test.class eller Test2.class
    // eller noget helt andet ???.class
    Class type = instance.getClass();
   
    // så min idee var at generere en wrapper class dynamisk med cglib
    CGLIBWrapperGenerator wrapperGenerator =
        new CGLIBWrapperGenerator(type);

    wrapperGenerator.setCallback(new MethodInterceptor());

    T wrappedInstance = wrapperGenerator.create(instance);
    return wrappedInstance;
}
Avatar billede arne_v Ekspert
22. maj 2011 - 04:30 #6

public class MixedinTest {
    public String fooBar() {
        return "mixedin";
    }
}



import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class Mixerin {
    @SuppressWarnings("unchecked")
    public static <S,M> S createMixedin(final S source, final M mixedin) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(source.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                for(Method m : mixedin.getClass().getDeclaredMethods()) {
                    if(m.getName().equals(method.getName())) {
                        return m.invoke(mixedin, args);
                    }
                }
                return methodProxy.invoke(source, args);
            }
        });
        return (S)enhancer.create();
    }
}



import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {
        Test o1 = new Test();
        System.out.println(o1.fooBar());
        Enhancer e = new Enhancer();
        e.setSuperclass(Test.class);
        e.setCallback(new TestMethodInterceptor());
        Test o2 = (Test)e.create();
        System.out.println(o2.fooBar());
        Test o3 = new Test();
        System.out.println(o3.fooBar());
        Test o4 = Wrapper.createWrapper(o3, WrappedTest.class);
        System.out.println(o4.fooBar());
        Test o5 = new Test();
        System.out.println(o5.fooBar());
        Test o6 = Mixerin.createMixedin(o5, new MixedinTest());
        System.out.println(o6.fooBar());
    }
}
Avatar billede arne_v Ekspert
22. maj 2011 - 04:31 #7
Bemaerk at jeg ikke er sikker paa at dette er den bedste maade at goere det paa, men det er en maade.
Avatar billede kernelx Juniormester
22. maj 2011 - 10:05 #8
jeg har ikke testet det endu, men hvis det virker ... perfekt!
Mange tak!
husk at skrive et eller andet som svar.
Avatar billede arne_v Ekspert
22. maj 2011 - 20:52 #9
Loesningen boer nok rafineres lidt.

if(m.getName().equals(method.getName())) {

tester kun paa metodens navn ikke paa argumenter, saa hvis der er flere metoder med samme navn og forskellige argumenter, saa gaar det i ged.

Men det kan fixes.
Avatar billede arne_v Ekspert
22. maj 2011 - 20:52 #10
Og et svar saafremt du faar det til at virke.
Avatar billede kernelx Juniormester
23. maj 2011 - 11:45 #11
Fik det ikke helt til at virke med enhance.create().

Hvis jeg har forstået det rigtigt, så er der i enden to instances af Test.class.

Denn ene som laver wrapper stuff og bliver created med enhancer.create() og den anden, som er den original class.

Nu har jeg en instance af følgende class:
==================================
public class Test2 {

    private String value;

    public Test2(String value) {
        this.value = value;
    }

    public String fooBar() {
        return fooBar;
    }

}
==================================

Den fik jeg ikke til at virke, da enhancer.create() ikke leverer constructor parameter.
Det burde være muligt at levere nogen.
Men i enden ved jeg ikke, hvad den original constructor laver med dem, og om det så er en god idee at have to instances af Test2.class

Mange tak for svaret!
Avatar billede arne_v Ekspert
24. maj 2011 - 03:35 #12
Alt kan jo loeses med lidt snille !

:-)


public class Test2 {
    private String value;
    public Test2(String value) {
        this.value = value;
    }
    public String fooBar() {
        return value;
    }
}



import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class Mixerin {
    @SuppressWarnings("unchecked")
    public static <S,M> S createMixedin(final S source, final M mixedin) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(source.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                for(Method m : mixedin.getClass().getDeclaredMethods()) {
                    if(m.getName().equals(method.getName())) {
                        return m.invoke(mixedin, args);
                    }
                }
                return methodProxy.invoke(source, args);
            }
        });
        Class[] args = source.getClass().getConstructors()[0].getParameterTypes();
        return (S)enhancer.create(args, new Object[args.length]);
    }
}



import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {
        Test o1 = new Test();
        System.out.println(o1.fooBar());
        Enhancer e = new Enhancer();
        e.setSuperclass(Test.class);
        e.setCallback(new TestMethodInterceptor());
        Test o2 = (Test)e.create();
        System.out.println(o2.fooBar());
        Test o3 = new Test();
        System.out.println(o3.fooBar());
        Test o4 = Wrapper.createWrapper(o3, WrappedTest.class);
        System.out.println(o4.fooBar());
        Test o5 = new Test();
        System.out.println(o5.fooBar());
        Test o6 = Mixerin.createMixedin(o5, new MixedinTest());
        System.out.println(o6.fooBar());
        Test2 o7 = new Test2("2");
        System.out.println(o7.fooBar());
        Test2 o8 = Mixerin.createMixedin(o7, new MixedinTest());
        System.out.println(o8.fooBar());
    }
}
Avatar billede arne_v Ekspert
24. maj 2011 - 03:37 #13
Det er nok lidt suspekt med bare at tage argument listen til foerste constructor og forsoege at sende null med over for alle argumenter.

Det kan forbedres, men det viser ideen.
Avatar billede kernelx Juniormester
25. maj 2011 - 09:26 #14
public class FooBarConstructorInterceptor
        implements ConstructorInterceptorFraCGLIB {

    public Object intercept() {
      // lad være med at invoke original constructoren
    }

}

// en konstructor interceptor som aldrig kalder originalen
enhancer.setConstructorInterceptor(new FooBarConstructorInterceptor());

public class FooBarMethodInterceptor implements MethodInterceptor {

    private Object original;

    public FooBarMethodInterceptor(Object original) {
        this.original = original;
    }

    public Object intercept(Object proxy,
                            Method method, Object[] args,
                            MethodProxy methodProxy) throws Throwable {
        Object returnValue = method.invoke(original, args);
        return returnValue;
    }
}

enhancer.setCallback(methodInterceptor);


måske er noget i denne stil muligt ...
Avatar billede kernelx Juniormester
25. maj 2011 - 09:30 #15
nej hellerikke :-(
så vil der komme problemer med
super(constructor params)
Avatar billede arne_v Ekspert
25. maj 2011 - 14:50 #16
Du kunne ikke bruge ideen i #12?
Avatar billede kernelx Juniormester
25. maj 2011 - 19:56 #17
Det hele skal fungere med hvad som helst class.
Hvis jeg nu har en class som:

========================================
public class Test2 {
    private String value;
    public Test2(String value) {
        // lav dumme ting, som kun må laves en gang i en database
        this.value = value;
    }
    public String fooBar() {
        return value;
    }
}
========================================

og denne constructor bliver kaldet to gange:
* engang til at mixin class
* engang til original class

og constructor coden bliver udføret to gange. Så kan det give problemer.
Avatar billede kernelx Juniormester
25. maj 2011 - 19:59 #18
Jeg tror, at jeg ikke vil komme uden om, at man bliver nødt til at definere et interface or en wrapper class explicit i coden, i sådan et tilfælde.
Avatar billede arne_v Ekspert
25. maj 2011 - 20:42 #19
Jeg kan ikke se nogen maade hvorpaa man kan:

* have et objekt af typen C1
* kan create et objekt af typen C2 hvor C2 er assignable til C1
* undgaa at C1 konstruktor kaldes 2 gange
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester



IT-JOB

Netcompany A/S

Managing Architect

Metroselskabet og Hovedstadens Letbane

IT Full Stack-udvikler

Cognizant Technology Solutions Denmark ApS

Graduate - Software Automation Program