xml way to customize the implementation of the Ioc container

@[TOC]

xml way to customize the implementation of the Ioc container

Use xml to implement a custom simple Ioc container

foreword

In the usual development process, we all use Spring for development. The Ioc container at the core of Spring helps us to create objects. This process is called inversion of control, also called Ioc.
When an object is instantiated, an attribute of an object type is used in the object, and the process of injecting the object into the instantiated object by the container is called Dependency Injection for short DI

Ioc and DI are talking about the same thing, the focus is different, IOC is to create objects from the perspective of the container, DI is from the perspective of use, injecting the use of objects;

When there is no IOC container

Mock Bank Transfer Example

Transfer interface

public interface AccountDao {

    Account queryAccountByCardNo(String cardNo) throws Exception;

    int updateAccountByCardNo(Account account) throws Exception;
}

interface implementation class

public class JdbcAccountDaoImpl implements AccountDao {

    public void init() {
        System.out.println("initialization method.....");
    }

    public void destory() {
        System.out.println("Destruction method......");
    }

    @Override
    public Account queryAccountByCardNo(String cardNo) throws Exception {
        //Get connection from connection pool
        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 {

        // Get connection from connection pool
        // Transformed to: Get the bound connection connection from the current thread
        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;
    }
}

business interface

public interface TransferService {

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

Implementation class

public class TransferServiceImpl implements TransferService {

    // 1 The original new method creates a dao interface implementation class object
   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 layer

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

    // 1. Instantiate the service layer object
    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 {

        // Set the character encoding of the request body
        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. Call the service layer method
            transferService.transfer(fromCardNo,toCardNo,money);
            result.setStatus("200");
        } catch (Exception e) {
            e.printStackTrace();
            result.setStatus("201");
            result.setMessage(e.toString());
        }

        // response
        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().print(JsonUtils.object2Json(result));
    }
}

It can be seen that the object of the new business layer in the controller layer, the implementation class of the dao layer has been new in the object of the new business layer

The business layer and the dao layer are connected by the new keyword, and the coupling is high

In the case of using the Ioc container

As you can see from the figure, the example of the AB object mentioned in the Ioc container is stored, and when the main function uses the AB object, it is directly obtained in the Ioc container;

Based on this understanding, we can implement our own Ioc container;

First of all, we create our own xml to configure our own bean s that need some examples, that is, objects

<?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>

Create a dao layer implementation class and a business layer implementation class, and obtain it from the container through its own Id;

Create BeanFactorys classes to parse xml and instantiate configured objects

/**
 * Factory Bean
 */
public class BeanFactorys {

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

    static {
        // 1 Read and parse beans.xml through reflection technology, produce bean objects, and store them in the map

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

        //get a document object
        try {
            Document read = new SAXReader().read(resourceAsStream);
            //get the object
            Element rootElement = read.getRootElement();

            /**
             * xpath expression usage
             *   // Selects nodes in the document from the current node that matches the selection, regardless of their position
             *   / Get from the root node
             *  . select current node
             *  .. Select the parent node of the current node
             *  @ select properties
             *
             */
            // //Indicates reading the bean tag at any location
            List<Element> list = rootElement.selectNodes("//bean");

            if (Objects.isNull(list) || list.size() == 0) {
                throw new RuntimeException("nothing bean Label");
            }

            list.forEach(x -> {
                //getId
                String id = x.attributeValue("id"); //accountDao
                //Get permission to name
                String clasz = x.attributeValue("class"); //com.udeam.edu.dao.impl.JdbcAccountDaoImpl
                System.out.println(id + " ---> " + clasz);
                //Create objects with reflection
                try {
                    Object o = Class.forName(clasz).newInstance();
                    //Stored in the ioc container
                    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 Provide external access to the sample object interface


    }

    /**
     * Provide external access to bean interface
     *
     * @param id
     * @return
     */
    public static Object getBean(String id) {
        return iocMap.get(id);
    }
}

It can be seen that when the class is loaded, the class permission name of the bean is obtained by parsing the xml, and the object is created by reflection, stored in the map, and the id is used as the key;

Then we need to replace the object created by the new keyword in the implementation class and the control layer

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

But this implementation is still a bit inelegant, there is still = connection;

remove the = sign

private AccountDao accountDao

 accountDao.queryAccountByCardNo(fromCardNo);

At this time, there is no assignment operation, and there will be a null pointer exception by default here;
To do this, we need to set the value, which can be set through set, construction parameters, etc.;

The value can be set in xml using the attribute set

Define an object attribute property in xml to set the name name and ref to refer to the object

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

Add the parsing method to the BeanFactorys just now

After parsing the xml and instantiating the object to the Ioc container, parse the property attribute

   //Get all properties properties and set set value
            List<Element> prList = rootElement.selectNodes("//property");

            prList.forEach(y -> {
                //Get property attribute name value
                String name = y.attributeValue("name"); //   <property name="setAccountDao" ref = "accountDao"></property>
                String ref = y.attributeValue("ref");
                //Get parent node id
                Element parent = y.getParent();
                //Get parent node id
                String id = parent.attributeValue("id");
                //Maintain object dependencies
                Object o = iocMap.get(id);
                //find all ways
                Method[] methods = o.getClass().getMethods();
                for (int i = 0; i < methods.length; i++) {
                    //The method is the opposite of set property
                    if (methods[i].getName().equalsIgnoreCase("set" + name)) {
                        try {
                            //set set object
                            methods[i].invoke(o, iocMap.get(ref));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }

                        //reassign after set
                        iocMap.put(id,o);
                    }


                }


            });

Then add a set method to the TransferServiceImpl class to set the value we need to set

  private AccountDao accountDao;

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

Get the set method from xml, then use the property attribute name value obtained by set + to judge, and then reflect the set value

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

                        //reassign after set
                        iocMap.put(id,o);
                    }

This process can be seen as a simple set injection method, similar to the set injection in Spring;

What problem does IoC solve

IoC solves the coupling problem between objects

We don't have to go to the new object by ourselves, but the IoC container (Spring framework) to help us instantiate the object
image and manage it, which object we need to use, just ask the IoC container for it

Inversion of Control

Control: refers to the right to create (instantiate, manage) objects
Inversion: control is given to the external environment (spring framework, IoC container)

dependencies used

  <!-- mysql database driver package -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.35</version>
    </dependency>
    <!--druid connection pool-->
    <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 rely -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.6</version>
    </dependency>

    <!--dom4j rely-->
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    <!--xpath Expression-dependent for fast positioning xml element-->
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>1.1.6</version>
    </dependency>

code

portal

Tags: Java Spring Back-end

Posted by roydukkey on Sat, 07 May 2022 07:32:39 +0300