随缘玩 一: 代理模式

代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,它允许一个对象代表另一个对象进行操作。代理模式通常用于控制对某个对象的访问,或者在访问该对象时添加一些额外的功能(如延迟加载、权限检查、日志记录等)。

静态代理和动态代理是代理模式的两种实现方式,它们各自有不同的特点和使用场景。以下是两者的对比:

1. 定义

  • 静态代理

    • 在编译时就确定代理类的实现。代理类和真实类都需要在代码中明确地定义,并且代理类通常会实现与真实类相同的接口。
  • 动态代理

    • 在运行时根据需要生成代理类。代理类不需要在编译时就定义,而是通过反射机制在运行时动态创建。

2. 实现方式

  • 静态代理

    • 需要手动创建代理类,通常为每个真实类创建一个代理类。例如,如果有 UserService,则需要手动创建 UserServiceProxy
  • 动态代理

    • 使用 Java 的 Proxy 类和 InvocationHandler 接口来创建代理。可以为多个真实类创建一个通用的代理类。

3. 灵活性

  • 静态代理

    • 灵活性较低,因为每个代理类都需要单独实现,代码量较大,维护起来也比较麻烦。
  • 动态代理

    • 灵活性较高,可以在运行时决定代理的行为,减少了代码重复,提高了可维护性。

4. 性能

  • 静态代理

    • 由于代理类在编译时就已经确定,因此性能较好,调用方法时不需要反射。
  • 动态代理

    • 由于使用了反射机制,性能相对较低,但在大多数情况下,这种性能差异是可以接受的。

5. 应用场景

  • 静态代理

    • 适用于代理类数量较少且相对固定的场景,比如一些简单的权限控制、日志记录等。
  • 动态代理

    • 适用于代理类数量较多且需要灵活处理的场景,比如 AOP(面向切面编程)、远程方法调用等。

示例

静态代理示例
java 复制代码
// 主题接口
interface Subject {
    void request();
}

// 真实主题
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 静态代理
class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        System.out.println("Proxy: Checking access before calling real subject.");
        realSubject.request();
        System.out.println("Proxy: Logging the request.");
    }
}
动态代理示例
java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 主题接口
interface Subject {
    void request();
}

// 真实主题
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 动态代理处理器
class DynamicProxyHandler implements InvocationHandler {
    private Object realSubject;

    public DynamicProxyHandler(Object realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Proxy: Checking access before calling real subject.");
        Object result = method.invoke(realSubject, args);
        System.out.println("Proxy: Logging the request.");
        return result;
    }
}

// 使用动态代理
public class DynamicProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                new Class<?>[]{Subject.class},
                new DynamicProxyHandler(realSubject)
        );

        proxyInstance.request();
    }
}

动态代理实例

复制代码
/**
 * 动态代理要求被代理对象必须实现一个接口
 */
interface IUser {
    fun addUser()
    fun delUser()
}

/**
 * 实现接口
 */
class UserImp : IUser{
    override fun addUser() {
        println("addUser")
    }

    override fun delUser() {
        println("delUser")
    }

}

/**
 * InvocationHandler 是动态代理的核心接口,用于定义代理对象的逻辑。我们实现一个简单的 InvocationHandler
 */
class UserHandler(private val target:Any):InvocationHandler{
    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
        println("invoke")
        val result = method?.invoke(target,*(args ?: arrayOf()))
        println("invoke1")
        return result
    }

}

fun main(){
    val user = UserImp()
    val handler = UserHandler(user)
    val proxy = Proxy.newProxyInstance(user::class.java.classLoader,user::class.java.interfaces,handler) as IUser
    proxy.addUser()
    proxy.delUser()
}

结果

`Proxy.newProxyInstance 说明

Proxy.newProxyInstance 是 Java 中用于创建动态代理的一个静态方法。它允许您在运行时创建一个实现了指定接口的代理实例,并将方法调用转发到指定的处理器。

方法签名

java 复制代码
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

参数说明

  1. ClassLoader loader

    • 用于加载代理类的类加载器。通常可以使用目标类的类加载器。
  2. Class<?>[] interfaces

    • 代理类要实现的接口数组。代理实例将实现这些接口。
  3. InvocationHandler h

    • 处理方法调用的处理器。您需要实现 InvocationHandler 接口,并重写 invoke 方法,以定义在代理实例上调用方法时的行为。

返回值

  • 返回一个实现了指定接口的代理实例,该实例会将所有方法调用委托给指定的 InvocationHandler

使用示例

以下是一个简单的使用示例:

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义一个接口
interface Hello {
    void sayHello(String name);
}

// 实现 InvocationHandler 接口
class HelloInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("sayHello")) {
            System.out.println("Hello, " + args[0]);
        }
        return null;
    }
}

public class DynamicProxyExample {
    public static void main(String[] args) {
        // 创建代理实例
        Hello helloProxy = (Hello) Proxy.newProxyInstance(
                Hello.class.getClassLoader(),
                new Class<?>[]{Hello.class},
                new HelloInvocationHandler()
        );

        // 调用代理方法
        helloProxy.sayHello("World");
    }
}

运行结果

运行上述代码时,输出将是:

复制代码
Hello, World

注意事项

  • 动态代理只能用于接口,而不能直接用于类。
  • 代理类在运行时生成,因此它们的性能可能不如直接调用实现类的方法。
  • 代理机制在很多框架中被广泛使用,例如 Spring 的 AOP(面向切面编程)。

通过 Proxy.newProxyInstance,您可以灵活地创建代理对象,以便在方法调用时插入自定义逻辑,如日志记录、事务处理等。

应用场景

动态代理在 Android 开发中有多种应用场景,以下是一些常见的应用场景:

1. 网络请求拦截

在进行网络请求时,可以使用动态代理来拦截请求和响应,以便进行日志记录、修改请求参数、添加请求头等。例如,使用 OkHttp 时,可以通过动态代理来实现自定义的拦截器。

2. AOP(面向切面编程)

动态代理常用于实现 AOP 功能,例如在方法执行前后添加日志、权限检查、事务管理等。通过动态代理,可以在不修改业务逻辑的情况下,增强方法的功能。

3. 事件监听

在 Android 中,动态代理可以用于实现事件监听的机制。例如,可以通过动态代理来创建一个通用的事件处理器,动态地为不同的视图设置监听器。

4. 数据绑定

在使用数据绑定库时,动态代理可以用于实现对数据变化的监听。通过动态代理,可以简化视图与数据模型之间的交互,使得 UI 更新更为方便。

5. 权限控制

在某些情况下,动态代理可以用于控制对特定方法的访问权限。通过在代理中添加权限检查逻辑,可以确保只有合适的用户才能调用某些敏感操作。

6. 延迟加载

在需要延迟加载某些资源(如图片、数据等)时,可以使用动态代理来控制何时加载这些资源。只有在真正需要时,才会创建和加载真实对象,从而节省资源。

7. 跨进程通信

在 Android 的 Binder 机制中,动态代理可以用来实现跨进程通信。通过动态代理,可以在客户端和服务端之间传递方法调用,简化跨进程的操作。

示例

以下是一个简单的动态代理示例,展示了如何在 Android 中使用动态代理进行方法调用的拦截:

kotlin 复制代码
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy

// 定义接口
interface UserService {
    fun addUser(name: String)
}

// 真实主题
class UserServiceImpl : UserService {
    override fun addUser(name: String) {
        println("User added: $name")
    }
}

// 动态代理处理器
class DynamicProxyHandler(private val target: UserService) : InvocationHandler {
    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
        println("Before method: ${method?.name}")
        val result = method?.invoke(target, *(args ?: arrayOf()))
        println("After method: ${method?.name}")
        return result
    }
}

// 使用动态代理
fun main() {
    val userService = UserServiceImpl()
    val proxyInstance = Proxy.newProxyInstance(
        userService::class.java.classLoader,
        arrayOf(UserService::class.java),
        DynamicProxyHandler(userService)
    ) as UserService

    proxyInstance.addUser("John Doe")
}

可看 Kotlin开发编码-动态代理

相关推荐
fouryears_234173 小时前
适配器模式——以springboot为例
java·spring boot·适配器模式
汽车功能安全啊4 小时前
利用对称算法及非对称算法实现安全启动
java·开发语言·安全
paopaokaka_luck4 小时前
基于Spring Boot+Vue的吉他社团系统设计和实现(协同过滤算法)
java·vue.js·spring boot·后端·spring
Warren986 小时前
Java Stream流的使用
java·开发语言·windows·spring boot·后端·python·硬件工程
一笑的小酒馆6 小时前
Android12去掉剪贴板复制成功的Toast
android
一笑的小酒馆7 小时前
Android12App启动图标自适应
android
架构师沉默7 小时前
Java优雅使用Spring Boot+MQTT推送与订阅
java·开发语言·spring boot
tuokuac7 小时前
MyBatis 与 Spring Boot版本匹配问题
java·spring boot·mybatis
zhysunny8 小时前
05.原型模式:从影分身术到细胞分裂的编程艺术
java·原型模式
程序员江同学8 小时前
Kotlin 技术月报 | 2025 年 7 月
android·kotlin