第七章 代理模式

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 动态代理生成对象的步骤如下:

  1. 获取被代理对象的引用,并且获取它的所有接口,反射获取;
  2. JDK 动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口;
  3. 动态生成 Java 代码,新加的业务逻辑方法由一定的逻辑代码调用(在代码中体现);
  4. 编译新生成的 Java 代码.class 文件;
  5. 重新加载到 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();
    
    
} 

在这里插入图片描述

总结

静态代理和动态代理的本质区别

  1. 静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加,违背开闭原则;
  2. 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则;
  3. 若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无须修改代理类的代码。

代理模式的优缺点

代理模式具有以下优点:

  1. 代理模式能将代理对象与真实被调用目标对象分离;
  2. 在一定程度上降低了系统的耦合度,扩展性好;
  3. 可以起到保护目标对象的作用;
  4. 可以增强目标对象的功能。

代理模式的缺点:

  1. 代理模式会造成系统设计中类的数量增加;
  2. 在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢;
  3. 增加了系统的复杂度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值