Java 代理(Proxy)的原理及应用

下面是Java 代理(Proxy)的原理及应用的详细攻略:

什么是Java代理?

Java代理是一种为其他对象提供代理服务的模式。代理是一种中介,它在客户端和实际对象之间起到缓冲的作用,使得客户端可以通过代理来访问对象。

Java代理的核心思想是:通过代理来访问实际对象,代理可以实现对实际对象的一些控制和管理,如访问控制、数据验证、安全控制等。

Java代理的分类

Java代理主要有两种形式:静态代理和动态代理。

静态代理

静态代理是由程序员编写代理类或特定工具自动生成源代码来实现代理。实现静态代理需要定义接口或父类,代理对象与目标对象实现相同的接口或继承相同的父类。

示例:定义一个接口Subject,其中包括一个抽象方法request(),然后实现一个RealSubject类和一个代理类ProxySubject,代理类可以在执行前和执行后做些额外的操作,如记录日志、验证用户等。

/**
 * 接口Subject
 */
public interface Subject {
    void request();
}

/**
 * 实现类RealSubject
 */
public class RealSubject implements Subject {
    public void request() {
        System.out.println("RealSubject:真实请求");
    }
}

/**
 * 代理类ProxySubject
 */
public class ProxySubject implements Subject {
    private RealSubject realSubject;
    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }
    public void request() {
        System.out.println("ProxySubject:代理前操作");
        realSubject.request();
        System.out.println("ProxySubject:代理后操作");
    }
}

动态代理

动态代理是在程序运行时创建一个实现某些接口的代理类的实例。动态代理通常是使用反射机制实现。动态代理可以在实际对象方法执行前后添加额外的操作,如统计执行时间、记录日志等。

示例:使用Java提供的动态代理机制来实现代理类,实际对象必须实现接口。

/**
 * 实现类RealSubject
 */
public class RealSubject implements Subject {
    public void request() {
        System.out.println("RealSubject:真实请求");
    }
}

/**
 * InvocationHandler接口实现类DynamicProxy
 */
public class DynamicProxy implements InvocationHandler {

    private Object target;

    public DynamicProxy(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("DynamicProxy:代理前操作");
        Object result = method.invoke(target, args);
        System.out.println("DynamicProxy:代理后操作");
        return result;
    }
}

/**
 * 客户端调用
 */
public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        InvocationHandler handler = new DynamicProxy(realSubject);
        Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
        subject.request();
    }
}

Java代理的应用

Java代理可以应用于许多场景,这里仅举两个例子:

数据库连接池

数据库连接池是一种连接管理工具,可以减少建立和断开数据库连接的频率,提高应用程序的性能。代理模式可以完成一个抽象工厂的功能,将代理作为工厂,用代理创建对象并进行统一的管理和维护。

示例:定义一个接口DbConnection,然后实现一个代理类DbConnectionProxy,在创建实际对象时,利用代理进行统一管理,如数据库连接的初始化、关闭等操作。

/**
 * 数据库连接接口
 */
public interface DbConnection {
    Connection getConnection();
    void close(Connection connection);
}

/**
 * 代理类DbConnectionProxy
 */
public class DbConnectionProxy implements DbConnection {
    private List<Connection> connections = new ArrayList<>();
    private static final int MAX_CONNECTIONS = 5;

    public DbConnectionProxy() {
        for (int i = 0; i < MAX_CONNECTIONS; i++) {
            connections.add(createConnection());
        }
    }

    public Connection getConnection() {
        Connection connection = null;
        if (connections.size() > 0) {
            connection = connections.remove(0);
            System.out.println("DbConnectionProxy:从连接池获取一条连接");
        }
        return connection;
    }

    public void close(Connection connection) {
        if(connections.size() < MAX_CONNECTIONS) {
            connections.add(connection);
            System.out.println("DbConnectionProxy:归还一条连接到连接池中");
        } else {
            try {
                connection.close();
                System.out.println("DbConnectionProxy:关闭一条连接");
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private Connection createConnection() {
        Connection connection = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
            System.out.println("DbConnectionProxy:创建一条新的连接");
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return connection;
    }
}

/**
 * 客户端调用
 */
public class Client {
    public static void main(String[] args) {
        DbConnection dbConnection = new DbConnectionProxy();

        Connection connection1 = dbConnection.getConnection();
        Connection connection2 = dbConnection.getConnection();
        Connection connection3 = dbConnection.getConnection();
        Connection connection4 = dbConnection.getConnection();

        dbConnection.close(connection4);
        dbConnection.close(connection3);
        dbConnection.close(connection2);
        dbConnection.close(connection1);
    }
}

rpc调用

RPC框架是一种远程调用框架,可以实现进程间的数据交换和互相调用,实现分布式系统之间的通信。代理模式可以实现rpc框架中的远程服务调用,使得客户端可以使用代理来访问服务端的方法。

示例:定义一个接口UserService,然后实现一个代理类UserProxy,利用代理来进行服务方法的远程调用。

/**
 * 用户服务接口
 */
public interface UserService {
    String getNameById(int id);
}

/**
 * 代理类UserProxy
 */
public class UserProxy implements UserService {
    private String host;
    private int port;

    public UserProxy(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public String getNameById(int id) {
        String result = null;
        try (Socket socket = new Socket(this.host, this.port);
             ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
             ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
            //请求消息
            oos.writeUTF("UserService");
            oos.writeUTF("getNameById");
            oos.writeInt(id);
            oos.flush();
            //接收回应消息
            result = ois.readUTF();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
}

/**
 * 服务端实现UserService接口
 */
public class UserServiceImpl implements UserService {
    public String getNameById(int id) {
        //模拟实现 getNameById 接口
        return "Alice";
    }
}

/**
 * 服务端调用
 */
public class Server {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(8888)) {
            System.out.println("server starting...");
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println("client connected");
                //接收请求消息
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                String serviceName = ois.readUTF();
                String methodName = ois.readUTF();
                int id = ois.readInt();
                //反射调用服务方法
                Object serviceObject = getService(serviceName);
                Method method = serviceObject.getClass().getMethod(methodName, int.class);
                String result = (String) method.invoke(serviceObject, id);
                //发送回应消息
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                oos.writeUTF(result);
                oos.flush();
                socket.close();
                System.out.println("client disconnected");
            }
        } catch (IOException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private static Object getService(String serviceName) {
        if("UserService".equals(serviceName)) {
            return new UserServiceImpl();
        }
        return null;
    }
}

/**
 * 客户端调用
 */
public class Client {
    public static void main(String[] args) {
        UserService userService = new UserProxy("127.0.0.1", 8888);
        String result = userService.getNameById(123);
        System.out.println(result);
    }
}

以上就是Java代理(Proxy)的原理及应用的完整攻略,希望对您有帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java 代理(Proxy)的原理及应用 - Python技术站

(0)
上一篇 2023年5月27日
下一篇 2023年5月27日

相关文章

  • 深入了解Java内部类的用法

    来给大家介绍一下深入了解Java内部类的用法的攻略。 什么是Java内部类 Java内部类是定义在另一个类中的类,它可以访问外部类的所有成员和方法,而且可以与外部类进行私有访问和更好地封装性。Java的内部类分为四种:成员内部类、静态内部类、局部内部类和匿名内部类。 成员内部类 成员内部类即在类中定义的类,其特点是具有与外部类相同的访问权限,即public,…

    Java 2023年5月26日
    00
  • 没有外网IDEA离线使用maven仓库的方法

    请看以下攻略: 问题背景 在没有外网的情况下,我们在使用 IDEA 进行开发时,如何使用 Maven 的依赖包? 解决方案 1. 下载 Maven 仓库依赖包 在有外网的环境下,打开 IDEA,新建一个空项目,在 pom.xml 文件中添加需要的依赖,然后将项目打包,此时 Maven 会将依赖包下载到本地仓库(默认路径为用户目录下的 .m2 目录)中。将本地…

    Java 2023年5月20日
    00
  • SpringMVC 单文件上传与多文件上传实例

    下面就给您详细讲解“SpringMVC 单文件上传与多文件上传实例”的完整攻略。 一、SpringMVC 单文件上传实例 1.1 相关依赖说明 在 pom.xml 文件中增加以下依赖: <dependency> <groupId>commons-fileupload</groupId> <artifactId>…

    Java 2023年6月15日
    00
  • Java反转字符串的10种方法

    Java反转字符串的10种方法 在Java中,反转字符串是非常常见的操作。在本篇攻略中,我们将会讲解10种Java反转字符串的方法,并详细说明它们的使用场景。以下是我们将要讲解的10种方法: StringBuilder反转法 StringBuffer反转法 toCharArray()反转法 递归反转法 charAt()反转法 CharArray反转法 Str…

    Java 2023年5月26日
    00
  • springboot下使用mybatis的方法

    下面是详细的“springboot下使用mybatis的方法”的攻略: 1. 引入依赖 在pom.xml文件中引入mybatis-spring-boot-starter依赖,如下: <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId&…

    Java 2023年5月20日
    00
  • SpringBoot扩展SpringMVC原理并实现全面接管

    对于这个话题,首先我们需要了解SpringMVC框架和SpringBoot框架的基本概念,然后再探讨SpringBoot如何扩展和接管SpringMVC框架的原理,最后给出具体实现的示例。 SpringMVC和SpringBoot框架的基本概念 SpringMVC框架 SpringMVC框架是一种基于Java的Web框架,它提供了一种轻量级的方式来构建Web…

    Java 2023年5月16日
    00
  • docker inspect 操作详解

    “docker inspect”命令用于获取Docker容器、镜像或其他相关对象的详细信息。以下是“docker inspect”的详细操作攻略。 1. 命令格式 Docker命令通常采用以下格式: docker inspect [OPTIONS] NAME|ID [NAME|ID…] 其中,OPTIONS是可选参数,NAME|ID是Docker对象的名…

    Java 2023年6月15日
    00
  • SpringBoot如何整合mybatis-generator-maven-plugin 1.4.0

    下面我将为您讲解如何在Spring Boot项目中整合mybatis-generator-maven-plugin 1.4.0,供您参考。 1. 添加maven依赖 在pom.xml文件中添加mybatis-generator-maven-plugin 1.4.0的依赖,如下所示: <dependency> <groupId>org.…

    Java 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部