欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

动态代理的两种实现方式(基于接口的动态代理和基于子类的的动态代理)

程序员文章站 2022-06-09 21:39:39
...

动态代理的两种实现方式(基于接口的动态代理和基于子类的的动态代理)

一、动态代理的特点:

  1. 字节码随用随创建,随用随加载。
  2. 它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
  3. 装饰者模式就是静态代理的一种体现。

二、动态代理常用的有两种方式

1)基于接口的动态代理

    提供者:JDK 官方的 Proxy 类。
    要求:被代理类最少实现一个接口。
2)基于子类的动态代理
    提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
    要求:被代理类不能用 final 修饰的类(最终类)。

三、使用 JDK  官方的 Proxy 

此处我们使用的是一个演员的例子:
在很久以前,演员和剧组都是直接见面联系的。没有中间人环节。
而随着时间的推移,产生了一个新兴职业:经纪人(中间人),这个时候剧组再想找演员就需要通过经纪
人来找了。下面我们就用代码演示出来。
/**
* 一个经纪公司的要求:
* 能做基本的表演和危险的表演
*/
public interface IActor {
	/**
	* 基本演出
	* @param money
	*/
	public void basicAct(float money);
	/**
	* 危险演出
	* @param money
	*/
	public void dangerAct(float money);
}
	
	
/**
* 一个演员
*/
//实现了接口,就表示具有接口中的方法实现。即:符合经纪公司的要求
public class Actor implements IActor{
	public void basicAct(float money){
	
		System.out.println("拿到钱,开始基本的表演:"+money);
	}
	
	public void dangerAct(float money){
	
		System.out.println("拿到钱,开始危险的表演:"+money);
	
	}
}
public class Client {
    public static void main(String[] args) {
//一个剧组找演员:
        final Actor actor = new Actor();//直接
        /**
         * 代理:
         * 间接。
         * 获取代理对象:
         * 要求:
         * 被代理类最少实现一个接口
         * 创建的方式
         * Proxy.newProxyInstance(三个参数)
         * 参数含义:
         * ClassLoader:和被代理对象使用相同的类加载器。
         * Interfaces:和被代理对象具有相同的行为。实现相同的接口。
         * InvocationHandler:如何代理。
         * 策略模式:使用场景是:
         * 数据有了,目的明确。
         * 如何达成目标,就是策略。
         *
         */
        IActor proxyActor = (IActor) Proxy.newProxyInstance(
                actor.getClass().getClassLoader(),
                actor.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 执行被代理对象的任何方法,都会经过该方法。
                     * 此方法有拦截的功能。
                     *
                     * 参数:
                     * proxy:代理对象的引用。不一定每次都用得到
                     * method:当前执行的方法对象
                     * args:执行方法所需的参数
                     * 返回值:
                     * 当前执行方法的返回值
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        String name = method.getName();
                        Float money = (Float) args[0];
                        Object rtValue = null;
                        传智播客——专注于 Java、.Net 和Php、网页平面设计工程师的培训
                        北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
                        //每个经纪公司对不同演出收费不一样,此处开始判断
                        if("basicAct".equals(name)){
                            //基本演出,没有 2000 不演
                            if(money > 2000){
                                //看上去剧组是给了 8000,实际到演员手里只有 4000
                                //这就是我们没有修改原来 basicAct 方法源码,对方法进行了增强
                                rtValue = method.invoke(actor, money/2);
                            }
                        }
                        if("dangerAct".equals(name)){
                            //危险演出,没有 5000 不演
                            if(money > 5000){
                                //看上去剧组是给了 50000,实际到演员手里只有 25000
                                //这就是我们没有修改原来 dangerAct 方法源码,对方法进行了增强
                                rtValue = method.invoke(actor, money/2);
                            }
                        }
                        return rtValue;
                    }
                });
        //没有经纪公司的时候,直接找演员。
        // actor.basicAct(1000f);
        // actor.dangerAct(5000f);
        //剧组无法直接联系演员,而是由经纪公司找的演员
        proxyActor.basicAct(8000f);
        proxyActor.dangerAct(50000f);
    }
}

四、使用 CGLib 的 的 Enhancer

还是那个演员的例子,只不过不让他实现接口。
/**
 * 一个演员
 */
public class Actor{//没有实现任何接口
    public void basicAct(float money){
        System.out.println("拿到钱,开始基本的表演:"+money);
    }
    public void dangerAct(float money){
        System.out.println("拿到钱,开始危险的表演:"+money);

    }
}



public class Client {
    /**
     * 基于子类的动态代理
     * 要求:
     * 被代理对象不能是最终类
     * 用到的类:
     * Enhancer
     * 用到的方法:
     * create(Class, Callback)
     * 方法的参数:
     * Class:被代理对象的字节码
     * Callback:如何代理
     * @param args
     */
    public static void main(String[] args) {
        final Actor actor = new Actor();
        Actor cglibActor = (Actor) Enhancer.create(actor.getClass(),
                new MethodInterceptor() {
                    /**
                     * 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何
                     方法进行增强。
                     *
                     * 参数:
                     * 前三个和基于接口的动态代理是一样的。
                     * MethodProxy:当前执行方法的代理对象。
                     * 返回值:
                     * 当前执行方法的返回值
                     */
                    @Override
                    public Object intercept(Object proxy, Method method, Object[] args,
                                            MethodProxy methodProxy) throws Throwable {
                        String name = method.getName();
                        Float money = (Float) args[0];
                        Object rtValue = null;
                        if("basicAct".equals(name)){
                            //基本演出
                            if(money > 2000){
                                rtValue = method.invoke(actor, money/2);
                            }
                        }

                        if("dangerAct".equals(name)){
                            //危险演出
                            if(money > 5000){
                                rtValue = method.invoke(actor, money/2);
                            }
                        }
                        return rtValue;
                    }
                });
        cglibActor.basicAct(10000);
        cglibActor.dangerAct(100000);
    }
}