设计模式--JDK动态代理的实现与原理解析(1)

代理模式是一种很重要很有用的设计模式。被应用到很多著名的框架中,其中Spring Aop的底层就是通过JDK动态代理和CGLib动态代理实现的。其中,JDK动态代理是其默认实现。动态代理的作用就是在不修改原目标类的前提下,对目标类方法进行增强。比如横切一些逻辑:事务管理,日志记录,检验等。J相对于静态代理,JDK动态代理避免了重复编写代理类的缺点。只需要简单的指定一组接口及目标类就可以获得代理对象。Java在JDK1.3以后支持JDK动态代理。
相比于CGLib动态代理,JDK动态代理的特点就是:被代理的目标类必须有接口,生成的代理类实际上是实现了此接口。JDK动态代理是通过Java反射机制实现的。
JDK提供的动态代理就是通过:java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现的。InvocationHandler只提供了一个方法:invoke()方法,其具体的参数意义后面代码中进行分析。其方法实现了代理的逻辑,相当于Spring中的增强:Advice。代理逻辑类需要实现此接口。另外,通过Proxy类的newProxyInstance()方法来获得代理类,其具体参数的意义也在后边代码中进行解释。
下面通过代码,来看一下如何实现JDK的动态代理:
被代理的接口:

package com.blog.jdkproxy;

/**
 * @Description: 模拟用户dao
 * @Author: Jingzeng Wang
 * @Date: Created in 20:51  2017/6/26.
 */
public interface IUserDao {

    /**
     * 模拟添加用户
     * @return 添加成功的条数
     */
    int add();
}

接口的一个简单实现类:

package com.blog.jdkproxy;

/**
 * @Description: 用户操作实现类
 * @Author: Jingzeng Wang
 * @Date: Created in 20:53  2017/6/26.
 */
public class UserDao implements IUserDao{
    /**
     * 模拟添加用户
     *
     * @return 添加成功的条数
     */
    public int add() {
        System.out.println("添加用户成功!!!");
        return 0;
    }
}

实现代理逻辑:

package com.blog.jdkproxy;

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

/**
 * @Description: 代理实现
 * @Author: Jingzeng Wang
 * @Date: Created in 20:56  2017/6/26.
 */
public class MyInvocationHandler implements InvocationHandler {

    // 被代理的目标类
    private Object target;

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

    /***
     *
     * @param proxy  生成的代理对象
     * @param method 被代理的目标类的方法
     * @param args   被代理的目标类方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //前置增强逻辑
        System.out.println("---------------------before 事务开始--------------------");
        //调用目标类方法
        Object obj = method.invoke(target, args);
        //后置增强逻辑
        System.out.println("---------------------after 事务结束---------------------");
        return obj;
    }

    /**
     * 生成代理对象
     *
     * @return 返回代理对象(调用时转型为目标对象的接口)
     */
    public Object getProxy() {
      //参数意义:1. 当前类加载器 2. 目标类的class对象 3. 目标类的所有接口 4. 实现InvocationHandler接口的类
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
    }
}

测试类:

package com.blog.jdkproxy;

/**
 * @Description: JDK动态代理测试类
 * @Author: Jingzeng Wang
 * @Date: Created in 21:04  2017/6/26.
 */
public class Test {

    public static void main(String[] args) {
        IUserDao userDao = new UserDao();
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(userDao);
        IUserDao proxy = (IUserDao) myInvocationHandler.getProxy();
        proxy.add();

        /**
         * 相同接口的代理对象,生成的是同一个代理类class com.sun.proxy.$Proxy0   $Proxy0类名
         * 是使用了缓存  WeakCache  类实现
         * If the proxy class defined by the given loader implementing the given interfaces exists, this will simply return the cached copy;
         * otherwise, it will create the proxy class via the ProxyClassFactory
         */
        IUserDao proxy2 = (IUserDao) myInvocationHandler.getProxy();

        IUserDao userDao3 = new UserDao();
        MyInvocationHandler myInvocationHandler3 = new MyInvocationHandler(userDao3);
        IUserDao proxy3 = (IUserDao) myInvocationHandler3.getProxy();

        System.out.println(proxy.getClass());
        System.out.println(proxy2.getClass());
        System.out.println(proxy3.getClass());
    }
}

结果为:

---------------------before 事务开始--------------------
添加用户成功!!!
---------------------after 事务结束---------------------
class com.sun.proxy.$Proxy0
class com.sun.proxy.$Proxy0
class com.sun.proxy.$Proxy0

从结果上,可以看到,我们可以看到,已经对目标类的add方法完成了代理操作。同时,JDK帮我们生成的代理类名字为:class com.sun.proxy.$Proxy0。同时可以看到,如果是实现的是相同接口的代理类,生成的是同一个代理类。这是因为,JDK为代理类做了缓存操作。
通过上边的代码,就可以实现一个简单的JDK的动态代理。但是,JDK动态代理到底是怎么完成的,JDK的Proxy类到底为我们做了什么,是怎么生成的代理类,怎样完成的方法的调用。这些问题需要查看源码进行分析,将在下一篇博客中进行详细的分析。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值