CGLIB无法拦截超类/超接口中的方法

mai*_*nas 8 java proxy cglib

可能是我没有足够的思考,或者答案真是难以捉摸.快速方案(尝试代码.它编译).

考虑遗留接口

public interface LegacyInterfaceNoCodeAvailable{
    void logInfo(String message);
}
Run Code Online (Sandbox Code Playgroud)

考虑上面的接口的遗留实现

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我作为这个雄心勃勃的人进入并为"新"系统编写了一个类但是在"Legacy"框架内运行,因此我必须扩展遗留基类.

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!!");
    }
}
Run Code Online (Sandbox Code Playgroud)

一切都很好,就像你期望的那样:

SpankingShiny shiny = new SpankingShiny();
shiny.executeSomething();
Run Code Online (Sandbox Code Playgroud)

上面的代码产生(如预期的那样):

bustin' chops, old-school style
I'm the King around here now
this new stuff rocks!!
Run Code Online (Sandbox Code Playgroud)

现在如您所见,'System.out.println()'忠实地打印所需的输出.但我希望用记录器替换'System.out.println()'.

问题:

我无法让CGLIB代理拦截'logInfo(string)'的方法,并让它通过记录器打印出我想要的消息(顺便说一下,我已经完成了日志记录配置).那个方法调用'显然'没有命中代理.

码:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

主要方法:

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

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

        SpankingShiny shinyO = SpankingShinyProxy.createProxy(shiny);
        shinyO.executeSomething();
    }
Run Code Online (Sandbox Code Playgroud)

上面的代码产生(不是预期的):

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!!
Run Code Online (Sandbox Code Playgroud)

我哪里会出错?

谢谢!

Raf*_*ter 1

好吧,首先,你很幸运你的代理没有被击中。如果您引用 中的实际代理intercept,则最终会陷入无限循环,因为您的反射方法 incocation 将由相同的 调度SpankingShinyProxy。一次又一次。

代理不起作用,因为您只是将executeSomething代理上的方法调用委托给某个未代理的对象。您不得使用realObj. 所有方法调用都必须由您的代理分派,并且由代理调用的那些方法调用也必须命中代理本身!

intercept将方法中的最后一行更改为methodProxy.invokeSuper(proxyObj, args). 然后,使用Enhancer. 如果您的构造函数SpankingShiny不需要参数,create则无需任何参数即可调用。否则,将通常提供给构造函数的对象提供给该create方法。然后,只使用你从中获得的物体create,你就可以了。

如果您想了解有关 cglib 的更多信息,您可能需要阅读这篇博客文章:http://mydailyjava.blogspot.no/2013/11/cglib-missing-manual.html