设计模式之-代理模式

设计模式之-代理模式

代理模式详解

特点:

  • 1、 执行者、 被代理人
  • 2、 对于被代理人来说, 这件事情是一定要做的, 但是我自己又不想做或者没有时间做, 找代理。
  • 3、 需要获取到被代理的人个人资料。
  • 4、关心过程

穷举法:

  • 租房中介: 中介和你
  • 火车票黄牛: 黄牛和你
  • 媒人: 媒婆和你
  • 明星经纪人: 经纪人和明星 刘德华要开演唱会(长沙) 、 准备工作和善后工作

总结:

  • 做了一件什么事情呢? 字节码重组
  • 可以做一件什么事情? 可以在每一个方法调用之前加一些代码, 在方法调用之后再加一些代码

代理模式在Spring中的使用 
可以做一件什么事情? 
在调用方法前和方法后加入一些处理逻辑

AOP中使用场景

  • 事务代理(声明式事务, 哪个方法需要加事务, 哪个方法不需要加事务)
  • 日志监听

假如我们定义了一个service 方法

  • 开启一个事务(open) 代理来做
  • 事务的执行 执行我们的service方法
  • 监听到是否有异常, 可能需要根据异常的类型来决定这个事务是否要回滚还是继续提交 代理来做
  • (commit/rollback) 代理来做
  • 事务要关闭(close) 代理来做

代理模式的三种玩法

  • jdk代理模式
  • cglib代理模式实现
  • 手写一个代理模式

jdk代理模式实现

通过接口实现代理模式,先定义一个接口:

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:54
 */
public interface Person {
    //寻找真爱、相亲
    void  findLove();
}

实现这个接口:

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:54
 */
public interface Person {
    //寻找真爱、相亲
    void  findLove();
}
定义一个被代理人
```java

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:53
 */
public class Jack implements Person {
    private String sex = "男";
    private String name = "Jack";

    public void findLove() {
        System.out.println("我叫" + this.name + ",性别是" + this.sex);
        System.out.println("喜欢长的漂亮的");
        System.out.println("身高165的");
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}





<div class="se-preview-section-delimiter"></div>

定义一个代理:




<div class="se-preview-section-delimiter"></div>

```java

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:53
 */
public class MyProxy implements InvocationHandler {
    private Person person;

    //获取被代理人的资料
    public Object getInstance(Person target) throws Exception {
        this.person = target;
        Class zz = target.getClass();
        return Proxy.newProxyInstance(zz.getClassLoader(), zz.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是媒婆:您的性别是:男");
        System.out.println("开始海选");
        method.invoke(this.person,args);
        System.out.println("如何合适的话,就准备办事");
        return null;
    }
}

测试代理

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:54
 */
public class TestFindLove {
    public static void main(String[] args) {
        try {
            Person person = (Person) new MyProxy().getInstance(new Jack());
            person.findLove();
            System.out.println(person.getClass());
            //原理:
            //1.拿到被代理对象的引用,然后获取它的接口
            //2.JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口
            //3.把被代理对象的引用也拿到了
            //4.重新动态生成了一个class字节码
            //5.然后编译
            byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{person.getClass()});
            FileOutputStream os = new FileOutputStream("E:/learn/$Proxy0.class");
            os.write(data);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

cglib代理模式实现

maven需要导入Spring的核心库

<exclusions>
    <exclusion>
        <artifactId>servlet-api</artifactId>
        <groupId>javax.servlet</groupId>
    </exclusion>
</exclusions>

定义一个被代理人

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:53
 */
public class Python {
    public void learnPython() {
        System.out.println("看我72变");
    }
}

代理执行者

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:52
 */
public class LanguageProxy implements MethodInterceptor {

    public Object getInstance(Class clazz) throws Exception{
        //通过反射机制给他实例化
        Enhancer enhancer=new Enhancer();
        //注入父类,告诉CGLib,生成的子类需要继承那个类
        enhancer.setSuperclass(clazz);
        //添加监听
        enhancer.setCallback(this);
        //1.生成源代码
        //2.编译成.class文件
        //3.加载到JVM中,返回对象
        return  enhancer.create();
    }

    //也是做了字节码重组,jdk实现需要实现接口,cglib则是通过继承的方式
    //对于使用api无法感知的
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("-----------------学习开始-----------------");
        System.out.println("我正在学习Python");
        System.out.println("我正在学习大数据");
        //JDK的动态代理是通过接口来强制转换的
        //生成以后的代理对象是可以强制转化为我们的接口

        //CGLib的动态代理是通过生成一个被代理对象的子类,然后重写了父类的方法
        //生成的对象,可以强制为被代理的对象(也就是用户自己写的类)
        //子类引用赋值给父类


        //此处的 Object o对象是CGLib帮我么new出来子类的对象
        //Java OOP,在new子类的同时,实际上默认先调用了我们super()的方法
        //new了父类的同时,必须向new出来父类,这也就是间接持有了我们父类的引用
        //我们改变了子类对象的某些属性,是可以间接的操作父类的属性的
        methodProxy.invokeSuper(o,objects);
        System.out.println("-----------------学习结束-----------------");
        return o;
    }
}

测试实现

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:53
 */
public class TestLanguage {
    public static void main(String[] args) {
        try {
            Python python = (Python)new LanguageProxy().getInstance(Python.class);
            python.learnPython();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

手写一个代理模式

定义一个被代理人

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:53
 */
public class Jack implements Person {
    private String sex = "男";
    private String name = "Jack";

    public void findLove() {
        System.out.println("我叫" + this.name + ",性别是" + this.sex);
        System.out.println("喜欢长的漂亮的");
        System.out.println("身高165的");
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

自定义一个代理

import main.proxy.jdk.Person;

import java.lang.reflect.Method;

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:53
 */
public class ZdyMeiPo implements MyInvocationHandler {
    private Person person;

    //获取被代理人的资料
    public Object getInstance(Person target) throws Exception {
        this.person = target;
        Class zz = target.getClass();
        return ZdyPorxy.newProxyInstance(new ZdyClassLoader(), zz.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是媒婆:您的性别是:男" );
        System.out.println("开始海选");
        method.invoke(this.person, args);
        System.out.println("如何合适的话,就准备办事");
        return null;
    }
}

定义一个加载器

/**
 *
 * @author DoubleChina
 * @date 2018/1/6 11:53
 */
public class ZdyClassLoader extends ClassLoader {
    private File baseDir;

    public ZdyClassLoader() {
        String basePath = ZdyClassLoader.class.getResource("").getPath();
        this.baseDir = new File(basePath);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = ZdyClassLoader.class.getPackage().getName() + "." + name;
        if (baseDir != null) {
            File classFile=new File(baseDir,name.replaceAll("\\.","/")+".class");
            if (classFile.exists()){
                FileInputStream inputStream=null;
                ByteArrayOutputStream outputStream=null;
                try {
                    inputStream=new FileInputStream(classFile);
                     outputStream=new ByteArrayOutputStream();
                    byte[]  buff=new byte[1024];
                    int len;
                    while ((len=inputStream.read(buff))!=-1){
                        outputStream.write(buff,0,len);
                    }
                    return  defineClass(className,outputStream.toByteArray(),0,outputStream.size());
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    classFile.delete();
                    if (inputStream!=null){
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (null==outputStream){
                        try {
                            outputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}

底层实现代理全过程实现

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;

/**
 * 生成代理对象的代码
 * @author DoubleChina
 * @date 2018/1/6 11:53
 */
public class ZdyPorxy {
    private final static String ln = "\n\t";

    public static Object newProxyInstance(ZdyClassLoader loader,
                                          Class<?> interfaces[],
                                          MyInvocationHandler h)
            throws IllegalArgumentException {
        //1.生成源代码
        String porxySrc = generateSrc(interfaces);
        //2.将生成的源代码输出到磁盘,保存为.java文件
        String filePath = ZdyPorxy.class.getResource("").getPath();
        File file = new File(filePath + "$Proxy0.java");
        System.out.println(filePath + "$Proxy0.java");
        try {
            FileWriter fileWriter = new FileWriter(file);
            fileWriter.write(porxySrc);
            fileWriter.flush();
            fileWriter.close();
            //3.编译源代码,生成.class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable iterable = manager.getJavaFileObjects(file);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
            task.call();
            manager.close();
            //4.将class文件中的内容,动态加载到JVM中来
            Class proClass = loader.findClass("$Proxy0");
            //5.返回被代理后的对象
            Constructor constructor = proClass.getConstructor(MyInvocationHandler.class);
//            file.delete();
            return  constructor.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //将classh
        return null;
    }

    private static String generateSrc(Class<?>[] interfaces) {
        StringBuffer src = new StringBuffer();
        src.append("package main.proxy.custom; " + ln);
        src.append("import java.lang.reflect.Method; " + ln);
//         src.append("public  class $Proxy0 extend ZdyPorxy implements ");
        src.append("public  class $Proxy0  implements " + interfaces[0].getName() + "{ " + ln);
        //声明变量
        src.append("MyInvocationHandler h; " + ln);
        //写入构造方法
        src.append("public $Proxy0(MyInvocationHandler h)  {");
        src.append("this.h=h;" + ln);
        src.append("}" + ln);
        //迭代方法
        for (Method method : interfaces[0].getMethods()) {
            src.append("public  " + method.getReturnType().getName() + " " + method.getName() + "() {" + ln);
            src.append("try {" + ln);
            src.append("Method m=" + interfaces[0].getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + ln);
            src.append("this.h.invoke(this,m,null);" + ln);
            src.append(" } catch (Throwable throwable) {" + ln);
            src.append("  throwable.printStackTrace();" + ln);
            src.append(" }" + ln);
            src.append("}" + ln);
        }

        src.append("}");
        return src.toString();
    }
}
  • 发表于 2018-01-06 14:49
  • 阅读 ( 1675 )
  • 分类:Java

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
不写代码的码农
doublechina

IT

31 篇文章

作家榜 »

  1. 威猛的小站长 124 文章
  2. Jonny 65 文章
  3. 江南烟雨 36 文章
  4. - Nightmare 33 文章
  5. doublechina 31 文章
  6. HJ社区-肖峰 29 文章
  7. 伪摄影 22 文章
  8. Alan 14 文章