开发者社区> 阿豪聊干货> 正文

通俗易懂详解Java代理及代码实战

简介: 一、概述   代理模式是Java常用的设计模式之一,实现代理模式要求代理类和委托类(被代理的类)具有相同的方法(提供相同的服务),代理类对象自身并不实现真正的核心逻辑,而是通过调用委托类对象的相关方法来处理核心逻辑,而代理类对象主要负责为委托类对象过滤消息、预处理消息、转发消息给委托类、事后处理消息等等。
+关注继续查看

一、概述

  理模式是Java常用的设计模式之一,实现代理模式要求代理类和委托类(被代理的类)具有相同的方法(提供相同的服务),代理类对象自身并不实现真正的核心逻辑,而是通过调用委托类对象的相关方法来处理核心逻辑,而代理类对象主要负责为委托类对象过滤消息、预处理消息、转发消息给委托类、事后处理消息等等。通常代理类会与委托类存在关联关系。

  按照代理的创建时期,代理可分为:静态代理和动态代理。静态代理由开发者手动创建,在程序运行前,已经存在;而动态代理不需要手动创建,它是在程序运行时动态的创建代理类。

二、静态代理

  我们直接用代码来说明什么叫静态代理,场景是我要卖掉我的车子,但是由于我很忙,所以卖掉车子的过程中不想每天被电话骚扰,于是我就在附近找了一个二手车交易的中介,希望在他的帮助下很轻松的卖掉车子。
1.卖车子接口
public interface SaleCar {
    void sale();
}

2.hafiz真正卖车子实现类

public class HafizSaleCar implements SaleCar {
    @Override
    public void sale() {
        System.out.println("hafiz sale his car...");
    }
}

3.二手车交易中介类

public class CarTradeProxy implements SaleCar {
    private HafizSaleCar owner;
    public CarTradeProxy(HafizSaleCar owner) {
        this.owner = owner;
    }
    @Override
    public void sale() {
        System.out.println("proxy add price...");
        owner.sale();
    }
}

4.测试类

public class Client {
    public static void main(String[] args) {
        HafizSaleCar owner = new HafizSaleCar();
        CarTradeProxy proxy = new CarTradeProxy(owner);
        proxy.sale();
    }
}

5.测试结果

从上面的代码中,我们可以看出,其实代理类(CarTradeProxy)和委托类(HafizSaleCar)好像区别并不大,我们直接创建一个HafizSaleCar对象,然后调用它的sale()方法不就好了?细心的同学你会发现,其实代理在真正调用委托类的方法之前做了中介加价的操作,这也就意味着我们使用代理模式实现在委托类的基础上增加额外的逻辑操作。

  以上就是一个很简单的静态代理的实现过程。但是这个时候我又有了一个新需求,我想用我手里的存款以及买车子赚的钱来给自己买一套新房子,那我又不想东奔西跑找房源,于是我又把买房这件事委托给了房产中介,下面我们就来实现这个逻辑。

1.再定义一个买房的接口

public interface BuyHouse {
    void buy();
}

2.重写委托类,实现卖车和买房两个接口

public class HafizTrade implements SaleCar, BuyHouse {
    @Override
    public void buy() {
        System.out.println("hafiz buy house...");
    }
    @Override
    public void sale() {
        System.out.println("hafiz sale car...");
    }
}

可以看到,我现在既要卖掉我的车子,又要购买新的房子。

3.再创建一个买房子的中介代理类

public class HouseTradeProxy implements BuyHouse {
    private HafizTrade customer;
    public HouseTradeProxy(HafizTrade customer) {
        this.customer = customer;
    }
    @Override
    public void buy() {
        System.out.println("proxy add price...");
        customer.buy();
    }
}

4.卖车子的代理类修改如下

public class CarTradeProxy implements SaleCar {
    private HafizTrade owner;
    public CarTradeProxy(HafizTrade owner) {
        this.owner = owner;
    }
    @Override
    public void sale() {
        System.out.println("proxy add price...");
        owner.sale();
    }
}

5.新的测试类

public class Client {
    public static void main(String[] args) {
        HafizTrade trader = new HafizTrade();
        CarTradeProxy carTradeProxy = new CarTradeProxy(trader);
        carTradeProxy.sale();
        System.out.println("-----------------------------------------------");
        HouseTradeProxy houseTradeProxy = new HouseTradeProxy(trader);
        houseTradeProxy.buy();
        System.out.println("-----------------------------------------------");
    }
}

6.测试结果

这样通过静态代理的方式,我们的确也可以很完美的解决我们的问题,但当我们有越来越多的委托类需要代理,而且代理做的工作又一样,那是不是会多出来很多的代理类,我们开发者会疯掉的,这时候我们就想:如果我们可以只做一次,就能代理一类委托类该多好啊?那么这个时候,动态代理就应运而生了,它可以使得我们只定义一次就能为一类委托类做代理。

三、动态代理

  静态代理要求我们在程序发布上线运行之前,就要开发好对应委托类的代理类,而动态代理是我们在程序发布之前,并没有创建好对应的代理类,而是在运行的时候动态的创建代理类。
  动态代理实现方式有两种:jdk自带动态代理实现以及cglib实现。jdk代理只适合代理实现接口的目标对象,cglib可以代理没有实现接口的目标对象。

四、基于JDK实现动态代理

1.实现步骤

  1).通过实现 InvocationHandler 接口创建自己的调用处理器

  2).通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类

  3).通过反射机制获得动态代理类的构造函数(jdk自带,不需手动处理)

  4).通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入(jdk自带,不需手动处理)

2.创建代理处理器

public class ProxyHandler implements InvocationHandler {
    private Object target;
    public ProxyHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy add price...");
        Object result = method.invoke(target, args);
        return result;
    }
}

3.测试类

public class Client {
    public static void main(String[] args) {
        HafizTrade trader = new HafizTrade();
        ProxyHandler handler = new ProxyHandler(trader);
        Class<? extends HafizTrade> clazz = trader.getClass();
        ClassLoader classLoader = clazz.getClassLoader();
        Class<?>[] interfaces = clazz.getInterfaces();
        SaleCar carProxy = (SaleCar)Proxy.newProxyInstance(classLoader, interfaces, handler);
        carProxy.sale();
        System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");
        BuyHouse houseProxy = (BuyHouse)Proxy.newProxyInstance(classLoader, interfaces, handler);
        houseProxy.buy();
        System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");

    }
}

4.测试结果

5.原理

  生成一个代理类,这个代理类继承Proxy类并且实现了我们定义的接口,代理对象调用方法的时候,调用这个代理对象的一个成员InvocationHandler(上面我们传入了一个InvocationHandler实现对象)的方法,也就是我们包装了委托类后的方法。

五、基于cglib实现动态代理

1.实现步骤

  1).通过实现CGLib包提供的MethodInterceptor接口,重写intercept方法,创建自己的方法拦截器

  2).通过CGLib中的Enhancer的creat方法创建动态代理对象

2.添加cglib的maven依赖

 <dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

3.自定义ProxyInterceptor

public class ProxyInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Trade proxy add price...");
        Object result = methodProxy.invokeSuper(o, objects);
        return result;
    }
}

4.测试client

public class Client {
    public static void main(String[] args) {
        ProxyInterceptor proxy = new ProxyInterceptor();
        HafizTrade tradeProxy = (HafizTrade)Enhancer.create(HafizTrade.class, proxy);
        tradeProxy.sale();
        System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");
        tradeProxy.buy();
    }
}

5.测试结果

6.原理

  首先通过asm字节码生成框架生成代理类Class的二进制字节码,然后通过Class.forName加载二进制字节码,生成Class对象,最后通过反射机制获取实例构造,并初始化代理类对象。

六、总结

  动态代理可以使得我们一次可以解决一批需要创建代理的问题,使得代码更加灵活,提高了程序的扩展性。动态代理在主流java框架中也非常常用,比如最著名的spring,它在AOP的功能就是使用动态代理实现,还有Dubbo等这样的RPC服务框架,在客户端都是通过代理完成服务的真正调用。了解和学会代理以及实现方式能帮助我们更好地理解主流框架。

  关于动态代理的实现细节,可以参考:http://www.360doc.com/content/14/0801/14/1073512_398598312.shtml#

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
18980 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
25225 0
JavaScript正则表达式的模式匹配教程,并且附带充足的实战代码(二)
我们继续来学习正则表达式吧,没有看过之前文章的小伙伴建议点击上方链接,从第一篇开始看,本系列文章适合从未接触过或不太了解正则表达式的人
19 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
18711 0
《Spring Cloud与Docker微服务架构实战》配套代码
不才写了本使用Spring Cloud玩转微服务架构的书,书名是《Spring Cloud与Docker微服务架构实战》 - 周立,已于2017-01-12交稿。
1238 0
《JavaScript和jQuery实战手册(原书第2版)》——3.4节函数:把有用的代码转换为可复用的命令
本节书摘来自华章社区《JavaScript和jQuery实战手册(原书第2版)》一书中的第3章,第3.4节函数:把有用的代码转换为可复用的命令,作者:David Sawyer McFarland,更多章节内容可以访问云栖社区“华章社区”公众号查看
977 0
+关注
阿豪聊干货
Java后端,以代码为情人,期望用代码为世界做出自己的贡献~
134
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载