Java动态代理的作用是什么?

  • Post category:Java

Java动态代理是Java语言中一种非常常用的技术,它可以在运行时自动生成代理类,从而实现对目标对象的动态代理。其主要作用如下:

  1. AOP编程

动态代理作为AOP编程的重要手段之一,可以在不影响原有业务逻辑的情况下,在目标方法的前后进行一些通用逻辑的处理,例如日志、事务等。

  1. 拦截不合法调用

有时候我们希望限制某些对象的访问权限,或者对某些方法调用进行拦截,这时候可以使用动态代理进行实现。

下面我们通过两条示例来详细讲解Java动态代理的使用。

示例一:实现动态代理进行日志记录

在这个示例中,我们会创建一个代理类,在调用目标方法之前和之后打印日志,从而实现日志记录的功能。

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

public class LogProxy implements InvocationHandler {

    private Object target; // 目标对象

    /**
     * 构造函数
     * @param target 目标对象
     */
    public LogProxy(Object target) {
        this.target = target;
    }

    /**
     * 代理方法
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[Before] " + method.getName() + " is called");
        Object result = method.invoke(target, args); // 调用目标方法
        System.out.println("[After] " + method.getName() + " is called");
        return result;
    }

    /**
     * 获取代理对象
     * @return 代理对象
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
}

在这个代理类中,我们通过实现InvocationHandler接口来自定义代理方法,然后在代理方法内部分别在目标方法调用前后打印日志。getProxy方法用于生成代理对象。

下面是一个示例代码片段,对Calculator对象进行代理:

Calculator target = new CalculatorImpl();
LogProxy proxy = new LogProxy(target);
Calculator calculatorProxy = (Calculator)proxy.getProxy();
int result = calculatorProxy.add(1, 2);

我们通过实例化CalculatorImpl对象,然后创建LogProxy类的实例并将其作为构造函数的参数传入,最后通过getProxy方法来获取代理对象。然后就可以像正常调用Calculator对象一样去调用代理对象。在实际执行过程中,代理类会在目标方法调用前、调用后打印日志。

示例二:实现动态代理限制对对象的访问

在这个示例中,我们会创建一个代理类,拦截对某个对象的访问,从而实现限制访问的功能。

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

public class AccessLimitProxy implements InvocationHandler {

    private Object target; // 目标对象

    /**
     * 构造函数
     * @param target 目标对象
     */
    public AccessLimitProxy(Object target) {
        this.target = target;
    }

    /**
     * 代理方法
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().startsWith("get")) {
            System.out.println("Access limit: only set method is allowed");
            return null;
        } else {
            return method.invoke(target, args); // 调用目标方法
        }
    }

    /**
     * 获取代理对象
     * @return 代理对象
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
}

在这个代理类中,我们通过实现InvocationHandler接口来自定义代理方法。在代理方法中,我们检查当前调用的方法是否为get开头,如果是,则说明当前对象只能进行设置操作,不可以进行获取操作,于是返回null。如果不是,则调用目标方法。

下面是一个示例代码片段,对User对象进行代理:

User user = new User();
AccessLimitProxy proxy = new AccessLimitProxy(user);
User userProxy = (User)proxy.getProxy();
userProxy.setName("Alice"); // 可以设置姓名
userProxy.setAge(18); // 可以设置年龄
String name = userProxy.getName(); // 不允许获取姓名

我们通过实例化User对象,然后创建AccessLimitProxy类的实例并将其作为构造函数的参数传入,最后通过getProxy方法来获取代理对象。然后就可以像正常调用User对象一样去调用代理对象。在实际执行过程中,代理类会在进行获取姓名时返回null,即表示访问受到限制。