动态代理和静态代理

发布时间:2023年12月22日

代理模式(Proxy Pattern):通过创建代理对象来控制对另一个对象的访问,通常用于实现横切关注点(cross-cutting concerns),例如日志记录、系统安全性检查等。常见的代理模式就是动态代理和静态代理。

一、静态代理

静态代理:在编译时就已经确定代理关系,也就是在编译时创建代理对象。在静态代理中,代理类和被代理类通常是通过接口或继承关系来建立联系,并且代理类需要实现与被代理类相同的接口,以便能够对外提供相同的服务。

静态代理的简单实现步骤如下:

  1. 定义一个接口(或抽象类),声明代理类和被代理类需要实现的方法。
  2. 创建一个被代理类,实现接口并提供具体的实现逻辑。
  3. 创建一个代理类,实现接口并在内部持有一个被代理类的引用。
  4. 在代理类的方法中,调用被代理类的相应方法,可以在调用前后进行额外的处理。

实例:用户登录代理认证

// 接口
interface UserService {
    //目标方法
    void login(String username, String password);
}

// 被代理类
class UserServiceImpl implements UserService {
    //具体实现
    public void login(String username, String password) {
        System.out.println("用户:" + username + " 登录成功.");
    }
}

// 静态代理类
class AuthProxy implements UserService {
    //引用
    private UserService userService;
    
    //初始化
    public AuthProxy(UserService userService) {
        this.userService = userService;
    }

    //增强方法
    public void login(String username, String password) {
        //在调用目标方法前增强逻辑
        if (authenticate(username, password)) {
            userService.login(username, password);//原始方法
        } else {
            System.out.println("身份认证失败: " + username);
        }
    }
    
    private boolean authenticate(String username, String password) {
        // 简单实现身份验证逻辑
        return username.equals("alice") && password.equals("123");
    }
}

// 简单测试
public class Main{
    public static void main(String[] args) {
        UserService userService = new AuthProxy(new UserServiceImpl());
        userService.login("alice", "123"); // 身份验证成功
        userService.login("john", "123");    // 身份验证失败
    }
}

静态代理的优点:简单易懂、编码方便,可以在代理类中灵活添加额外的功能。

静态代理的缺点:代理类和被代理类之间的关系在编译时就确定了,如果有多个类需要代理,就需要编写多个代理类,导致代码冗余。

二、动态代理

动态代理:在运行时动态生成代理类,运行时才创建代理对象。

相比于静态代理,动态代理更加灵活,可以在运行时决定代理类和被代理类的关系,无需提前编写代理类。Java中实现动态代理的关键类是Proxy和InvocationHandler。Proxy类提供了创建动态代理类的方法,而InvocationHandler接口则需要自定义一个实现类,用于处理代理类的方法调用。

动态代理的简单实现步骤如下:

  1. 定义一个接口,声明代理类和被代理类需要实现的方法。
  2. 创建一个InvocationHandler的实现类,实现invoke方法,在该方法中定义对被代理类方法的处理逻辑。
  3. 使用Proxy类的静态方法newProxyInstance创建动态代理类的实例,传入ClassLoader、接口列表和InvocationHandler实例。
  4. 调用动态代理类的方法,实际上会调用InvocationHandler中的invoke方法来处理方法调用。

实例:(和上面静态代理一样)

// 接口
interface UserService {
    //目标方法
    void login(String username, String password);
}

// 被代理类
class UserServiceImpl implements UserService {
    //具体实现
    public void login(String username, String password) {
        System.out.println("用户:" + username + " 登录成功.");
    }
}

// 实现类,也就是工具类
class AuthenticationHandler implements InvocationHandler {
    private Object target;
    
    public AuthenticationHandler (Object target) {
        this.target = target;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (authenticate(args[0].toString(), args[1].toString())) {
            return method.invoke(target, args);
        } else {
            System.out.println("身份认证失败: " + args[0]);
            return null;
        }
    }
    
    private boolean authenticate(String username, String password) {
        return username.equals("alice") && password.equals("123");
    }
}

// 简单测试
public class Main{
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        
        //创建代理对象,参数有类加载器、被代理对象、代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            userService.getClass().getClassLoader(),
            userService.getClass().getInterfaces(),
            new AuthenticationHandler (userService)
        );
        
        proxy.login("alice", "123"); // 身份验证成功
        proxy.login("john", "123");    // 身份验证失败
    }
}

动态代理优点:在运行时决定代理类和被代理类的关系,无需提前编写代理类,减少了代码冗余。

动态代理缺点:相比于静态代理,它在运行时会带来一定的性能开销。

详细内容链接:Java动态代理

三、动态代理和静态代理的区别

  1. 实现方式:静态代理在编译时就确定代理关系,而动态代理是在运行时动态生成代理类。

  2. 灵活性:静态代理的关系在编译时确定,无法动态修改,而动态代理可以在运行时决定代理关系。

  3. 代码复用:动态代理相比静态代理具有更好的代码复用性。在静态代理中,每个被代理类都需要编写一个对应的代理类,导致代码冗余。而在动态代理中,可以通过一个通用的InvocationHandler实现类来处理多个被代理类的方法调用,减少了代码的冗余。

  4. 性能开销:动态代理在运行时通过反射机制动态生成代理类,相比静态代理会带来一定的性能开销。每次方法调用都需要通过反射调用InvocationHandler中的invoke方法,在方法调用频繁的情况下可能会影响性能。而静态代理在编译时就确定了代理关系,直接调用被代理类的方法,性能更高。

应用场景:

  • 静态代理适用于代理类数量较少且固定的情况,可以在编译时确定代理关系,并在代理类中添加额外的功能。例如,日志记录、权限校验等横切关注点可以通过静态代理来实现。
  • 动态代理适用于代理类数量较多或不确定的情况,可以在运行时动态生成代理类,并通过InvocationHandler实现类来处理不同的代理逻辑。例如,AOP(面向切面编程)中的方法拦截、事务管理等功能就是通过动态代理来实现。

文章来源:https://blog.csdn.net/DU9999999/article/details/135148565
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。