Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CGLIB not able to intercept methods in a superclass/superinterface

Tags:

java

proxy

cglib

May be I'm not thinking hard enough or the answer is really elusive. Quick scenario (Try the code out. It compiles).

Consider a legacy interface

public interface LegacyInterfaceNoCodeAvailable{
    void logInfo(String message);
}

The consider a legacy implementation of the interface above

public abstract class LegacyClassNoCodeAvailable implements LegacyInterfaceNoCodeAvailable{

    public abstract void executeSomething();

    public void rockItOldSchool(){
        logInfo("bustin' chops, old-school style");
    }

    @Override
    public void logInfo(String message){
        System.out.println(message);
    }
}

Now I come in as this ambitious person and writes a class for a 'New' system but that runs inside the 'Legacy' framework, hence I have to extend the legacy base class.

public class lass SpankingShiny extends LegacyClassNoCodeAvailable{

    public void executeSomething(){
        rockItOldSchool();
        logInfo("I'm the King around here now");
        System.out.println("this new stuff rocks!!");
    }
}

Everything works great, just like you would expect:

SpankingShiny shiny = new SpankingShiny();
shiny.executeSomething();

The above code yields (as expected):

bustin' chops, old-school style
I'm the King around here now
this new stuff rocks!!

Now as you can see, the 'System.out.println()' faithfully prints the desired output. But I wish to replace the 'System.out.println()' with a logger.

Problem:

I'm unable to have the CGLIB proxy intercept the method to 'logInfo(string)' and have it print out my desired message through a logger (I have done the logging configuration right by the way). That method invocation 'apparently' does not hit the proxy.

Code:

public class SpankingShinyProxy implements MethodInterceptor{

    private SpankingShiny realShiny;
    private final Logger logger = Logger.getLogger(SpankingShinyProxy.class);

    public SpankingShinyProxy(SpankingShiny realShiny) {
        super();
        this.realShiny = realShiny;
    }

    @Override
    public Object intercept(Object proxyObj, Method proxyMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
        String methodName = proxyMethod.getName();
        if("logInfo".equals(methodName)){
            logger.info(methodParams[0]);
        }
        return proxyMethod.invoke(realShiny, methodParams);
    }

    public static SpankingShiny createProxy(SpankingShiny realObj){
        Enhancer e = new Enhancer();
        e.setSuperclass(realObj.getClass());
        e.setCallback(new SpankingShinyProxy(realObj));
        SpankingShiny proxifiedObj = (SpankingShiny) e.create();
        return proxifiedObj;
    }
}

Main method:

public static void main(String... args) {

        SpankingShiny shiny = new SpankingShiny();
        shiny.executeSomething();

        SpankingShiny shinyO = SpankingShinyProxy.createProxy(shiny);
        shinyO.executeSomething();
    }

The above code yields (NOT as expected):

bustin' chops, old-school style
I'm the King around here now
this new stuff rocks!!
bustin' chops, old-school style
I'm the King around here now
this new stuff rocks!!

Where would I be going wrong?

Thanks!

like image 515
mainas Avatar asked Nov 03 '13 01:11

mainas


People also ask

What is the use of cglib in spring?

Cglib is used extensively by the Spring framework. One example of using a cglib proxy by Spring is adding security constraints to method calls. Instead of calling a method directly, Spring security will first check (via proxy) if a specified security check passes and delegate to the actual method only if this verification was successful.

Why can’t we call a particular method from a specific class?

This is because they are defined in subclass not in the superclass. If we know the exact runtime type of an object, then this approach is better. Using this approach, we can also call a particular object specific methods.

What is cglib (code generation library)?

In this article, we will be looking at the cglib (Code Generation Library) library. It is a byte instrumentation library used in many Java frameworks such as Hibernate or Spring. The bytecode instrumentation allows manipulating or creating classes after the compilation phase of a program. 2. Maven Dependency

What is beangenerator in cglib?

Another useful construct from the cglib is a BeanGenerator class. It allows us to dynamically create beans and to add fields together with setter and getter methods. It can be used by code generation tools to generate simple POJO objects: 6. Creating Mixin A mixin is a construct that allows combining multiple objects into one.


2 Answers

I had the same problem. In my case, the realObj was a proxy itself (a Spring Bean - a @Component).

So what I had to do was change the .setSuperClass() part in:

Enhancer e = new Enhancer();
e.setSuperclass(realObj.getClass());
e.setCallback(new SpankingShinyProxy(realObj));
SpankingShiny proxifiedObj = (SpankingShiny) e.create();

I changed:

e.setSuperclass(realObj.getClass());

To:

e.setSuperclass(realObj.getClass().getSuperClass());

This worked because, as said, realObj.getClass() was a CGLIB proxy itself, and that method returned a crazy-name-CGLIB-generated class, such as a.b.c.MyClass$$EnhancerBySpringCGLIB$$1e18666c. When I added .getSuperClass() it returned the class it should have been returning in the first place.

like image 60
acdcjunior Avatar answered Oct 16 '22 10:10

acdcjunior


Well, first of all, you are lucky that your proxy is not hit. If you were referencing the actual proxy within intercept, you would end up with an endless loop since your reflective method incocation would get dispatched by the same SpankingShinyProxy. Again and again.

The proxy is not working since you simply delegate the method call executeSomething on your proxy to some unproxied object. You must not use realObj. All method calls must be dispatched by your proxy, also those method calls that are invoked by the must hit the proxy itself!

Change the last line in your intercept method to methodProxy.invokeSuper(proxyObj, args). Then, construct your object by using the Enhancer. If your constructor for SpankingShiny does not need arguments, calling create without any arguments if fine. Otherwise, supply the objects you would normally supply to the constructor to the create method. Then, only use the object that you get from create and you are good.

If you want more information on cglib, you might want to read this blog article: http://mydailyjava.blogspot.no/2013/11/cglib-missing-manual.html

like image 20
Rafael Winterhalter Avatar answered Oct 16 '22 10:10

Rafael Winterhalter