静态代理与动态代理

简介

代理模式作用是:在不修改被代理对象功能的基础上,通过对代理类进行扩展,进行一些功能上的附加与增强。如Spring AOP就是用动态代理来实现的。下图可以表述代理模式的流程:

 

静态代理

我们在app上看电影的时候会看到很多广告,app厂家拿到电影的代理权限后,再通过穿插各种广告来增加自己的收益,下面通过代理模式来模拟一下

创建代表电影播放的接口 

public interface Movie {

    void play();

}

实现电影的播放接口

public class MovieProxy implements Movie {

    private String movieName;

    public MovieProxy(String movieName) {
        this.movieName = movieName;
    }

    @Override
    public void play() {
        System.out.println("正在播放" + movieName);
    }
}

创建电影的代理类,表示上述例子中的app 

public class Player implements Movie {

    private String movieName;

    public Player(String movieName) {
        this.movieName = movieName;
    }

    @Override
    public void play() {
        System.out.println("片头广告....");
        MovieProxy movieProxy = new MovieProxy(movieName);
        movieProxy.play();
        System.out.println("片尾广告....");
    }
}

测试类

public class Test {

    public static void main(String[] args) {
        Player player = new Player("功夫熊猫");
        player.play();
    }
}

结果

以上就是静态代理的实现,通过拓展代理类进行一些功能的拓展或增强 ,什么叫静态代理呢?就是增强的内容是事先定义好的,如果需要在运行时进行增强就需要用到动态代理。

动态代理

JDK动态代理

JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理,用JDK动态代理模拟看电影的例子

代码演示

创建接口,JDK代理中被代理的对象必须实现接口

public interface Movie {

    void play();
}

接口实现类

public class MyMovie implements Movie {

    @Override
    public void play() {
        System.out.println("正在播放电影");
    }
}

测试类

public class Test {

    public static void main(String[] args) {
        // 保存动态代理生成的文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        ClassLoader classLoader = MyMovie.class.getClassLoader();
        Class<?>[] interfaces = MyMovie.class.getInterfaces();
        // proxy代理对象本身
        // method被执行的方法
        // args1方法参数
        Movie movie = (Movie) Proxy.newProxyInstance(classLoader, interfaces, (proxy, method, args1) -> {
            MyMovie myMovie = new MyMovie();
            System.out.println("片头广告");
            Object invoke = method.invoke(myMovie, args1);
            System.out.println("片尾广告");
            return invoke;
        });
        movie.play();
    }
}

源码分析

newProxyInstance方法

在此方法中可以分为三个步骤

1)查找或生成指定的代理接口类,如果将sun.misc.ProxyGenerator.saveGeneratedFiles设置为true,则会在com.sun.proxy路径下保存生成的代理类

 2)获取构造方法

3)利用反射实例化代理类

 

CGLIB代理 

cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

代码演示

 创建被代理类 

public class Movie {

    public void play() {
        System.out.println("播放电影");
    }
}

创建回调方法

public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("片头广告");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("片尾广告");
        return o1;
    }
}

测试类

public class Test {

    public static void main(String[] args) {
        // 生成的代理类存储路径
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,".\\code");
        // 在创建Enhancer时会通过动态代理将EnhanceKey创建好,
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(Movie.class);
        // 设置回调方法
        enhancer.setCallback(new MyMethodInterceptor());
        // 创建代理类
        Movie movie = (Movie) enhancer.create();
        movie.play();
    }
}

总结

JDK和CGLIB动态代理的区别:

  1. JDK代理使用的是反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
    CGLIB代理使用字节码处理框架asm,对代理对象类的class文件加载进来,通过修改字节码生成子类。
  2. JDK创建代理对象效率较高,执行效率较低;
    CGLIB创建代理对象效率较低,执行效率高。
  3. JDK动态代理机制是委托机制,只能对实现接口的类生成代理,通过反射动态实现接口类;
    CGLIB则使用的继承机制,针对类实现代理,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,因为是继承机制,不能代理final修饰的类。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值