理解Java动态代理(1)—找我还钱?我出钱要你的命

news/2025/2/9 6:18:20 标签: java, 设计模式, runtime

代理模式是最常用的一个设计模式之一,理解起来也是很简单,一张图足以说明了,LZ就不废话了。

至于代理模式能干嘛也不是LZ今天想说的,今天主要想简单介绍下JAVA里面的动态代理。“动”当然是相对“静”来说的,那么什么是静,怎么就又动了呢?LZ想举个生活中常见的例子来说明,俗话说“谈钱伤感情”,但生活所迫LZ曾经可没少找人借个一百两百五的,话说借钱一时爽,还钱……(请自行造句),好点的心平气和的委婉的说,横点的就拳脚相加啊。我们来用接口表示下借钱者这个角色,他们可以采取peace或force的方式找我还钱:

/**
 * 借钱方
 * 
 * @author moon
 * 
 */
public interface ILender {
    /**和平方式*/
    public long peace(String name);

    /**暴力方式*/
    public long force(String name);
}

我月中找张三借钱:

public class ZhangSanImpl implements ILender {

    public long peace(String name) {
        System.out.println("心平气和的找" + name + "要账。");
        return 100;
    }

    public long force(String name) {
        System.out.println("以暴力方式找" + name + "要账");
        return 250;
    }

}

月底又找李四借钱(都是好欺负的货,动手干不过我):

public class LiSiImpl implements ILender {  
      
    public long peace(String name) {
        System.out.println("心平气和的找" + name + "要账。");
        return 100;
    }

    public long force(String name) {
        System.out.println("以暴力方式找" + name + "要账");
        return 250;
    }
} 

张三和李四发现我老是借钱又拖帐,于是就合伙找了年级里一个比较横的角色(外号"JDK",由来是因为我叫jd,他是jd killer)来代他们要帐,好说不行就动手。看这就是代理模式。

/**
 * 借钱者的代理人
 * 
 * @author jdzhan
 * 
 */
public class CommonLenderProxy implements ILender {

    /** 委托者 */
    private ILender delegator;

    public CommonLenderProxy(ILender delegator) {
        this.delegator = delegator;
    }

    public long peace(String name) {
        return delegator.peace(name);
    }

    public long force(String name) {
        System.out.println("开始工作");
        // 执行方法
        long result = delegator.force(name);
        System.out.println("搞定收工");
        return result;
    }

}

于是乎我屈于淫威或者武力只能乖乖还钱鸟:

 // Common Proxy
 CommonLenderProxy proxy = new CommonLenderProxy(new ZhangSanImpl());
 long money = proxy.force("zhanjindong");
 System.out.println("要回了" + money + "块QB");
开始工作
以暴力方式找zhanjindong要账
搞定收工
要回了250块QB

 Ok,上面我用了亲身的一个小栗子说明了静态代理,那动态代理到底怎么实现,又有什么好处呢?我们先直接把上面的实现给出来。JDK动态代理中包含一个接口和一个类: 

InvocationHandler接口:

public interface InvocationHandler { 
  public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 
}

Proxy类:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler h) 
                               throws IllegalArgumentException 

"JDK"觉得代人要钱很有钱途,于是搞了个“帮会”:

/**
 * “JDK”代人人要账帮会
 * 
 * @author jdzhan
 * 
 */
public class DynamicLenderProxy implements InvocationHandler {
    private Object target;

    /**
     * 绑定委托对象并返回一个代理类
     * 
     * @param target
     * @return
     */
    public Object bind(Object target) {
        this.target = target;
        // 取得代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); 
    }

    /**
     * 调用方法
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if (method.getName().equals("force")) {
            Object result = null;
            System.out.println("开始工作");
            // 执行方法
            result = method.invoke(target, args);
            System.out.println("工作结束");
            return result;
        } else {
            return method.invoke(target, args);
        }

    }

}

是不是感觉比之前牛X多了,当然目的还是找人要账,可能是我也可能是马六:

 //JDK Dynamic Proxy
 DynamicLenderProxy proxy = new DynamicLenderProxy();
 ILender bookProxy = (ILender) proxy.bind(new LiSiImpl());
 long money = bookProxy.force("maliu");
 System.out.println("要回了" + money + "块QB");
心平气和的找maliu要账。
要回了100块QB

我们通常还可以把动态代理跟注解结合起来用,“帮会”越来越大,想立稳得提供多种业务,比如暴力要账,可以让人选择用什么样的暴力,是赤手空拳还是刀墙棍棒伺候:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Force {
    
    String weapon() default "拳头";

}

有了这个注解可以在接口里使用:

/**
 * 借钱方
 * 
 * @author moon
 * 
 */
public interface ILender {
    /**和平方式*/
    public long peace(String name);

    /**暴力方式*/
    @Force(weapon = "AK47")
    public long force(String name);
}

改下DynamicLenderProxy里的invoke方法:

/**
     * 调用方法
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if (method.isAnnotationPresent(Force.class)) {
            Force force = method.getAnnotation(Force.class);
            String weapon = force.weapon();
            System.out.println("用" + weapon + "开始工作");
            Object result = method.invoke(target, args);
            System.out.println("搞定收工");
            return result;

        } else {
            return method.invoke(target, args);
        }
    }

拿AK47找人要钱谁敢不还:

用AK47开始工作
以暴力方式找zhanjindong要账
搞定收工
要回了250块QB

(这里有个问题注解只能用在接口里,用在实现类里如何获取到呢?)

JDK动态代理的使用就是这么简单,下面就说为什么叫动态代理了。假设有一天我为实在还不了钱了,于是就想花钱找人把借钱给我的人干掉,于是我成了个凶手:

public interface IMurderer {

    @Force(weapon = "沙漠之音")
    public long kill(String name);
}
public class JdzhanImpl implements IMurderer {

    public void kill(String name) {
        System.out.println("找我还钱?我要你" + name + "的命");
    }

}

恰好这时"JDK"拓展了业务,也干杀手这一行当了,于是乎当初找我还钱的人成了我雇的杀手,但是实现一点没动:

  DynamicLenderProxy2 proxy = new DynamicLenderProxy2();
  IMurderer murderer = (IMurderer) proxy.bind(new JdzhanImpl());
  murderer.kill("LiSi");
用沙漠之音开始工作
找我还钱?我要你LiSi的命
搞定收工

好了,这个我胡扯的例子说完了,动态代理的好处也就体现出来了:

可以为不同的接口进行代理

如果我们用静态代理的话那么每个接口我们都得写一个代理类。

 

但是竟然动态为什么不更灵活点呢,JDK的动态代理还是需依赖接口的,接口就像一个契约或收据,你找我还钱最起码得有欠条吧,下篇文章介绍下Cglib动态代理,就是怎么在没有收据的情况下找人还钱。

 

 


http://www.niftyadmin.cn/n/712307.html

相关文章

GukiZ and Binary Operations CodeForces - 551D (组合计数)

大意: 给定$n,k,l,m$, 求有多少个长度为$n$, 元素全部严格小于$2^l$, 且满足 的序列. 刚开始想着暴力枚举当前or和上一个数二进制中$1$的分布, 但这样状态数是$O(64^3)$在加上矩阵幂的复杂度显然不行. 看了题解发现可以按每位单独来考虑. #include <iostream> #include &…

学习笔记:JAVA RMI远程方法调用简单实例

RMI的概念 RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制。使用这种机制&#xff0c;某一台计算机上的对象可以调用另外一台计算机上的对象来获取远程数据。RMI是Enterprise JavaBeans的支柱&#xff0c;是建立分…

一步一步学习Redis——简介与安装

1.Redis REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统&#xff0c;是跨平台的非关系型数据库。 Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库…

廉洁修身论文2000字_论文3000字符是几个版面

点击上方蓝字 关注我们1论文发表中字数要求是一项最基本要求&#xff0c;字数的多少关系到文章见刊占用的版面&#xff0c;很多作者在发表前也会对自己的文章以及所占版面进行大致的估算&#xff0c;控制字数对于作者和期刊来说都是很有必要的&#xff0c;论文3000字符是几个版…

jforum mysql_在linux環境下搭建JDK+JAVA+Mysql,並完成jforum的安裝

參考鏈接&#xff1a;YUM安裝MySQL和JDK和Tomcat&#xff1a;http://cmdschool.blog.51cto.com/2420395/1696206/因為我使用的是普通用戶lily&#xff0c;如果使用root的話&#xff0c;所有命令不用加sudo即可。今天先不加圖&#xff0c;改天再試試可以的話&#xff0c;再補充。…

希捷正式发布12TB硬盘:二代充氦 单碟1.5TB

在透露已经出样之后&#xff0c;希捷今天正式发布了新款Enterprise Capacity v7 12TB硬盘&#xff0c;这也是希捷的第二代充氦技术硬盘&#xff0c;面向企业和云计算市场。相比于此前的Enterprise Capacity v6 10TB&#xff0c;新硬盘增强了充氦技术&#xff0c;单盘封装多达八…

NTF服务器搭建及其遇到问题

1&#xff0c;服务器端软件&#xff1a;安装nfs-utils和rpcbindnfs-utils&#xff1a; 提供rpc.nfsd 及 rpc.mountd这两个NFS DAEMONS的套件 rpcbind: NFS其实可以被看作是一个RPC SERVER PROGRAM,而要启动一个RPC SERVER PROGRAM&#xff0c;都要做好客户端IP及其PORT的对应工…

web前端学习(二十三)——CSS3定位(position)、元素裁剪(clip)及鼠标样式(cursor)属性的相关设置

1.CSS定位属性&#xff08;position&#xff09; position 属性指定了元素的定位类型。 position 属性的五个值&#xff1a; staticrelativefixedabsolutesticky元素可以使用的顶部&#xff0c;底部&#xff0c;左侧和右侧属性定位。然而&#xff0c;这些属性无法工作&#xff…