本文主要从示例入手介绍代理模式
一、实现代理的方式继承、组合
二、动态代理
一、实现代理的方式继承、组合
1、引入:代理模式的实现类似于装饰者模式,都是对功能的增强。很多模式之间的实现都有类似点,不同点可能也是语义有差别
2、继承方式的代理
2.1、代码示例
说明:Moveable接口,以及Tank实现类为以下所有示例公用
package net.oschina.design.proxy;
/**
* 可移动的事物的接口
*
* @author Freedom
*
*/
public interface Moveable {
void move();
}
package net.oschina.design.proxy;
import java.util.Random;
/**
* tank
*
* @author Freedom
*
*/
public class Tank implements Moveable {
@Override
public void move() {
System.out.println("tank start run...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
继承方式实现代理的代码
package net.oschina.design.proxy.v1;
import net.oschina.design.proxy.Tank;
/**
* Tank1继承Tank目的是为了计算方法运行的时间
*
* 方案1.可以直接修改Tank类中move方法,但弊端是,违反了开放关闭的原则, 最好是不要在之前开发好的代码中,为了实现功能的增强而去修改之前写好的代码
*
* 方案2.功能的加强想到了通过继承的方式,子类重写父类的方法实现功能的加强
*
* 需求修改,现在需要重新对父类功能进行加强。如:在父类move方法执行前后打印日志
*
* 通过继承实现上述需求,则需要重新再写一个子类去继承Tank
*
* @author Freedom
*
*/
public class Tank1 extends Tank {
/**
* 重写move方法
*/
@Override
public void move() {
long start = System.currentTimeMillis();
System.out.println("start: " + System.currentTimeMillis());
// 调用父类的方法
super.move();
long end = System.currentTimeMillis();
System.out.println("move run time: " + (end - start));
}
}
package net.oschina.design.proxy.v1;
import net.oschina.design.proxy.Tank;
/**
* Tank2继承Tank目的是为了在父类方法执行前后打印日志
*
* 需求修改:如果现在想先实现move方法运行的时间,再记录日志?
*
* 只需要再写一个子类比如说Tank3继承Tank1,然后重写父类方法可以实现该功能。
*
* 重新思考该问题,不管出现是单一功能的增加需要继承,而且如果是增强之后各个功能之间进行组合功能也需要重新再写子类
* 去继承父类,这样随着功能不断加强继承体系就会越来越庞大。会带来继承体系臃肿,因此为了解决该问题,可以通过组合的方式来实现代理功能
*
* @author Freedom
*
*/
public class Tank2 extends Tank {
/**
* 重写move方法
*/
@Override
public void move() {
System.out.println("[INFO]:tank start run... ");
// 调用父类的方法
super.move();
System.out.println("[INFO]:tank tired stop run...");
}
}
2.2、继承实现代理优缺点
优点:较之于对于流程中某一个方法功能进行加强时,直接修改方法中的代码,继承实现代理遵循开放关闭的原则;
缺点:程序流程中某一个方法的功能进行加强,加强的功能方式有多种多样而且任何功能之间可以互相组合。利用继承方式实现代理,每次功能加强需要继承被代理的类,功能直接任意组合也需要继承实现,这样就导致继承体系结果臃肿,不便于维护。
3、组合方式实现代理
此处,实现代理方式有一个前提代理类与被代理类实现共同的接口。
3.1、代码示例
package net.oschina.design.proxy.v2;
import net.oschina.design.proxy.Moveable;
/**
* 使用组合的方式实现代理
*
* 代理类实现与被代理类相同的接口,目的也是为了让代理类能够成为被代理类的对象
*
* @author Freedom
*
*/
public class Tank3 implements Moveable {
// 为了后期维护方便,此处使用被代理对象的接口
Moveable m;
public Tank3(Moveable m) {
this.m = m;
}
/*
* (non-Javadoc)
*
* @see net.oschina.design.proxy.Moveable#move() 实现时间上的代理
*/
@Override
public void move() {
long start = System.currentTimeMillis();
System.out.println("start: " + System.currentTimeMillis());
// 调用被代理类的方法
m.move();
long end = System.currentTimeMillis();
System.out.println("move run time: " + (end - start));
}
}
package net.oschina.design.proxy.v2;
import net.oschina.design.proxy.Moveable;
/**
* 实现日志代理
*
* 如果还需要实现诸如,事务,权限的代理,只需要代理类与被代理类实现共同的接口即可
*
* @author Freedom
*
*/
public class Tank4 implements Moveable {
// 为了后期维护方便,此处使用被代理对象的接口
Moveable m;
public Tank4(Moveable m) {
this.m = m;
}
/*
* (non-Javadoc)
*
* @see net.oschina.design.proxy.Moveable#move() 日志的代理
*/
@Override
public void move() {
System.out.println("[INFO]:tank start run... ");
// 调用被代理类的方法
m.move();
System.out.println("[INFO]:tank tired stop run...");
}
}
运行类:
package net.oschina.design.proxy.v2;
import net.oschina.design.proxy.Moveable;
import net.oschina.design.proxy.Tank;
public class Test {
public static void main(String[] args) {
// 被代理对象
Moveable m = new Tank();
// 时间代理
Moveable timeTank = new Tank3(m);
// timeTank.move();
// 先日志代理,后时间代理
Moveable logTank = new Tank4(timeTank);
logTank.move();
}
}
3.2、组合方式实现代理的优缺点:
优点:较之于继承方式实现代理,组合的方式实现代理更为灵活,不同代理对象之间的可以实现任意组合;
缺点:每次当有新的代理功能需要实现时,都需要新增加一个代理类,导致代理类过多。
二、动态代理
前提:代理类与被代理类实现共同的接口
1、引入
前面的不管是继承方式或者是组合的方式实现代理,无论如何都会导致随着功能加强而类体系越来越庞大,后期不易维护。因此,无非是对被代理对象某个功能增强而去创建代理对象,我们可以采取动态的方式来生成某一个接口的代理对象,而用户并不能感知到代理类以及代理对象创建。
2、动态代理实现的步骤:
(1)首先,需要获取到代理对象的源码(.java);
(2)将源码以字符串的形式输出到文件中;
(3)利用JDK6之后,JavaCompiler实现对源码的编译;
(4)将(3)中编译完成后的字节码文件通过类加载器加载到内存;
(5)利用反射机制创建代理对象
3、代码示例
运行类以下所有不同阶段创建的代理类公用:
package net.oschina.design.proxy.dynamicproxy;
import net.oschina.design.proxy.Moveable;
import net.oschina.design.proxy.Tank;
import net.oschina.design.proxy.dynamicproxy.v6.Proxy;
import net.oschina.design.proxy.dynamicproxy.v6.TimeInvocation;
public class Client {
public static void main(String[] args) throws Exception {
Moveable o = (Moveable) Proxy.getProxyInstance(Moveable.class,
new TimeInvocation(new Tank()));
o.move();
}
}
3.1、特定接口特定被代理对象特定功能(代理对象实现的功能)的代理类
package net.oschina.design.proxy.dynamicproxy.v3;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import net.oschina.design.proxy.Moveable;
import net.oschina.design.proxy.Tank;
/**
*
* 动态代理v1,只能实现固定的类的代理,不能实现任何一个类的代理
*
* 关于组合实现代理类
*
* 组合方式实现代理:优点:方便扩展,便于多个功能模块之间互相组合 缺点:每次一个新的功能增加都需要新增一个代理类,这样代理类也会不断累加
*
* 思考:动态代理,不需要知道代理对象是什么,只需要每次在使用的时候,直接由方法去产生
*
* 实现:1.拿到代理对象的源码 2.对源码利用JDK提供的编译类进行编译 3.利用类加载器将字节码文件加载进内存 4.通过反射机制创建一个代理类对象
*
* @author Freedom
*
*/
public class Proxy {
private static final String RN = System.getProperty("line.separator");
private static final String PATH = System.getProperty("user.dir")
+ "/src/net/oschina/design/proxy/v2/TimeProxy.java";
public static Object proxyInstance() throws Exception {
// 1.源码
String src = "package net.oschina.design.proxy.v2;"
+ RN
+ "import net.oschina.design.proxy.Moveable;"
+ RN
+ "public class TimeProxy implements Moveable {"
+ RN
+ " Moveable m;"
+ RN
+ " public TimeProxy(Moveable m) {"
+ RN
+ " this.m = m;"
+ RN
+ " }"
+ RN
+ " @Override"
+ RN
+ " public void move() {"
+ RN
+ " long start = System.currentTimeMillis();"
+ RN
+ " System.out.println(\"start: \" + System.currentTimeMillis());"
+ RN
+ " m.move();"
+ RN
+ " long end = System.currentTimeMillis();"
+ RN
+ " System.out.println(\"move run time: \" + (end - start));"
+ RN + " }" + RN + "}";
// 1.1 JDK编译之前先将文件输出到一个本地目录中
writeStr2File(src, PATH);
// 2.JDK编译工具编译该类,此处要使用JDK,不能使用JRE环境
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// System.out.println(compiler);
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null,
null, null);
Iterable units = fileMgr.getJavaFileObjects(PATH);
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null,
units);
t.call();
try {
fileMgr.close();
} catch (IOException e) {
e.printStackTrace();
}
// 3.将编译的字节码文件加载进内存,因为当前字节码文件在src下,所以,要使用URLClassLoader
URL[] urls = new URL[] { new URL("file:/"
+ System.getProperty("user.dir") + "/src") };
URLClassLoader loader = new URLClassLoader(urls);
Class c = loader.loadClass("net.oschina.design.proxy.v2.TimeProxy");
System.out.println(c);
// 4.通过反射机制创建该类的对象
Constructor cons = c.getConstructor(Moveable.class);
Object o = cons.newInstance(new Tank());
return o;
}
private static void writeStr2File(String str, String path) {
FileWriter writer = null;
try {
writer = new FileWriter(new File(path));
writer.write(str);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.2、任意接口的特定被代理对象、特定功能(代理对象实现的功能)的代理
package net.oschina.design.proxy.dynamicproxy.v4;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import net.oschina.design.proxy.Tank;
/**
* 实现任何类的代理,代理类与被代理类都实现同一个接口,因此方法中传入接口对象即可实现任意类的代理
*
* 1.当前该类只能传入Moveable接口对象,不能传入其他类的字节码文件对象(因为动态生成代码里面方法为Moveable接口的方法)
*
* 2.解决该问题的办法:动态生成任意接口的方法
*
* @author Freedom
*
*/
public class Proxy {
private static final String RN = System.getProperty("line.separator");
private static final String PATH = System.getProperty("user.dir")
+ "/src/net/oschina/design/proxy/v2/TimeProxy.java";
public static Object getProxyInstance(Class intef) throws Exception {
// 1.源码
String src = "package net.oschina.design.proxy.v2;"
+ RN
+ "import net.oschina.design.proxy.Moveable;"
+ RN
+ "public class TimeProxy implements "
+ intef.getName()
+ "{"
+ RN
+ " Moveable m;"
+ RN
+ " public TimeProxy(Moveable m) {"
+ RN
+ " this.m = m;"
+ RN
+ " }"
+ RN
+ " @Override"
+ RN
+ " public void move() {"
+ RN
+ " long start = System.currentTimeMillis();"
+ RN
+ " System.out.println(\"start: \" + System.currentTimeMillis());"
+ RN
+ " m.move();"
+ RN
+ " long end = System.currentTimeMillis();"
+ RN
+ " System.out.println(\"move run time: \" + (end - start));"
+ RN + " }" + RN + "}";
// 1.1 JDK编译之前先将文件输出到一个本地目录中
writeStr2File(src, PATH);
// 2.JDK编译工具编译该类,此处要使用JDK,不能使用JRE环境
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// System.out.println(compiler);
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null,
null, null);
Iterable units = fileMgr.getJavaFileObjects(PATH);
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null,
units);
t.call();
try {
fileMgr.close();
} catch (IOException e) {
e.printStackTrace();
}
// 3.将编译的字节码文件加载进内存,因为当前字节码文件在src下,所以,要使用URLClassLoader
URL[] urls = new URL[] { new URL("file:/"
+ System.getProperty("user.dir") + "/src") };
URLClassLoader loader = new URLClassLoader(urls);
Class c = loader.loadClass("net.oschina.design.proxy.v2.TimeProxy");
System.out.println(c);
// 4.通过反射机制创建该类的对象
Constructor cons = c.getConstructor(intef);
Object o = cons.newInstance(new Tank());
return o;
}
private static void writeStr2File(String str, String path) {
FileWriter writer = null;
try {
writer = new FileWriter(new File(path));
writer.write(str);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.3、任意接口任意被代理对象任意功能的代理
用于处理代理类方法的接口
package net.oschina.design.proxy.dynamicproxy.v6;
import java.lang.reflect.Method;
/**
* 用于执行代理类对象的方法
*
* @author Freedom
*
*/
public interface InvocationHandler {
void invoke(Object o, Method m);
}
处理代理类方法接口的实现类
package net.oschina.design.proxy.dynamicproxy.v6;
import java.lang.reflect.Method;
/**
* 处理时间代理的方法的类
*
* @author Freedom
*
*/
public class TimeInvocation implements InvocationHandler {
// 传入被代理的对象,可以是任意对象
Object mo;
public TimeInvocation(Object mo) {
this.mo = mo;
}
@Override
public void invoke(Object o, Method m) {
try {
long start = System.currentTimeMillis();
System.out.println("start: " + System.currentTimeMillis());
// 被代理类的方法
m.invoke(mo);
long end = System.currentTimeMillis();
System.out.println("move run time: " + (end - start));
// m.invoke(o, new Object[] {});
} catch (Exception e) {
e.printStackTrace();
}
}
}
动态代理类
package net.oschina.design.proxy.dynamicproxy.v6;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
/**
* 动态生成任意接口的
*
* 为了实现方法的灵活处理,需要传入一个对象用于处理任意代理类方法的调用
*
* @author Freedom
*
*/
public class Proxy {
private static final String RN = System.getProperty("line.separator");
private static final String PATH = System.getProperty("user.dir")
+ "/src/net/oschina/design/proxy/v2/TimeProxy.java";
public static Object getProxyInstance(Class intef, InvocationHandler h)
throws Exception {
/*
* 动态生成任意接口的方法
*/
String methodStr = "";
Method[] methods = intef.getMethods();
// for (Method m : methods) {
// methodStr += "@Override"
// + RN
// + "public void "
// + m.getName()
// + "(){"
// + RN
// + " long start = System.currentTimeMillis();"
// + RN
// +
// " System.out.println(\"start: \" + System.currentTimeMillis());"
// + RN
// + " m.move();"
// + RN
// + " long end = System.currentTimeMillis();"
// + RN
// + " System.out.println(\"move run time: \" + (end - start));"
// + RN + "}" + RN;
// }
for (Method m : methods) {
methodStr += "@Override" + RN + "public void " + m.getName()
+ "(){" + RN + "Method me;" + RN + "try{" + RN + " me ="
+ intef.getName() + ".class.getMethod(\"" + m.getName()
+ "\");" + RN + " h.invoke(this,me);" + RN + "}" + RN
+ "catch(Exception e)" + RN + "{" + RN
+ "e.printStackTrace();" + RN + "}" + RN + "}" + RN;
}
// System.out.println(methodStr);
// 1.源码
String src = "package net.oschina.design.proxy.v2;"
+ RN
+ "import java.lang.reflect.Method;"
+ RN
+ "import net.oschina.design.proxy.dynamicproxy.v6.InvocationHandler;"
+ RN + "import net.oschina.design.proxy.Moveable;" + RN
+ "public class TimeProxy implements " + intef.getName() + "{"
+ RN + " InvocationHandler h;" + RN
+ " public TimeProxy(InvocationHandler h) {" + RN
+ " this.h = h;" + RN + " }" + RN + methodStr + RN
+ "}";
// 1.1 JDK编译之前先将文件输出到一个本地目录中
writeStr2File(src, PATH);
// 2.JDK编译工具编译该类,此处要使用JDK,不能使用JRE环境
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// System.out.println(compiler);
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null,
null, null);
Iterable units = fileMgr.getJavaFileObjects(PATH);
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null,
units);
t.call();
try {
fileMgr.close();
} catch (IOException e) {
e.printStackTrace();
}
// 3.将编译的字节码文件加载进内存,因为当前字节码文件在src下,所以,要使用URLClassLoader
URL[] urls = new URL[] { new URL("file:/"
+ System.getProperty("user.dir") + "/src") };
URLClassLoader loader = new URLClassLoader(urls);
Class c = loader.loadClass("net.oschina.design.proxy.v2.TimeProxy");
System.out.println(c);
// 4.通过反射机制创建该类的对象
Constructor cons = c.getConstructor(InvocationHandler.class);
Object o = cons.newInstance(h);
return o;
}
private static void writeStr2File(String str, String path) {
FileWriter writer = null;
try {
writer = new FileWriter(new File(path));
writer.write(str);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
此文,仅为个人理解,欠缺的地方忘斧正...
相关推荐
代理模式是一种设计模式,它在软件工程中扮演着重要的角色,允许我们为其他对象提供一个替代接口,以控制对原始对象的访问。这种模式的主要目的是为了增加灵活性、安全性或者在不修改原有对象的情况下,增强或扩展其...
代理模式是设计模式的一种,它提供了一种对目标对象进行增强或者控制访问的方式。在本实例中,我们将深入探讨Java中的代理模式及其应用。 代理模式的核心思想是为一个对象创建一个代理对象,这个代理对象在客户端和...
代理模式是设计模式中的一种结构型模式,它在对象交互中起到了中介的作用,允许通过代理对象来控制对原对象的访问。代理模式的核心思想是为一个对象提供一个替身,以便增加新的功能或者控制对原对象的访问。这种模式...
代理模式是设计模式的一种,它的主要目的是在不改变原有对象的基础上,为一个对象提供额外的功能或者控制对这个对象的访问。在Android开发中,代理模式的应用尤为常见,尤其在处理复杂的业务逻辑、网络请求、界面...
SignalR提供了两种主要的工作模式:代理模式和非代理模式。这两种模式在实现上有所不同,各自具有优缺点,适用于不同的场景。 **1. 代理模式(Proxy Mode)** 在代理模式下,SignalR为每个Hub(服务端的业务逻辑...
代理模式是一种常用的设计模式,它在软件开发中扮演着重要的角色,特别是在iOS平台的应用程序设计中。代理模式的核心思想是为一个对象提供一个替身或代理,以控制对这个对象的访问。这种模式允许我们通过代理来间接...
在Java编程中,代理模式是一种常用的面向对象设计模式,它允许我们为一个对象提供一个代理以控制对该对象的访问。代理模式通常用于增加额外的功能,如日志、权限检查等,或者为了创建虚拟代理以提高性能。以下是Java...
**Java设计模式——代理模式详解** 代理模式是软件设计模式中的一个重要组成部分,它在Java编程中扮演着举足轻重的角色。代理模式的核心思想是为一个对象提供一个替身,这个替身即代理对象,代理对象可以控制对原...
代理模式是一种设计模式,它在软件工程中扮演着重要的角色,允许我们为其他对象提供一个替代接口,以控制对原对象的访问。这种模式的主要目的是为了增加灵活性、安全性或者为对象提供额外的功能,同时保持客户端代码...
**设计模式之代理模式(Proxy Pattern)** 设计模式是软件工程中的一种最佳实践,它是在特定情境下解决常见问题的模板。代理模式是其中一种行为设计模式,它的核心思想是为一个对象提供一个替身或者代理,以控制对...
**设计模式实现——代理模式** 在软件工程中,设计模式是一种通用可重用的解决方案,它描述了在特定上下文中经常出现的问题以及该问题的解决方案。代理模式是设计模式的一种,它提供了一种对目标对象的间接访问方式...
代理模式是一种设计模式,属于结构型模式之一,其主要目的是为其他对象提供一个代理,以控制对该对象的访问。在实际应用中,代理模式能够帮助我们实现如下的功能: 1. 远程代理:代理对象可以代表一个位于远程系统...
在这个“Java设计模式-代理模式例子”中,我们将深入探讨代理模式的概念、实现方式以及它在实际开发中的应用。 代理模式的核心思想是为一个对象提供一个替身,这个替身即代理对象,代理对象控制对原对象的访问。在...
代理模式在软件设计中是一种常用的设计模式,尤其在Android开发中,它可以帮助我们实现复杂的控制逻辑,隔离复杂性,以及提供额外的功能。在Android上下文中,代理模式常常用于数据加载、权限控制、事件处理等方面。...
代理模式是一种设计模式,它是结构型模式之一,主要用于在客户端和目标对象之间建立一个代理对象,以便控制对目标对象的访问。在C++中,代理模式可以用来为其他对象提供一种代理以控制对这个对象的访问,或者增加...
### Java代理模式与Java动态代理详解 #### 一、代理模式概述 代理模式是一种软件设计模式,它在客户端和目标对象之间提供了一种间接层。这种模式的主要目的是控制客户端对目标对象的访问,并且可以在不修改原有...
代理模式是一种常用的设计模式,它在软件开发中扮演着重要角色,允许我们通过一个代理类来控制对原对象的访问。在《设计模式:可复用面向对象软件的基础》(通常称为GoF设计模式)中,代理模式被定义为“为其他对象...
代理模式(Proxy) 定义: 为其他对象提供一种代理以控制对这个对象的访问 结构: 由三部分组成 1.RealSubject(真实对象): 真正会调用到的对象 2.Proxy(代理对象): 代理真实对象的地方 3.Subject(共同点): 代理对象...
在IT行业中,代理模式是一种常见的设计模式,它允许我们在不修改原有对象的基础上,为对象添加新的功能或控制访问。在本示例中,我们将重点讨论如何在Java环境下使用代理模式来实现代理逻辑,特别是在CAS(Central ...