前言

代理模式(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. C# indexof和indexofany区别(转)

    定位子串是指在一个字符串中寻找其中包含的子串或者某个字符.在String类中,常用的定位子串和字符的方法包括IndexOf/LastIndexOf及IndexOfAny/LastIndexOfAny, ...

  2. 阿里技术专家十五问,真题面试刀刀见肉,快来和阿里面试官battle

    引言 2020阿里巴巴专家组出题,等你来答: 题目:如何判断两个链表是否相交 出题人:阿里巴巴新零售技术质量部 参考答案: $O(n^2)$: 两层遍历,总能发现是否相交 $O(n)$: 一层遍历,遍 ...

  3. RAM ROM区别记忆

    我老是忘这个 1.概念 RAM即随机存储内存,这种存储器在断电时将丢失其存储内容,故主要用于存储短时间使用的程序.ROM即只读内存,是一种只能读出事先所存数据的固态半导体存储器. 2.对比 手机中的R ...

  4. FFmpeg滤镜使用

    滤镜(filter)详细介绍参考官方文档. 常用的滤镜功能,像图像加水印/字幕.去logo.图形旋转缩放等,但滤镜不仅仅包括视频部分,还包括音频处理的,像变声变调.声场控制(重低音/留声机/摇滚等效果 ...

  5. scrapy和scrapy-redis 详解一 入门demo及内容解析

    架构及简介 Scrapy是用纯Python实现一个为了爬取网站数据.提取结构性数据而编写的应用框架,用途非常广泛. Scrapy 使用了 Twisted(其主要对手是Tornado)异步网络框架来处理 ...

  6. 1. HttpRunner介绍及环境准备

    介绍 HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架 只需编写维护一份 YAML/JSON脚本,即可实现自动化测试.性能测试.线上监控.持续集成等多种测试需求 官方文档: htt ...

  7. 企业级数据大屏设计如何实现,div+html+echarts

    大屏是什么? 大屏设计是最近比较流行的概念,一般按照功能来分有几种: 1. 可交互的触摸屏,大多运用在互动教学课程或者报告演示现场,用户可结合交互操作来阐述具体内容.设计师需要对交互形式和传达内容作统 ...

  8. 《Head First 设计模式》:迭代器模式

    正文 一.定义 迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示. 要点: 迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象.这样简化了聚合的接口和实现,也让责 ...

  9. SpringBoot整合Logback日志框架配置全解析

    目录 本篇要点 一.Logback日志框架介绍 二.SpringBoot与Logback 1.默认日志格式 2.控制台输出 3.文件输出 4.日志级别 5.日志组 6.自定义log配置 三.logba ...

  10. Java到处运行的基础之 Class 文件

    Java 实现一次编译到处运行的基础,来源于 Java 虚拟机屏蔽了操作系统的底层细节.使用 class 文件存储编译后的源程序,使得 Java 程序的编译与操作系统解耦.正是因为 Java clas ...