一、概述

代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。 其特征是代理类与委托类有同样的接口,真正的核心业务逻辑还是在实际对象里面。

二、为什么要使用代理模式

  • 当客户端不想直接调用实际对象,或是客户端直接调用实际对象有困难。
  • 比如:想在实际对象的业务方法执行前或执行后额外处理一些事情。但是又不能修改原来的实际对象的方法,这时候也可以用代理模式。

三、代理模式的分类

1、静态代理(在程序运行前,代理类的.class文件已经存在,所以称为静态代理)

1)原业务接口

public interface SourceObject {
void operation();
}

2)原业务接口的实现类

public class SourceObjectImpl implements SourceObject {
public void operation() {
System.out.println("这是原业务核心方法");
}
}

3)静态代理类

public class StaticProxy implements SourceObject {
private SourceObjectImpl srcObj;
public StaticProxy(SourceObjectImpl srcObj){
this.srcObj = srcObj;
}
public void operation() {
System.out.println("原业务方法执行前:记录处理中日志");
srcObj.operation();
System.out.println("原业务方法执行前:记录完成日志");
}
}
public class StaticProxy implements SourceObject {
private SourceObject srcObj;
public StaticProxy(SourceObject srcObj){
this.srcObj = srcObj;
}
public void operation() {
System.out.println("原业务方法执行前:记录处理中日志");
srcObj.operation();
System.out.println("原业务方法执行前:记录完成日志");
}
}

4)测试类

public class Client {
public static void main(String[] args){
SourceObject srcObj = new SourceObjectImpl();
StaticProxy staticProxy = new StaticProxy(srcObj);
staticProxy.operation();
}
}
原业务方法执行前:记录处理中日志
这是原业务核心方法
原业务方法执行前:记录完成日志

由以上的结构可知:每个接口(SourceObject)有一个与之对应的代理类(StaticProxy),如果再有一个接口,那么就要同时新增一个代理类,这种情况在接口很多的时候,就会产生很多代理类。是否能只有一个代理类呢?当然是可以的,就是动态代理

 2、动态代理(在程序运行前,代理类的.class文件不存在,是运行时序由Java反射机制动态生成)

动态代理主要是借助 类Proxy、接口InvocationHandler 来实现的。下面以一个具体的例子来说明 动态代理的过程

1)原业务接口

public interface SourceObject {
void operation();
}

2)原业务接口的实现类

public class SourceObjectImpl implements SourceObject {
public void operation() {
System.out.println("这是原业务核心方法");
}
}

3)新建动态代理类处理器(注意,该类并不是SourceObject的代理类,它并没有实现于接口SourceObject,因此它不能调用operation()方法,这是与静态代理 最大的不同之处,代理类是程序运行的时候才动态产生的。)

public class DynamicProxyHandler implements InvocationHandler {
private Object srcObj;//被代理的实际对象,定义为Object类型表明,可以是任意对象 /**
* 该方法负责动态创建代理类,并返回代理类的实例,这个实例的类继承于java.lang.reflect.Proxy,还实现了传入进来的srcObject的原业务接口。
*
* @param srcObj
* @return
*/
public Object getProxyInstance(Object srcObj) {
this.srcObj = srcObj;
return Proxy.newProxyInstance(srcObj.getClass().getClassLoader(), srcObj.getClass().getInterfaces(), this);
} /**
* 注意:客户端不会真正的显示调用该方法,当通过getProxyInstance取得的实例执行真正的方法时,该方法会执行。
*
* @param proxy 参数传递的是动态生成的代理类的实例,本方法并没有使用它
* @param method 是调用的方法,即需要执行的方法
* @param args 是执行方法的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("原业务方法执行前:记录处理中日志");
System.out.println("invoke 方法的第一个参数proxy 的类是:" + proxy.getClass().toString());
method.invoke(srcObj, args);
System.out.println("原业务方法执行前:记录完成日志");
return null;
}
}

4)测试类

public class Client {
public static void main(String[] args){
SourceObject srcObj = new SourceObjectImpl();
DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler();
//为什么这里可以用SourceObject来接收getProxyInstance返回的实例呢?从后面的结果可知道动态产生的代理类实质上是继承于Proxy类,并实现于SourceObject
SourceObject dynamicProxyInstance = (SourceObject)dynamicProxyHandler.getProxyInstance(srcObj);
System.out.println("dynamicProxyInstance 的类是:================="+dynamicProxyInstance.getClass().toString());
System.out.println("dynamicProxyInstance 的父类是:==============="+dynamicProxyInstance.getClass().getSuperclass());
System.out.println("dynamicProxyInstance 实现的接口如下:===========");
Class<?>[] interfaces=dynamicProxyInstance.getClass().getInterfaces();
for(Class<?> i:interfaces){
System.out.println(i.getName());
}
System.out.println("dynamicProxyInstance 的方法有:===============");
Method[] method=dynamicProxyInstance.getClass().getDeclaredMethods();
for(Method m:method){
System.out.println(m.getName());
}
//执行真正的核心业务方法
System.out.println("dynamicProxyInstance 执行原业务方法:===============");
dynamicProxyInstance.operation(); //以下的代码是将动态生成的代理类保存到target的classes里面,便于查看。
byte[] bts = ProxyGenerator.generateProxyClass("$Proxy0", interfaces);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File("target/classes/proxy/demo/$Proxy0.class"));
fos.write(bts);
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
dynamicProxyInstance 的类是:=================class com.sun.proxy.$Proxy0
dynamicProxyInstance 的父类是:===============class java.lang.reflect.Proxy
dynamicProxyInstance 实现的接口如下:===========
proxy.demo.SourceObject
dynamicProxyInstance 的方法有:===============
equals
toString
hashCode
operation
dynamicProxyInstance 执行原业务方法:===============
原业务方法执行前:记录处理中日志
invoke 方法的第一个参数proxy 的类是:class com.sun.proxy.$Proxy0
这是原业务核心方法
原业务方法执行前:记录完成日志

由以上结果可知:程序运行时动态生成了一个代理类com.sun.proxy.$Proxy0,它的父类是Proxy,并且还实现了接口SourceObject。这个代理类是由Proxy的 newProxyInstance 方法生成的。生成的代理类的源代码如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.demo.SourceObject; public final class $Proxy0 extends Proxy implements SourceObject {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0; public $Proxy0(InvocationHandler var1) throws {
super(var1);
} public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
} public final void operation() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
} public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
} static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("proxy.demo.SourceObject").getMethod("operation");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}

当调用  dynamicProxyInstance.operation() 的时候,实质上就是调用了以上生成的代理类中红色标注的方法。

   super.h.invoke(this, m3, (Object[])null);

super就是指父类Proxy,h就是指InvocationHandler的实例,这里应该是传入的子类DynamicProxyHandler 的实例,所以h.invoke方法,就是执行DynamicProxyHandler的invoke方法。

java常用设计模式八:代理模式的更多相关文章

  1. Java设计模式11:常用设计模式之代理模式(结构型模式)

    1. Java之代理模式(Proxy Pattern) (1)概述: 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问. 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象 ...

  2. Java基础-设计模式之-代理模式Proxy

    代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理 ...

  3. 基于JAVA的设计模式之代理模式

    概念 王宝强有一个经纪人叫宋喆,这个经纪人很吊,可以代理王宝强做一系列的事情,与粉丝活动.王宝强的微博维护.安排王宝强的行程以及什么什么等等.如果王宝强一个人做岂不是累死.通过这个代理人为王宝强节省了 ...

  4. Java常见设计模式之代理模式

    指由一个代理主题来操作真实主题,真实主题执行具体的业务操作,而代理主题负责其它相关业务的处理.比如生活中的通过代理访问网络,客户通过网络代理连接网络(具体业务),由代理服务器完成用户权限和访问限制等与 ...

  5. java常用设计模式总览

    一.java的设计模式大体上分为三大类: 创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式. 结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组 ...

  6. Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景

    我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...

  7. 夜话JAVA设计模式之代理模式(Proxy)

    代理模式定义:为另一个对象提供一个替身或者占位符以控制对这个对象的访问.---<Head First 设计模式> 代理模式换句话说就是给某一个对象创建一个代理对象,由这个代理对象控制对原对 ...

  8. Java设计模式:代理模式(转)

    代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.这里使用到编程中的一 ...

  9. java设计模式6——代理模式

    java设计模式6--代理模式 1.代理模式介绍: 1.1.为什么要学习代理模式?因为这就是Spring Aop的底层!(SpringAop 和 SpringMvc) 1.2.代理模式的分类: 静态代 ...

随机推荐

  1. TZOJ 2588 Bad Grass(DFS)

    描述 Bessie was munching on tender shoots of grass and, as cows do, contemplating the state of the uni ...

  2. 《centos系列》配置vim编辑器

    直接wget到~/目录下: wget  http://files.cnblogs.com/ma6174/vimrc.zip 然后进行解压: unzip  -f  vimrc.zip  -d  ~/ 参 ...

  3. swift - view的指定位置切圆角

    1. extension UIView{ func addCorner(conrners: UIRectCorner , radius: CGFloat) { let maskPath = UIBez ...

  4. python脚本删除文件与目录的命令

    1. 删除文件的命令 import os os.remove(file) os.unlink(file) 2.删除目录的命令 import shutil shutil.rmtree(directory ...

  5. wampserver 403forbidden问题

    1.c:\wamp\alias\phpmyadmin.conf 打开之后又这么一段源码; <Directory "D:\wamp\apps\phpmyadmin3.4.10.1/&qu ...

  6. VIO回顾:从滤波和优化的视角

    https://mp.weixin.qq.com/s/zpZERtWPKljWNAiASBLJxA 根据以上网页自己做的总结: 在机器人社区中,定位与构图问题属于状态估计问题.主流使用的工具可以对给定 ...

  7. 关系测试# 或 print(s2-s)Python 集合

    1集合是一个无序的,不重复的数据组合,它的主要作用如下(set和dict类似,也是一组key的集合,但不存储value.由于key不能重复,所以,在set中,没有重复的key): 去重,把一个列表变成 ...

  8. C# Contains 包含空字符串的问题

    一个基本的条件判断,之前没有遇到,这次遇到后,感觉真是这些年白写程序了. if(("1,2,3").Contains("")) { MessageBox.Sho ...

  9. 利用spring boot构建一个简单的web工程

    1.选择Spring InitiaLizr,    jdk选择好路径 2.设置项目信息 3.这一步是设置选择使用哪些组件,这里我们只需要选择web 4.设置工程名和路径

  10. 函数调用的四种方式 和 相关的 --- this指向

    this:表示被调用函数的上下文对象. arguments:表示函数调用过程中传递的所有参数. 这两个参数都是隐式的函数参数.会静默传递给函数,并且和函数体内显式声明的参数一样可正常访问. argum ...