java动态代理详细介绍

1.动态代理概述

        动态代理(Dynamic Proxy)是指在运行时动态地生成代理对象的技术,通常用于为目标对象提供额外的功能,比如日志、事务管理、权限控制等。与静态代理不同,静态代理在编译时已经确定代理类,而动态代理则是在程序运行时动态生成代理类,能够有效减轻代码的重复性。

动态代理的主要作用:

  • 增强功能:可以为目标对象添加附加功能(例如日志记录、性能统计、安全控制等)。
  • 解耦:使得业务逻辑和增强功能分离,增加代码的可维护性。
  • 灵活性:代理类可以在运行时动态生成,不需要手动创建。

什么叫运行期动态创建?听起来好像很复杂。所谓动态代理,是和静态相对应的。我们来看静态代码怎么写:

  1. 定义接口
public interface Hello {
    void morning(String name);
}
  1. 编写实现类
public class HelloImpl implements Hello {
    public void morning(String name) {
        System.out.println("Good morning, " + name);
    }
}
  1. 创建实例,转型为接口并调用
Hello hello = new HelloImpl();
hello.morning("Bob");

        这种方式就是我们通常编写代码的方式。
        还有一种方式是动态代码,我们仍然先定义了接口Hello,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。
        这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。
一个最简单的动态代理实现如下:

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

public class Main {
    public static void main(String[] args) {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
        hello.morning("Bob");
    }
}

interface Hello {
    void morning(String name);
}

在运行期动态创建一个interface实例的方法如下:

  1. 定义一个InvocationHandler实例,它负责实现接口的方法调用;
  2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    1. 使用的ClassLoader,通常就是接口类的ClassLoader;
    2. 需要实现的接口数组,至少需要传入一个接口进去;
    3. 用来处理接口方法调用的InvocationHandler实例。
  3. 将返回的Object强制转型为接口。

动态代理实际上是JVM在运行期动态创建class字节码并加载的过程,把上面的动态代理改写为静态实现类大概就是下面Java中的动态代理的实现。

2.Java中的动态代理

Java 提供了两种方式来实现动态代理:

  1. JDK 动态代理(基于接口)
  2. CGLIB 动态代理(基于继承)

2.1 JDK 动态代理

JDK 动态代理是通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现的。使用 JDK 动态代理时,代理对象需要实现至少一个接口。代理对象会在运行时根据接口生成。
主要类和接口:

  • java.lang.reflect.Proxy:用于创建代理实例。
  • java.lang.reflect.InvocationHandler:处理被代理对象方法调用的接口。

JDK 动态代理示例

2.1.1 定义接口

public interface UserService {
    void addUser(String userName);
    void deleteUser(String userName);
}

2.1.2 定义接口实现类

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String userName) {
        System.out.println("User " + userName + " added.");
    }

    @Override
    public void deleteUser(String userName) {
        System.out.println("User " + userName + " deleted.");
    }
}

2.1.3 创建 InvocationHandler 实现类

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

public class UserServiceInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Method " + method.getName() + " is invoked with arguments: " + args[0]);
        
        // 调用目标对象的方法
        Object result = method.invoke(target, args);
        
        System.out.println("Method " + method.getName() + " executed.");
        
        return result;
    }
}

2.1.4 创建代理对象并测试

import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();

        // 创建 InvocationHandler
        UserServiceInvocationHandler handler = new UserServiceInvocationHandler(userService);

        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                handler);

        // 调用代理对象的方法
        proxy.addUser("John");
        proxy.deleteUser("John");
    }
}

解释:

  1. 目标对象:UserServiceImpl ,它实现了 UserService 接口,实际的业务逻辑类。
  2. InvocationHandler:UserServiceInvocationHandler 是对目标对象的代理处理类。它会拦截对目标对象方法的调用,在方法执行前后添加自己的逻辑。
  3. 代理对象:通过 Proxy.newProxyInstance 动态创建的代理对象。该代理对象会通过 InvocationHandler 来执行方法。
  4. 方法执行:通过代理对象调用目标对象的方法时,实际上是由 InvocationHandler 来处理,代理对象在方法调用前后可以执行额外的操作。

2.2 CGLIB 动态代理

        CGLIB 动态代理通过字节码技术,在运行时生成一个目标类的子类,并重写其中的方法。CGLIB 代理不要求目标类实现接口,因此它适用于没有接口的类。
主要类:

  • net.sf.cglib.proxy.Enhancer:用于创建代理类。
  • net.sf.cglib.proxy.MethodInterceptor:用于拦截目标对象的方法调用。
    CGLIB 动态代理示例
    引入依赖
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.12</version> <!-- 你可以根据需要选择其他版本 -->
        </dependency>

2.2.1 定义目标类

public class UserService {
    public void addUser(String userName) {
        System.out.println("User " + userName + " added.");
    }

    public void deleteUser(String userName) {
        System.out.println("User " + userName + " deleted.");
    }
}

2.2.2 创建 MethodInterceptor 实现类

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class UserServiceInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Method " + method.getName() + " is invoked with arguments: " + args[0]);
        
        // 调用目标对象的方法
        Object result = proxy.invokeSuper(obj, args);
        
        System.out.println("Method " + method.getName() + " executed.");
        
        return result;
    }
}

2.2.3 创建代理对象并测试

import net.sf.cglib.proxy.Enhancer;

public class ProxyTest {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserService();

        // 创建 MethodInterceptor
        UserServiceInterceptor interceptor = new UserServiceInterceptor();

        // 创建 CGLIB 代理对象
        UserService proxy = (UserService) Enhancer.create(UserService.class, interceptor);

        // 调用代理对象的方法
        proxy.addUser("John");
        proxy.deleteUser("John");
    }
}

解释:

  1. 目标对象:UserService 类,定义了业务方法。
  2. MethodInterceptor:UserServiceInterceptor 实现了 MethodInterceptor 接口,用于在方法执行前后添加自定义逻辑。
  3. 代理对象:Enhancer.create 创建了 UserService 类的代理对象,代理对象会通过 MethodInterceptor 来处理方法调用。

2.3 主要区别

特性JDK 动态代理CGLIB 动态代理
代理对象类型必须是接口类型生成目标类的子类
适用场景适用于目标类实现了接口的情况适用于目标类没有实现接口的情况
性能相比 CGLIB 更快,且不需要依赖第三方库生成子类,性能相对较慢,且需要依赖 CGLIB 库
使用限制只能代理接口类型可以代理任何类(不需要接口)
生成的代理类动态生成实现接口的类动态生成目标类的子类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值