源码学习之设计模式(代理模式)

今天要说的是设计模式中的代理模式。java

代理模式在咱们生活中极其常见----中介机构,委托人等,这些都是代理,就连老美想当世界警察,打的战争都是代理人战争。mysql

那什么是代理呢?代理在百度百科上这样解释的 : 通常作法是由委托人代理人签定代理协议,受权代理人在必定范围内表明其向第三者进行商品买卖或处理有关事务 。而在编程中是一个对象为其余对象提供一个代理,以控制对这个对象的访问。实际上来讲,代理模式是从一些方面进行解耦,是咱们的程序更加符合” 高内聚,低耦合“的思想。程序员

那为何要使用代理呢?使用代理主要的目的有两个:保护目标对象和加强目标对象。、spring

java中代理的种类主要有三种,分别是静态代理,动态代理和CgLib。下面就着三种代理分别讨论。sql

静态代理

下面 来看一个场景,在实际开发或生产中,不可避免会使用到各类数据库,若是手动切换 数据的话,不只麻烦,易出错,并且生产环境也不容许啊。这时候静态代理就能够发挥做用了,根据实际状况和条件 更换 数据库。下面看代码 。数据库

订单实体类
/**
  * 订单实体类
  * */
public class Order {
    private Object orderInfo;
    private long createTime;
    private String id;

    public Object getOrderInfo() {
        return orderInfo;
    }

    public Order setOrderInfo(Object orderInfo) {
        this.orderInfo = orderInfo;
        return this;
    }

    public long getCreateTime() {
        return createTime;
    }

    public Order setCreateTime(long createTime) {
        this.createTime = createTime;
        return this;
    }

    public String getId() {
        return id;
    }

    public Order setId(String id) {
        this.id = id;
        return this;
    }
}
持久层操做接口

由于 这里是模拟,因此这里就不用接口了。编程

/**
 * 持久层操做类
 * */
public class OrderDao {
    public int insert(Order order){
        System.out.println("订单 "+ order+ "建立成功");
        return 1;
    }
}
建立服务层接口
/**
 * Order服务层接口
 * */

public interface OrderService {
    int createOrder(Order order);
}
实现服务层接口

调用持久层操做类,进行服务。segmentfault

/**
 *OrderService实现类。
 * */
public class OrderServiceImp implements OrderService {
    private  OrderDao orderDao;

    public OrderServiceImp() {
        //对比spring构造器注入
        //此处为方便,直接初始化
        orderDao = new OrderDao();
    }

    @Override
    public int createOrder(Order order) {
        System.out.println("调用orderService建立");
        return orderDao.insert(order);
    }
}
建立数据源切换实体
/**
 * 数据源路由切换对象
 * */
public class DynamicDataSourceEntry {
    public final static String DATASOURCE=null;
    private final static ThreadLocal<String> local =new ThreadLocal<>();
    private  DynamicDataSourceEntry(){}
    //清空数据源
    public static void clear(){
        local.remove();
    }
    //获取当前正在使用的 数据源
    public static void get()   {
        local.get();
    }
    //还原默认数据源
    public static void restore(){
        local.set(DATASOURCE);
    }
    //设置已知名字的数据源
    public static void set(String source){
        local.set(source);
    }
    //根据年份动态设置数据源
    public static void setDatasource(int year){
        local.set("DB_"+year);
    }
    
}
数据源代理类

这里就是静态代理类,此处是根据时间来切换数据源。设计模式

/**
 * 切换数据源代理类
 * */
public class OrderServiceStaticPorxy implements OrderService{
    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    private  OrderService service;

    public OrderServiceStaticPorxy(OrderService service) {
        this.service = service;
    }

    @Override
    public int createOrder(Order order) {
        before();
        Long time = order.getCreateTime();
        Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
        System.out.println("静态代理类自动分配到{DB_ " + dbRouter+"}数据源处理数据");
        DynamicDataSourceEntry.set(String.valueOf(dbRouter));
        service.createOrder(order);
        after();
        return 0;
    }

    private void after() {
        System.out.println("代理后");
    }

    private void before() {
        System.out.println("代理前");
    }
}

从这里能够看到,咱们已经实现了根据条件切换数据库。在大三时,咱们专业开了一门《软件体系与设计模式》这门课,当时老师 布置做业让用设计模式重构本身的程序,当时我在网上找了下mybatis的数据 无关性是怎样实现的,根据mybatis思想,我本身实现了一个加强的JDBC,能够根据配置文件自动切换数据库。与上面的这个例子有殊途同归之妙,可是当时限于对代理模式的理解,写的代码有些烂,但愿不要介意。可是,基本的功能都实现了。先来看看数据库设置相关的类图。缓存

img

此类图主要是获取数据库类图,DataBaseOperation 是一个接口,定义了数据库的主要操做;AbstractDataBaseOperationFactory 是一个抽象类,是全部数据库操做的基类,全部的数据库局要继承次此类,去实现特定的操做;此类图中使用了模板方法。 但愿用户能够根据本身 的需求实现相应的数据连接,加载相应的数据库驱动 。

动态代理

配置文件
Datasource:
  username: root
  password: 123456
  url: jdbc:mysql://127.0.0.1:3306/test?useSSL=true&serverTimezone=GMT&characterEncoding=utf8
 # url: jdbc:sqlserver://localhost:1433;databaseName=test
  #url: jdbc:postgresql://localhost/dbname
  #url:  jdbc:db2://127.0.0.1:50000/dbname
  #url:  jdbc:sybase:Tds:localhost:5007/dbname
  #url: jdbc:oracle:thin:@127.0.0.1:1521:test
  autoCommit: false

个人设想是这样的:根据用户提供的URL自动的设置数据库类型,以及其余的设置。这里采用的时yaml格式文件。

AbstractDataBaseOperationFactory 这个类是一个抽象类,里面定义了通用的方法,实现了 DataBaseOperation 接口,此接口规定了数据库操做,为用户扩展提供接口;在抽象类对配置 文件解析 ,实际上Yaml格式的文件就是一个k-v的文件。抽象类的代码以下:

import com.framework.betterorm.reflection.ObjectFields;
import com.framework.betterorm.utils.YamlHelper;
import com.framework.betterorm.parsing.PropertyParser;
 
import java.sql.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
 
public abstract class AbstractDataBaseOperationFactory implements DataBaseOperation {
    public static String databaseType;
    /**
     * 声明一个 Connection类型的静态属性,用来缓存一个已经存在的链接对象
     */
    public static Connection conn = null;
    /**
     * 用户名
     */
    protected static String username;
    /**
     * 密码
     */
    protected static String password;
    /**
     * 是否自动提交事务
     */
    protected static boolean autoCommit = false;
    protected static String url;
 
    static {
 
        config(Objects.requireNonNull(AbstractDataBaseOperationFactory.class.getClassLoader().getResource("DataBaseConfig.yml")).getFile());
    }
 
    /**
     * 获取文件的链接信息,采用最简洁的yaml文件
     */
    private static void config(String file) {
        System.out.println("config");
        if (YamlHelper.fileRead(file) == null) {
            return;
        }
        Map<String, Object> infoMap = (Map<String, Object>) YamlHelper.fileRead(file).get("Datasource");
        if (infoMap.size() <= 0) {
            return;
        }
        //获取用户名
        username = String.valueOf(infoMap.get("username")).trim();
        //获取密码
        password = String.valueOf(infoMap.get("password")).trim();
        //设置是否自动提交
        autoCommit = false;
        if (infoMap.get("autoCommit") != null) {
            autoCommit = (boolean) infoMap.get("autoCommit");
        }
        //获取链接字符串
        url = String.valueOf(infoMap.get("url")).trim();
        databaseType = url.split(":")[1];
 
    }
 
    /**
     * 释放资源
     **/
    public static void release(Object cloaseable) {
        if (cloaseable != null) {
            if (cloaseable instanceof ResultSet) {
                ResultSet rs = (ResultSet) cloaseable;
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (cloaseable instanceof Statement) {
                Statement st = (Statement) cloaseable;
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (cloaseable instanceof Connection) {
                Connection c = (Connection) cloaseable;
                try {
                    c.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
 
    }
 
    /**
     * 回滚事务
     */
    private static void rollback(Connection c) {
        if (c != null && !autoCommit) {
            try {
                c.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
 
    private static Object typeof(Object o) {
        Object r = o;
 
        if (o instanceof Timestamp) {
            return r;
        }
        // 将 java.util.Date 转成 java.session.Date
        if (o instanceof java.util.Date) {
            java.util.Date d = (java.util.Date) o;
            r = new Date(d.getTime());
            return r;
        }
        // 将 Character 或 char 变成 String
        if (o instanceof Character || o.getClass() == char.class) {
            r = String.valueOf(o);
            return r;
        }
        return r;
    }
 
    public String getInstance() {
        return this.getClass().getSimpleName();
    }
 
    /**
     * 有子类实现
     */
    @Override
    public abstract Connection connect();
 
    /**
     * 专门检查缓存的链接是否不能够被使用 ,不能够被使用的话,就返回 true
     */
    protected boolean invalid() {
        if (conn != null) {
            try {
 
                //isValid方法是判断Connection是否有效,若是链接还没有关闭而且仍然有效,则返回true
 
                if (conn.isClosed() || !conn.isValid(3)) {
                    return true;
 
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            /**
             * conn 既不是 null 且也没有关闭 ,且 isValid 返回 true,说明是可使用的 (返回false)
             */
            return false;
        } else {
            return true;
 
        }
    }
 
    /**
     * 设置是否自动提交事务
     **/
    public void transaction() {
        try {
            conn.setAutoCommit(autoCommit);
        } catch (SQLException e) {
            System.out.println("设置事务的提交方式为 : " + (autoCommit ? "自动提交" : "手动提交") + " 时失败: " + e.getMessage());
        }
    }
 
    /**
     * 提交事务
     */
    private void commit(Connection c) {
        if (c != null && !autoCommit) {
            try {
                c.commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
 
    }
 
 
}

通用的数据库操做就在这里。以MySQL为例继承此抽象类,配置MySQL。相应的代码以下。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
 
public class MySQLDataSource extends AbstractDataBaseOperationFactory {
 
    @Override
    public Connection connect() {
        if (invalid()) {
            try {
                //载入数据库驱动
                Class.forName("com.mysql.cj.jdbc.Driver");
                //创建链接
                conn = DriverManager.getConnection(url, username, password);
                System.out.println("Mysql 已链接");
            } catch (SQLException e) {
                System.out.println("创建 " + conn + " 数据库链接失败 , " + e.getMessage());
            } catch (ClassNotFoundException e) {
                System.out.println("MySql驱动加载失败:" + e.getMessage());
            }
 
        }
        return conn;
    }
}

写完这个类就能够操做数据库了,CUDR的方法都在抽象类封装了 。直接使用就能够。

至于如何去相应的链接,初始化connection 对象。由于数据库有不少种,在这里使用 策略模式+单例模式+java反射机制去决定实例化那个类 。相应的代码以下

public enum DataBaseTypeEnum {
    /**
     * mysql
     */
    MYSQL("mysql", "com.framework.betterorm.Datesource.MySQLDataSource"),
    /**
     * SQLServer
     */
    MSSQL("sqlserver", "com.framework.betterorm.Datesource.MsSQLDatasource"),
    /**
     * oracle
     */
    oracle("oracle", "com.framework.betterorm.Datesource.OracleDataSource");
    /**
     * 数据库类型
     */
    private String type;
    /**
     * 对应操做的实体类
     */
    private String clazz;
 
    DataBaseTypeEnum(String type, String clazz) {
        this.type = type;
        this.clazz = clazz;
    }
 
    public String getType() {
        return type;
    }
 
    public String getClazz() {
        return clazz;
    }
}


####################################################################################################

 
 
import com.framework.betterorm.session.SqlType;
 
import java.util.HashMap;
import java.util.Map;
 
public class CommonSingleton {
 
    /**
     * 数据库操做类型Map
     */
    private static Map<String, String> sqlTypeMap = new HashMap<>();
 
    static {
 
        for (SqlType sqlType : SqlType.values()) {
            sqlTypeMap.put(sqlType.getAnnotation(), sqlType.getMethod());
        }
    }
 
    private CommonSingleton() {
    }
 
    public static CommonSingleton getInstance() {
        return CommonSingleton.SingletonInstance.instance;
    }
 
 
    /**
     * 根据注解取得对应的操做方法
     */
    public String sqlTypeStratgy(String annotation) {
        return sqlTypeMap.get(annotation);
    }
 
    private static class SingletonInstance {
        static CommonSingleton instance = new CommonSingleton();
    }
}

能够看到,经过枚举很简单的就定义相应的操做类,也方便了咱们初始化connection 对象,在抽象类中AbstractDataBaseOperationFactory 中咱们已经获取到了数据库类型,此处的单例也获取了数据库对象的链接类,下面直接根据这些条件使用反射实例化connection 对象就能够了。相应的代码就不展现了。你们自行实现就能够了。说了这么对,彷佛没有和咱们今天的主题有半分联系;这个别慌,接下来就是咱们的重头戏--------动态代理。我是仿造了MyBatis,因此基本思想是一直的,在Mybatis中也是用了代理去实现操做数据库,有兴趣的小伙伴能够去读读相应的源码。

import com.framework.betterorm.Datesource.AbstractDataBaseOperationFactory;
import com.framework.betterorm.annotation.Delete;
import com.framework.betterorm.annotation.Insert;
import com.framework.betterorm.annotation.Select;
import com.framework.betterorm.annotation.Update;
import com.framework.betterorm.common.CommonSingleton;
 
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
/**
 * 用于动态代理,获取方法的参数而且给返回值
 */
public class InvocationHandlerOrm implements InvocationHandler {
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 
        System.out.println("method:" + method);
        System.out.println("arg.length:" + args.length);
        Object object = null;
        //获取此时实例化的类
        String clz = CommonSingleton.getInstance().dataBaseOperateStrategy(AbstractDataBaseOperationFactory.databaseType);
        Class clazz = Class.forName(clz);
        System.out.println("clazz:" + clazz);
        Select select = method.getAnnotation(Select.class);
        if (select != null) {
            System.out.println("select"+select);
            System.out.println("select方法被代理");
            Method selectMethod = clazz.getMethod("select", String.class, Object.class);
            System.out.println("selectMethod:" + selectMethod);
            object = selectMethod.invoke(clazz.newInstance(), select.value()[0], args[0]);
        } else {
            Annotation[] annotations=method.getDeclaredAnnotations();
            System.out.println("annotations"+annotations[0]);
            Insert insert = method.getAnnotation(Insert.class);
            Update update = method.getAnnotation(Update.class);
            Delete delete = method.getAnnotation(Delete.class);
            String value = "";
            if (insert != null) {
                value = insert.value()[0];
            }
            if (update != null) {
                value = update.value()[0];
            }
            if (delete != null) {
                value = delete.value()[0];
            }
            System.out.println("update方法被代理");
            Method selectMethod = clazz.getMethod("update", String.class, Object.class);
            System.out.println("value:"+value);
            object = selectMethod.invoke(clazz.newInstance(), value, args[0]);
        }
        return object;
    }
}

通过测试,已经实现了基本的CUDR。

下面咱们将,上面的静态代理修改为动态代理,你们对比结合我本身给的例子,共同探讨一下。

package designmode.proxy.dynamic;

import designmode.proxy.staticproxy.DynamicDataSourceEntry;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author :奔跑的小虾米
 * @date :Created in 2019.12.17 2019/12/17 15:22
 * description:oderService动态代理类
 * modified By:
 * version: 1.0.0$
 */
public class OrderServiceDynamicPorxy implements InvocationHandler {
    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    //缓存被代理对象的引用
    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Class<?> clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before(args[0]);
        Object object = method.invoke(target,args);
        after();
        return object;
    }

    private void after() {
        System.out.println("代理后");
    }


    private void before(Object arg) {
        try {
            System.out.println("代理前");
            Long time = (Long)target.getClass().getMethod("getCreate()").invoke(target);
            Integer  dbRouter =  Integer.valueOf(yearFormat.format(new Date()));
            System.out.println("代理类分配到{Db_" + dbRouter+"}数据源处理数据");
            DynamicDataSourceEntry.set(String.valueOf(dbRouter));
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

很简单,就是换下代理类,使用java代理便可。那静态代理和动态代理区别在哪里呢?

静态代理和动态代理比较

都是代理模式,有效的保护了被代理的对象,实现了程序的解耦。而不一样的是,静态代理在运行前就已经肯定,其行为由程序员手动添加;动态代理则是在运行期间肯定被代理的对象,更加灵活多变。

静态代理的扩展

我在《设计模式之禅》中了解到,静态代理又分为普通代理和强制代理。这二者主要区别是:是否可以对真实的对象进行访问。这么说吧,普通代理经过代理来间接的访问真实对象,不能直接对真实对象进行访问;而强制代理则不一样,强制代理是由真实对象来找到代理对象,直接访问真实对象。是否是有点晕了?没事,我也把本身说晕了。多说无益,,代码解释一切。仍是上面切换数据库的例子。最上面的例子是一个普通代理的例子,下面我们切换一下我们的“上帝视角”,继续看看强制代理。改造 上面的例子相关代码以下.

修改后OrderServiceImp
/**
 *OrderService实现类。
 * */
public class OrderServiceImp implements OrderService {
    private OrderDao orderDao;
    /**
     * 绑定代理对象
     **/
    OrderService proxy = null;
    public OrderServiceImp() {
        //对比spring构造器注入
        //此处为方便,直接初始化
        orderDao = new OrderDao();
    }

    public OrderService getProxy() {
        proxy = new OrderServiceStaticPorxy(this);
        return proxy;
    }
    /**
     * 找到代理 对象
     */
    @Override
    public int createOrder(Order order) {
        System.out.println("调用orderService建立");
        return orderDao.insert(order);
    }
}
修改的 OrderServiceStaticPorxy
/**
 * 切换数据源代理类
 * */
public class OrderServiceStaticPorxy implements OrderService{
    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    private  OrderService service;

    public OrderServiceStaticPorxy(OrderService service) {
        this.service = service;
    }

    @Override
    public int createOrder(Order order) {
        before();
        Long time = order.getCreateTime();
        Integer dbRouter = Integer.valueOf(yearFormat.format(time));
        System.out.println("静态代理类自动分配到{DB_ " + dbRouter+"}数据源处理数据");
        DynamicDataSourceEntry.set(String.valueOf(dbRouter));
        after();
        return 0;
    }

    private void after() {
        System.out.println("代理后");
    }

    private void before() {
        System.out.println("代理前");
    }
}

童鞋们 ,能够 看到我在这里就修改OrderServiceImpOrderServiceStaticPorxy,这两个类的关系是OrderServiceStaticPorxy代理类,OrderServiceImp被代理的对象。因而可知普通代理和强制代理做为静态代理的两个分支,有不少共同之处;二者相互转换只须要修改代理类和代理对象便可,其余部分不用动。经测试发现,强制代理之因此强制,是由于客户端既不能直接访问代理对象也不能访问代理类,必须经过代理对象找到代理类,代理类的托管由代理对象完成。

手写动态代理

咱们学习要知其然知其因此然,虽然JDK为咱们实现好了动态代理,可是咱们要知道其中原理。下面开撕。file

实际上动态代理和静态代理有不少类似的地方,上面咱们也讨论过二者异同点。那动态代理如何在运行中实现的呢?通过简单查看JDK源码以及上图分支能够知道,主要由一下步骤:

1 . 代理类进行代理对象的建立,此处要生成.java文件而后在由编译生成.class文件,最后由classLoader加载 .class文件并返回实例对象。此处相关代码 以下 :

自定义生成 源文件
public class SelfProxy {
    private final static AtomicLong SERIAL_NUMBER = new AtomicLong();
    private final static String LINE_BREAK = "\r\n";
    private final static String TAB = "\t";

    public static Object newProxyInstance(SelfClassloader loader, Class<?>[] interfaces, SelfInvocationHandler h) {
        // 一、动态生成源代码.java 文件
        SrcJavaFile srcFile = generateSrc(interfaces);
        // 二、将 java 文件输出到磁盘
        try {
            srcFile.write();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 三、把生成的 .java 文件编译成 .class 文件
        try {
            srcFile.compiler();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 四、把编译生成的 .class 文件加载到 JVM 中来
        Class proxyClass = loader.findClass(srcFile.className);
        try {
            Constructor constructor =  proxyClass.getConstructor(SelfInvocationHandler.class);
            return constructor.newInstance(h);
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            srcFile.delete();
        }
        // 五、返回字节码重组之后新的代理对象
        return null;
    }

    /**
     * 生成 Java 源代码
     * @param interfaces
     * @return
     */
    public static SrcJavaFile generateSrc(Class<?>[] interfaces) {
        StringBuffer sb = new StringBuffer();
        // 反射包
        final String REFLECT_PACKAGE_TEXT = "import " + Method.class.getName() + ";" + LINE_BREAK;
        // 导包文本
        final String IMPORT_PACKAGE_TEXT = Arrays.stream(interfaces).map(Class::getName)
                .map(className -> "import " + className + ";" + LINE_BREAK)
                .reduce(REFLECT_PACKAGE_TEXT, (a, b) -> a + b);
        // 实现接口字符串
        final String INTERFACES_TEXT = Arrays.stream(interfaces).map(Class::getSimpleName).reduce((a, b) -> a + "," + b).get();
        // 类名
        final String CLASS_NAME = "$Proxy".concat(String.valueOf(SERIAL_NUMBER.incrementAndGet()));

        sb.append("package ").append(SelfProxy.class.getPackage().getName()).append(";").append(LINE_BREAK);
        sb.append(IMPORT_PACKAGE_TEXT);
        sb.append(String.format("public class %s implements %s {%s", CLASS_NAME, INTERFACES_TEXT, LINE_BREAK));                                                                        ;
        sb.append(tab(1) + "XLInvocationHandler h;" + LINE_BREAK);
        // 构造方法
        sb.append(tab(1) + String.format("public %s(XLInvocationHandler h) {", CLASS_NAME) + LINE_BREAK);
        sb.append(tab(2) + "this.h = h;" + LINE_BREAK);
        sb.append(tab(1) + "}" + LINE_BREAK);
        // 方法
        Arrays.stream(interfaces).forEach(i -> {
            Arrays.stream(i.getMethods()).forEach(m -> {
                Class[] classes = m.getParameterTypes();
                int paramNo = 0;
                String paramsWithType = "", params = "", paramsType = "";
                for (Class claz: classes) {
                    paramNo ++;
                    if (params.length() > 0) {
                        paramsWithType += ",";
                        params += ",";
                        paramsType += ",";
                    }
                    paramsWithType += String.format("%s param%d", claz.getName(), paramNo);
                    params += "param" + paramNo;
                    paramsType += claz.getName().concat(".class");
                }
                params = params.length() > 0 ? String.format("new Object[] {%s}", params) : "null";
                sb.append(tab(1) + String.format("public %s %s(%s){%s", m.getReturnType().getName(), m.getName(), paramsWithType, LINE_BREAK));
                sb.append(tab(2) + "try {" + LINE_BREAK);
                sb.append(tab(3) + String.format("Method m = %s.class.getMethod(\"%s\", new Class[]{%s});%s",i.getSimpleName(), m.getName(), paramsType, LINE_BREAK));
                sb.append(tab(3) + String.format("this.h.invoke(this, m, %s);%s", params, LINE_BREAK));
                sb.append(tab(2) + "} catch (Throwable throwable) {" + LINE_BREAK);
                sb.append(tab(3) + "throwable.printStackTrace();" + LINE_BREAK);
                sb.append(tab(2) + "}" + LINE_BREAK);
                sb.append(tab(1) + "}" + LINE_BREAK);
            });
        });

        sb.append("}" + LINE_BREAK);

        SrcJavaFile javaFile = new SrcJavaFile();
        javaFile.className = CLASS_NAME;
        javaFile.fileText = sb.toString();
        return javaFile;
    }

    /**
     * 重复屡次TAB
     * @param tabCount
     * @return
     */
    public static String tab(int tabCount) {
        String text = "";
        for (int i = 0; i < tabCount; i++) {
            text += TAB;
        }
        return text;
    }

    /**
     * Java 源文件信息
     */
    private static class SrcJavaFile {
        private String className;
        private String fileText;
        private File file;
        /**
         * 写到磁盘
         * @throws IOException
         */
        private void write() throws IOException {
            String filePath = SelfProxy.class.getResource("").getPath();
            file = new File(filePath + className + ".java");
            FileWriter fw = new FileWriter(file);
            fw.write(fileText);
            fw.flush();
            fw.close();
        }

        /**
         * 编译
         */
        public void compiler() throws IOException{
            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();
        }

        /**
         * 删除
         */
        public void delete() {
            file.delete();
        }
    }

}
自定义classloader
public class SelfClassloader extends ClassLoader{
    private File classPathFile = new File(SelfClassloader.class.getResource("").getPath());

    public SelfClassloader() {}

    @Override
    protected Class<?> findClass(String name) {
        String className = SelfClassloader.class.getPackage().getName() + "." + name;
        if (classPathFile != null) {
            File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
            if (classFile.exists()) {
                FileInputStream fis = null;
                ByteArrayOutputStream bos = null;
                try {
                    fis = new FileInputStream(classFile);
                    bos = new ByteArrayOutputStream();
                    byte[] buff = new byte[1024];
                    int len;
                    while ((len = fis.read(buff)) != -1) {
                        bos.write(buff, 0, len);
                    }
                    return defineClass(className, bos.toByteArray(), 0, bos.size());
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (null != fis) {
                        try {
                            fis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (null != bos) {
                        try {
                            bos.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}
定义顶层InvocationHandler接口
public interface SelfInvocationHandler {
    Object  invoke(Object proxy, Method method,Object[] arg) throws Throwable;
}

至此,咱们已经完成 了基本的动态代理,了解了动态模式的基本原理。固然,JDK中的 动态代理确定比这复杂,考虑的细节比咱们手撕的多上不,可是咱们只是试着去理解其中的原理,以便于咱们之后的开发中更好的使用动态代理。

先暂不讨论CgLib和代理模式在spring中应用,往后我会补上。[]()

声明

本文章为做者读书笔记及感悟,其中参考了《spring5核心原理与30个类手写实战》以及互联网上的内容。若有错误,请评论或者私聊我,欢迎探讨技术问题 。即将毕业,在准备找工做,有朋友想给我介绍的,欢迎添加微信:sllbiao。

相关文章
相关标签/搜索