Results 1 to 10 of 10

Thread: AOP use CGLib vs Interfaces

  1. #1
    Join Date
    Mar 2008
    Posts
    9

    Default 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. #2
    Join Date
    Mar 2008
    Posts
    9

    Default

    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?

  3. #3
    Join Date
    Mar 2008
    Posts
    9

    Default

    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.

  4. #4
    Join Date
    Jun 2006
    Location
    SF Bay Area, California
    Posts
    524

    Default

    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
    Ramnivas Laddad (Follow me on Twitter)
    AspectJ in Action: Enterprise AOP with Spring Applications (2nd edition). Now available!

  5. #5
    Join Date
    Mar 2008
    Posts
    9

    Default

    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.

  6. #6
    Join Date
    Jun 2006
    Location
    SF Bay Area, California
    Posts
    524

    Default

    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
    Ramnivas Laddad (Follow me on Twitter)
    AspectJ in Action: Enterprise AOP with Spring Applications (2nd edition). Now available!

  7. #7
    Join Date
    May 2007
    Location
    Saint Petersburg, Russian Federation
    Posts
    1,189

    Default

    Quote 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.

  8. #8
    Join Date
    Mar 2008
    Posts
    9

    Default

    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]

  9. #9
    Join Date
    May 2007
    Location
    Saint Petersburg, Russian Federation
    Posts
    1,189

    Default

    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.

  10. #10
    Join Date
    Apr 2006
    Posts
    6

    Default

    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •