代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,它允许一个对象代表另一个对象进行操作。代理模式通常用于控制对某个对象的访问,或者在访问该对象时添加一些额外的功能(如延迟加载、权限检查、日志记录等)。
静态代理和动态代理是代理模式的两种实现方式,它们各自有不同的特点和使用场景。以下是两者的对比:
1. 定义
-
静态代理:
- 在编译时就确定代理类的实现。代理类和真实类都需要在代码中明确地定义,并且代理类通常会实现与真实类相同的接口。
-
动态代理:
- 在运行时根据需要生成代理类。代理类不需要在编译时就定义,而是通过反射机制在运行时动态创建。
2. 实现方式
-
静态代理:
- 需要手动创建代理类,通常为每个真实类创建一个代理类。例如,如果有
UserService
,则需要手动创建UserServiceProxy
。
- 需要手动创建代理类,通常为每个真实类创建一个代理类。例如,如果有
-
动态代理:
- 使用 Java 的
Proxy
类和InvocationHandler
接口来创建代理。可以为多个真实类创建一个通用的代理类。
- 使用 Java 的
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)
参数说明
-
ClassLoader loader:
- 用于加载代理类的类加载器。通常可以使用目标类的类加载器。
-
Class<?>[] interfaces:
- 代理类要实现的接口数组。代理实例将实现这些接口。
-
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")
}