Setter eines Objektes überwachen

Hallo,

ein Java DynamicProxy (java.lang.reflect.Proxy) generiert zur Laufzeit Bytecode für eine neue Klasse die die angegebenen Interfaces implementiert und alle Aufrufe an den angegebenen InvocationHandler dispatched. Der daraus resultierende Proxy kann dann über die implementierten Interfaces angesprochen werden (man kann in auf die angegebenen Interfaces casten). Somit ist der Proxy mit allen Methoden ansprechbar die in den Interfaces definiert sind (natürlich werden alle Methoden von java.lang.Object auch dispatched).

Startet man das oben genannte Beispiel (PropertyChangeAwareProxyExample ) mit dem JVM Property:
Code:
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
So findet man im classpath-root die folgende generierte Klasse:

Java:
// Decompiled by DJ v3.7.7.81 Copyright 2004 Atanas Neshkov  Date: 27.05.2007 18:05:09
// Home Page : http://members.fortunecity.com/neshkov/dj.html  - Check often for new version!
// Decompiler options: packimports(3) 

import de.tutorials.IDomainObject;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
    implements IDomainObject
{

    public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }

    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int getAnotherProperty()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m6, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void setAnotherProperty(int i)
    {
        try
        {
            super.h.invoke(this, m5, new Object[] {
                Integer.valueOf(i)
            });
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void setProperty(String s)
    {
        try
        {
            super.h.invoke(this, m3, new Object[] {
                s
            });
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String getProperty()
    {
        try
        {
            return (String)super.h.invoke(this, m4, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static Method m1;
    private static Method m6;
    private static Method m5;
    private static Method m0;
    private static Method m3;
    private static Method m4;
    private static Method m2;

    static 
    {
        try
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m6 = Class.forName("de.tutorials.IDomainObject").getMethod("getAnotherProperty", new Class[0]);
            m5 = Class.forName("de.tutorials.IDomainObject").getMethod("setAnotherProperty", new Class[] {
                Integer.TYPE
            });
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m3 = Class.forName("de.tutorials.IDomainObject").getMethod("setProperty", new Class[] {
                Class.forName("java.lang.String")
            });
            m4 = Class.forName("de.tutorials.IDomainObject").getMethod("getProperty", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

Gruß Tom
 
Hallo,

das "Umleiten" für fehlende Methoden könnte man Beispielsweise so machen:
Java:
/**
 * 
 */
package de.tutorials;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author Tom
 * 
 */
public class CreateDynamicMethodExample {

    /**
     * @param args
     */
    public static void main(String[] args) {
        IFoo foo = (IFoo) Proxy.newProxyInstance(IFoo.class.getClassLoader(),
                new Class[] { IFoo.class },
                new TargetAwareInvocationHandler<IBubu>(new Bubu()));

        foo.operation1();
        foo.operation2();
        foo.operation3();

    }

    static interface IBubu {
        void operation1();

        void operation2();
    }

    static interface IFoo extends IBubu {
        void operation3();
    }

    static class Bubu implements IBubu {
        @Override
        public void operation1() {
            System.out.println("operation1");
        }

        @Override
        public void operation2() {
            System.out.println("operation2");
        }
    }

    static class TargetAwareInvocationHandler<TTargetType> implements
            java.lang.reflect.InvocationHandler {
        TTargetType target;

        public TargetAwareInvocationHandler(TTargetType target) {
            super();
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {

            if (isMethodDeclaredFor(getTarget().getClass(), method)) {
                return method.invoke(getTarget(), args);
            } else {
                System.out.println("handle missing method: " + method);
                return null;
            }

        }

        private boolean isMethodDeclaredFor(Class<?> clazz, Method method) {
            try {
                return null != clazz.getDeclaredMethod(method.getName(), method
                        .getParameterTypes());
            } catch (NoSuchMethodException e) {
                return false;
            }

        }

        public TTargetType getTarget() {
            return target;
        }

        public void setTarget(TTargetType target) {
            this.target = target;
        }

    }
}
... nur mal so als Spielerei ;-)

Gruß Tom
 
Schöne Lösung. Mit dynamisch erzeugten Interfaces und per Reflection könnte man damit sicher ein AUTOLOAD hinbekommen. ;)

Wenn ich mal viel Zeit habe versuche ich es...
 
Hallo,
erstmal danke für eure Hilfe, aber ich mache es jetzt doch mit dem Observer. Denn das hier vorgeschlagene ist zwar richtig tricky aber es mach die Sache auch nicht wirklich einfacher.
Aber das hier war auf jeden Fall nice to know.
THX
 
Aus Design Sicht ist die Proxy Geschichte besser, weil das Domain Objekt unberührt bleibt. Proxies sehen vielleicht "trickig" aus, aber sind heutzutage in der j2ee Welt allgegenwärtig.
 
Zurück