Java JDK 动态代理例子

本文详细阐述了Java中动态代理的概念,包括JDK提供的接口和类,如InvocationHandler和Proxy,以及如何通过动态代理实现安全性验证。通过具体示例展示了如何基于JDK的动态代理实现接口验证操作。

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

1 概述
    在Java中,动态代理是指代理类的生成发生在程序运行时期,根据被代理类动态生成代理类的字节码文件(class文件),并且装载到运行环境中,像其他的类一样使用,该字节码文件的生存期随着运行环境的关闭而消失。
    JDK1.3以及之上的版本提供了动态代理的机制,它和反射机制结合在一起。下面将以一个例子说明JDK的动态代理。
    

2 JDK动态代理
    2.1 JDK提供的接口和类
    InvocationHandler接口:
    public interface InvocationHandler {
        Object invoke(Object proxy, Method method, Object[]args) throws Throwable;
    }
    它定义了唯一的方法invoke,该方法的参数为代理对象proxy,截获的方法对象method,和方法调用的参数,返回方法执行的结果。开发者需要实现该接口,在invoke方法中添加对截获的方法的代理操作,并调用被代理对象的方法。
    Proxy类
    该类是生成代理类的帮助类,主要方法如下:
    public Static Class getProxyClass (ClassLoader loader, Class[] interfaces):根据指定的类加载器和接口获得一个代理类,其中,loader是类加载器,interfaces是被代理类所拥有的接口。
    public Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):根据指定的类加载器、接口以及截获器,返回代理类的一个实例对象。其中:

    ClassLoader loader   ----指定被代理对象的类加载器
    Class[] Interfaces   ----指定被代理对象所以事项的接口
    InvocationHandler h  ----指定需要调用的InvocationHandler对象


    2.2 动态代理的实现
    场景如下:定义一个人类接口UserManager,UserManagerImpl是该接口的一个实现类,用于实现用户的增、删、改、查功能。现在,需要在执行这些功能之前验证安全性。


    2.1.1 接口和实现类
    UserManager类:
    public interface UserManager {
        public void add(String username, String password);
        public void delete(String id);
        public String loadById(String id);
        public void update(String username, String password);
    }
    UserManagerImpl类:
    public class UserManagerImpl implements UserManager {
        public void add(String username, String password) {
            System.out.println("==========add()==========");
        }
        public void delete(String id) {
            System.out.println("==========delete()==========");
        }
        public String loadById(String id) {
            System.out.println("==========load()==========");
        return null;
        }
        public void update(String username, String password) {
            System.out.println("==========update()==========");
        }
    }


    2.1.2基于JDK的动态代理的实现
    截获器SecurityHandler类:
    public class SecurityHandler implements InvocationHandler {
        private Object targetObject;// 被拦截的类
        public Object newProxy(Object targetObject) {
            this.targetObject = targetObject;
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),this);// 利用Proxy类取得代理对象
        }
        // 调用目标对象的方法是会执行下面这个方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            checkSecurity();
            Object ret = null;
            try {
                ret = method.invoke(this.targetObject, args);
            } catch (Exception e) {
                e.printStackTrace();
            throw e;
            }
            return ret;
        }
        private void checkSecurity() {
            System.out.println("==========checkSecurity()==========");
        }
    }

    其中invoke()方法就是Proxy动态代理类所代理的接口类的抽象方法的真实实现:

    Object proxy     -----代理类对象
    Method method    -----被代理对象的方法(这里不是接口的抽象方法了,是具体的实现类中的方法)
    Object[] args    -----该方法的参数数组
    说明:这里可以把上面的类抽象分成两个类处理,即SecurityHandler和SecurityProxyCreator类:


    public class SecurityHandler implements InvocationHandler {
        private Object targetObject;// 被拦截的类
        public void setTargetObject(Object targetObject) {
           this.targetObject = targetObject;
        }
        // 调用目标对象的方法是会执行下面这个方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            checkSecurity();
            Object ret = null;
            try {
                ret = method.invoke(this.targetObject, args);
            } catch (Exception e) {
                e.printStackTrace();
            throw e;
            }
            return ret;
        }
        private void checkSecurity() {
            System.out.println("==========checkSecurity()==========");
        }
    }
    public class SecurityProxyCreator {
        public Object create(Class<?>[] interfaces, InvocationHandler h) {  
            return Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, h);
        }
    }
  

    Client测试类:
    public class Client {
        public static void main(String[] args) {
            SecurityHandler handler = new SecurityHandler();
            UserManager userManager = (UserManager) handler.newProxy(new UserManagerImpl());// 生成代理类
            userManager.add("", "");
        }
    }
    测试结果如下:
    ==========checkSecurity()==========
    ==========add()==========
   

 3 分析总结
    分析JDK提供的帮助类Proxy,可以发现,它只支持实现接口方式的代理,不支持继承超类方式的代理,这就意味着,被代理的类必须要有接口,并且需要拦截的方法必须都在接口中进行声明。在本例中,通过截获帮助类Proxy生成的代理类的字节码文件,反编译之后,发现,其类的声明如下:
    public final class proxy0 extends Proxy implements UserManager
    其中,proxy0就是动态生成的代理类的名字,在该类的方法中,都会将调用委托到拦截器的invoker方法。


    JDK中具体的动态代理类是怎么产生的呢?
    1、产生代理类$Proxy0类
    执行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
    产生$Proxy0类,它继承Proxy对象,并根据第二个参数,实现了被代理类的所有接口,自然就可以生成接口要实现的所有方法了(这时候会重写hashcode,toString和equals三个方法),但是还没有具体的实现体;
    2、将代理类$Proxy0类加载到JVM中
    根据Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一个参数----就是被代理类的类加载器,把当前的代理类加载到JVM中
    3、创建代理类$Proxy0类的对象
    调用的$Proxy0类的$Proxy0(InvocationHandler)构造函数,生成$Proxy0类的对象
    参数就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三个参数
    这个参数就是实现的InvocationHandler对象,InvocationHandler对象中组合加入了代理类代理的接口类的实现类;所以,$Proxy0对象调用所有要实现的接口的方法,都会调用InvocationHandler对象的invoke()方法实现;
    4、生成代理类的class byte
    动态代理生成的都是二进制class字节码
    备注:JDK提供的动态代理仅支持接口方式的代理,那么如果被代理的类没有实现任何接口,则可基于CGLib的动态代理,CGLib同时也支持继承超类的方式的代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值