前言

代理模式(Proxy Pattern),23种java常用设计模式之一。代理模式的定义:代理类对被代理对象提供一种代理以控制对这个对象的访问。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。个人理解:在生活中我们常常把不必要的事情丢给别人去完成,而这些不必要的部分相当于他们代替我们完成的,这就相当于是代理模式。

例如:

1、明星与经纪人:就假设在和甲方谈商演的时候,影视明星只负责决定是否去演出,而经纪人就需要先去联系甲方并和甲方商定角色和报酬,然后将结果告诉影视明星,影视明星决定是否去演出,经纪人在得到明星的答复后将结果通知甲方,走后面的流程。

2、中介与房东:在房屋租(售)的时候,租(购)房人会先找中介咨询房屋信息,如果中介有钥匙还可以带租(购)房人去看房,如果租(购)房人对房屋无购买意愿,则本次流程结束;如果租(购)房人对房屋有兴趣,则先与中介协商价格;如果中介觉得房东不会接受这个价格,则直接拒绝;如果中介觉得价格合理,则与房东沟通,中介将房东的决定告知租(购)房人,然后走接下来的流程。

应用场景

1、当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理模式。

2、当客户端对象需要访问远程主机中的对象时可以使用远程代理模式,如RPC。

3、当需要为一个对象的访问(引用)提供一些额外的操作时可以使用代理模式。

4、当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。(待论证)

5、当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。(待论证)

静态代理

静态代理要求代理类必须继承被代理对象的接口。相当于中介(代理类)知道房东(被代理对象)有哪几个方面的要求(方法)。

代码实现

房东接口

public interface ILandlord {
//出售
String sell(int price);
//出租
String lease(int leaseTime,int price);
}

房东实现类

public class Landlord implements ILandlord {
String houseName;//房屋名称
int houseSellPrice;//房屋出售价格
int houseLeasePrice;//房屋出租价格(单价)
int houseLeaseTime;//最短出租时间
@Override
public String sell(int price) {
if(price>=houseSellPrice){
return "我同意以"+price+"元的价格出售"+houseName;
}else{
return "我不同意以"+price+"元的价格出售"+houseName;
}
} @Override
public String lease(int leaseTime, int price) {
if(price<houseLeasePrice){
return "我不同意以"+price+"元的价格将"+houseName+"出租";
}
if(leaseTime<houseLeaseTime){
return "我不同意将"+houseName+"出租"+leaseTime+"个月";
}
return "我同意以"+price+"元的价格将"+houseName+"出租"+leaseTime+"个月"; } public Landlord() {
} public Landlord(String houseName, int houseSellPrice, int houseLeasePrice, int houseLeaseTime) {
this.houseName = houseName;
this.houseSellPrice = houseSellPrice;
this.houseLeasePrice = houseLeasePrice;
this.houseLeaseTime = houseLeaseTime;
} public String getHouseName() {
return houseName;
} public void setHouseName(String houseName) {
this.houseName = houseName;
} public int getHouseSellPrice() {
return houseSellPrice;
} public void setHouseSellPrice(int houseSellPrice) {
this.houseSellPrice = houseSellPrice;
} public int getHouseLeasePrice() {
return houseLeasePrice;
} public void setHouseLeasePrice(int houseLeasePrice) {
this.houseLeasePrice = houseLeasePrice;
} public int getHouseLeaseTime() {
return houseLeaseTime;
} public void setHouseLeaseTime(int houseLeaseTime) {
this.houseLeaseTime = houseLeaseTime;
}
}

中介类

public class Intermediary implements ILandlord {
private ILandlord obj; Intermediary(ILandlord obj){
super();
this.obj=obj;
}
@Override
public String sell(int price) {
System.out.println("中介将购房人的出价告知房东");
String res = obj.sell(price);
System.out.println("房东:"+res);
System.out.println("中介将房东的结果告知购房人");
return "房东:"+res;
} @Override
public String lease(int leaseTime, int price) {
System.out.println("中介将租房人的出价告知房东");
String res = obj.lease(leaseTime,price);
System.out.println("房东:"+res);
System.out.println("中介将房东的结果告知租房人");
return "房东:"+res;
}
}

测试类

public class StaticProxyTest {
public static void main(String[] args) { Landlord landlordA=new Landlord("保利001号",3000000,2000,6);
Intermediary proxy=new Intermediary(landlordA);
String res = proxy.sell(3010000);
System.out.println("购房者得到的结果:"+res);
res = proxy.lease(5,1998);
System.out.println("购房者得到的结果:"+res);
}
}

结果

中介将购房人的出价告知房东
房东:我同意以3010000元的价格出售保利001号
中介将房东的结果告知购房人
购房者得到的结果:房东:我同意以3010000元的价格出售保利001号
中介将租房人的出价告知房东
房东:我不同意以1998元的价格将保利001号出租
中介将房东的结果告知租房人
购房者得到的结果:房东:我不同意以1998元的价格将保利001号出租

JDK动态代理

JDK中的动态代理是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。JDK中的动态代理使用的是java.lang.reflect.Proxy中的newProxyInstance方法来实现。

newProxyInstance方法的三个参数是ClassLoader loader、Class<?>[] interfaces和InvocationHandler h。

ClassLoader loader:类加载器,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用。

Class<?>[] interfaces:指明被代理类实现的接口,之后我们通过拼接字节码生成的类才能知道调用哪些方法。

InvocationHandler h:这是一个方法委托接口,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它的invoke方法决定处理。InvocationHandler 接口内部只是一个 invoke() 方法。

invoke方法的三个参数是Object proxy、 Method method和 Object[] args。

Object proxy:代理类实例。注意:这个代理类实例不是代理接口的实现类对象,而是JDK根据传入的接口生成一个extends Proxy implements Interface的代理类的对象。这个对象其实就是Proxy.newProxyInstance方法生成的对象(结尾处做验证)。

Method method:被调用的方法对象。

Object[] args:传入的方法参数。

代码实现

待代理的接口

public interface Star {
/**
* 唱歌
* @param song
* @return
*/
String sing(String song); /**
* 演戏
* @return
*/
String acting(); }

接口的实现类

/**
* 歌星
*/
public class SingingStar implements Star {
@Override
public String sing(String song) {
System.out.println("明星本人表示愿意唱:《"+song+"》");
return "愿意唱:《"+song+"》";
} @Override
public String acting() {
System.out.println("明星本人表示不愿意演戏");
return "不愿意演戏";
} }

InvocationHandler接口实现类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class DynamicsStarProxy implements InvocationHandler {
Object obj;
DynamicsStarProxy(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态明星代理和甲方协商出演的酬劳");
Object res = method.invoke(obj, args);
System.out.println("动态明星代理得到明星的决定,并告知甲方");
return res;
}
}

测试类

public class DynamicsProxyTest {
public static void main(String[] args) {
Star taylorSwift=new SingingStar();
DynamicsStarProxy starProxy=new DynamicsStarProxy(taylorSwift);
Star proxy= (Star) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),taylorSwift.getClass().getInterfaces(),starProxy);
//甲方找霉霉演唱Mean
String singRes=proxy.sing("Mean");
System.out.println("甲方得到的结果:"+singRes);
//甲方又想找霉霉演戏
String actRes=proxy.acting();
System.out.println("甲方得到的结果:"+actRes); }
}

结果

动态明星代理和甲方协商出演的酬劳
明星本人表示愿意唱:《Mean》
动态明星代理得到明星的决定,并告知甲方
甲方得到的结果:愿意唱:《Mean》
动态明星代理和甲方协商出演的酬劳
明星本表示不愿意演戏
动态明星代理得到明星的决定,并告知甲方
甲方得到的结果:不愿意演戏

CGLib动态代理

Cglib是一个强大的,高性能,高质量的代码生成类库。它可以在运行期扩展JAVA类与实现JAVA接口。其底层实现是通过ASM字节码处理框架来转换字节码并生成新的类。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类,大部分功能实际上是ASM所提供的,Cglib只是封装了ASM,简化了ASM操作。GLib动态代理不要求被代理类实现接口,任何一个类都可以被轻松的代理(final类不能,final方法也不能)。

Cglib动态代理需要自定义实现MethodInterceptor接口,在代理对象调用方法时,会被拦截到intercept方法中。

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)

Object obj:cglib生成的代理对象,对应JDK动态代理中invoke方法的Object proxy。

Method method:被调用的代理对象方法,对应JDK动态代理中invoke方法的Method method。

Object[] args:方法入参,对应JDK动态代理中invoke方法的Object[] args。

MethodProxy proxy:被调用的方法的代理方法,CGLib动态代理通过该参数的invokeSuper方法来调用被代理对象的方法。

代码实现

maven依赖

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

待代理的类

/**
* 舞蹈名星
*/
public class DanceStar {
private String name;//艺名
//跳舞方法
public String dancing(String typeName){
System.out.println(name+"表示愿意跳"+typeName);
return name+"愿意跳"+typeName;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
DanceStar(){
super();
}
DanceStar(String name){
super();
this.name=name;
}
}

Cglib代理类

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibStarProxy implements MethodInterceptor { /**
* 自定义方法:利用Enhancer类生成代理类
* @param clazz
* @param <T>
* @return
*/
public <T> T getObjByEnhancer(Class<T> clazz){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
T res = (T) enhancer.create();
return res;
} /**
* 自定义方法:利用Enhancer类生成代理类(模拟JDK代理的用法)
* @param target
* @return
*/
private Object object;
public Object getObjByEnhancer(Object target){
this.object=target;
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
Object res = enhancer.create();
return res;
} @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Cglib动态明星代理和甲方协商出演的酬劳");
Object res=null;
res = proxy.invokeSuper(obj,args);
//res = method.invoke(object, args);//模拟JDK代理的用法
System.out.println("Cglib动态明星代理得到明星的决定,并告知甲方");
return res;
}
}

测试类

public class CglibStarProxyTest {
public static void main(String[] args) {
CglibStarProxy starProxy=new CglibStarProxy();
//DanceStar ssyy=new DanceStar("三上悠亚");//模拟JDK动态代理
DanceStar ssyy2=null;
ssyy2 = starProxy.getObjByEnhancer(DanceStar.class);
//ssyy2= (DanceStar) starProxy.getObjByEnhancer(ssyy);//模拟JDK动态代理
String res=ssyy2.dancing("钢管舞");
System.out.println("甲方得到的结果:"+res); }
}

代理总结

静态代理:是通过静态代理类实现了被代理类的接口的方式来实现的。

JDK动态代理:是通过JVM根据反射等机制动态的生成一个被代理类的接口的实现类实现的,该实现类的继承方法会调用代理类中的invoke方法。

private MyInvocationHandler h;
m3 = Class.forName("com.***.Star").getMethod("sing", new Class[] { Class.forName("java.lang.String") }); public final void sing(Sting song)
{
try
{
this.h.invoke(this, m3, new Object[] {song});
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

CGLib动态代理:是使用ASM来生成被代理类的一个子类的字节码,该子类会重写父类的方法,在方法中调用代理类对象中的intercept()方法。

public final String dancing(String paramString)
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null) {
return (String)tmp17_14.intercept(this, CGLIB$dancing$2$Method, new Object[] { paramString }, CGLIB$dancing$2$Proxy);
}
return super.dancing(paramString);
}

JDK动态代理验证

接口

public interface Star {
/**
* 唱歌
* @param song 此处用对象封装,可以将代理生成的对象传入
* @return
*/
Object sing(Object song);
}

实现类

/**
* 歌星
*/
public class SingingStar implements Star { @Override
public Object sing(Object song) {
return song;
} }

代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class DynamicsStarProxy implements InvocationHandler {
Object obj;
DynamicsStarProxy(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("sing".equals(method.getName())){//判断是否为sing方法
for(Object o:args){//sing方法c传入的就是代理生成的对象
System.out.println(proxy==o);//判断地址是否相同
}
}
Object res = method.invoke(obj, args);
return res;
}
}

测试类

import java.lang.reflect.Proxy;

public class DynamicsProxyTest {
public static void main(String[] args) {
Star taylorSwift=new SingingStar();
DynamicsStarProxy starProxy=new DynamicsStarProxy(taylorSwift);
Star proxy= (Star) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),taylorSwift.getClass().getInterfaces(),starProxy);
Object singRes=proxy.sing(proxy); }
}

结果

true

最后

感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

java开发两年了,连个java代理模式都摸不透,你怎么跳槽涨薪?的更多相关文章

  1. java开发两年!这些异常处理的方式你得知道,不然你凭什么涨薪!

    前言 异常是在程序中导致程序中断运行的一种指令流,当异常发生时,程序将直接中断,不再执行后续的任何操作! 示例:两数相除,若不处理任何异常,则只有在正确输入两个数字时,才能显示出运算结果. publi ...

  2. Java开发不懂Docker,学尽Java也枉然,阿里P8架构师手把手带你玩转Docker实战

    转: Java开发不懂Docker,学尽Java也枉然,阿里P8架构师手把手带你玩转Docker实战 Docker简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一 ...

  3. java开发两年,连Spring的依赖注入的方式都搞不清楚,你工作可能有点悬!

    Spring依赖注入 常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的 ...

  4. java开发两年,这些线程知识你都不知道,你怎么涨薪?

    前言 什么是线程:程序中负责执行的哪个东东就叫做线程(执行路线,进程内部的执行序列),或者说是进程的子任务. Java中实现多线程有几种方法 继承Thread类: 实现Runnable接口: 实现Ca ...

  5. 十年Java开发程序员回答,自学Java,培训Java的利和弊

    最近有一个朋友在群里面问我:是应该去培训Java还是应该自学Java,我想的说的是我并不是想给他一个去培训或者不去培训,我用自己多年对于行业的了解去分析这个问题,然后让他自己去思考,哪种更加适合他.他 ...

  6. Java 开发环境配置--eclipse工具进行java开发

    Java 开发环境配置 在本章节中我们将为大家介绍如何搭建Java开发环境. Windows 上安装开发环境 Linux 上安装开发环境 安装 Eclipse 运行 Java Cloud Studio ...

  7. 《阿里巴巴Java开发手册》更新为《Java开发手册》

    新版一览:华山版<Java开发手册> <阿里巴巴Java开发手册>始于阿里内部规约,在全球Java开发者共同努力下,已成为业界普遍遵循的开发规范,涵盖编程规约.异常日志.单元测 ...

  8. JAVA设计模式(09):结构化-代理模式(Proxy)

    一,定义:  代理模式(Proxy):为其它对象提供一种代理以控制对这个对象的訪问. 二.其类图: 三,分类一:静态代理 1,介绍:也就是须要我们为目标对象编写一个代理对象,在编译期就生成了这个代理对 ...

  9. 2.1多线程(java学习笔记) java中多线程的实现(附静态代理模式)

    一.多线程 首先我们要清楚程序.进程.线程的关系. 首先进程从属于程序,线程从属于进程. 程序指计算机执行操作或任务的指令集合,是一个静态的概念. 但我们实际运行程序时,并发程序因为相互制约,具有“执 ...

随机推荐

  1. Nacos快速入门

    什么是 Nacos Nacos 是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现.配置管理和服务管理平台. Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提供 ...

  2. cgdb安装

    cgdb官网:http://cgdb.github.io/ 一.cgdb安装 可使用wget命令下载,wget  http://cgdb.me/files/cgdb-0.7.0.tar.gz 之后解压 ...

  3. Java9系列第九篇-对HTTP2协议的支持与非阻塞HTTP-API

    在HTTP/1.1 发布了16 年之后,IETF在2015年终于通过了HTTP/2 协议.HTTP/2协议旨在降低延迟,满足当今时代对于信息响应时间的要求.在这篇文章中,我会简要的对HTTP/2协议进 ...

  4. es6 新的数组操作

    ES6数组新增的几个方法 2017年03月24日 13:38:04 tang15886395749 阅读数:10461 标签: ES6数组新增方法 更多 个人分类: js相关   关于数组中forEa ...

  5. pandas dataframe 时间字段 diff 函数

    pandas pandas 是数据处理的利器,非常方便进行表格数据处理,用过的人应该都很清楚,没接触的可以自行查阅pandas 官网. 需求介绍 最近在使用 pandas 的过程中碰到一个问题,需要计 ...

  6. Spring MVC json配置

    接口类的Controller,一般返回的是json数据,而Spring MVC中默认返回的string,而jsp页面的话,会按配置中自己行匹配转义字符串为对应的jsp文件. @Controller @ ...

  7. CodeForces 1408I Bitwise Magic

    题意 给定三个整数 \(n,k,c\) 和一个长度为 \(n\) 的序列 \(a\),保证 \(a_i\) 互不相同.可以操作 \(k\) 次,每次随机选择一个 \(a_i\) 变成 \(a_i-1\ ...

  8. vue-cli @4安装

    10月16日,官方发布消息称Vue-cli 4.0正式版发布,安装和vue-cli3.0的是一模一样的,与3.0的脚手架,除了目录发生变化一些,其他的都一样,由于近期才推出,企业中还在使用3.0,但是 ...

  9. 最长公共子串算法(Longest Common Substring)

    给两个字符串,求两个字符串的最长子串 (例如:"abc""xyz"的最长子串为空字符串,"abcde"和"bcde"的最 ...

  10. 2. Hive常见操作命令整理

    该笔记主要整理了<Hive编程指南>中一些常见的操作命令,大致如下(持续补充中): 1. 查看/设置/修改变量2. 执行命令3. 搜索相关内容4. 查看库表信息5. 创建表6. 分区7. ...