一、概述

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

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

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

三、代理模式的分类

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. sizeof 4字节对齐

    #include <iostream> #include<assert.h> using namespace std; typedef struct sys{ char a; ...

  2. 关于json.stringify的注意事项

    今天通过navigator.getCurrentPosition中的success回调,用json.stringify打印postion时,怎么打印都只是一个{}, 而打印postion.coords ...

  3. [leetcode]300. Longest Increasing Subsequence最长递增子序列

    Given an unsorted array of integers, find the length of longest increasing subsequence. Example: Inp ...

  4. C++ 中 int 与string相互转换

    int -->  string 1.使用itoa()函数 将任意类型的数字变量转换为字串子变量. #include<stdio.h> #include<iostream> ...

  5. 13-linux定时任务不起作用到的问题解决办法

    基本操作下面这篇: centos定时任务-不起作用- 没指明路径!!! 最大的问题是路径问题,以及权限问题. 用定时任务执行某些脚本是出现一系列问题,一步一步解决. 问题一:定时任务没反应: 查看日志 ...

  6. RabbitMQ消息队列(一):详细介绍

    1. 历史 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有 ...

  7. centos 7下安装mysql

    可参考: http://blog.csdn.net/xyang81/article/details/51759200 1.去mysql官方网站查询最新的版本: 2.运行指令: wget http:// ...

  8. Servlet会话管理三(HttpSession)

    Session是服务器端技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的HttpSession对象.由于Session为浏览器用户所独享,所以用户在访问服务器的web资源时,可以把各自的数 ...

  9. Eclipse中配置Tomcat服务器并创建标准Web目录

    Eclipse创建 Java Web 项目,并生成标准的目录结构 file --> New --> Dynamic Web project 填写 Project name (该名称项目的名 ...

  10. sqli盲注自用脚本

    盲注脚本 # -*- coding:utf-8 -*- import requests import re url = "http://123.206.87.240:8002/chengji ...