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

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

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

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

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

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

静态代理

下面 来看一个场景,在实际开发或生产中,不可避免会使用到各种数据库,如果手动切换 数据的话,不仅麻烦,易出错,而且生产环境也不允许啊。这时候静态代理就可以发挥作用了,根据实际情况和条件 更换 数据库。下面看代码 。

订单实体类

/**
  * 订单实体类
  * */
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);
}

实现服务层接口

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

/**
 *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,可以根据配置文件自动切换数据库。与上面的这个例子有异曲同工之妙,但是当时限于对代理模式的理解,写的代码有些烂,希望不要介意。但是,基本的功能都实现了。先来看看数据库设置相关的类图。

此类图主要是获取数据库类图, 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为我们实现好了动态代理,但是我们要知道其中原理。下面开撕。

实际上动态代理和静态代理有很多相似的地方,上面我们也讨论过两者异同点。那动态代理如何在运行中实现的呢?经过简单查看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) {
        // 1、动态生成源代码.java 文件
        SrcJavaFile srcFile = generateSrc(interfaces);
        // 2、将 java 文件输出到磁盘
        try {
            srcFile.write();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 3、把生成的 .java 文件编译成 .class 文件
        try {
            srcFile.compiler();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 4、把编译生成的 .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();
        }
        // 5、返回字节码重组以后新的代理对象
        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。

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章