2

时间:2017年08月28日星期一
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com
教学源码:https://github.com/zccodere/s...
学习源码:https://github.com/zccodere/s...

第一章:代理模式

1-1 概念介绍

学习本课程基础

面向对象的设计思维
了解多态的概念
了解反射机制

课程目标

代理模式基本概念及分类
了解代理模式开发中应用场景
掌握代理模式实现方式
理解JDK动态代理实现

代理模式定义

为其他对象提供一种代理以控制对这个对象的访问
代理对象起到中介作用,可去掉功能服务或增加额外的服务

常见的几种代理模式

远程代理:类似于客户端服务器这种模式,列一个为不同地理对象提供局域网代表对象
虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建
保护代理:控制对象的访问权限
智能代理:提供对目标对象额外的服务

代理模式示意图

clipboard.png

第二章:常用代理模式

2-1 静态代理

智能引用代理

静态代理
动态代理

静态代理定义

代理和被代理对象在代理之前是确定的。他们都实现相同的接口或者继承相同的抽象类

静态代理类图

clipboard.png

代码编写

1.编写Moveable接口

package com.myimooc.designpattern.c3proxy.car;

/**
 * @describe 可行驶的接口
 * @author zc
 * @version 1.0 2017-08-28
 */
public interface Moveable {
    
    /**
     * 行驶的方法
     */
    void move();
    
}

2.编写Car类

package com.myimooc.designpattern.c3proxy.car;

import java.util.Random;

/**
 * @describe 一辆车实现可行驶的接口
 * @author zc
 * @version 1.0 2017-08-28
 */
public class Car implements Moveable {
    
    @Override
    public void move() {
        // 记录汽车行驶的时间
//        long starttime = System.currentTimeMillis();
//        System.out.println("汽车开始行驶...");
        
        // 实现开车
        try {
            System.out.println("汽车行驶中...");
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
//        long endtime = System.currentTimeMillis();
//        System.out.println("汽车结束行驶...汽车行驶时间:"+(endtime - starttime) + "毫秒");
    }
    
}

3.编写Car2类

package com.myimooc.designpattern.c3proxy.car;

/**
 * @describe 继承的方式实现静态代理
 * @author zc
 * @version 1.0 2017-08-28
 */
public class Car2 extends Car {

    @Override
    public void move() {
        
        long starttime = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");
        
        super.move();
        
        long endtime = System.currentTimeMillis();
        System.out.println("汽车结束行驶...汽车行驶时间:"+(endtime - starttime) + "毫秒");
        
    }
}

4.编写Car3类

package com.myimooc.designpattern.c3proxy.car;

/**
 * @describe 聚合的方式实现静态代理
 * @author zc
 * @version 1.0 2017-08-28
 */
public class Car3 implements Moveable {
    
    public Car3(Car car) {
        super();
        this.car = car;
    }

    private Car car;
    
    @Override
    public void move() {
        
        long starttime = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");
        
        car.move();
        
        long endtime = System.currentTimeMillis();
        System.out.println("汽车结束行驶...汽车行驶时间:"+(endtime - starttime) + "毫秒");
        
    }

}

5.编写Client类

package com.myimooc.designpattern.c3proxy.car;


/**
 * @describe 测试类
 * @author zc
 * @version 1.0 2017-08-28
 */
public class Client {
    
    public static void main(String[] args) {
//        test1();
        test2();
        
    }
    
    // 2-2 聚合与继承 代理功能叠加测试方法
    public static void test2(){
        Car car = new Car();
        CarLogProxy clp = new CarLogProxy(car);
        CarTimeProxy ctp = new CarTimeProxy(clp);
        ctp.move();
    }
    
    // 2-1 静态代理测试方法
    public static void test1(){
//        Car car = new Car();
//        car.move();
        
        // 使用继承的方式
//        Moveable m = new Car2();
//        m.move();
        
        // 使用聚合方式
        Car car = new Car();
        Moveable m = new Car3(car);
        m.move();
    }
    
}

2-2 聚合与继承

场景分析

clipboard.png

代理类功能叠加

1.记录日志
2.记录时间
3.权限功能

使用继承方式

clipboard.png

使用继承方式来实现代理功能的叠加,代理类会无限的膨胀下去,所以这种方式不推荐使用。

使用聚合方式,通过代码演示

代码编写

1.复制Car3命名为CarTimeProxy

package com.myimooc.designpattern.c3proxy.car;

/**
 * @describe 汽车行驶时间的代理
 * @author zc
 * @version 1.0 2017-08-28
 */
public class CarTimeProxy implements Moveable {
    
    // 因为代理类和被代理类都是实现相同的接口,所以构造方法传递的对象也可以是Moveable对象
    public CarTimeProxy(Moveable m) {
        super();
        this.m = m;
    }

    private Moveable m;
    
    @Override
    public void move() {
        
        long starttime = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");
        
        m.move();
        
        long endtime = System.currentTimeMillis();
        System.out.println("汽车结束行驶...汽车行驶时间:"+(endtime - starttime) + "毫秒");
        
    }

}

2.编写CarLogProxy类

package com.myimooc.designpattern.c3proxy.car;

/**
 * @describe 汽车日志功能的代理
 * @author zc
 * @version 1.0 2017-08-28
 */
public class CarLogProxy implements Moveable {
    
    // 因为代理类和被代理类都是实现相同的接口,所以构造方法传递的对象也可以是Moveable对象
    public CarLogProxy(Moveable m) {
        super();
        this.m = m;
    }
    private Moveable m;
    
    @Override
    public void move() {
        System.out.println("日志开始");
        m.move();
        System.out.println("日志结束");
    }
}

3.编写Client类

package com.myimooc.designpattern.c3proxy.car;


/**
 * @describe 测试类
 * @author zc
 * @version 1.0 2017-08-28
 */
public class Client {
    
    public static void main(String[] args) {
//        test1();
        test2();
        
    }
    
    // 2-2 聚合与继承 代理功能叠加测试方法
    public static void test2(){
        Car car = new Car();
        CarLogProxy clp = new CarLogProxy(car);
        CarTimeProxy ctp = new CarTimeProxy(clp);
        ctp.move();
    }
    
    // 2-1 静态代理测试方法
    public static void test1(){
//        Car car = new Car();
//        car.move();
        
        // 使用继承的方式
//        Moveable m = new Car2();
//        m.move();
        
        // 使用聚合方式
        Car car = new Car();
        Moveable m = new Car3(car);
        m.move();
    }
    
}

2-3 JDK动态代理

场景分析

clipboard.png

有没有方法动态产生代理,实现对不同类,不同方法的代理呢

JDK动态代理类图

clipboard.png

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类

Interface InvocationHandler:该接口中仅定义了一个方法
    public Object invoke(Object obj,Method method,Object[] args)
    在实际使用时,第一参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。
    这个抽象方法在代理类中动态实现。
Proxy:该类即为动态代理类
    static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
    返回代理类的一个实例,返回后的代理类可以当做被代理类使用
    (可使用被代理类的在接口中声明过的方法)

所谓Dynamic Proxy是这样一种class

它是在运行时生成的class
该class需要实现一组interface
使用动态代理类时,必须实现InvocationHandler接口

动态代理实现步骤

1.创建一个实现InvocationHandler接口的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.调用Proxy的静态方法,创建一个代理类
    newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
4.通过代理调用方法

代码编写

1.编写TimeHandler类

package com.myimooc.designpattern.c3proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @describe 对时间上的处理-使用JDK动态代理
 * @author zc
 * @version 1.0 2017-08-28
 */
public class TimeHandler implements InvocationHandler {
    
    public TimeHandler(Object target) {
        super();
        this.target = target;
    }

    private Object target;
    
    /**
     * 参数:
     * proxy 被代理对象
     * method 被代理对象方法
     * args 方法的参数
     * 返回值:
     * Object 方法的返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        long starttime = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");
        
        method.invoke(target);
        
        long endtime = System.currentTimeMillis();
        System.out.println("汽车结束行驶...汽车行驶时间:"+(endtime - starttime) + "毫秒");
        
        return null;
    }

}

2.编写Test类

package com.myimooc.designpattern.c3proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.myimooc.designpattern.c3proxy.car.Car;
import com.myimooc.designpattern.c3proxy.car.Moveable;

/**
 * @describe JDK动态代理测试类
 * @author zc
 * @version 1.0 2017-08-28
 */
public class Test {
    
    public static void main(String[] args) {
        Car car = new Car();
        InvocationHandler h = new TimeHandler(car);
        Class<?> cls = car.getClass();
        
        // 使用Proxy类newProxyInstance方法动态创建代理类
        /**
         * loader 类加载器
         * interfaces 实现接口
         * h InvocationHandler
         */
        Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
        
        m.move();
        
    }
    
}

2-4 使用cglib

JDK动态代理与CGLIB动态代理区别

JDK动态代理
1.只能代理实现了接口的类
2.没有实现接口的类不能实现JDK的动态代理
CGLIB动态代理
1.针对类来实现代理的
2.对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用
3.因为是使用继承的方式,所以不能对final修饰的类来进行代理

代码编写

1.添加相关依赖

      <!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>3.2.5</version>
    </dependency>
      <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.5</version>
    </dependency>

2.编写CglibProxy类

package com.myimooc.designpattern.c3proxy.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * @describe 代理类
 * @author zc
 * @version 1.0 2017-08-28
 */
public class CglibProxy implements MethodInterceptor {
    
    private Enhancer enhance = new Enhancer();
    
    @SuppressWarnings("rawtypes")
    public Object getProxy(Class clazz){
        // 设置创建子类的类
        enhance.setSuperclass(clazz);
        enhance.setCallback(this);
        return enhance.create();
    }

    /**
     * 拦截所有目标类方法的调用
     * 参数:
     * obj 目标类的实例
     * method 目标方法的反射对象
     * args 方法的参数
     * proxy 代理类的实例
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        
        System.out.println("日志开始...");
        
        // 代理类调用父类的方法
        proxy.invokeSuper(obj, args);
        
        System.out.println("日志结束...");
        
        return null;
    }

}

3.编写Train类

package com.myimooc.designpattern.c3proxy.cglib;

/**
 * @describe 火车
 * @author zc
 * @version 1.0 2017-08-28
 */
public class Train {
    
    public void move(){
        System.out.println("火车行驶中");
    }
    
}

4.编写Client类

package com.myimooc.designpattern.c3proxy.cglib;


/**
 * @describe cglib代理测试类
 * @author zc
 * @version 1.0 2017-08-28
 */
public class Client {
    
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        Train t = (Train)proxy.getProxy(Train.class);
        t.move();
    }
}

第三章:模拟JDK动态代理

3-1 实现动态代理

动态代理实现思路

实现功能:通过Proxy的newProxyInstance返回代理对象
1.声明一段源码(动态产生代理)
2.编译编码(JDK Compiler API),产生新的类(代理类)
3.将这个类load到内存当中,产生一个新的对象(代理对象)
4.return 代理对象

代码编写

1.编写InvocationHandler类

package com.myimooc.designpattern.c3proxy.simulationjdk;

import java.lang.reflect.Method;

/**
 * @describe 模拟JDK动态代理-业务处理类
 * @author zc
 * @version 1.0 2017-08-28
 */
public interface InvocationHandler {
    
    public void invoke(Object obj,Method method);
    
}

2.编写Proxy类

package com.myimooc.designpattern.c3proxy.simulationjdk;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import org.apache.commons.io.FileUtils;

/**
 * @describe 模拟JDK动态代理-代理类
 * @author zc
 * @version 1.0 2017-08-28
 */
@SuppressWarnings({"rawtypes","unchecked"})
public class Proxy {
    
    public static Object newProxyInstance( Class infce,InvocationHandler h)throws Exception{
        String rt = "\r\n";
        String methodStr = "";
        for(Method m : infce.getMethods()){
            methodStr += "    @Override" + rt +
            "    public void "+ m.getName() +"(){" + rt +
            "     try{" + rt +
            "        Method md = " +infce.getName()+".class.getMethod(\""+m.getName()+"\");" + rt +
            "        h.invoke(this,md);" + rt +
            "     }catch(Exception e){e.printStackTrace();}" + rt +
            "    }" + rt;
        }
        
        String str =
        "package com.myimooc.designpattern.c3proxy.simulationjdk; " + rt +
        "import com.myimooc.designpattern.c3proxy.simulationjdk.InvocationHandler;" + rt +
        "import java.lang.reflect.Method;" + rt +
        "public class $Proxy0 implements "+ infce.getName() +" { " + rt +
        "    public $Proxy0(InvocationHandler h) {" + rt +
        "        this.h = h;" + rt +
        "    }" + rt +
        "    private InvocationHandler h;" + rt +
        methodStr + rt +
        "}";
        
        // 产生代理类的java文件
        String filename = System.getProperty("user.dir") + "/target/classes/com/myimooc/designpattern/c3proxy/simulationjdk/$Proxy0.java";
        File file = new File(filename);
        FileUtils.writeStringToFile(file, str,"UTF-8");
        
        // 编译-拿到编辑器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        // 文件管理者
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        // 获取文件
        Iterable units = fileMgr.getJavaFileObjects(filename);
        // 编译任务
        CompilationTask task = compiler.getTask(null, fileMgr, null, null, null, units);
        // 进行编译
        task.call();
        fileMgr.close();
        
        // load到内存
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        Class c = cl.loadClass("com.myimooc.designpattern.c3proxy.simulationjdk.$Proxy0");
        
        Constructor ctr = c.getConstructor(InvocationHandler.class);
        return ctr.newInstance(h);
    }
}

3.编写TimeHandler类

package com.myimooc.designpattern.c3proxy.simulationjdk;

import java.lang.reflect.Method;

/**
 * @describe 模拟JDK动态代理-时间业务逻辑处理
 * @author zc
 * @version 1.0 2017-08-28
 */
public class TimeHandler implements InvocationHandler{
    
    private Object target;
    
    public TimeHandler(Object target) {
        super();
        this.target = target;
    }

    @Override
    public void invoke(Object obj, Method method) {
        try {
            
            long starttime = System.currentTimeMillis();
            System.out.println("汽车开始行驶...");
            
            method.invoke(target);
            
            long endtime = System.currentTimeMillis();
            System.out.println("汽车结束行驶...汽车行驶时间:"+(endtime - starttime) + "毫秒");
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.编写Client类

package com.myimooc.designpattern.c3proxy.simulationjdk;

import com.myimooc.designpattern.c3proxy.car.Car;
import com.myimooc.designpattern.c3proxy.car.Moveable;

/**
 * @describe 模拟JDK动态代理-测试类
 * @author zc
 * @version 1.0 2017-08-28
 */
public class Client {
    
    public static void main(String[] args) throws Exception {
        Car car = new Car();
        InvocationHandler h = new TimeHandler(car);
        Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class,h);
        m.move();
    }
}

第四章:总结

4-1 课程总结

总结

代理模式概念、分类及应用场景
场景代理模式
静态代理(继承、聚合)
JDK动态代理实现日志处理功能
模拟JDK动态代理实现

代理模式-动态代理

clipboard.png

clipboard.png


妙手空空
1.3k 声望368 粉丝

博观而约取,厚积而薄发