带你深入了解java-代理机制

带你深入了解 Java 代理机制

代理机制是 Java 语言的一个重要特性,它允许我们在运行时生成一个替代某个对象的对象,从而能够控制访问、修改被代理对象的属性或方法。在本文中,我们将深入讲解 Java 的代理机制,包括代理类型、创建方式、使用场景等。

代理类型

Java 语言中有两种代理类型:静态代理和动态代理。

静态代理

静态代理是指在编译时确定代理类和被代理类的关系,代理类和被代理类实现共同的接口,代理类中调用被代理类中的方法。在使用静态代理时,需要手动编写代理类,这种方式比较麻烦且容易出现重复代码。

动态代理

动态代理是指在运行时根据需要动态生成代理类,不需要手动编写代理类。Java 中动态代理主要使用了两个类:InvocationHandlerProxy。其中,InvocationHandler是一个接口,它只有一个invoke方法,在代理对象调用方法时,会先调用invoke方法,再执行相应的方法。Proxy类是动态代理的核心类,可以通过它的静态方法newProxyInstance创建一个动态代理对象。

创建动态代理

创建动态代理的步骤主要包括以下几个步骤:

  1. 实现InvocationHandler接口,重写invoke方法;
  2. 创建被代理类的实例对象和InvocationHandler对象;
  3. 使用Proxy类的newProxyInstance方法创建代理类;
  4. 通过代理类调用被代理类的方法。

以下是一个示例代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Hello {
    void sayHello();
}

class HelloImpl implements Hello {
    @Override
    public void sayHello() {
        System.out.println("Hello World!");
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method invoke!");
        Object result = method.invoke(target, args);
        System.out.println("After method invoke!");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        Hello hello = new HelloImpl();
        MyInvocationHandler handler = new MyInvocationHandler(hello);
        Hello proxy = (Hello) Proxy.newProxyInstance(
                hello.getClass().getClassLoader(),
                hello.getClass().getInterfaces(),
                handler);
        proxy.sayHello();
    }
}

以上代码中,我们定义了一个Hello接口和它的实现类HelloImpl,然后实现了一个MyInvocationHandler类,并实现了其中的invoke方法,在代理类调用方法前后打印日志。最后,通过Proxy类的newProxyInstance方法创建代理类,并调用其中的sayHello方法。

使用代理机制的场景

代理机制可以用于以下场景:

  • AOP(面向切面编程):通过代理对方法进行增强,如日志记录、权限控制等;
  • 数据库连接池:通过代理实现数据库连接的多线程复用;
  • 远程调用:通过代理实现远程调用,如 RMI(远程方法调用)等。

示例说明

示例 1:AOP

在日常开发中,我们经常需要在方法调用前后打印日志,可以通过代理实现这一功能。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface UserService {
    void login();
}

class UserServiceImpl implements UserService {
    @Override
    public void login() {
        System.out.println("User Login");
    }
}

class LogHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method invoke!");
        Object result = method.invoke(target, args);
        System.out.println("After method invoke!");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        LogHandler handler = new LogHandler(userService);
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                handler);
        proxy.login();
    }
}

以上代码中,我们通过创建一个LogHandler类实现在调用login方法前后打印日志的功能。最后通过代理类proxy调用login方法。

示例 2:数据库连接池

在多线程环境下,为了避免频繁创建和销毁数据库连接,可以通过代理实现数据库连接的多线程复用。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

interface DataSource {
    Connection getConnection();
}

class DataSourceImpl implements DataSource {
    private String url;
    private String username;
    private String password;

    public DataSourceImpl(String url, String username, String password) {
        this.url = url;
        this.username = username;
        this.password = password;
    }

    @Override
    public Connection getConnection() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
}

class ConnectionHandler implements InvocationHandler {
    private DataSource dataSource;

    public ConnectionHandler(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("getConnection".equals(method.getName())) {
            Connection conn = (Connection) method.invoke(dataSource, args);
            return new ConnectionProxy(conn);
        }
        return null;
    }
}

class ConnectionProxy implements InvocationHandler {
    private Connection conn;

    public ConnectionProxy(Connection conn) {
        this.conn = conn;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("close".equals(method.getName())) {
            System.out.println("Connection return to pool!");
            return null;
        }
        return method.invoke(conn, args);
    }
}

public class Main {
    public static void main(String[] args) {
        DataSource dataSource = new DataSourceImpl("jdbc:mysql://localhost:3306/test", "root", "password");
        ConnectionHandler handler = new ConnectionHandler(dataSource);
        DataSource proxy = (DataSource) Proxy.newProxyInstance(
                dataSource.getClass().getClassLoader(),
                dataSource.getClass().getInterfaces(),
                handler);
        Connection conn = proxy.getConnection();
        conn.prepareStatement("SELECT * FROM user WHERE id = 1");
        conn.close();
    }
}

以上代码中,我们通过创建一个ConnectionProxy类实现对获取数据库连接的代理,通过代理类ConnectionProxy实现对连接的控制。最后通过代理类proxy获取数据库连接并进行操作。

结论

通过本文,我们了解了 Java 的代理机制及其应用场景,学会了使用动态代理实现对方法的增强和数据库连接的控制。在实际开发中,代理机制是一个非常有用的工具,能够提高代码的可维护性和可扩展性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:带你深入了解java-代理机制 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • C语言超详细讲解数据结构中双向带头循环链表

    C语言超详细讲解数据结构中双向带头循环链表 什么是双向带头循环链表 双向带头循环链表是一种非常常用的数据结构,它由多个节点组成,每个节点都有一个前驱指针和一个后继指针,形成一个双向链表;同时,它也是循环链表,即链表的头指针和尾指针是相连的形成一个环的结构;而带头链表则是在链表的开头添加一个头节点来方便书写,方便读者理解,常见于书籍和教程中。 因此,双向带头循…

    other 2023年6月27日
    00
  • Java微信公众平台开发(11) 微信三大平台的关联

    Java微信公众平台开发(11) 微信三大平台的关联 1. 基本概念 在微信生态系统中,微信公众号、小程序、企业号/企业微信统称为微信三大平台。三大平台能够相互关联,从而实现更加丰富的业务场景。 具体说来: 微信公众号:适用于各类组织、个人向微信用户群体推送服务订阅号消息,实现信息传递、客户服务、营销推广等功能。 微信小程序:一种新型的应用形态,同时具备应用…

    other 2023年6月26日
    00
  • ci框架浅析(全篇)

    CI框架浅析 CI(CodeIgniter)是一个轻量级的PHP框架,是现代化的Web应用程序开发的理想选择。下面我们就来具体分析一下CI框架的特点。 优点 简单易用 CI框架非常简单直观,对于初学者也比较友好,易于理解和上手。同时CI框架提供了强大的工具箱,支持快速开发和部署Web应用程序。 速度快 CI框架是轻量级框架,所以它的运行速度非常快。CI框架内…

    其他 2023年3月29日
    00
  • Java NIO实战之聊天室功能详解

    Java NIO实战之聊天室功能详解 简介 本文将介绍如何使用Java NIO实现一个简单的聊天室功能,包括客户端和服务器端的实现,以及如何使用Java NIO的相关API实现该功能。 聊天室功能介绍 聊天室功能是指用户可以登录到聊天室,然后可以发送消息给其他用户,也可以接收其他用户发送的消息,并在自己的聊天窗口中显示。聊天室功能是一种常见的用户交互方式,被…

    other 2023年6月27日
    00
  • Win11正式版发现新问题:不兼容注册表中带有非 ASCII 字符的应用程序

    下面是关于“Win11正式版发现新问题:不兼容注册表中带有非 ASCII 字符的应用程序”这个问题的详细讲解的攻略: 问题简介 Windows 11 正式版在最近的更新中,发现不兼容带有非 ASCII 字符的应用程序。这是因为在新版的操作系统中,注册表默认采用 UTF-16 编码,而之前的一些应用程序使用的是其他编码方式,如 GBK、GB2312、BIG5 …

    other 2023年6月25日
    00
  • java配置多个过滤器优先级以及几个常用过滤器操作

    Java配置多个过滤器优先级及常用操作 1. 配置多个过滤器实例 在Java Web应用中,可以通过配置多个过滤器实例来处理请求和响应。每个过滤器可以执行特定的操作或应用特定的规则。 1.1 配置web.xml 在web.xml文件中,使用<filter>和<filter-mapping>标签来配置过滤器实例和其映射。 示例代码: &…

    other 2023年6月28日
    00
  • css多行省略-webkit-box-orient打包编译后失效原因

    CSS多行省略-webkit-box-orient打包编译后失效原因 在CSS中,我们可以使用-webkit-box-orient属性来实现多行省略。但是,在打包编译后,这个属性可能会失效。本攻略将介绍这个问题的原因和解决方法。 失效原因 -webkit-box-orient属性是Webkit内核浏览器的私有属性,只有在Webkit内核浏览器中才能生效。在打…

    other 2023年5月8日
    00
  • unityhub破解

    UnityHub破解 UnityHub是一款非常好用的游戏引擎管理器,但是它的付费政策却让很多用户感到不便。如果您需要使用收费版本的Unity,就需要购买付费许可证,否则无法使用。但是,有些用户并不希望花费大量金钱购买付费许可证,因此需要破解UnityHub。 在此提醒各位,破解软件是不被允许的行为,且使用破解版UnityHub可能会带来各种潜在的安全问题,…

    其他 2023年3月29日
    00
合作推广
合作推广
分享本页
返回顶部