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日

相关文章

  • 纯JSP实现的简单登录示例

    下面是“纯JSP实现的简单登录示例”的完整攻略: 1. 准备工作 首先需要创建一个简单的web应用程序。在该程序的根目录下创建一个名为“login.jsp”的文件,用于用户登录。 2. 页面设计 下面来设计程序的页面。在login.jsp文件中,创建一个表单以便用户输入用户名和密码: <form name="loginform" m…

    Java 2023年6月15日
    00
  • 解析MySql与Java的时间类型

    下面是“解析MySql与Java的时间类型”的完整攻略。 1. MySql时间类型 MySql中定义了多种时间类型,包括日期时间、时间戳、时间等。下面分别介绍不同时间类型的定义及其在Java中的映射类型。 1.1. DATETIME类型 DATETIME类型表示年、月、日、小时、分钟、秒。格式为:YYYY-MM-DD HH:MM:SS。 在Java中,可以使…

    Java 2023年5月20日
    00
  • Spring Security使用中Preflight请求和跨域问题详解

    Spring Security使用中Preflight请求和跨域问题详解 什么是Preflight请求 Preflight请求也被称为CORS预检请求,是跨域请求中的一种。在进行跨域请求时,客户端会自动发送Preflight请求到服务器来检查是否可以跨域请求。具体来说,Preflight请求是一个附带预检请求头信息的OPTIONS请求,用于检查实际请求是否可…

    Java 2023年5月20日
    00
  • Javaweb resin4如何配置端口虚拟目录

    下面是关于Javaweb Resin4如何配置端口虚拟目录的攻略。 1. 端口配置 1.1 修改 Resin 配置文件 首先需要打开Resin的配置文件resin.xml。可以在该文件中找到以下代码段: <cluster id="app"> <host id="app0"> <web-ap…

    Java 2023年6月15日
    00
  • 如何通过Java代码实现KMP算法

    下面我将为你讲解“如何通过Java代码实现KMP算法”的完整攻略。 1. 什么是KMP算法? KMP算法是一种字符串匹配算法,其全称是Knuth-Morris-Pratt算法,其主要思想是在匹配过程中充分利用已知信息,尽可能地减少比较次数,从而达到快速匹配的目的。 2. KMP算法的实现过程 2.1 计算字符串的next数组 在KMP算法中,关键在于如何计算…

    Java 2023年5月18日
    00
  • springboot jta atomikos实现分布式事物管理

    下面是讲解“springboot jta atomikos实现分布式事物管理”的完整攻略。 简介 分布式事务管理是一个很常见的需求,使用 JTA(Java Transaction API)接口可以比较容易地实现分布式事务管理,而 Atomikos 是一个比较流行的 JTA 事务管理器。 在 Spring Boot 中,我们可以基于 Atomikos 实现分布…

    Java 2023年5月20日
    00
  • Java Map所有的值转为String类型

    要将Java Map中的所有值转换为String类型,可以采用以下步骤: 获取Map中所有的键值对 遍历所有的键值对,将值转换为String类型 以下是一个实现这个过程的Java示例代码: Map<String, Object> map = new HashMap<String, Object>(); map.put("ke…

    Java 2023年5月20日
    00
  • Mybatis分页的4种方式实例

    针对“Mybatis分页的4种方式实例”的完整攻略,我提供如下的讲解: 概述 在使用Mybatis进行数据查询时,分页查询是一项非常常见的需求。而Mybatis提供了4种方式来实现分页查询,分别是: 使用RowBounds进行物理分页 使用Mybatis自带的PageHelper进行物理分页 使用Mybatis插件实现物理分页 在SQL语句中使用limit进…

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