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

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

一、动态代理的特点:

  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);
    }
}

 

上一篇:2021-07-06


下一篇:Akka详细介绍