Java设计模式浅析
第一章 简单工厂模式
第二章 工厂方法模式
第三章 抽象工厂模式
第四章 单例模式
第五章 破坏单例模式
第六章 原型模式
第七章 代理模式
代理模式
一、概述
代理模式(Proxy Pattern)的定义也非常简单,是指为其他对象提供一种代理,
以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,
代理模式属于结构型设计模式。
使用代理模式主要有两个目的:
一保护目标对象,
二增强目标对象。
下面我们来看一下代理模式的类结构图,如下图所示。
一个典型的代理模式通常有三个角色,
这里称之为代理三要素:
● 共同接口
● 真实对象
● 代理对象
Subject 是顶层接口,
RealSubject 是真实对象(被代理对象),
Proxy 是代理对象,代理对象持有被代理对象的引用,客户端调用代理对象的方法,同时也调用被代理对象的方法,
但是会在代理对象前后增加一些处理代码。在代码中,一般代理会被理解为代码增强,
实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知。
代理模式属于结构型模式,分为静态代理和动态代理。
动态代理:
JDK 动态代理、CGLIB 动态代理、Javassist 动态代理。
静态代理:
由程序员创建或特定工具自动生成源代码,
也就是在编译时就已经将接口,被代理类,代理类等确定下来。
在程序运行之前,代理类的 .class 文件就已经生成。
二、静态代理
示例代码
共同接口 Person
public interface Person {
public void findLove();
}
真实对象 Son
public class Son implements Person {
@Override
public void findLove() {
System.out.println("儿子要求:肤白貌美大长腿");
}
}
代理对象 Father
public class Father implements Person{
//person 为私有成员变量
private Person person;
public Father(Person person){
this.person = person;
}
@Override
//重写顶层接口方法
public void findLove() {
System.out.println("父亲物色对象");
person.findLove();
System.out.println("双方同意交往,确立关系");
}
}
真实对象和代理对象都实现了共同的接口
测试代码
public class StaticProxyTest {
public static void main(String[] args) {
Father father = new Father(new Son());
father.findLove();
}
}
/*静态代理的优缺点:
● 优点:扩展原功能,不侵入原代码;
● 缺点:不同的代理类、代理方法,需要提供不同的代理对象*/
三、动态代理
动态代理的目的就是为了解决静态代理的缺点。
通过使用动态代理,我们可以通过在运行时,
动态生成一个持有真实对象并实现代理接口的代理对象,同时注入扩展逻辑。
示例代码
共同接口 Person
public interface Person {
public void findLove();
}
真实对象 Customer
public class Customer implements Person{
@Override
public void findLove() {
System.out.println("高富帅,身高180,有6块腹肌");
}
}
代理对象 JdkMeipoTest
import java.lang.reflect.*;
/*
动态代理类 实现 InvocationHandler 调用处理程序接口
*/
public class JdkMeipo implements InvocationHandler{
private Person person;
/*public JdkMeipo(Person person) {
this.person = person;
}*/
public Object blind(Object object) {
this.person = (Person)object;
System.out.println(person);
return /*(Person)*/Proxy.newProxyInstance(Person.class.getClassLoader(),
person.getClass().getInterfaces(), this);
}
/*
*@proxy 调用改方法的代理实例
*@method 目标对象的方法
*@args 目标对象的方法形参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println(args);
//System.out.println(method);
before();
Object object = method.invoke(person, args);
after();
return object;
}
private void before() {
System.out.println("媒婆:我要给你找对象,现在已经确认你的需求");
System.out.println("媒婆:开始物色");
}
private void after() {
System.out.println("媒婆:如果合适的话,就准备办事");
}
}
}
测试代码
public class JdkMeipoTest {
public static void main(String[] args) {
//被代理对象
Customer cus = new Customer();
//代理对象
JdkMeipo dyProxy = new JdkMeipo();
//代理对象 与 被代理对象 进行绑定 dyProxy代理了cus
//Person 顶层接口
Person per = (Person)dyProxy.blind(cus);
per.findLove();
}
}
四、手动实现动态代理
JDK 动态代理采用字节码重组,重新生成对象来替代原始对象,
以达到动态代理的目的。
JDK 动态代理生成对象的步骤如下:
- 获取被代理对象的引用,并且获取它的所有接口,反射获取;
- JDK 动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口;
- 动态生成 Java 代码,新加的业务逻辑方法由一定的逻辑代码调用(在代码中体现);
- 编译新生成的 Java 代码.class 文件;
- 重新加载到 JVM 中运行。
以上过程就叫字节码重组
示例代码
类加载器 GpClassLoader
import java.io.*;
import java.net.URLDecoder;
/*
GpClassLoader 继承 类加载 ClassLoader
*/
public class GpClassLoader extends ClassLoader{
private File classPathFile;
public GpClassLoader() {
String classPath = null;
try {
//class类文件路径加载
classPath = URLDecoder.decode(GpClassLoader.class.getResource("").getPath(), "UTF-8");
System.out.println("URL路径: " + classPath);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//
this.classPathFile = new File(classPath);
}
@Override
//Class<?> 泛型 不确定的类
protected Class<?> findClass(String name) throws ClassNotFoundException {
//加载url路径后解析包中的类名
String className = GpClassLoader.class.getPackage().getName() + "." + name;
System.out.println("className: "+className);
if (classPathFile != null) {
System.out.println("classPathFile: "+classPathFile);
System.out.println("name: "+name);
//拼接 class文件 正则替换 com.xx.xx.xx.class
File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
//File classFile = new File(classPathFile);
//判断 类文件 是否存在
if (classFile.exists()) {
System.out.println("classFile: "+classFile);
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
//class文件 加载到 内存
in = new FileInputStream(classFile);
//ByteArrayOutputStream 中间缓冲层
//对字节进行操作,属于内存操作流
out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1) {
//写入操作
out.write(buff, 0, len);
}
return defineClass(className, out.toByteArray(), 0, out.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != in) {
try {
//关闭
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
//关闭
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
//找不到类
return null;
}
}
动态代理类 GpProxy
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
public class GpProxy {
public static final String ln = "\r\n";
public static Object newProxyInstance(GpClassLoader classLoader, Class<?>[] interfaces, GpInvocationHandler h) {
try {
//1、动态生成源代码.java文件
//调用 generateSrc 方法
String src = generateSrc(interfaces);
//System.out.println("GpProxy src: "+src);
//2、Java文件输出磁盘
String filePath = URLDecoder.decode(GpProxy.class.getResource("").getPath(), "UTF-8");
System.out.println("GpProxy filePath: "+filePath);
File f = new File(filePath + "$Proxy0.java");
System.out.println("GpProxy File: "+f);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//3、把生成的.java文件编译成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manage.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
task.call();
manage.close();
//4、编译生成的.class文件加载到JVM中来
Class proxyClass = classLoader.findClass("$Proxy0");
Constructor c = proxyClass.getConstructor(GpInvocationHandler.class);
System.out.println("GpProxy Constructor: "+c );
f.delete();
//5、返回字节码重组以后的新的代理对象
return c.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String generateSrc(Class<?>[] interfaces) {
StringBuffer sb = new StringBuffer();
sb.append("package com.Design.Proxy.dyProxyByHand;" + ln);
sb.append("import com.Design.Proxy.DynamicProxy.Person;" + ln);
sb.append("import java.lang.reflect.*;" + ln);
sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
sb.append("GpInvocationHandler h;" + ln);
sb.append("public $Proxy0(GpInvocationHandler h) { " + ln);
sb.append("this.h = h;");
sb.append("}" + ln);
for (Method m : interfaces[0].getMethods()) {
Class<?>[] params = m.getParameterTypes();
//System.out.println("params: "+params.toString());
StringBuffer paramNames = new StringBuffer();
StringBuffer paramValues = new StringBuffer();
StringBuffer paramClasses = new StringBuffer();
for (int i = 0; i < params.length; i++) {
Class clazz = params[i];
String type = clazz.getName();
String paramName = toLowerFirstCase(clazz.getSimpleName());
paramNames.append(type + " " + paramName);
paramValues.append(paramName);
paramClasses.append(clazz.getName() + ".class");
if (i > 0 && i < params.length - 1) {
paramNames.append(",");
paramValues.append(",");
paramClasses.append(",");
}
}
System.out.println("paramNames: "+paramNames);
System.out.println("paramValues: "+paramValues);
System.out.println("paramClasses: "+paramClasses);
sb.append("public " + m.getReturnType().getName() + " " + m.getName()
+ "(" + paramNames.toString() + ") {" + ln);
sb.append("try{" + ln);
sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\""
+ m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);
sb.append((hasReturnValue(m.getReturnType()) ? "return " : "")
+ getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})", m.getReturnType())
+ ";" + ln);
sb.append("}catch(Error _ex) { }");
sb.append("catch(Throwable e){" + ln);
sb.append("throw new UndeclaredThrowableException(e);" + ln);
sb.append("}");
sb.append(getReturnEmptyCode(m.getReturnType()));
sb.append("}");
}
sb.append("}" + ln);
//System.out.println("sb: "+sb);
return sb.toString();
}
private static Map<Class, Class> mappings = new HashMap<>();
static {
mappings.put(int.class, Integer.class);
}
private static String getReturnEmptyCode(Class<?> returnClass) {
if (mappings.containsKey(returnClass)) {
return "return 0;";
} else if (returnClass == void.class) {
return "";
} else {
return "return null;";
}
}
private static String getCaseCode(String code, Class<?> returnClass) {
if (mappings.containsKey(returnClass)) {
return "((" + mappings.get(returnClass).getName() + ")" + code + ")."
+ returnClass.getSimpleName() + "Values()";
}
System.out.println("code: "+code);
return code;
}
private static boolean hasReturnValue(Class<?> clazz) {
return clazz != void.class;
}
private static String toLowerFirstCase(String src) {
char[] chars = src.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
}
代理对象
import com.Design.Proxy.DynamicProxy.Person;
import java.lang.reflect.Method;
import java.util.*;
import java.lang.*;
/*
*实现手动代理接口
*/
public class GpMeipo implements GpInvocationHandler{
// 被代理的对象,把引用保存下来
private Person person;
public GpMeipo(Person person) {
this.person = person;
}
/*构造方法
* new GpClassLoader() 手工实现的类加载器
*clazz.getInterfaces() 接口数组
*this 接口的实现类
*return 代理实例
*/
public Person getInstance() {
Class<?> clazz = person.getClass();
System.out.println("开始进行代理");
//System.out.println("接口数组"+Array.asList(clazz.getInterfaces()));
System.out.println("接口数组"+clazz.getInterfaces()[0]);
return (Person) GpProxy.newProxyInstance(new GpClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
System.out.println("调用代理 method: "+method);
Object result = method.invoke(person, args);
after();
return result;
}
private void before() {
System.out.println("媒婆:我要给你找对象,现在已经确认你的需求");
System.out.println("媒婆:开始物色");
}
private void after() {
System.out.println("媒婆:如果合适的话,就准备办事");
}
}
测试代码
public class DyProxyByHandTest {
public static void main(String[] args) {
Person person = new GpMeipo(new Customer()).getInstance();
person.findLove();
}
总结
静态代理和动态代理的本质区别
- 静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加,违背开闭原则;
- 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则;
- 若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无须修改代理类的代码。
代理模式的优缺点
代理模式具有以下优点:
- 代理模式能将代理对象与真实被调用目标对象分离;
- 在一定程度上降低了系统的耦合度,扩展性好;
- 可以起到保护目标对象的作用;
- 可以增强目标对象的功能。
代理模式的缺点:
- 代理模式会造成系统设计中类的数量增加;
- 在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢;
- 增加了系统的复杂度。