Announcement Announcement Module
Collapse
No announcement yet.
AOP use CGLib vs Interfaces Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • AOP use CGLib vs Interfaces

    Hi guys am I able to use both methods, Interfaces AND CGLIB for class proxing with an include/exclude list managing which beans to use in either camp?

    We have some legacy code that goes not play well with CGLIB (@Resource private fields, private classes for mocking etc), and a requirement to annotate behind an interface at an implementation level and from the Advice still have access to the Annotation values.

    I believe that I have a foot in each camp is there a way out?

  • #2
    Just thinking about it now I think I'm asking the wrong question, I think I need to find out how to be able to access the Annotation values in the Advice without using CGLIB. Is this possible?

    The code looks like this
    Code:
    public @interface CustomAnnotation {
        String customValue = null;
        public String getCustomValue() { return customValue; }
    }
    Code:
    public interface Foo {
        public void doFoo();
    }
    Code:
    public class FooImpl implements Foo {
        @CustomAnnotation(customValue="")
        public void doFoo() {
            ....
        }
    }
    Code:
    @Aspect
    public class FooAdvice {
        @Before(value = "@annotation(Foo)")
        public void checkCustomValue(JoinPoint jp) throws Throwable {
            MethodSignature methodSignature = (MethodSignature)jp.getSignature();
            Method method = methodSignature.getMethod();
            CustomAnnotation annotation = method.getAnnotation(CustomAnnotation.class);
            if( annotation.getCustomValue().equals("......."){
                ....;
            }
        }
    
    }
    With the above code the annotaion.getCustomValue() throws a NullPointerException because the annotation is not present. This is overcome by either annotating BOTH the implementation and the interface (*screws up face*) or using CGLIB to proxy classes, which as I already mentioned will not play well with our other code.


    Hmmmmmm well after typing all of that out I was secretly hoping that I would work out my problem by the time I finished typing.... well it turns out I haven't, haha, so any suggestions?

    Comment


    • #3
      I presume no one has answered because this is a) really simple or b) 'the answer is in the documentation'. any chance someone can give me a lead to chase down?

      Cheers,
      Mik.

      Comment


      • #4
        A better way would be to modify your advice as follows:

        Code:
            @Before(value = "@annotation(annotation)")
            public void checkCustomValue(JoinPoint jp, CustomAnnotation annotation) throws Throwable {
                if( annotation.getCustomValue().equals("......."){
                    ....;
                }
            }
        Even better (more performant) way would be:
        Code:
            @Before(value = "execution(@com.yourco..CustomAnnotation * *(..)) && @annotation(annotation)")
             ... unchanged from the previous snippet
        -Ramnivas

        Comment


        • #5
          Thanks Ramnivas, that is a better way to write the advice and it works for one of my test cases. I mentioned that we ahve two environment, modules really, and it will work in the new code module however test cases in a legacy module (that use the same context) fail with the following message:

          Code:
          java.lang.IllegalStateException: Failed to load ApplicationContext
                  at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:201)
                  at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
                  at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
                  at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:255)
                  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:111)
                  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:148)
                  at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
                  at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
                  at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
                  at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
                  at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
                  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:97)
                  at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
                  at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
                  at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:165)
                  at org.apache.maven.surefire.Surefire.run(Surefire.java:107)
                  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                  at java.lang.reflect.Method.invoke(Method.java:597)
                  at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:289)
                  at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:993)
          Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ldapAuthProvider': Injection of resource fields failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userSecurityServicesImpl': Injection of resource fields failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'OurBean' defined in file [....path/OurBean.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class au.com.....impl.OurBean]: Common causes of this problem include using a final class or a non-visible class; nested exception is net.sf.cglib.core.CodeGenerationException: java.lang.ClassCastException-->java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
          This is consistent with the behavior using the original code. I believe this is a product of some of the classes using private classes, non interface fronted services etc. Unfortunately this code we cannot change. The solution needs to play well with both sets of code.

          Comment


          • #6
            This kind of situation (legacy code that cannot be modified) often calls for AspectJ weaving, where you have much better liberty in terms of the structure of the target classes.

            -Ramnivas

            Comment


            • #7
              Originally posted by MikGan View Post
              ...
              This is consistent with the behavior using the original code. I believe this is a product of some of the classes using private classes, non interface fronted services etc. Unfortunately this code we cannot change. The solution needs to play well with both sets of code.
              Error message you provided ('nested exception is net.sf.cglib.core.CodeGenerationException: java.lang.ClassCastException-->java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType') makes me suspect that you use generics-related processing at your application code.

              I mean that you retrieve type via 'getGenericSuperclass()'/'getGenericInterfaces()' and cast it to 'ParameterizedType'. Is that assumption correct? Provide the code that you use for that if any.

              Comment


              • #8
                You are correct, on occasion we use code as below to determine the class type of a generic template.

                Code:
                (Class<M>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]

                Comment


                • #9
                  Ok, the problem is clear then - spring aop creates cglib proxy for the target bean. Cglib way is to create a subclass for the target bean class at runtime, so, call to getGenericSuperclass() returns different value in comparison with non-aop environment.

                  The easiest way to work that out is to use smart type argument resolution, i.e. avoid direct calls like '((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeAr guments()[0]'.

                  I created mini framework that ideally suits that need - Improved type arguments resolving. You can exploit it at your application in order to eliminate the problem.

                  Comment


                  • #10
                    Hi guys,

                    Here is some code to fix the problem. It's less elegant than Denis' framework, but it helps understanding the problem.

                    Code:
                    public abstract class GenericDao<T extends IdEntity<K>, K extends Serializable> {
                    	
                        public Class<T> entityClass;
                    
                        @PersistenceContext protected EntityManager em;
                    
                        public GenericDao() {
                            Class bankDaoClass;  // Your dao class, i.e. BankDao (extending GenericDao<Bank,Long>)
                        	if (this.getClass().getSuperclass() == GenericDao.class) { // We are instantiated without CGLIB: new BankDao()
                                bankDaoClass = this.getClass();
                        	} else {  // Spring instantiates as CGLIB class extending our concrete DAO class (as BankDao): new BankDaoCGLIB() 
                                Class cglibConcreteDaoClass = this.getClass();
                                bankDaoClass = cglibConcreteDaoClass.getSuperclass();
                        	}
                            ParameterizedType genericDaoType = (ParameterizedType) bankDaoClass.getGenericSuperclass();
                            Type entityType =  (genericDaoType).getActualTypeArguments()[0];
                            entityClass = (Class<T>) entityType;
                        }
                       ...
                    }
                    Code:
                    class BankDao extends GenericDao<Bank, Long> { // No interface, thanks god!
                       ....
                    }
                    John Rizzo - JavaBlackBelt.com

                    Comment

                    Working...
                    X