JDK、CGLIB、Spring三种实现代理的区别(一)JDK Proxy 解析基于Java 8

本文介绍了JDK的动态代理机制,通过一个简单的例子展示如何使用Proxy和InvocationHandler实现接口的动态代理。内容包括代理类的生成过程、Proxy.newProxyInstance的关键代码分析以及生成的proxy class的结构。最后,文章讨论了为什么JDK动态代理只能代理接口,并提到了保存代理类到磁盘的选项。
摘要由CSDN通过智能技术生成

Java中从1.3中引入Proxy,实现接口的动态代理。JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。本文从简单例子入手,通过分析源码看看其内部实现原理,使用的是JDK 1.8。

简单运用

使用动态代理主要涉及接口InvocationHandler,以及Proxy类。
通过动态代理实现对接口中方法调用前后进行拦截处理

创建要代理的接口,以及一个实现类。

package com.proxy;

/**
 * 
 * 要代理的接口<br/>
 * 
 * @version
 */
public interface PeopleService {
   
    public void sayHello();

    public void printName(String name);
}

简单实现类EnglishService

package com.proxy;

/**
 * 
 * 注释内容<br/>
 * 
 * 
 * @version
 */
public class EnglishService implements PeopleService {
   
    @Override
    public void sayHello() {
        System.out.println("Hi~");
    }

    @Override
    public void printName(String name) {
        System.err.println("Your name:" + name);
    }
}

定义自己的InvocationHandler

package com.proxy;

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

/**
 * 
 * 注释内容<br/>
 * 
 * @version
 */
public 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.err.println("------begin----------");
        method.invoke(target, args);
        System.err.println("------end----------");
        return null;
    }
}

编写测试类

package com.test;

import java.lang.reflect.Proxy;

import org.testng.annotations.Test;

import com.proxy.EnglishService;
import com.proxy.MyInvocationHandler;
import com.proxy.PeopleService;

public class MyTest {

    @Test
    public void proxyTest() {
        MyInvocationHandler handler = new MyInvocationHandler(new EnglishService());
        PeopleService proxyService = (PeopleService) Proxy.newProxyInstance(MyTest.class.getClassLoader(), new Class[] { PeopleService.class }, handler);
        proxyService.sayHello();
    }
}

运行结果:

这里写图片描述
成功实现对接口方法的前后拦截。JDK内部究竟怎么实现代理的?什么时候调用MyInvocationHandler 的invoke方法?下面分析原理:
看代理类获取时,关键代码为Proxy.newProxyInstance,源码:

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 代理类生成的核心代码
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 通过传入的InvocationHander调用其构造器获取接口实例
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //非公开接口的处理
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值