我的博客
  • 关于Java的动态代理

关于Java的动态代理

在Spring中我们可以见到有以下一些写法

public class Foo {
    @before("....")
    public void foo() {

    }
}

这样,在执行Foo.foo()时,会先执行before()中的内容。 如果有Python 的使用经验,会发现和Python的装饰器很像。但Java想要实现在原有类的基础上添加新功能却不那么容易,要使用代理模式。 代理分为动态代理和静态代理 动态代理在运行时生成一个代理类。 例如,我们有一个需求,作用为

  1. 打招呼
  2. 说再见

先看一下Python的简单实现

def greeting():
    print("morning")


def bye():
    print("bye")


if __name__ == '__main__':
    greeting()
    bye()

如果使用Java,首先创建一个接口,要有两个方法

public interface Polite {
    public void greeting() throws Throwable;
    public void bye() throws Throwable;
}

接口实现类

public class Person implements Polite {
    String name;
    public EnglishGreeting(String name) {
        this.name = name;
    }
    @Override
    public void greeting() {
    System.out.println("Morning "+name);
    }

    @Override
    public void bye() {
        System.out.println("Bye "+name);
    }

为什么Java需要先定义接口而不是直接实现类?因为接下来实现动态代理的方法是基于接口的。 现在我们需要第三个功能,我们希望在打招呼和说再见之前要保持微笑。静态方式实现代理需要提前创建一个类,并且将需要修改的类传给这个代理类,编译它,在运行时加载进来,让这个代理类来代替原本的类,与其他类进行交互。但是这样很不灵活。 Python提供了装饰器的功能让我们可以不用修改

def smile(polite_func):
    def __smile():
        print("smile ~")
        polite_func()

    return __smile


@smile
def greeting():
    print("morning")


@smile
def bye():
    print("bye")


if __name__ == '__main__':
    greeting()
    bye()
@decorator
def func():
    return
# 其实是一个语法糖
# 等同于
func = decorator(func)

JAVA 提供了一个方法让我们能够在运行时动态的创建代理类,也就是"InvocationHandler.newProxyInstance"这个方法。 需要我们传入三个参数(原本的类,原本类的 classloader,InvocationHandler 的 Instance)。 在这个 InvocationHandler 的实例中,我们改写这个原本的类的方法。

public class ProxyDemo {
    public static void main(String[] args) throws Throwable {
        // 要被代理的类
        EnglishGreeting englishGreeting = new EnglishGreeting("John");
        // 代理类
        Greeting greetingProxy = (Greeting) Proxy.newProxyInstance(
        englishGreeting.getClass().getClassLoader(),
        englishGreeting.getClass().getInterfaces(),
        new InvocationHandler() {
            @Override
            // 代理类中被代理的方法
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("~wave hands");
                return method.invoke(englishGreeting, args);
                }
            }
        );
        greetingProxy.morning();
        greetingProxy.bye();
    }
}

其实,当我们调用greetingProxy.morning()时,调用的是InvocationHanlder的 invoke 方法。这里是Proxy.newInstance为我们创建了一种类的实例

public class EnglishGreetingProxy implements Greeting {
    private InvocationHandler invocationHandler;
    private Greeting greeting;
    public EnglishGreetingProxy(InvocationHandler handler, Greeting greeting) {
        invocationHandler = handler;
        this.greeting = greeting;
    }
    @Override
    public void morning() throws Throwable {
        invocationHandler.invoke(this, greeting.getClass().getMethod("morning"), new Object[]{});
    }

    @Override
    public void bye() throws Throwable {
        invocationHandler.invoke(this, greeting.getClass().getMethod("bye"), new Object[]{});
    }
}

用 Proxy.newInstance 创建出来的类是基于被代理类实现的接口,只有这个接口中的方法才能被代理。 与JDK提供的接口代理服务不同的,CGLib基于ASM实现了使用类进行代理。 假设类

public class Person {
    public void greeting() {
        System.out.println("Hi");
    }
    public void bye() {
        System.out.println("bye");
    }
}

可以看到这个person类没有实现任何接口,接下来要做的是创建代理类

public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    public Object getProxy(Object obj) {
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(new CglibProxy());
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("wave ~");
        methodProxy.invokeSuper(o, objects);
        return null;
    }
}

intercept和Enhance实现了使用类,而不是接口来动态代理。但是能否像Python一样简单的使用@something,目前我还没有找到办法,能够想到的只有在运行期间处理注解,再根据注解处理方法。

最近更新: 2026/3/15 14:17
Contributors: Keyang Li