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

@[TOC]

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

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

前言

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

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

沒有IOC容器的時候

在這里插入圖片描述

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

轉(zhuǎn)賬接口

public interface AccountDao {

    Account queryAccountByCardNo(String cardNo) throws Exception;

    int updateAccountByCardNo(Account account) throws Exception;
}

接口實現(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;
}

實現(xiàn)類

public class TransferServiceImpl implements TransferService {

    // 1 原始的new 方法創(chuàng)建dao接口實現(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. 實例化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層的實現(xiàn)類

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

在這里插入圖片描述

使用Ioc容器情況下

在這里插入圖片描述

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

基于這個理解,我們可以實現(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層實現(xiàn)類和業(yè)務(wù)層實現(xiàn)類,并且過自己的Id從容器中獲取;

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

/**
 * 工廠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é)點選擇文檔中的節(jié)點,而不考慮他們的位置
             *   / 從根節(jié)點獲取
             *  . 選取當(dāng)前節(jié)點
             *  .. 選取當(dāng)前節(jié)點的父節(jié)點
             *  @ 選取屬性
             *
             */
            // //表示讀取任意位置的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;

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

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

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

但是這樣子實現(xià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完 實例化對象到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é)點id
                Element parent = y.getParent();
                //獲取父節(jié)點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框架)去幫助我們實例化對
象并且管理它,我們需要使?哪個對象,去問IoC容器要即可

控制反轉(zhuǎn)

控制:指的是對象創(chuàng)建(實例化、管理)的權(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容