动态代理:用孙悟空的毫毛分身理解运行时代理

56 阅读7分钟

一、故事解说:孙悟空的毫毛如何帮他打怪

假设孙悟空遇到妖怪:

  1. 本体专注战斗:孙悟空负责挥金箍棒打怪,不关心其他杂事;

  2. 毫毛分身当代理

    • 变个分身去引开妖怪注意力(预处理);
    • 本体打完怪后,分身清理战场(后处理);
  3. 动态变化:遇到不同妖怪,毫毛可以动态变化(如变成牛魔王、土地公)。

动态代理核心:在运行时动态创建代理对象,代理对象的方法调用会被拦截,可添加预处理 / 后处理逻辑,无需像静态代理那样提前写好代理类。

二、动态代理核心结构(Java 反射实现)

java

// 1. 共同接口:妖怪(被代理的接口)
interface Monster {
    void attack();  // 攻击方法
    String getName(); // 获取名字
}

// 2. 真实对象:具体妖怪
class Skeleton implements Monster {
    @Override
    public void attack() {
        System.out.println("骷髅怪用骨头攻击");
    }
    
    @Override
    public String getName() {
        return "骷髅怪";
    }
}

// 3. 调用处理器:拦截方法调用,添加额外逻辑
class MonsterInvocationHandler implements InvocationHandler {
    private final Object target; // 真实对象
    
    public MonsterInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 预处理:引开注意力
        System.out.println("孙悟空分身引开" + ((Monster) target).getName() + "的注意力");
        
        // 调用真实方法
        Object result = method.invoke(target, args);
        
        // 后处理:清理战场
        System.out.println("孙悟空分身清理" + ((Monster) target).getName() + "的残骸");
        
        return result;
    }
}

// 4. 客户端:动态创建代理并调用方法
public class Client {
    public static void main(String[] args) {
        // 创建真实对象
        Monster realMonster = new Skeleton();
        
        // 创建InvocationHandler
        InvocationHandler handler = new MonsterInvocationHandler(realMonster);
        
        // 动态创建代理对象
        Monster proxyMonster = (Monster) Proxy.newProxyInstance(
            Monster.class.getClassLoader(),
            new Class<?>[]{Monster.class},
            handler
        );
        
        // 调用代理对象的方法
        proxyMonster.attack();
        // 输出:
        // 孙悟空分身引开骷髅怪的注意力
        // 骷髅怪用骨头攻击
        // 孙悟空分身清理骷髅怪的残骸
    }
}

三、Android 常用动态代理案例与实现

案例 1:View 点击事件的动态代理(替代静态代理)

java

// 1. 共同接口:点击事件
interface OnClickListener {
    void onClick(View v);
}

// 2. 真实点击逻辑:Activity中的点击处理
class RealOnClickListener implements OnClickListener {
    @Override
    public void onClick(View v) {
        System.out.println("真实逻辑:跳转到详情页");
    }
}

// 3. 调用处理器:添加权限检查
class PermissionInvocationHandler implements InvocationHandler {
    private final Object target;
    private final Context context;
    
    public PermissionInvocationHandler(Context context, Object target) {
        this.context = context;
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 预处理:检查权限
        if (method.getName().equals("onClick") && !checkPermission(context)) {
            System.out.println("代理:权限不足,提示用户授权");
            return null;
        }
        
        // 调用真实方法
        return method.invoke(target, args);
    }
    
    private boolean checkPermission(Context context) {
        // 实际中检查权限
        return true;
    }
}

// 4. 在Activity中使用
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        View button = findViewById(R.id.button);
        
        // 创建真实点击监听器
        OnClickListener realListener = v -> {
            System.out.println("跳转到详情页");
        };
        
        // 创建动态代理
        OnClickListener proxyListener = (OnClickListener) Proxy.newProxyInstance(
            OnClickListener.class.getClassLoader(),
            new Class<?>[]{OnClickListener.class},
            new PermissionInvocationHandler(this, realListener)
        );
        
        // 设置代理监听器
        button.setOnClickListener(proxyListener);
    }
}

优点

  • 零代码侵入:无需创建具体代理类,直接在运行时生成代理;

  • 灵活扩展:可在运行时动态添加 / 修改代理逻辑(如不同按钮添加不同权限检查);

  • 减少类数量:相比静态代理,无需为每个功能创建代理类。

缺点

  • 只能代理接口:Java 动态代理只能代理实现了接口的类,无法代理普通类;
  • 调试困难:代理逻辑分散在 InvocationHandler 中,出错时难以定位;
  • 性能开销:反射调用方法比直接调用慢,频繁调用可能影响性能。

案例 2:Retrofit 的动态代理(网络请求接口代理)

java

// 1. 网络请求接口(定义API)
public interface ApiService {
    @GET("users/{username}")
    Call<User> getUser(@Path("username") String username);
}

// 2. 调用处理器:处理网络请求
class RetrofitInvocationHandler implements InvocationHandler {
    private final String baseUrl;
    
    public RetrofitInvocationHandler(String baseUrl) {
        this.baseUrl = baseUrl;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 解析方法上的注解(如@GET、@Path)
        String path = parsePathFromAnnotations(method);
        String httpMethod = parseHttpMethodFromAnnotations(method);
        
        // 2. 解析参数(如@Path、@Query)
        Map<String, String> params = parseParamsFromArgs(method, args);
        
        // 3. 构建完整URL
        String url = baseUrl + path + buildQueryString(params);
        
        // 4. 执行网络请求(简化示例)
        System.out.println("发送" + httpMethod + "请求到:" + url);
        return new MockCall<>(); // 实际中返回OkHttp的Call对象
    }
    
    // 解析注解和参数的方法(实际Retrofit实现更复杂)
    private String parsePathFromAnnotations(Method method) { /* ... */ }
    private String parseHttpMethodFromAnnotations(Method method) { /* ... */ }
    private Map<String, String> parseParamsFromArgs(Method method, Object[] args) { /* ... */ }
    private String buildQueryString(Map<String, String> params) { /* ... */ }
}

// 3. 模拟Call对象(实际Retrofit使用OkHttp的Call)
class MockCall<T> implements Call<T> {
    @Override
    public Response<T> execute() throws IOException { /* ... */ }
    @Override
    public void enqueue(Callback<T> callback) { /* ... */ }
    // 省略其他方法
}

// 4. 创建代理实例(类似Retrofit.create())
public class Retrofit {
    private final String baseUrl;
    
    public Retrofit(String baseUrl) {
        this.baseUrl = baseUrl;
    }
    
    @SuppressWarnings("unchecked")
    public <T> T create(Class<T> service) {
        return (T) Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[]{service},
            new RetrofitInvocationHandler(baseUrl)
        );
    }
}

// 5. 使用方式
public class Main {
    public static void main(String[] args) {
        Retrofit retrofit = new Retrofit("https://api.github.com/");
        ApiService api = retrofit.create(ApiService.class);
        
        // 调用接口方法(实际会触发InvocationHandler的invoke)
        Call<User> call = api.getUser("octocat");
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                System.out.println("获取用户成功");
            }
            
            @Override
            public void onFailure(Call<User> call, Throwable t) {
                System.out.println("获取用户失败");
            }
        });
    }
}

优点

  • 零实现类:无需编写接口的实现类,直接通过代理动态生成;

  • 注解驱动:通过注解(如 @GET、@Path)定义请求参数,代码简洁;

  • 扩展性强:可在 InvocationHandler 中添加统一拦截逻辑(如添加 token、日志记录)。

缺点

  • 学习成本高:需要理解注解解析、反射调用等复杂机制;
  • 调试困难:接口方法调用被转发到 InvocationHandler,堆栈信息不直观;
  • 只能代理接口:无法代理普通类,限制了使用场景。

案例 3:权限检查的动态代理(替代 AOP)

java

// 1. 权限注解:标记需要权限的方法
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiresPermission {
    String value(); // 权限名称
}

// 2. 业务接口:定义需要权限的方法
public interface UserService {
    @RequiresPermission("read_user")
    User getUserById(String userId);
    
    @RequiresPermission("delete_user")
    void deleteUser(String userId);
}

// 3. 真实实现类
public class UserServiceImpl implements UserService {
    @Override
    public User getUserById(String userId) {
        System.out.println("获取用户:" + userId);
        return new User(userId, "张三");
    }
    
    @Override
    public void deleteUser(String userId) {
        System.out.println("删除用户:" + userId);
    }
}

// 4. 权限检查调用处理器
public class PermissionInvocationHandler implements InvocationHandler {
    private final Object target;
    private final Context context;
    
    public PermissionInvocationHandler(Context context, Object target) {
        this.context = context;
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 检查方法是否有@RequiresPermission注解
        RequiresPermission annotation = method.getAnnotation(RequiresPermission.class);
        if (annotation != null) {
            String permission = annotation.value();
            // 2. 检查权限
            if (!checkPermission(context, permission)) {
                throw new SecurityException("权限不足:" + permission);
            }
        }
        
        // 3. 调用真实方法
        return method.invoke(target, args);
    }
    
    private boolean checkPermission(Context context, String permission) {
        // 实际中检查权限
        return true;
    }
}

// 5. 创建代理工厂
public class ProxyFactory {
    public static <T> T createProxy(Context context, T target) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new PermissionInvocationHandler(context, target)
        );
    }
}

// 6. 使用方式
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 创建真实对象
        UserService realService = new UserServiceImpl();
        
        // 创建代理对象
        UserService proxyService = ProxyFactory.createProxy(this, realService);
        
        // 调用方法(自动触发权限检查)
        try {
            proxyService.getUserById("123");
        } catch (SecurityException e) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }
}

优点

  • 非侵入式权限控制:无需在每个方法中手动添加权限检查代码;

  • 集中管理权限:所有权限检查逻辑集中在 InvocationHandler 中;

  • 灵活配置:通过注解(@RequiresPermission)标记需要权限的方法,无需修改原有代码。

缺点

  • 只能代理接口:如果 UserService 不是接口而是普通类,无法使用 Java 动态代理;
  • 性能开销:反射调用比直接调用慢,频繁调用可能影响性能;
  • 注解处理复杂:需要理解 Method.getAnnotation () 等反射 API。

四、动态代理的适用场景与总结

适用场景

  1. 横切关注点:如权限检查、日志记录、事务管理(类似 AOP);
  2. 接口实现类缺失:如 Retrofit 需要动态实现 API 接口;
  3. 运行时动态增强:需要在运行时动态添加功能(如添加网络请求拦截器);
  4. 框架开发:如 Spring AOP、Retrofit、Mockito 等框架大量使用动态代理。

核心优点

  • 零代码侵入:无需修改原有代码,通过代理动态添加功能;
  • 灵活扩展:可在运行时动态调整代理逻辑,无需重新编译;
  • 减少类数量:相比静态代理,无需为每个功能创建独立代理类;
  • 接口解耦:客户端只依赖接口,不依赖具体实现。

核心缺点

  • 只能代理接口:Java 动态代理要求目标类必须实现接口,限制了使用范围;
  • 性能开销:反射调用比直接调用慢,对性能敏感的场景需谨慎使用;
  • 调试困难:代理逻辑分散在 InvocationHandler 中,堆栈信息不直观;
  • 学习成本高:需要理解反射、Method、InvocationHandler 等复杂概念。

Android 中的最佳实践

  • 优先使用系统动态代理:如 Retrofit、OkHttp 等框架已封装好动态代理,避免重复造轮子;

  • 小型功能增强:用于简单的功能扩展(如点击事件添加权限检查),复杂场景改用字节码插桩(如 AspectJ);

  • 结合 CGLIB:对于没有实现接口的类,可使用 CGLIB(需添加依赖)实现动态代理;

  • 性能优化:在性能敏感场景(如循环中频繁调用),避免使用动态代理。

动态代理是 Java 和 Android 框架的核心技术之一,理解它有助于深入掌握 Retrofit、OkHttp、EventBus 等开源框架的工作原理。