JDK动态代理实现与原理分析

1.何为代理

代理在我们的日常生活中,就有很多体现,房屋租赁代理,校园辅导班招生代理,化妆产品销售代理等。为什么我们要找代理呢,代理是专业的,是方便的。例如我们买一件化妆品,或买一本书,肯定是不能去找生产厂家买的,是要通过中间的代理商实现交易。

2.静态代理

2.1 介绍

1.代理类是自己手工实现的,自己创建一个java类,表示代理类

2.同时也要实现你所要代理的目标

3.静态代理的优缺点

优点:

   1)实现简单

   2)容易理解

缺点:当你的项目中,目标类的代理类很多的时候,有一下的缺点

  1)当目标类增加了,代理类可能也需要成倍的增加

  2)当你的接口中功能在增加了,或者修改了,会影响众多的实现类,厂家类,代理都需要修改,影响比较多

2.2 实现步骤

1)创建一个接口,定义图书销售的方法,表示厂家和当当网商家做的事情
2)创建厂家类,实现1步骤的接口
3)创建当当网商家类,就是代理,也需要实现1步骤中的接口
4)创建客户端类,调用当当网商家的方法买2本书,计算返回需要花费的价格

2.3 具体代码实现

/**
 * 定义一个卖书的接口,只对厂家和商家开放
 */
public interface SellBook {

    /**
     * 一次购买amount本书需要多少钱
     * @param amount
     * @return 总价
     */
    double bookPrice(int amount);
}
/**
 * 目标类:图书厂家,不接受用户的单独购买
 */
public class BookFactory implements SellBook{


    /**
     * 一本书定价25元,根据amount,可以实现不同的价格
     * @param amount
     * @return
     */
    @Override
    public double bookPrice(int amount) {
        return 25.0 * amount;
    }
}
/**
 * 当当网是一个商家,代理图书的销售
 */
public class DangDangWang implements SellBook{

    //声明代理的厂家
    private BookFactory factory = new BookFactory();

    /**
     * 实现代理图书销售的功能,厂家给25元的单价,当当网赚钱可以加价10块销售(功能增强)
     * @param amount
     * @return
     */
    @Override
    public double bookPrice(int amount) {
        //获取厂家原价
        double originalPrice = factory.bookPrice(amount);
        //代理商家为了赚钱,加价销售
        double price = originalPrice + 10.0;
        return price;
    }
}
/**
 * 静态代理测试
 */
public class StaticProxyTest {

    public static void main(String[] args){
        DangDangWang dd = new DangDangWang();
        System.out.println("用户想买2本书,需要的价格是: " + dd.bookPrice(2) + "元");
    }
}

3. 动态代理

3.1 介绍

1) 动态代理是指代理类对象在程序运行时由JVM根据反射机制动态生成的。动态代理不需要定义代理类的,java源文件。

2) 动态代理其实就是jdk运行期间,动态创建class字节码并加载到JVM。

3) 动态代理的实现方式常用的有两种:JDK动态代理和CGLlB动态代理(暂不介绍)。JDK动态代理是使用java反射包中的类和接口实现动态代理的功能,反射包java.lang.reflect里面有三个类:InvocationHandler,Method,Proxy

3.2 JDK反射包介绍

1)public interface InvocationHandler

InvocationHandler是由代理实例的调用处理程序实现的接口 。每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

​ InvocationHandler接口(调用处理器):就一个方法 invoke()
​ invoke():表示代理对象要执行的功能代码。你的代理类要完成的功能就写在
​ invoke()方法中。

代理类完成的功能

1)调用目标方法,执行目标方法的功能

2)功能增强,在目标方法调用时,增加功能

方法原型

/**
 * 处理代理实例上的方法调用并返回结果
 * @param proxy    jdk创建的代理对象,无需赋值。
 * @param method   目标类中的方法,jdk提供的method对象
 * @param args     目标类中方法的参数,jdk提供
 * @return
 * @throws Throwable
 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

2)Method

Method类: 表示目标类中的方法。
作用: 通过 Method可以执行某个目标类的方法, Method.invoke();
method.invoke(目标对象,方法的参数)

object res = method.invoke(service22,"李四")

说明: method.invoke()就是为了用来执行目标方法,等同于静态代理中

//向厂家发送订单,返回报价
double originalPrice = factory.bookPrice(amount);

3)Proxy类

proxy类:创建代理对象。之前创建对象都是new类的构造方法,现在我们是使用proxy类的方法,代替new的使用。
方法:静态方法 newProxyInstance()
作用是:创建代理对象,等同于静态代理中

DangDangWang dd = new DangDangWang();

方法原型:

/**
     * 创建代理对象
     * @param loader       类加载器
     * @param interfaces    接口
     * @param h            自己实现的代理类,要完成的功能
     * @return  
     */
    public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h) throws IllegalArgumentException

参数介绍:

1) ClassLoader loader 类加载器,负责向内存中加载对象的,使用反射机制获取对象的classLoader,

如何获取类 a, 如:a.getCalss().getClassLoader(),目标对象a的类加载器

这里我们细分:每一个类都继承Object类,在Object中有一个getClass方法,表示类对象的运行时类的Class对象。而Class类里面有一个public ClassLoader getClassLoader()方法

2) Class[] interfaces: 接口,目标对象实现的接口,也是反射获取的

3) InvocationHandler h : 自己实现的代理类,要完成的功能

返回值就是代理对象

3.3 实现步骤

1)创建接口,定义目标类要完成的功能

2)创建目标类实现接口

3)创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能

  • 调用目标方法
  • 增强功能

4)使用Proxy类的静态方法,创建代理对象,并把返回值转换成接口类型

3.4 具体代码实现

1)创建接口,定义目标类所需功能

/**
 * 定义一个卖书的接口,只对厂家和商家开放
 */
public interface SellBook {

    /**
     * 一次购买amount本书需要多少钱
     * @param amount
     * @return 总价
     */
    double bookPrice(int amount);
}

2)创建目标类实现接口

/**
 * 目标类:图书厂家,不接受用户的单独购买
 */
public class BookFactory implements SellBook{


    /**
     * 一本书定价25元,根据amount,可以实现不同的价格
     * @param amount
     * @return
     */
    @Override
    public double bookPrice(int amount) {
        return 25.0 * amount;
    }
}

3)创建Invocationhandler实现类.在invoke()方法中完成代理类的对象

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

/**
 * 实现InvocationHandler接口,实现代理类的功能
 */
public class BookHandle implements InvocationHandler {

    //动态代理:目标对象是活动灵活的,不是固定的,传入哪个就创建哪个
    private Object target = null;

    /**
     * 传入需要创建的目标对象
     * @param target
     */
    public BookHandle(Object target) {
        this.target = target;
    }

    /**
     * 处理代理实例上的方法调用并返回结果
     * @param proxy    jdk创建的代理对象,无需赋值。
     * @param method   目标类中的方法,jdk提供的method对象
     * @param args     目标类中方法的参数,jdk提供
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //实现代理
        Object res = method.invoke(target,args);
        //功能增强
        if (res != null){
            double price = (double) res;
            res = price + 10.0;
        }
        return res;
    }
}

4)使用Proxy类的静态方法,创建代理对象,并把返回值转换成接口类型

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

/**
 * JDK动态代理测试
 */
public class DynamicProxyTest {

    public static void main(String[] args){
        //1.创建目标对象--JDK动态代理必须有
        SellBook factory = new BookFactory();
        //2.创建Invocationhandler对象,谁使用返回谁的对象
        InvocationHandler bookHand = new BookHandle(factory);
        //3.创建代理对象
        SellBook proxy = (SellBook) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(),bookHand);
        System.out.println("商家订购2本书需要: " + proxy.bookPrice(2) + "元");
    }
}

4.总结

动态代理是一种创建java对象的能力,让你不用创建 DangDangWang类就能创建代理类对象,避免静态代理的缺点。动态代理中目标类即使很多,代理类数量仍然可以很少,当你修改了接口中的方法时,不会影响代理类。但是JDK动态代理有一个缺点,必须要有接口出现。如果没有接口,我们可以使用cglib动态代理实现。

你可能感兴趣的:(源码学习分析,Java学习笔记,java,开发语言)