<三> MyBatis代理模式

概述

????我們首先要理解凛辣,代理模式是干什么的绝编?我們知道孕豹,代理模式是用于松耦合的框弛,其實(shí)代理模式是通過將主要業(yè)務(wù)與次要業(yè)務(wù)分開處理實(shí)現(xiàn)松耦合的辛辨。而代理模式的本質(zhì)是在監(jiān)控行為特征。

代理模式

????代理模式主要分兩種瑟枫,靜態(tài)代理模式和動(dòng)態(tài)代理模式斗搞,下面我們通過實(shí)現(xiàn)來看看兩者的具體區(qū)別到底是什么。

靜態(tài)代理模式

主要角色:

??接口(主題):定義需要被監(jiān)控的方法
??被代理類:接口的具體實(shí)現(xiàn)類
??代理類:代理被代理類的類

主要角色:

????1. 代理類和被代理類實(shí)現(xiàn)同一個(gè)主題接口慷妙。
????2. 代理類具備一個(gè)屬性僻焚,為被代理類對象。

實(shí)現(xiàn)的目的:

????核心業(yè)務(wù)邏輯由被代理類去實(shí)現(xiàn)景殷,非核心需求多變的邏輯溅呢,交給代理類實(shí)現(xiàn)澡屡。

代碼實(shí)現(xiàn)案例:

接口(主題)
public interface Proxy {
     public abstract void todo();
}
被代理類
public class Reality implements Proxy {
    @Override
    public void todo() {
        System.out.println("我是核心功能......");
    }
}
代理類
public class ProxyReality implements Proxy {
    // 被代理對象
    private Proxy reality;

    //傳入一個(gè)被代理對象
    public ProxyRole(Proxy proxy) {
        reality = proxy;
    }
    @Override
    public void todo() {
        //1. 代理類的前期準(zhǔn)備
        doBefore();
        //2. 執(zhí)行核心功能
        reality.todo();
        //3. 代理類的收尾工作
        doAfter();
    }
    private void doBefore() {
        System.out.println("我做非核心功能......");
    }
    private void doAfter() {
        System.out.println("我是多變的需求......");
    }
}
案例測試
public static void main(String[] args) {
    //1. 創(chuàng)建被代理對象
    Proxy reality = new RealityRole();
    //2. 創(chuàng)建代理對象,傳入被代理對象
    ProxyRole proxyRole = new ProxyRole(reality);
    //3. 代理對象執(zhí)行
    proxyRole.todo();
}
總結(jié):
  1. 代理類和被代理類實(shí)現(xiàn)一樣的接口咐旧。
  2. 代理類具有一個(gè)屬性對象驶鹉,值為被代理類。
  3. 執(zhí)行時(shí)铣墨,調(diào)用代理類室埋,再由代理類調(diào)用被代理類。

動(dòng)態(tài)代理模式

主要角色:

??接口(主題):定義需要被監(jiān)控的方法
??被代理類:接口的具體實(shí)現(xiàn)類
??通知類:包含次要業(yè)務(wù)的具體實(shí)現(xiàn)和次要與主要的綁定
??監(jiān)控對象(代理對象):被監(jiān)控實(shí)例對象伊约,需要被監(jiān)控的監(jiān)控行為姚淆,具體通知類實(shí)例對象

實(shí)現(xiàn)的思路:

????1. 采用通知類的方式,通知類具備一個(gè)屬性屡律,為被代理類對象腌逢。
????2. 使用JDK的給通知類分配一個(gè)監(jiān)控對象。
????3. 通知類統(tǒng)一實(shí)現(xiàn)InvocationHandler接口超埋。

實(shí)現(xiàn)的目的:

????核心業(yè)務(wù)邏輯由被代理類去實(shí)現(xiàn)搏讶,非核心需求多變的邏輯,交給通知類實(shí)現(xiàn)霍殴,不需要一個(gè)具體的代理類媒惕,而是由JDK動(dòng)態(tài)生成代理對象。

JDK方式代碼實(shí)現(xiàn)案例:

接口(主題)
public interface Proxy {
     public abstract void todo();
}
被代理類
public class Reality implements Proxy {
    @Override
    public void todo() {
        System.out.println("我是核心功能......");
    }
}
通知類
public class ProxyReality implements InvocationHandler {
    // 被代理對象
    private Proxy reality;

    //傳入一個(gè)被代理對象
    public ProxyRole(Proxy proxy) {
        reality = proxy;
    }
    /**
     * 在被監(jiān)控的方法要被執(zhí)行時(shí)来庭,執(zhí)行該方法
     * @param proxy 代理對象妒蔚,例如小明.eat()
     * @param method 類似eat方法的封裝對象
     * @param args  入?yún)⒌臄?shù)組
     */
    public Object invoke(Object proxy, Method method, Object[] args) {
        //0.接受主要業(yè)務(wù)返回的對象
        Object resultObj;
          //1. 代理類的前期準(zhǔn)備
        doBefore();
         //2. 執(zhí)行核心功能
         resultObj = method.invoke(proxy,args);
        //3. 代理類的收尾工作
        doAfter();
        return resultObj;
    }
    private void doBefore() {
        System.out.println("我做非核心功能......");
    }
    private void doAfter() {
        System.out.println("我是多變的需求......");
    }
}
代理工廠類
public class ProxyFactory {
    /**
     * JDK動(dòng)態(tài)代理模式下,代理對象的數(shù)據(jù)類型應(yīng)由監(jiān)控行為來描述
     * @param obj 監(jiān)控的類
     * @return
     */
    public static Proxy builder(Class obj) throws IllegalAccessException, InstantiationException {
        //1.實(shí)例化被監(jiān)控對象
        Proxy $proxy= (Proxy) obj.newInstance();
        //2.實(shí)例化通知對象月弛,將被監(jiān)控對象傳入
        InvocationHandler adviser = new ProxyReality($proxy);
        //2肴盏、向JVM申請一個(gè)負(fù)責(zé)監(jiān)控被監(jiān)控對象的代理對象
        Proxy proxy = (Proxy) Proxy.newProxyInstance($proxy.getClass().getClassLoader(),
                        $proxy.getClass().getInterfaces(),adviser);
        return proxy;
    }
}
案例測試
public static void main(String[] args) {
    //1. 創(chuàng)建被代理對象
    Proxy reality = ProxyFactory.builder(Reality.class);
    //2. 直接調(diào)用代理對象方法執(zhí)行
    reality.todo();
}


MyBatis的動(dòng)態(tài)代理模式

一、JDBC開發(fā)步驟

public void test(){
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //1. 加載驅(qū)動(dòng)
            Class.forName("com.mysql.jdbc.Driver");
            //2. 建立鏈接通道
            con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "root");
           //3. 建立PreparedStament
            ps = con.prepareStatement("SELECT * FROM ...");
           //4. 輸送SQL命令到數(shù)據(jù)庫中執(zhí)行尊搬,并帶回運(yùn)行結(jié)果
            rs = ps.executeQuery();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            // 5. 銷毀鏈接通道叁鉴、PreparedStament和結(jié)果集對象
            try {
                if (rs != null) {
                    rs.close();
                    rs = null;
                }
                if (ps != null) {
                    ps.close();
                    ps = null;
                }
                if (con != null) {
                    con.close();
                    con = null;
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

二土涝、MyBatis動(dòng)態(tài)代理模式封裝JDBC

1佛寿、JDBC的主要業(yè)務(wù)和次要業(yè)務(wù)

主要業(yè)務(wù):輸送SQL命令到數(shù)據(jù)庫中執(zhí)行,并帶回運(yùn)行結(jié)果
次要業(yè)務(wù):加載驅(qū)動(dòng)但壮、建立鏈接通道冀泻、建立PreparedStament和銷毀鏈接通道和PreparedStament

2、聲明接口SqlSession(定義具體的方法行為)
/**
 * 定義被監(jiān)控者接口
 */
public interface SqlSession {
    public int insert(String template,Object obj) throws SQLException;
    public int update(String template,Object obj) throws SQLException;
    public void delete(String template,Object obj) throws SQLException;
    public Object queryInfo(String template,Object obj) throws SQLException;
}
3蜡饵、具體實(shí)現(xiàn)類被監(jiān)控對象

主要業(yè)務(wù):具體實(shí)現(xiàn)SQL命令執(zhí)行

/**
 * 被監(jiān)控對象實(shí)現(xiàn)
 */
public class SimpleSqlSession implements SqlSession {
    ParamentHandler parament;//入?yún)⑻幚砥?    StatementHandler statement;//執(zhí)行命令處理器
    ResultSetHandler resultSet;//結(jié)果集處理器
    PreparedStatement ps;

    /**
     * 初始化處理器
     */
    public SimpleSqlSession(){
        this.resultSet = new SimpleResultSetHandler();
        this.statement = new SimpleStatementHandler();
        this.parament = new SimpleParmentHandler();
    }
    public int insert(String template,Object obj) throws SQLException {
        String sql = parament.insert(template,obj);
        return statement.update(ps,sql);
    }
    public int update(String template,Object obj) throws SQLException {
        String sql = parament.insert(template,obj);
        return statement.update(ps,sql);
    }
    public void delete(String template,Object obj) throws SQLException {
        String sql = parament.insert(template,obj);
        statement.update(ps,sql);
    }
    public Object queryInfo(String template,Object obj) throws SQLException {
        String sql = parament.insert(template,obj);
        ResultSet object = statement.query(ps,sql);
        return resultSet.query(object);
    }
}
/**
 * 參數(shù)處理器接口
 */
public interface ParamentHandler{
    public String insert(String template,Object paremt);
    public String delete(String template,Object paremt);
    public String update(String template,Object paremt);
    public String query(String template,Object paremt);
}
/**
 * 參數(shù)處理器具體實(shí)現(xiàn)
 */
public class SimpleParmentHandler implements ParamentHandler {
    /**
     * 新增的參數(shù)處理
     */
    public String insert(String template, Object paremt) {
        String sql = null;
        //拼接入?yún)⒑蚐QL模板
        return sql;
    }
    /**
     * 刪除的參數(shù)處理
     */
    public String delete(String template, Object paremt) {
        String sql = null;
        //拼接入?yún)⒑蚐QL模板
        return sql;
    }
    /**
     * 更新的參數(shù)處理
     */
    public String update(String template, Object paremt) {
        String sql = null;
        //拼接入?yún)⒑蚐QL模板
        return sql;
    }
    /**
     * 查詢的參數(shù)處理
     */
    public String query(String template, Object paremt) {
        String sql = null;
        //拼接入?yún)⒑蚐QL模板
        return sql;
    }
}
/**
 * 命令執(zhí)行處理器接口
 */
public interface  StatementHandler{
    public int update(PreparedStatement ps,String sql) throws SQLException;
    public ResultSet query(PreparedStatement ps, String sql) throws SQLException;
}
/**
 * 命令執(zhí)行處理器具體實(shí)現(xiàn)
 */
public class SimpleStatementHandler implements StatementHandler{
    /**
     * 執(zhí)行更新指令
     */
    public int update(PreparedStatement ps,String sql) throws SQLException {
        return ps.executeUpdate(sql);
    }
    /**
     * 執(zhí)行查詢指令
     */
    public ResultSet query(PreparedStatement ps, String sql) throws SQLException {
        return ps.executeQuery(sql);
    }
}
/**
 * 返回參數(shù)格式化處理器接口
 */
public interface ResultSetHandler {
    public Object query(ResultSet resultSet) throws SQLException;
}
/**
 * 返回參數(shù)格式化處理器具體實(shí)現(xiàn)
 */
public class SimpleResultSetHandler implements ResultSetHandler{

    public Object query(ResultSet resultSet) throws SQLException {
        //格式化為具體對象(反射賦值)
        resultSet.close();
        resultSet = null;
        return null;
    }
}
4弹渔、通知類:實(shí)現(xiàn)InvocationHandler,在構(gòu)造方法定義具體入?yún)qlSession溯祸,實(shí)現(xiàn)具體次要業(yè)務(wù)肢专。
/**
 * 通知類(實(shí)現(xiàn)次要業(yè)務(wù))
 */
public class Plugin implements InvocationHandler {

    SqlSession session;
    Connection con = null;
    PreparedStatement ps = null;

    public Plugin(SqlSession session){
        this.session = session;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1. 傳值給會(huì)話對象
        setPreparedStatement();
        //2. 前置處理
        frontLoad();
        //3. 執(zhí)行主要業(yè)務(wù)
        Object result = method.invoke(session,args);
        //4. 后置處理
        closeLoad();
        return result;
    }
    /**
     * 反射賦值
     */
    private void setPreparedStatement() throws NoSuchFieldException, IllegalAccessException {
        Field field = session.getClass().getDeclaredField("ps");
        field.setAccessible(true);
        field.set(session,ps);
    }
    /**
     * 前置處理
     * @throws Exception
     */
    private void frontLoad() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "root");
        ps = con.prepareStatement("");
    }
    /**
     * 后置處理
     * @throws SQLException
     */
    private void closeLoad() throws SQLException {
        if (ps != null) {
            ps.close();
            ps = null;
        }
        if (con != null) {
            con.close();
            con = null;
        }
    }
}
5舞肆、實(shí)現(xiàn)代理工廠SqlSessionFactory
/**
 * 代理工廠
 */
public class SqlSessionFactory {
    /**
     * JDK動(dòng)態(tài)代理模式下,代理對象的數(shù)據(jù)類型應(yīng)由監(jiān)控行為來描述
     * @param obj 監(jiān)控的類
     * @return
     */
    public static SqlSession builder(Class obj) throws IllegalAccessException, InstantiationException {
        //1.創(chuàng)建被監(jiān)控對象
        SqlSession session = (SqlSession) obj.newInstance();
        //2.創(chuàng)建通知對象
        InvocationHandler adviser = new Plugin(session);
        //2博杖、向JVM申請一個(gè)負(fù)責(zé)監(jiān)控被監(jiān)控對象的代理對象
        SqlSession proxy = (SqlSession) Proxy.newProxyInstance(session.getClass().getClassLoader(),
                session.getClass().getInterfaces(),adviser);
        return proxy;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末椿胯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子剃根,更是在濱河造成了極大的恐慌哩盲,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狈醉,死亡現(xiàn)場離奇詭異廉油,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)苗傅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門抒线,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渣慕,你說我怎么就攤上這事十兢。” “怎么了摇庙?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵旱物,是天一觀的道長。 經(jīng)常有香客問我卫袒,道長宵呛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任夕凝,我火速辦了婚禮宝穗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘码秉。我一直安慰自己逮矛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布转砖。 她就那樣靜靜地躺著须鼎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪府蔗。 梳的紋絲不亂的頭發(fā)上晋控,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機(jī)與錄音姓赤,去河邊找鬼赡译。 笑死,一個(gè)胖子當(dāng)著我的面吹牛不铆,可吹牛的內(nèi)容都是我干的蝌焚。 我是一名探鬼主播裹唆,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼只洒!你這毒婦竟也來了品腹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤红碑,失蹤者是張志新(化名)和其女友劉穎舞吭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體析珊,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡羡鸥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忠寻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惧浴。...
    茶點(diǎn)故事閱讀 39,773評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖奕剃,靈堂內(nèi)的尸體忽然破棺而出衷旅,到底是詐尸還是另有隱情,我是刑警寧澤纵朋,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布柿顶,位于F島的核電站,受9級特大地震影響操软,放射性物質(zhì)發(fā)生泄漏嘁锯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一聂薪、第九天 我趴在偏房一處隱蔽的房頂上張望家乘。 院中可真熱鬧,春花似錦藏澳、人聲如沸仁锯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽业崖。三九已至,卻和暖如春凉驻,著一層夾襖步出監(jiān)牢的瞬間腻要,已是汗流浹背复罐。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工涝登, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人效诅。 一個(gè)月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓胀滚,卻偏偏與公主長得像趟济,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子咽笼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評論 2 354