前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 动态代理小记

Java 动态代理小记

原创
作者头像
8菠萝
修改2021-06-25 17:35:24
4480
修改2021-06-25 17:35:24
举报
文章被收录于专栏:菠萝上市没有

概念

Spring 框架中很多地方用到了动态代理机制,今天稍微梳理下。设计模式中的代理模式理解起来很简单,即访问目标对象属性或方法前先经过代理类。而动态代理主要是为了减少手工重复编码,通过JVM机制,当目标对象发生变化时候,自动添加代理方法。

实践

Java 实现动态代理有两种方法

  1. 通过实现接口的方式 -> JDK动态代理
  2. 通过继承类的方式 -> CGLIB动态代理 (略)

使用JDK实现动态代理的流程如下:

  1. 声明目标接口
代码语言:javascript
复制
public interface TestService {
    public void select();
}

2. 实现目标接口

代码语言:javascript
复制
public class TestServiceImpl implements TestService{
    @Override
    public void select() {
        System.out.println("select");
    }
}

3. 实现Java InvocationHandler 代理接口,现实代理类的功能逻辑, 比如打印日志,参数校验等。

代码语言:javascript
复制
public class ProxyHandler implements InvocationHandler {
    Object target;
    public  ProxyHandler(Object target){
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy begin");
        Object result = method.invoke(target, args);
        System.out.println("proxy end");
        return result;
    }
}

4. 调用Proxy.newProxyInstance() 生成代理类实例。

代码语言:javascript
复制
  public static void main(String[] args) {
        TestServiceImpl userService = new TestServiceImpl();
        ClassLoader classLoader = userService.getClass().getClassLoader();
        Class<?>[] interfaces = userService.getClass().getInterfaces();

        ProxyHandler handler = new ProxyHandler(userService);
        TestService p = (TestService)Proxy.newProxyInstance(classLoader, interfaces, handler);
        p.select();
    }

// 输出
//proxy begin
//select
//proxy end

深入

Proxy.newProxyInstance() 方法调用链

getProxyClass0 => ProxyClassFactory => ProxyGenerator.generateProxyClass => defineClass0

=>Constructor.newInstance

  1. getProxyClass0会查找缓存 ProxyClassFactory对象, 没有则生成 ProxyClassFactory 对象并缓存
  2. ProxyClassFactory 使用 ProxyGenerator.generateProxyClass() 生成代理类字节流。
代码语言:javascript
复制
// Proxy.java 641           

// 生成代理类字节流 
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
// 返回动态代理类Class对象
return defineClass0(loader, proxyName,
                    proxyClassFile, 0, proxyClassFile.length);
...

3. defineClass0 返回的Class对象只有代理方法架子 , 因为generateProxyClass的参数包含方法名和加载类信息,所以完全可用通过反射机制在方法外部套一个同名接口,内部则调用代理函数invoke方法。

代码语言:javascript
复制
// 伪代码
class $ProxyTestService implements TestService {
    InvocationHandler proxy = null;
    // 生成架子后,后面使用Instance来调用它的构造函数
    $ProxyTestService(InvocationHandler handle){
        this.proxy = handle;
    }
    @Override
    public void select() {
        // 伪代码
     	proxy.invoke(TestService, Methon.select, args);
    }
}

4.使用newInstance方法 ,新建对象并设置代理类。

代码语言:javascript
复制
// Proxy.java 718 删减

Class<?> cl = getProxyClass0(loader, intfs);
// 获取构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
// h为InvocationHandler实现类
return cons.newInstance(new Object[]{h});

第三步中 defineClass0 是一个JNI函数,翻了下OpenJDK源码 defineClass0, 有兴趣深入的可用瞧瞧。

顺便安利个OpenJDK源码浏览工具网站:

https://blog.weghos.com/openjdk/OpenJDK/src/

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概念
  • 实践
  • 深入
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档