动态代理原理

本文详细介绍了Java动态代理的原理和实现,包括如何通过Proxy类和InvocationHandler接口创建动态代理对象,以及如何拦截和增强方法的执行。文中通过明星代理和不可修改List的案例展示了动态代理的实际运用,强调了其在不修改源码情况下实现功能增强的能力,并提供了相关代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态代理 (原理)

1.动态代理原理及代码实现

  • 动态代理非常的灵活,可以为任意的接口实现类对象做代理。
  • 动态代理可以为被代理对象的所有接口的所有方法做代理,动态代理可以在不改变方法源码的情况下,实现对方法功能的增强,
  • 动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。
  • 动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。
  • 动态代理同时也提高了开发效率。
  • 缺点:只能针对接口的实现类做代理对象,普通类是不能做代理对象的。

java.lang.reflect.InvocationHandler接口 是代理实例的调用处理程序 实现的接口。用来生产代理人对象的动态代理接口。

可以把被代理对象传递到InvocationHandlerImpl类中,让InvocationHandlerImpl生产被代理对象的代理人对象。

InvocationHandler接口中的方法:

// 在代理实例上处理方法调用并返回结果。
// 作用:使用invoke方法对被代理人的方法进行拦截,让部分方法可以运行,部分方法不能运行
Object invoke(Object proxy, Method method, Object[] args) 

参数:
Object proxy	// 内部产生的代理人对象,不用管
Method method	// invoke方法对传递被代理对象的方法进行拦截,方法内部使用反射技术获取到拦截到的方法
Object[] args	// 拦截到方法的参数

返回值:
Object		// 就是拦截到的方法的返回值

想要实现动态代理:使用Proxy类(java.lang.reflect.Proxy)生成代理人对象

Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

Proxy类中的静态方法:

// 返回一个指定接口的代理类对象实例,该接口可以将方法调用指派到指定的调用处理程序。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
                
参数:
    ClassLoader loader		// 传递类加载器
    Class<?>[] interfaces	// 传递被代理对象实现的所有接口
    InvocationHandler h		// 生成代理人的接口,传递InvocationHandler接口的实现类对象
返回值:
    Object		// 返回的就是创建好的代理人对象

InvocationHandler接口的实现类:

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

public class InvocationHandlerImpl implements InvocationHandler{
    //在实现类中定义一个Star类型的变量,用于接收被代理人的对象
    private Star star;

    //使用构造方法,把明星传递到类中,给Star变量赋值
    public InvocationHandlerImpl(Star star) {
        this.star = star;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取到方法的名称
        String methodName = method.getName();
        //对方法的名称进行判断
        if("chifan".equals(methodName)){
            throw new RuntimeException("不和你吃饭!");
        }
        if("wanyouxi".equals(methodName)){
            throw new RuntimeException("不和你玩游戏!");
        }
        //其他的方法,使用Method对象调用invoke方法运行方法
        Object v = method.invoke(star, args);
        return v;
    }
}

测试类:

import java.lang.reflect.Proxy;

/*
    动态代理:
        创建代理人对象,对明星进行代理,对明星的方法进行拦截
        调用明星的changge,tiaowu,yandianying方法放行==>可以运行
        调用明星的chifan,wanyouxi方法拦截==>不让方法运行
 */
public class Demo01Proxy {
    public static void main(String[] args) {
        
        CaiXuKun cxk = new CaiXuKun();

        //使用Proxy类中的静态方法newProxyInstance获取代理人对象
        Star cxkProxy = (Star)Proxy.newProxyInstance(CaiXuKun.class.getClassLoader(),
                CaiXuKun.class.getInterfaces(), new InvocationHandlerImpl(cxk));
        cxkProxy.changge();
        cxkProxy.tiaowu();
        cxkProxy.chifan();	//RuntimeException: 不和你吃饭!
        String s = cxkProxy.yandianying(100);
        System.out.println(s);
    }
}

2.动态代理综合案例

代码实现:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/*
    需求:
	使用动态代理模拟unmodifiableList方法,对List接口进行代理
		调用List接口的方法会被拦截
		如果使用的size,get方法,没有对集合进行修改,则允许执行
		如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常

    分析:
        1.定义一个代理方法proxyList
            参数:传递List集合
            返回值:被代理之后的List集合
        2.方法内部可以使用Proxy类中的方法实现动态代理
 */
@SuppressWarnings("all")	//注解的作用是抑制警告,不让警告出现
public class Demo02Proxy {
    public static void main(String[] args) {
        //创建List集合对象
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        List<String> proxyList = proxyList(list);
        System.out.println(proxyList.size());//3
        System.out.println(proxyList.get(2));//c
        proxyList.add("d");//UnsupportedOperationException: add no run!
        proxyList.remove(1);//UnsupportedOperationException: remove no run!
        proxyList.set(1,"www");//UnsupportedOperationException: set no run!
    }

    //1.定义一个代理方法proxyList 参数:传递List集合 返回值:被代理之后的List集合
    public static List<String> proxyList(List<String> list){
        //2.方法内部可以使用Proxy类中的方法实现动态代理
        List<String> listProxy = (List<String>)Proxy.newProxyInstance(
            	list.getClass().getClassLoader(), list.getClass().getInterfaces(),
                new InvocationHandler() {
                    //invoke方法对List集合的方法进行拦截,让部分方法可以运行,让部分方法不能运行,抛出异常
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //获取拦截到的方法的名称
                        String methodName = method.getName();
                        //如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常
                        if("add".equals(methodName)){
                            throw new UnsupportedOperationException("add no run!");
                        }
                        if("remove".equals(methodName)){
                            throw new UnsupportedOperationException("remove no run!");
                        }
                        if("set".equals(methodName)){
                            throw new UnsupportedOperationException("set no run!");
                        }
                        //如果使用的size,get方法,没有对集合进行修改,则允许执行
                        Object v = method.invoke(list, args);
                        return v;
                    }
                });
        return listProxy;
    }
}

在这里插入图片描述

动态代理案例流程图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨鸦_Cormorant

大家喜欢的话可以点个关注投币哟

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值