Dynamic Proxy und Annotationen

che777

Mitglied
Hallo,

habe folgendes Problem mit Annotationen in Verbindung mit der java.lang.reflect.Proxy -Klasse:

Java:
public class ProxyTest implements InvocationHandler
{
  private Object obj;

  private ProxyTest(Object obj) {
    this.obj = obj;
  }

  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
    return m.invoke(obj, args);
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  
  static public class AnyClassImpl
  {
    public void foo() {
    }
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  public static void main(String[] args) throws Exception
  {
    AnyClassImpl obj = new AnyClassImpl();
    
    obj = (AnyClassImpl) Proxy.newProxyInstance(
      obj.getClass().getClassLoader(),
      obj.getClass().getInterfaces(),
      new ProxyTest(obj));
  }
}

die Methode Proxy.newProxyInstance(...) liefert mir hier folgende Exception:

Code:
Exception in thread "main" java.lang.ClassCastException: $Proxy0 cannot be cast to ioc.ProxyTest$AnyClassImpl

füge ich ein Interface hinzu und ändere die Instanzierung des Objektes folgendermassen

Java:
public class ProxyTest implements InvocationHandler
{
  private Object obj;

  private ProxyTest(Object obj) {
    this.obj = obj;
  }

  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
    return m.invoke(obj, args);
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  
  public interface AnyClass 
  {
    void foo();
  }
  
  static public class AnyClassImpl implements AnyClass
  {
    public void foo() {
    }
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  public static void main(String[] args) throws Exception
  {
    AnyClass obj = new AnyClassImpl();
    
    obj = (AnyClass) Proxy.newProxyInstance(
      obj.getClass().getClassLoader(),
      obj.getClass().getInterfaces(),
      new ProxyTest(obj));

    System.err.println(obj.getClass());
    
  }
}

bekomme ich war keine ClassCastException mehr, allerdings liefert obj.getClass() class $Proxy0.

ich annotiere die Methode foo() um in Folge darauf zu reflektieren (z.B. DI)

Java:
...

  static public class AnyClassImpl implements AnyClass
  {
    @AnyAnnotation
    public void foo() {
    }
  }

...

    final Class clazz = obj.getClass();
    for(Method m : clazz.getDeclaredMethods()) {
      System.err.println(m.getName());
      if(m.isAnnotationPresent(AnyAnnotation.class)) {
        throw new Exception("AnyAnnotation present");
      }
    }

...
ist die Methode foo() zwar gelistet,

Code:
  foo
  equals
  toString
  hashCode

die erwartete Exception bleibt allerdings aus...

1. Frage: warum benötige ich ein Interface, warum reicht die Implementierung nicht aus?

2. Frage: Was mach ich hier falsch, dass die Annotation nicht gefunden wird, wenn ich das
Object hinter einem Proxy verstecke?


Danke und mfG,
Christian
 
T

Tomate_Salat

Gast
1.) Hier noch eine Ergänzung zu meinen nachrednern (also die unter mir):
Mal ein Beispiel aus meinem aktuellen Projekt (der Proxy kommt hier auch zum Einsatz):
Java:
MyFace face=JMapper.createFromMap(props, MyFace.class);
System.out.println(face.getClass());
for(Class<?> cl:face.getClass().getInterfaces())
	System.out.println(cl.getName())
Ausgabe:
Code:
class de.jfruit.factory.demo.$Proxy0
de.jfruit.factory.demo.MyFace

Bei deinem ersten Beispiel, kennt [c]AnyClassImpl[/c] keine Interfaces, weswegen folgendes gilt:
Java:
AnyClassImpl obj=new AnyClassImpl();
obj.getClass().getInterfaces().length; // = 0

was bedeutet: (wie SlaterB sagt) Proxy implementiert kein Interface, weswegen ein casten nicht möglich ist.

2.) Annotationen die du während der Laufzeit auswerten willst, müssen mit [c]@Retention(RetentionPolicy.RUNTIME)[/c] annotiert werden:

Java:
@Retention(RetentionPolicy.RUNTIME)
public @interface AnyAnnotation
{	
}
 
Zuletzt bearbeitet von einem Moderator:

Noctarius

Top Contributor
Die ClassCastException fliegt, weil du keine Instanz der AnyClassImpl mehr hast, sondern eine Proxy-Instanz mit Interfaces dieser Klasse.
 
S

SlaterB

Gast
ein Proxy ist in erster Linie ein Wrapper, wie eine Einkaufstüte um den Kohlkopf herum,
der Proxy erbt nicht von der Ursprungsklasse, sondern ist eine neue eigene Klasse, die immerhin dieselben Interface implementiert,
technisch wäre Vererbung vielleicht möglich, ich schätze so ist es aber sauberer

> bekomme ich war keine ClassCastException mehr, allerdings liefert obj.getClass() class $Proxy0.

das passt doch zusammen


--

alles worauf sich der Proxy bezieht sind die Interface, verhält sich exakt so, Annotations der aktuellen Klasse sollten dort keinen Einfluss haben,
ok richtig was neues dazu sagen kann ich mit diesen Worten nicht ;)

Annotations passen generell überhaupt nicht in die kleine abgeschlossene Welt von Interface, Klassen, Vererbung, OO usw.
 

che777

Mitglied
Aha, Danke für die Antworten...

das mit dem Interface ist mir nun klar, den Proxy wollte ich eigentlich als Interceptor missbrauchen, gibt es hier eine bessere Lösung?
...und die Annotation ist bereits mit RetentionPolicy.RUNTIME annotiert:

Java:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AnyAnnotation
{
}

Die Annotation benötigte ich für etwaige Dependency Injection, dafür eignen sie sich imo recht gut.

Danke und mfG,
Christian
 
T

Tomate_Salat

Gast
Java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;

public class Demo 
{
	@Retention(RetentionPolicy.RUNTIME)
	public static @interface AnyAnnotation {}
	
	@AnyAnnotation
	public void foo() {}
	
	public static void main(String[] args) throws Exception 
	{
		for(Method m:Demo.class.getMethods()) {
			if(m.isAnnotationPresent(AnyAnnotation.class))
				throw new Exception(AnyAnnotation.class.getSimpleName() + " is present");
			else
				System.out.println(m.getName());
		}
	}
}

funktioniert bei mir. Verschluckst du vllt die Exception?
 

che777

Mitglied
der Unterschied ist folgender:

verwende ich

Java:
final Class clazz = AnyClassImpl.class;

konform Demo.class.getMethods() in deinem Test - funktionierts, ich hole mir aber die Klasse vom Objekt

Java:
final Class clazz = obj.getClass();

und da ist eben der Proxy davor, dort wird zwar foo() (da sie ja im interface definiert ist) gefunden, die Annotation in der Implementierung wird natürlich nicht gefunden - wie soeben gelernt:)

Ein Annotieren der Methode im Interface bringt auch nix, muss mir wohl was anderes überlegen...
 
T

Tomate_Salat

Gast
Tatsache, da habe ich nicht drauf geachtet, aber vllt wäre das eine Möglichkeit für dich:
Java:
public class InterfaceDemo 
{
	public static void main(String[] args) 
	{
		SampleFace face=JMapper.createFromMap(new Properties(), SampleFace.class);
		
		for(Class<?> interf:face.getClass().getInterfaces()) {
			System.out.println("Check Interface: " + interf.getName());
			for(Method m : interf.getMethods()) {
				if(m.isAnnotationPresent(Link.class))
					System.out.println(m.getName());
			}
		}
	}
	
	public static interface SampleFace
	{
		@Link(key="name")
		String getName();
		
		@Link(key="name", type=MethodType.SETTER)
		void setName(String name);
	}
}

Ausgabe:
Code:
Check Interface: de.jfruit.factory.demo.InterfaceDemo$SampleFace
getName
setName
 

che777

Mitglied
@Tomate_Salat: Danke, Beispiel werd ich mal ausprobieren..

@Noctarius: Das Objekt ist eh ein Member des Handlers, allerdings hab ich an anderer Stelle nur mehr das (geproxte) Objekt in der Hand, wie komm ich da an den InvocationHandler repektive den Endpunkt (die Impl.) ran?
 

Noctarius

Top Contributor
Du bist doch im InvocationHandler wenn die Methode ausgeführt wird. Dafür ist die invoke Methode doch da oder verstehe ich nur deinen Verwendungszweck nicht?
 

che777

Mitglied
OK, wie oben erwähnt möchte ich den Proxy als Interceptor verwenden, davon möchte ich mehrere hintereinanderschalten können um das Objekt am Ende der Kette (CoC) zu verwalten/überwachen - im einfachsten Fall die Methodenaufrufe zu tracen. Die Interceptoren haben nix mit den Annotationen zu tun, letztere benötige ich für die Dependency Injection.
Grundsätzlich soll's ein Mini-Micki-Maus-Container werden (LW-Container wie JBOSS-MicroContainer, ... sind hier zu übergewichtig, von einem AS ganz zu Schweigen).

Allerdings stell ich mir gerade die Frage, warum ich nicht

1. die Objekte instanziere,
2. die dependencies injecte, und
3. erst dann die proxies vorschalte.

Obige Fragen sind aus einem kurzen Test entstanden, warum die Annotationen auf die proxies nicht funktionieren ist mir nun klar, vl. sollte ich nun doch beginnen mir ein Konzept zu überlegen:)
 

che777

Mitglied
das ist der Punkt: brauch ja nur ein mini-framework für eine Standalone-Applikation, kein JBossMicrocontainer, PicoContainer, Spring, etc... die Lernkurve ist dann doch etwas zu steil und 98% der Funktionalität die i.e. Spring bietet, benötig ich ja gar nicht;)
 

tfa

Top Contributor
Für Spring brauchst du keinen Application-Server. Es ist recht modular aufgebaut, was dich nicht interessiert, kannst du weglassen. Bevor ich anfange, selber sowas zusammen zu frickeln, würde ich eher Spring verwenden. Aber jeder so, wie er mag...
 

Niki

Top Contributor
ich hab jetzt nciht den ganze thread gelesen. aber könnte sein, dass du sowas suchst?

Java:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Traceable {

}

Java:
public class Worker implements Runnable {
	
	@Traceable
	public void run() {
		System.out.println("Worker#run");

	}
}

Java:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class AnnotationInvocationHandler implements InvocationHandler{
	
	private Object target = null;
	
	public AnnotationInvocationHandler(Object target){
		this.target = target;
	}
	
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		
		
		Class[] argTypes = new Class[args == null ? 0 : args.length];
		
		Method implMethod = target.getClass().getMethod(method.getName(), argTypes);
		boolean trace = implMethod.isAnnotationPresent(Traceable.class);
		if(trace){
			System.out.println("before invocation");
		}
		Object o = method.invoke(target, args);
		if(trace){
			System.out.println("after invocation");
		}
		
		
		return o;
	}

}

Java:
import java.lang.reflect.Proxy;

public class ProxyFactory {

	
	public static <T> T createProxy(T object){
		Class[] interfaces = object.getClass().getInterfaces();
		
		return (T)Proxy.newProxyInstance(object.getClass().getClassLoader(), interfaces, new AnnotationInvocationHandler(object));
	}
}

Java:
public class ProxyTest {
	public static void main(String[] args) {
		Runnable r = ProxyFactory.createProxy(new Worker());
		r.run();
	}
}
 

che777

Mitglied
@Niki: leider nicht ganz:) Vielen Dank aber für die Anregung...

Grundsätzlich löst das Durchforsten der Interfaces im Beispiel von Tomate_Salat mein Problem.
 

Ähnliche Java Themen

Neue Themen


Oben