手寫篇——JNDI 實現(xiàn)依賴查找净响,實現(xiàn)一個最簡單spring

原文來自公眾號三不猴

基礎(chǔ)概念

關(guān)于JNDI

Java命名和目錄接口(Java Naming and Directory Interface声诸,縮寫JNDI),是Java的一個目錄服務應用程序接口(API)夜矗,它提供一個目錄系統(tǒng)沈条,并將服務名稱與對象關(guān)聯(lián)起來,從而使得開發(fā)人員在開發(fā)過程中可以使用名稱來訪問對象拖叙。

JNDI 與 Service Provider

JNDI 是統(tǒng)一抽象出來的接口氓润,Service Provider是對接口的具體實現(xiàn)。如上面提到的默認的 JNDI Service Provider 有 RMI/LDAP 等等薯鳍。

ObjectFactory

每一個 Service Provider 可能配有多個 Object Factory旺芽。Object Factory 用于將 Naming Service(如 RMI [Remote Method Invocation] / LDAP [Light Directory Access Portocol] )中存儲的數(shù)據(jù)轉(zhuǎn)換為 Java 中可表達的數(shù)據(jù),如 Java 中的對象或 Java 中的基本數(shù)據(jù)類型辐啄。

開始手寫

  1. 先啟動一個本地RMI
public class MainTest {
    public static void main(String[] args) throws Exception {

        // 在本機 1999 端口開啟 rmi registry,可以通過 JNDI API 來訪問此 rmi registry
        Registry registry = LocateRegistry.createRegistry(1999);

        // 創(chuàng)建一個 Reference运嗜,第一個參數(shù)無所謂壶辜,第二個參數(shù)指定 Object Factory 的類名:

        // 第三個參數(shù)是 codebase,表明如果客戶端在 classpath 里面找不到
        // jndiinj.EvilObjectFactory担租,則去 http://localhost:9999/ 下載

        // 當然利用的時候這里應該是一個真正的 codebase 的地址
        Reference ref = new Reference("test",
                "jndiinj.EvilObjectFactory", "http://localhost:9999/");

        // 因為只為只有實現(xiàn) Remote 接口的對象才能綁定到 rmi registry 里面去
        ReferenceWrapper wrapper = new ReferenceWrapper(ref);
        registry.bind("evil", wrapper);
    }
}

    
  1. 連接本地客戶端
public class LookupTest {
    public static void main(String[] args) throws NamingException {
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        Context ctx = new InitialContext();
        // ctx.lookup 參數(shù)需要可控
        Object lookup = ctx.lookup("rmi://localhost:1999/evil");
        System.out.println(lookup);
    }
}

我們先啟動MainTest類然后啟動LookupTest類最后看看輸出

  • Connected to the target VM, address: '127.0.0.1:49254', transport: 'socket'
    Reference Class Name: test

這里似乎好像沒什么用砸民,只是獲取到了他們類名而已,能不能存?zhèn)€對象進去呢奋救?

Reference中有個add方法

public void add(RefAddr addr) {
    addrs.addElement(addr);
}

這里添加的是一個RefAddr對象岭参,RefAddr是一個抽象類,所以我們對這個拓展一下尝艘。

/**
 * @author yhx
 * @since 2021/9/10 5:33 下午
 */
public class BeanRef<T> extends RefAddr {
    private T t;

    public BeanRef(T t) {
        super(t.getClass().getName());
        this.t = t;
    }

    @Override
    public T getContent() {
        return t;
    }
}

我們對代碼稍微修改一下演侯,首先定義一個要存的對象,User類背亥,這里去要注意的就是一定要實現(xiàn)序列化接口秒际。

/**
 * @author yhx
 * @since 2021/9/10 3:21 下午
 */

public class User implements Serializable {

    private Long id;
    private String name;
    private String password;
    private String email;
    private String phone;
        // 省略set get方法,為了方便觀察我們再定義一個String方法這里也省略
}

然后就是在創(chuàng)建完Reference對象以后加入下面的代碼

 BeanRef<User> addr = new BeanRef<>(_getUser());
 ref.add(addr);
        
 private static User _getUser() {
        User user = new User();
        user.setId(0L);
        user.setName("小明");
        user.setPassword("***");
        user.setEmail("123@qq.com");
        user.setPhone("1821732098");
        return user;
    }       
        

重新執(zhí)行LookupTest會得到下面的結(jié)果

Reference Class Name: user
Type: jndi.domain.User
Content: User[id=0, name='小明', password='***', email='123@qq.com', phone='1821732098']

這樣我們就完成了一個依賴查找的小功能狡汉,可能你會說:就這娄徊??盾戴?

其實Tomcat容器也提供了JNDI寄锐,這樣我們也不用手動啟動一個RMI。

首先我們先初始化一個web工程尖啡,不要引spring的依賴橄仆。在webapp下的META-INF新建context.xml文件,模板在Tomcat的example目錄下有可婶。

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <Resource 
      auth="Container" 
      driverClassName="com.mysql.jdbc.Driver" 
      maxIdle="30" 
      maxTotal="50" 
      maxWaitMillis="-1" 
      name="jdbc/MVNT1" 
      username="root"
      password="yhx" 
      type="javax.sql.DataSource" 
      url="jdbc:mysql://localhost:3306/learning?useSSL=true"/>
</Context>

然后我們在web.xml中新增下面的配置沿癞。注意名字要和context.xml中的名字一樣。

<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE web-app PUBLIC
  '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN'
  'http://java.sun.com/j2ee/dtds/web-app_2_3.dtd'>
<web-app> 
    <resource-ref>
          <description>DB Connection</description>
          <!-- 這個名字必須和數(shù)據(jù)源名字一致 -->
          <res-ref-name>jdbc/MVNT1</res-ref-name>
          <res-type>javax.sql.DataSource</res-type>
          <res-auth>Container</res-auth>
    </resource-ref>
</web-app>

獲取數(shù)據(jù)源

數(shù)據(jù)源是獲取和數(shù)據(jù)庫的連接矛渴,所以一般我們可以創(chuàng)建 ServletContext監(jiān)聽器(implements ServletContextListener)在 ServletContext 創(chuàng)建的時候就去獲取數(shù)據(jù)源椎扬。如下:

import javax.annotation.Resource;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.sql.DataSource;

@WebListener
public class WebContextListener implements ServletContextListener {
    /*
     * 使用Resource注解成員變量惫搏,通過名字查找server.xml中配置的數(shù)據(jù)源并注入進來
     * lookup:指定目錄處的名稱,此屬性是固定的
     * name:指定數(shù)據(jù)源的名稱蚕涤,即數(shù)據(jù)源處配置的name屬性
     */
    @Resource(lookup="java:/comp/env", name="jdbc/MVNT1")

    /*將找到的數(shù)據(jù)源保存在此變量中筐赔,javax.sql.DataSource*/
    private DataSource dataSource;

    @Override
    public void contextDestroyed(ServletContextEvent event) {

    }

    @Override
    public void contextInitialized(ServletContextEvent event) {
        /*測試數(shù)據(jù)源*/
        //System.out.println(dataSource);

        /*將數(shù)據(jù)源注入連接管理*/
        ConnectionManager.setDadaSource(dataSource);
    }
}

然后再通過數(shù)據(jù)源獲取連接

import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;

/*連接對象的管理者*/

public final class ConnectionManager {
    /*確保在每一個連接里是同一個連接對象,方便以后做事務的管理揖铜,針對每個線程創(chuàng)建一個獨立的容器*/
    /*使用泛型標準*/
    private final static ThreadLocal<Connection> LOCAL=new ThreadLocal();
    private static DataSource dataSource;

    public static void setDadaSource(DataSource dataSource) {
        /*不能使用this*/
        ConnectionManager.dataSource=dataSource;
    }

    /*返回連接對象*/
    public static Connection getConnection() throws SQLException {
        /*獲取連接對象*/
        Connection conn=LOCAL.get();
        if(null != conn) {
            return conn;
        }

        /*通過數(shù)據(jù)源得到連接茴丰,并放入線程中管理,再返回連接對象*/
        conn=dataSource.getConnection();
        LOCAL.set(conn);
        return conn;
    }

    /*釋放連接對象*/
    public static void release() {
        Connection conn=LOCAL.get();
        if(null != conn) {
            DBUtil.release(conn);
            LOCAL.remove();
        }
    }
}

也可以直接使用Context來獲取數(shù)據(jù)源(注意拋出異常)

Context cxt=new InitialContext();
//獲取與邏輯名相關(guān)聯(lián)的數(shù)據(jù)源對象
DataSource ds=(DataSource)cxt.lookup("java:comp/env/jdbc/MVNT1");

到此我們就把數(shù)據(jù)庫DataSource對象使用jdni管理起來實現(xiàn)了一個依賴查找天吓。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贿肩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子龄寞,更是在濱河造成了極大的恐慌汰规,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件物邑,死亡現(xiàn)場離奇詭異溜哮,居然都是意外死亡,警方通過查閱死者的電腦和手機色解,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門茂嗓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人科阎,你說我怎么就攤上這事述吸。” “怎么了锣笨?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵刚梭,是天一觀的道長。 經(jīng)常有香客問我票唆,道長朴读,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任走趋,我火速辦了婚禮衅金,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘簿煌。我一直安慰自己氮唯,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布姨伟。 她就那樣靜靜地躺著惩琉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪夺荒。 梳的紋絲不亂的頭發(fā)上瞒渠,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天良蒸,我揣著相機與錄音,去河邊找鬼伍玖。 笑死嫩痰,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的窍箍。 我是一名探鬼主播串纺,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼椰棘!你這毒婦竟也來了纺棺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤邪狞,失蹤者是張志新(化名)和其女友劉穎五辽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體外恕,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年乡翅,在試婚紗的時候發(fā)現(xiàn)自己被綠了鳞疲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蠕蚜,死狀恐怖尚洽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情靶累,我是刑警寧澤腺毫,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站挣柬,受9級特大地震影響潮酒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜邪蛔,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一急黎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侧到,春花似錦勃教、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至汞贸,卻和暖如春绳军,著一層夾襖步出監(jiān)牢的瞬間印机,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工删铃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留耳贬,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓猎唁,卻偏偏與公主長得像咒劲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子诫隅,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容