源碼學習之設計模式(代理模式)

今天要說的是設計模式中的代理模式。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。

相關文章
相關標籤/搜索