动态代理

动态代理

动态代理是一种在运行时动态构建代理、动态处理代理方法调用的机制。

动态代理常应用于包装RPC调用、AOP等场景。

动态代理的常见实现方式有:

  • 利用Java反射机制,如JDK自身提供的动态代理。

  • 利用字节码操作机制,如ASM、CGLib、Javassist等。

在实际开发中常用的动态代理实现方式是:JDK动态代理和CGLib动态代理。

JDK动态代理

JDK动态代理的原理

JDK动态代理主要是利用Java反射机制在运行时创建代理对象。

JDK动态代理的核心是InvocationHandler(调用处理程序)。

  • 每个代理对象都会有一个关联的InvocationHandler,而每个InvocationHandler对象都持有一个被代理对象。

  • 对代理对象进行调用,是通过InvocationHandler#invoke()来完成的。而invoke()会根据传入的代理对象、方法以及参数决定调用被代理对象的哪个方法。

代理对象是由Proxy.newProxyInstance()根据被代理对象和InvocationHandler对象来生成的。

JDK动态代理的示例

被代理必须实现某个接口

interface Subject {
    fun visit()

    fun greet(): String

    fun say(content: String)
}
1
2
3
4
5
6
7
class RealSubject : Subject {

    override fun visit() {
        println("visit() in RealSubject")
    }

    override fun greet(): String = "Hello"

    override fun say(content: String) {
        println("say: $content")
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

InvocationHandler对象必须持有对应的被代理对象,所以,InvocationHandler的构造方法需要传入被代理对象。

通过Proxy.newProxyInstance()来生成InvocationHandler所关联的代理对象。

InvocationHandler#invoke()根据传入的代理对象proxy、方法method和参数args调用被代理对象target

class JDKProxy(
        private val target: Any
) : InvocationHandler {

    override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
        println("before invoke")
        val result = if (args != null) {
            method.invoke(target, *args)
        } else {
            method.invoke(target)
        }
        println("after invoke")
        return result
    }

    @Suppress("UNCHECKED_CAST")
    fun <T : Any> newProxy(): T =
            Proxy.newProxyInstance(target.javaClass.classLoader,
                    target.javaClass.interfaces, this) as T
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
val realSubject = RealSubject()
val proxy = JDKProxy(realSubject)
val subject = proxy.newProxy<Subject>()

subject.visit() // visit() in RealSubject
println(subject.greet()) // Hello
subject.say("I am daking") // say: I am daking
1
2
3
4
5
6
7

JDK动态代理的优势

  • 最小化依赖关系。减少依赖意味着简化开发和维护,而且是JDK本身的支持。

  • 平滑地进行JDK版本升级。而字节码类库通常需要进行更新以保证在新版Java上能够使用。

  • 代码实现简单。

JDK动态代理的缺点

  • 被代理的目标必须要实现某个接口。

CGLib动态代理

CGLib动态代理的原理

CGLib动态代理是基于ASM框架实现的,它会让生成的代理类继承被代理类。

CGLib动态代理的核心是MethodInterceptor(方法拦截器)。

  • 每个代理对象都会有一个关联的MethodInterceptor,而每个MethodInterceptor对象都持有一个被代理对象。

  • 对代理对象进行调用,是通过MethodInterceptor#intercept()来完成的。而intercept()会根据传入的代理对象、方法以及参数决定调用被代理对象的哪个方法。

代理类对象是由CGLib中的字节码增强器Enhancer类根据被代理类和MethodInterceptor对象来生成的。

CGLib动态代理的示例

引入cglib库。

compile "cglib:cglib:$cglib_version"
1

被代理不需要实现某个接口,但不能为final类,而且被代理的方法不能为final。

open class RealSubject {

    open fun visit() {
        println("visit() in RealSubject")
    }

    open fun greet(): String = "Hello"

    open fun say(content: String) {
        println("say $content")
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

MethodInterceptor对象必须持有对应的被代理对象,所以,MethodInterceptor的构造方法需要传入被代理对象。

通过Enhancer来生成MethodInterceptor所关联的代理对象。

MethodInterceptor#intercept()根据传入的代理对象obj、方法method、参数args和方法代理对象proxy来调用被代理对象target

class CGLibProxy(
        private val target: Any
) : MethodInterceptor {

    override fun intercept(obj: Any, method: Method, args: Array<out Any>?, proxy: MethodProxy): Any? {
        println("before invoke")
        val result = if (args != null) {
            method.invoke(target, *args)
        } else {
            method.invoke(target)
        }
        println("after invoke")
        return result
    }

    @Suppress("UNCHECKED_CAST")
    fun <T : Any> newProxy(): T {
        val enhancer = Enhancer()
        enhancer.setSuperclass(target.javaClass)
        enhancer.setCallback(this)
        return enhancer.create() as T
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
val realSubject = RealSubject()
val proxy = CGLibProxy(realSubject)
val subject = proxy.newProxy<RealSubject>()

subject.visit() // visit() in RealSubject
println(subject.greet()) // Hello
subject.say("I am daking") // say: I am dakin
1
2
3
4
5
6
7

CGLib动态代理的优势

  • 不像JDK动态代理那样限定被代理的目标必须要实现某个接口。

  • 更高性能。

CGLib动态代理的缺点

  • 需要引入CGLib这个第三方框架。

  • 不能对final类以及final方法进行代理。