bineanzhou / google-guice

Automatically exported from code.google.com/p/google-guice
Apache License 2.0
0 stars 0 forks source link

AOP classes don't get the annotations of the superclass #201

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
@ME :
I'm quite suprised... I annoted a method @MyAnnotation, in order to
intercept calls of this method. My problem is when I instanciate an object
which has a such annotation using guice. when i search the annoted method
through reflection, and i call isAnnotationPresent(MyAnnotation.class),
this method returns false!

@jesse@swank.ca: Under the hood, Guice creates a subclass of TotoImpl
in order to perform method interception.

For the most part, this is completely transparent to
the developer, but there's a few consequences. getClass()
doesn't return TotoImpl.class. This constraints how your
equals() method must be implemented.

I also expect Serializable won't interoperate with
method interception.

The workaround in this case is to use TotoImpl.class
rather than toto.getClass() to introspect on your
type.

Original issue reported on code.google.com by anthony....@gmail.com on 30 May 2008 at 9:41

GoogleCodeExporter commented 9 years ago
package guice;

import static com.google.inject.matcher.Matchers.annotatedWith;
import static com.google.inject.matcher.Matchers.any;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.ImplementedBy;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class AnnotationTest {

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

       }

       @ImplementedBy(TotoImpl.class)
       interface Toto {
               String getBidule();

               void setBidule(String bidule);
       }

       class TotoImpl implements Toto {

               private String bidule;

               @Inject
               public TotoImpl() { }

               public String getBidule() {
                       return bidule;
               }

               @MyAnno
               public void setBidule(String bidule) {
                       this.bidule = bidule;
               }
       }

       static class MyInterceptor implements MethodInterceptor {

               public Object invoke(MethodInvocation mi) throws Throwable {
                       return mi.proceed();
               }

       }

       static class MyModule extends AbstractModule {
               public void configure() {
                       // Comment or uncomment this line to see the problem:
                       // When this binding is uncomment, bellow main method
                       // will print 'false' otherwise 'true'
                       bindInterceptor(any(), annotatedWith(MyAnno.class), new
MyInterceptor());
               }
       }

       public static void main(String[] args) throws Throwable {
               Injector injector = Guice.createInjector(new MyModule());
               Toto toto = injector.getInstance(Toto.class);
               Method setter = toto.getClass().getMethod("setBidule",
                               new Class[] { String.class });
               boolean isPresent = setter.isAnnotationPresent(MyAnno.class);
               System.out.println("Present : " + isPresent);
       }
}

Original comment by anthony....@gmail.com on 30 May 2008 at 9:41

GoogleCodeExporter commented 9 years ago
I'd like to know more -- is there a particular use case, tool or library that 
requires this?

Original comment by limpbizkit on 3 Jun 2008 at 9:49

GoogleCodeExporter commented 9 years ago
As I noted on the mailing list, there's a workaround:

You could use an @Inherited-enabled annotation at the class level, and sneak the
class in like that:

  @Retention(RUNTIME)
  @Target(TYPE)
  @Inherited
  public @interface TheType {
    Class<?> value();
  }

  @TheType(TotoImpl.class)
  static class TotoImpl implements Toto {
  ...
  }

    Method setter =
              toto.getClass().getAnnotation(TheType.class).value()
                    .getMethod("setBidule", new Class[] { String.class });
    boolean isPresent = setter.isAnnotationPresent(MyAnno.class);
    System.out.println("Present : " + isPresent);

I'm also curious to see what the use case is though.

Original comment by robbie.v...@gmail.com on 3 Jun 2008 at 5:13

GoogleCodeExporter commented 9 years ago
My Use Case is quite simple, I add some annotations in my business classes like
@Property, @GenerateEvent, @Children, @Parent, ... That allows to add extra
information  to make usage easier, to perform some tests to print out reports 
about
object model structure...

Original comment by anthony....@gmail.com on 3 Jun 2008 at 7:26

GoogleCodeExporter commented 9 years ago
Chris - I'm totally ignorant of cglib's API, so I'll ask you. Would fixing this 
require a whole lot of code?

Original comment by limpbizkit on 4 Jun 2008 at 12:19

GoogleCodeExporter commented 9 years ago
It would require code to copy the annotations from the superclass to the 
subclass. It
would be a significant effort, and would have to be optional as it is not
backwards-compatible, which means the option would have to be propagated into 
the
Guice API somehow, which would be ugly. It is usually easier to just modify your
application code to look "one level up" when dealing with a proxy. It might be
helpful if Guice had a static method that could tell you if an object is from a 
proxy
class, or maybe it could just give you back the unproxied class regardless.

Original comment by chris.no...@gmail.com on 4 Jun 2008 at 12:49

GoogleCodeExporter commented 9 years ago
See also issue 101.

Original comment by limpbizkit on 8 Jun 2008 at 11:28

GoogleCodeExporter commented 9 years ago
When integrating to existing frameworks annotation inheritence on the subclass 
is
very useful. For example, the swing application framework uses @Action on 
methods
that you can bind to a JButton. To make it work with Guice you'll have to 
explicitly
pass in the class that contains the actions, in stead of "this" as Chris 
suggests.
This works, but it also implicates that you can't just enable some AOP aspect 
without
risking to break existing code.

Original comment by botte...@gmail.com on 29 Jun 2008 at 2:18

GoogleCodeExporter commented 9 years ago
FYI, Jersey has a new hook that allows me to pass it the unproxied class, it 
then
examines *that* class for Jersey-specific annotations.

Original comment by gili.tza...@gmail.com on 24 Nov 2008 at 4:19

GoogleCodeExporter commented 9 years ago
In my particular use-case I had a problem with Struts2 @SkipValidation action 
method
annotation when I used other interceptors... at the time I was convinced that
@SkipValidation wasn't set on the proxy class method. 

http://struts.apache.org/2.x/struts2-core/apidocs/org/apache/struts2/interceptor
/validation/SkipValidation.html

Original comment by pavel.jb...@gmail.com on 24 Nov 2008 at 5:28

GoogleCodeExporter commented 9 years ago
I assume this issue also implies that parameter annotations are not copied to 
the proxy?

I tried intercepting some service methods that use annotations to define 
parameter
names for JSON-RPC clients, but the required annotations were all gone.  
Looking "up
a level" doesn't seem like a viable option in this case.

Original comment by roger.go...@gmail.com on 17 Sep 2009 at 1:31

GoogleCodeExporter commented 9 years ago
I've been very frustrated by this shortcoming myself: my team is using 
JPA/Hibernate with Guice. To facilitate a "rich domain model" (ie, domain-logic 
directly in our entity-classes), I'm using a Hibernate Interceptor to augment 
Hibernate's Entity-instantiation, and constructing the entity instances with a 
Guice Injector.  This enables Guice AOP and injection in our Entity instances: 
we can annotate model-methods with @Transactional (a-la warp-persist), and we 
can @Inject dependencies into our Entities directly.  Sweet!

This works beautifully, EXCEPT that the standard JPA method annotations for 
lifecycle-event observers (@PrePersist, @PreUpdate, etc) aren't retained on the 
Guice-enhanced entity instances.  Surprise!  This caused a subtle bug that only 
appeared when we added a guice MethodInterceptor to an existing entity class... 
 Suddenly, our entity-lifecycle callbacks stopped being invoked! Thank goodness 
for integration-tests.

I'm currently building a workaround involving a class-level EntityListener that 
will look up-the-tree to find and invoke persistence-event annotations, but 
this is very cumbersome and, more importantly, doesn't feel like it should be 
*my* concern: Guice is constructing my objects and providing "magic" AOP, but 
the abstraction is leaking here.

If it's too expensive to perform method-annotation-preservation by default, it 
seems appropriate that the framework should at least support it *upon 
request*, as a binding configuration..?

Annotation-based tools and functionality continue to become more pervasive; I 
anticipate the impact of this subtle failure may become more common and 
expensive for guice-enhanced developers.  Plz fix.

Original comment by joshgr@gmail.com on 22 Aug 2010 at 12:44

GoogleCodeExporter commented 9 years ago

Original comment by cgruber@google.com on 16 Jul 2012 at 6:13

GoogleCodeExporter commented 9 years ago
I'm facing the very same problem joshgr described and it's really frustrating. 
I just add a method annotation and Guice stops "coping" some (others) 
annotations to the proxy object. Please, fix it!

Original comment by leitao.o...@gmail.com on 16 Feb 2014 at 5:50

GoogleCodeExporter commented 9 years ago
+1 for this. Appreciate the complexity of copying the annotations to the proxy, 
but without it there are a number of unexpected side affects. This is a deal 
breaker unfortunately.

Original comment by jameshar...@gmail.com on 8 Aug 2014 at 7:12

GoogleCodeExporter commented 9 years ago
The code.google.com guice project has migrated to GitHub.  This issue site is 
no longer being used.  Please use https://github.com/google/guice/issues/201 
instead.

Original comment by sberlin on 8 Aug 2014 at 11:28