前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发编程(二)--番外篇代理

并发编程(二)--番外篇代理

作者头像
小土豆Yuki
发布2021-09-24 10:42:23
2010
发布2021-09-24 10:42:23
举报
文章被收录于专栏:洁癖是一只狗

面试中经常会被问到代理的问题,今天我们说说代理,后期在并发文章中可能会被用到,这里先做一个铺垫

代理

代理就是设置一个中间代理来控制访问原目标对象,以达到增强原对象和简化访问的方式

分类

静态代理:由程序员创建或工具生成的代理类的源码,在编译代理类,即代理和委托类的关系在程序运行前就已经存在了

动态代理:运行期间使用动态生成字节码形式,动态创建代理类,使用工具JDK代理,CGLIB代理

静态代理

代理和被代理类实现同一个接口,在代理类的构造函数定义一个代理类的对象

EatServer类

代码语言:javascript
复制
public interface EatServer {
     //吃饭
public void  eat();
}

被代理类 EatServerImp,实现EatServer

代码语言:javascript
复制
public class EatServerImp implements  EatServer {
@Override
public void eat() {
        System.out.println("下班了要进行吃饭了");
    }
}

代理类EatServerProxy,同样实现EatServer,且引入被代理类EatServer

代码语言:javascript
复制
public class EatServerProxy implements EatServer {

private EatServer eatServer;

public EatServerProxy(EatServer eatServer) {
this.eatServer = eatServer;
    }

@Override
public void eat() {
        System.out.println("吃饭前洗手");
        eatServer.eat();
        System.out.println("吃饭后洗完");
    }
}
代码语言:javascript
复制
 public static void main(String[] args) {
      EatServer eatServer = new EatServerImp();
      EatServerProxy eatServerProxy = new EatServerProxy(eatServer);
      eatServerProxy.eat();
 }

运行结果

吃饭前洗手

下班了要进行吃饭了

吃饭后洗碗

JDK动态代理

使用JDK API,动态代理不需要实现接口,但是目标对象必须实现接口

目标对象EatServerImp,实现EatServer

代码语言:javascript
复制
public class EatServerImp implements  EatServer {
@Override
public void eat() {
        System.out.println("下班了要进行吃饭了");
    }
}

动态代理对象EatServerJDKProxy,实现InvocationHandler接口

代码语言:javascript
复制
public class EatServerJDKProxy implements InvocationHandler {
    //目标对象
private Object target;

public EatServerJDKProxy(Object target) {
this.target = target;
    } 
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("吃饭前洗手");
//通过反射调用,目标对象的方法
        method.invoke(target, args);
        System.out.println("吃饭后洗碗");
return null;
    }
//获取代理对象
public Object getProxyInstance() {
         //第一个参数,目标对象的类加载器
         //第二参数,目标对象实现的接口
         //第三个参数,就是代理对象,每次需要调用目标对象的方法时候
         //就需要使用代理对象的invoke方法调用
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), new EatServerJDKProxy(target));
    }
}
代码语言:javascript
复制
public static void main(String[] args) {
    EatServer eatServer = new EatServerImp();
    EatServerJDKProxy jdkProxy = new EatServerJDKProxy(eatServer);
    EatServer proxyInstance = (EatServer)jdkProxy.getProxyInstance();
    proxyInstance.eat();
 }
代码语言:javascript
复制
吃饭前洗手
下班了要进行吃饭了
吃饭后洗碗

CGLIB动态代理

如果目标对象没有实现接口的类,就可以使用CGLIB代理,CGLIB是一个强大的高性能的代码生成包,他可以在运行期扩展java类与实现java的接口,底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类.

目标对象EatServerImp,不用实现接口

代码语言:javascript
复制
public class EatServerImp  {
public void eat() {
        System.out.println("下班了要进行吃饭了");
    }
}

代理对象,实现MethodInterceptor

代码语言:javascript
复制
public class EatServerCGLIBProxy implements MethodInterceptor {
    //目标对象
private Object target;

public EatServerCGLIBProxy(Object target) {
this.target = target;
    }
    //为目标对象生成代理类
public  Object getTargetInstance(){
       //工具
        Enhancer enhancer = new Enhancer();
//设置父类
        enhancer.setSuperclass(target.getClass());
//设置回调函数
        enhancer.setCallback(this);
return  enhancer.create();
    }


@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("吃饭前洗手");
        method.invoke(target,objects);
        System.out.println("吃饭后洗碗");
return null;
    }
}
代码语言:javascript
复制
 public static void main(String[] args) {
    EatServerCGLIBProxy cglibProxy = new EatServerCGLIBProxy(new EatServerImp());
    EatServerImp eatServer1 = (EatServerImp)cglibProxy.getTargetInstance();
    eatServer1.eat();
 }
代码语言:javascript
复制
吃饭前洗手
下班了要进行吃饭了
吃饭后洗碗

总结

  1. 静态代理类实现比较简单,只要代理对象目标进行封装,即可实现增强功能,但静态代理只能为一个目标对象服务,如果目标对象过多,则产生很多代理类
  2. JDK动态代理需要目标对象实现业务接口,代理类只需要实现InvocationHandler接口
  3. 静态代理类编译时产生class字节码文件,可以直接使用,效率高
  4. 动态代理必须实现InvocationHandler接口,通过代理的方式,比较消耗性能,但是减少代理类的数量,使用灵活
  5. cglib代理无需实现接口,通过生成类字节码实现代理,比反射要快,不存在性能问题,但是会继承目标对象,需要重写方法,所以目标对象不能是fianl类
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档