xml方式自定義實(shí)現(xiàn)Ioc容器

@[TOC]

xml方式自定義實(shí)現(xiàn)Ioc容器

使用xml實(shí)現(xiàn)自定義簡單的Ioc容器

前言

平時開發(fā)過程中,我們都是使用Spring來進(jìn)行開發(fā),Spring核心的Ioc容器幫助我們?nèi)?chuàng)建對象這一過程被稱作控制反轉(zhuǎn)也叫Ioc
在實(shí)例化一個對象時候,這個對象中用到一個對象類型的屬性,容器把這個對象注入到實(shí)例化對象的過程被稱作依賴注入簡稱DI

Ioc和DI說的是一個事情,針對的側(cè)重點(diǎn)不同,IOC是站在容器角度創(chuàng)建對象,DI是站在使用的角度,注入使用對象;

沒有IOC容器的時候

在這里插入圖片描述

模擬銀行轉(zhuǎn)賬例子

轉(zhuǎn)賬接口

public interface AccountDao {

    Account queryAccountByCardNo(String cardNo) throws Exception;

    int updateAccountByCardNo(Account account) throws Exception;
}

接口實(shí)現(xiàn)類

public class JdbcAccountDaoImpl implements AccountDao {

    public void init() {
        System.out.println("初始化方法.....");
    }

    public void destory() {
        System.out.println("銷毀方法......");
    }

    @Override
    public Account queryAccountByCardNo(String cardNo) throws Exception {
        //從連接池獲取連接
        Connection con = DruidUtils.getInstance().getConnection();
        String sql = "select * from account where cardNo=?";
        PreparedStatement preparedStatement = con.prepareStatement(sql);
        preparedStatement.setString(1,cardNo);
        ResultSet resultSet = preparedStatement.executeQuery();

        Account account = new Account();
        while(resultSet.next()) {
            account.setCardNo(resultSet.getString("cardNo"));
            account.setName(resultSet.getString("name"));
            account.setMoney(resultSet.getInt("money"));
        }

        resultSet.close();
        preparedStatement.close();
        //con.close();

        return account;
    }

    @Override
    public int updateAccountByCardNo(Account account) throws Exception {

        // 從連接池獲取連接
        // 改造為:從當(dāng)前線程當(dāng)中獲取綁定的connection連接
        Connection con = DruidUtils.getInstance().getConnection();
        String sql = "update account set money=? where cardNo=?";
        PreparedStatement preparedStatement = con.prepareStatement(sql);
        preparedStatement.setInt(1,account.getMoney());
        preparedStatement.setString(2,account.getCardNo());
        int i = preparedStatement.executeUpdate();

        preparedStatement.close();
        //con.close();
        return i;
    }
}

業(yè)務(wù)接口

public interface TransferService {

    void transfer(String fromCardNo,String toCardNo,int money) throws Exception;
}

實(shí)現(xiàn)類

public class TransferServiceImpl implements TransferService {

    // 1 原始的new 方法創(chuàng)建dao接口實(shí)現(xiàn)類對象
   private AccountDao accountDao = new JdbcAccountDaoImpl();

    @Override
    public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {

        Account from = accountDao.queryAccountByCardNo(fromCardNo);
            Account to = accountDao.queryAccountByCardNo(toCardNo);

            from.setMoney(from.getMoney()-money);
            to.setMoney(to.getMoney()+money);

            accountDao.updateAccountByCardNo(to);
            //int c = 1/0;
            accountDao.updateAccountByCardNo(from);

    }
}

controller層

@WebServlet(name="transferServlet",urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {

    // 1. 實(shí)例化service層對象
    private TransferService transferService = new TransferServiceImpl();


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 設(shè)置請求體的字符編碼
        req.setCharacterEncoding("UTF-8");

        String fromCardNo = req.getParameter("fromCardNo");
        String toCardNo = req.getParameter("toCardNo");
        String moneyStr = req.getParameter("money");
        int money = Integer.parseInt(moneyStr);

        Result result = new Result();

        try {

            // 2. 調(diào)用service層方法
            transferService.transfer(fromCardNo,toCardNo,money);
            result.setStatus("200");
        } catch (Exception e) {
            e.printStackTrace();
            result.setStatus("201");
            result.setMessage(e.toString());
        }

        // 響應(yīng)
        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().print(JsonUtils.object2Json(result));
    }
}

可以看出來在controller層 new業(yè)務(wù)層的對象,new 業(yè)務(wù)層的對象中已經(jīng)new出來dao層的實(shí)現(xiàn)類

業(yè)務(wù)層和dao層通過new關(guān)鍵字連接起來,耦合高

在這里插入圖片描述

使用Ioc容器情況下

在這里插入圖片描述

如圖可以看到,Ioc容器講過AB對象的示例存儲起來,main函數(shù)使用AB對象的時候,直接在Ioc容器中獲取;

基于這個理解,我們可以實(shí)現(xiàn)自己的Ioc容器;

首先呢,我們新建自己的xml,用來配置自己的需要一些示例話的bean也就是對象

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="accountDao" class="com.udeam.edu.dao.impl.JdbcAccountDaoImpl">

    </bean>

    <!--    TransferServiceImpl service-->
    <bean id="transferService" class="com.udeam.edu.service.impl.TransferServiceImpl">
</property>
    </bean>
</beans>

創(chuàng)建dao層實(shí)現(xiàn)類和業(yè)務(wù)層實(shí)現(xiàn)類,并且過自己的Id從容器中獲取;

創(chuàng)建BeanFactorys類來解析xml,實(shí)例化配置的對象

/**
 * 工廠Bean
 */
public class BeanFactorys {

    private final static Map<String, Object> iocMap = new HashMap<>();

    static {
        // 1 讀取解析beans.xml  通過反射技術(shù),生產(chǎn)bean對象,并將其存在map中

        InputStream resourceAsStream = BeanFactorys.class.getClassLoader().getResourceAsStream("beans.xml");

        //得到一個 文檔對象
        try {
            Document read = new SAXReader().read(resourceAsStream);
            //獲取跟對象
            Element rootElement = read.getRootElement();

            /**
             * xpath表達(dá)式 用法
             *   // 從匹配選擇的當(dāng)前節(jié)點(diǎn)選擇文檔中的節(jié)點(diǎn),而不考慮他們的位置
             *   / 從根節(jié)點(diǎn)獲取
             *  . 選取當(dāng)前節(jié)點(diǎn)
             *  .. 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)
             *  @ 選取屬性
             *
             */
            // //表示讀取任意位置的bean標(biāo)簽
            List<Element> list = rootElement.selectNodes("http://bean");

            if (Objects.isNull(list) || list.size() == 0) {
                throw new RuntimeException("無此bean標(biāo)簽");
            }

            list.forEach(x -> {
                //獲取Id
                String id = x.attributeValue("id"); //accountDao
                //獲取權(quán)限定命名
                String clasz = x.attributeValue("class"); //com.udeam.edu.dao.impl.JdbcAccountDaoImpl
                System.out.println(id + " ---> " + clasz);
                //通過反射創(chuàng)建對象
                try {
                    Object o = Class.forName(clasz).newInstance();
                    //存入ioc容器
                    iocMap.put(id, o);

                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }


            });
 

        } catch (DocumentException e) {
            e.printStackTrace();
        }


        // 2 對外提供獲取示例對象接口


    }

    /**
     * 對外提供獲取bean接口
     *
     * @param id
     * @return
     */
    public static Object getBean(String id) {
        return iocMap.get(id);
    }
}

可以看到在類加載的時候通過解析xml獲取其中的bean 的class權(quán)限定名,通過反射創(chuàng)建對象,存儲在map中,id作為key;

然后我們需要在實(shí)現(xiàn)類和控制層去替換這個new關(guān)鍵字創(chuàng)建的對象

  private TransferServiceImpl transferService = (TransferServiceImpl) BeanFactorys.getBean("transferService");

    private AccountDao accountDao = (AccountDao) BeanFactorys.getBean("accountDao");

但是這樣子實(shí)現(xiàn)還是有點(diǎn)不優(yōu)雅 還是有=連接;

=號去掉

private AccountDao accountDao

 accountDao.queryAccountByCardNo(fromCardNo);

此時,沒有賦值操作,這兒會默認(rèn)null 空出現(xiàn)空指針異常;
為此,我們需要進(jìn)行設(shè)置值,可以通過set,構(gòu)造參數(shù)等設(shè)置值;

可以在xml中使用屬性set設(shè)置值

在xml中定義一個 對象屬性 property 設(shè)置名字name和ref引用對象

    <bean id="transferService" class="com.udeam.edu.service.impl.TransferServiceImpl">
        <property name="AccountDao" ref="accountDao"></property>
    </bean>

在剛才的BeanFactorys中添加解析方法

在解析xml完 實(shí)例化對象到Ioc容器之后,來解析property屬性

   //獲取所有properties 屬性 并且set設(shè)置值
            List<Element> prList = rootElement.selectNodes("http://property");

            prList.forEach(y -> {
                //獲取 property 屬性name值
                String name = y.attributeValue("name"); //   <property name="setAccountDao" ref = "accountDao"></property>
                String ref = y.attributeValue("ref");
                //獲取父節(jié)點(diǎn)id
                Element parent = y.getParent();
                //獲取父節(jié)點(diǎn)id
                String id = parent.attributeValue("id");
                //維護(hù)對象依賴關(guān)系
                Object o = iocMap.get(id);
                //找到所有方法
                Method[] methods = o.getClass().getMethods();
                for (int i = 0; i < methods.length; i++) {
                    //方法就是set屬性反方
                    if (methods[i].getName().equalsIgnoreCase("set" + name)) {
                        try {
                            //set設(shè)置對象
                            methods[i].invoke(o, iocMap.get(ref));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }

                        //set之后重新賦值
                        iocMap.put(id,o);
                    }


                }


            });

然后TransferServiceImpl類中添加一個set方法設(shè)置我們需要設(shè)置的值

  private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

xml上中獲取這個set方法,然后利用set + 獲取的property屬性name值 進(jìn)行判斷,然后反射設(shè)置值

     if (methods[i].getName().equalsIgnoreCase("set" + name)) {
                        try {
                            //set設(shè)置對象
                            methods[i].invoke(o, iocMap.get(ref));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }

                        //set之后重新賦值
                        iocMap.put(id,o);
                    }

這個過程可以看做是簡單的set注入方式,類似于Spring中的set注入;

IoC解決了什么問題

IoC解決對象之間的耦合問題

我們不???去new對象了,?是由IoC容器(Spring框架)去幫助我們實(shí)例化對
象并且管理它黍聂,我們需要使?哪個對象躺苦,去問IoC容器要即可

控制反轉(zhuǎn)

控制:指的是對象創(chuàng)建(實(shí)例化、管理)的權(quán)利
反轉(zhuǎn):控制權(quán)交給外部環(huán)境了(spring框架产还、IoC容器)

用到的依賴

  <!-- mysql數(shù)據(jù)庫驅(qū)動包 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.35</version>
    </dependency>
    <!--druid連接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.21</version>
    </dependency>

    <!-- servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>

    <!-- jackson依賴 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.6</version>
    </dependency>

    <!--dom4j依賴-->
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    <!--xpath表達(dá)式依賴 為了快速定位xml元素-->
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>1.1.6</version>
    </dependency>

代碼

傳送門

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匹厘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子脐区,更是在濱河造成了極大的恐慌愈诚,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牛隅,死亡現(xiàn)場離奇詭異炕柔,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)媒佣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門匕累,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丈攒,你說我怎么就攤上這事∈诎裕” “怎么了巡验?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碘耳。 經(jīng)常有香客問我显设,道長,這世上最難降的妖魔是什么辛辨? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任捕捂,我火速辦了婚禮,結(jié)果婚禮上斗搞,老公的妹妹穿的比我還像新娘指攒。我一直安慰自己,他們只是感情好僻焚,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布允悦。 她就那樣靜靜地躺著,像睡著了一般虑啤。 火紅的嫁衣襯著肌膚如雪隙弛。 梳的紋絲不亂的頭發(fā)上架馋,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機(jī)與錄音全闷,去河邊找鬼叉寂。 笑死,一個胖子當(dāng)著我的面吹牛总珠,可吹牛的內(nèi)容都是我干的屏鳍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼姚淆,長吁一口氣:“原來是場噩夢啊……” “哼孕蝉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤产捞,失蹤者是張志新(化名)和其女友劉穎蚤蔓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佳鳖,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年媒惕,在試婚紗的時候發(fā)現(xiàn)自己被綠了系吩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡妒蔚,死狀恐怖穿挨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肴盏,我是刑警寧澤科盛,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站菜皂,受9級特大地震影響贞绵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恍飘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一榨崩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧章母,春花似錦母蛛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春焦辅,著一層夾襖步出監(jiān)牢的瞬間博杖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工筷登, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留剃根,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓前方,卻偏偏與公主長得像狈醉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惠险,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

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