@[TOC]

XML custom implementation of Ioc container

Implement a custom simple Ioc container using XML

preface

In normal development, we use Spring for development. The Ioc container at the Spring core helps us create objects, a process called inversion of control or Ioc When an object is instantiated, it uses attributes of an object type. The process by which the container injects this object into the instantiated object is called DI

Ioc and DI are talking about the same thing. They focus on different things. Ioc creates objects from the perspective of containers, while DI injects objects from the perspective of uses.

When there is no IOC container

Simulated bank transfer example

Transfer the interface

public interface AccountDao {

    Account queryAccountByCardNo(String cardNo) throws Exception;

    int updateAccountByCardNo(Account account) throws Exception;
}

Copy the code

Interface implementation class

public class JdbcAccountDaoImpl implements AccountDao {

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

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

    @Override
    public Account queryAccountByCardNo(String cardNo) throws Exception {
        // Get the connection from the 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 the connection from the connection pool
        // Get the binding 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();
        returni; }}Copy the code

Business interface

public interface TransferService {

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

Copy the code

The implementation class

public class TransferServiceImpl implements TransferService {

    // 1 The original new method creates the 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); }}Copy the code

The 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 {

        // Sets 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)); }}Copy the code

You can see that in the Controller layer new business layer object, the new business layer object has been new out of the DAO layer implementation class

The business layer and the DAO layer are connected by the new keyword for high coupling

In the case of Ioc containers

As you can see, the Ioc container says to store the AB object sample. When the main function uses the AB object, it obtains it directly from the Ioc container.

With this understanding, we can implement our own Ioc container;

First, we’ll create our own XML to configure our own bean, which needs some examples, that is, objects


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

Copy the code

Create the DAO layer implementation class and the business layer implementation class, and get it from the container with its own Id;

Create a BeanFactorys class to parse the XML and instantiate the configured objects

/** * factory Bean */
public class BeanFactorys {

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

    static {
        // 1 Read and parse beans.xml using reflection techniques, produce bean objects and store them in a 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 * // Select nodes in the document from the current node selected by match, regardless of their position * / get * from the root node. Select the current node *.. Select the parent of the current node * @ Select properties * */
            // // means to read bean labels at any location
            List<Element> list = rootElement.selectNodes("//bean");

            if (Objects.isNull(list) || list.size() == 0) {
                throw new RuntimeException("No such bean label");
            }

            list.forEach(x -> {
                / / to get Id
                String id = x.attributeValue("id"); //accountDao
                // Get permission to set the name
                String clasz = x.attributeValue("class"); //com.udeam.edu.dao.impl.JdbcAccountDaoImpl
                System.out.println(id + "- >" + clasz);
                // Create objects by reflection
                try {
                    Object o = Class.forName(clasz).newInstance();
                    // Store in 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 Provides an interface to obtain sample objects externally


    }

    /** * provides the access bean interface **@param id
     * @return* /
    public static Object getBean(String id) {
        returniocMap.get(id); }}Copy the code

You can see that when the class is loaded, the class permission name of the bean is obtained by parsing the XML. The object is created by reflection and stored in the map with the ID as the key.

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

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

Copy the code
    private AccountDao accountDao = (AccountDao) BeanFactorys.getBean("accountDao");

Copy the code

But this implementation is still a little bit inelegant and it still has = join;

Get rid of the equals

private AccountDao accountDao

 accountDao.queryAccountByCardNo(fromCardNo);
Copy the code

At this point, there’s no assignment, there’s null and there’s a null pointer exception; To do this, we need to set the values, we can set the values through set, construct parameters and so on;

You can set values in XML using the attribute set

Define an object property in XML and set the name name and ref to reference the object

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

Copy the code

Add the parsing method to the BeanFactorys

After parsing the XML and instantiating the object into the Ioc container, parse the property attribute

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

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

                        // reassign after setiocMap.put(id,o); }}});Copy the code

The TransferServiceImpl class then adds a set method to set the value we need to set

  private AccountDao accountDao;

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

Get the set method in XML, then use the property property name value obtained by set + to determine, and then reflect the set value

     if (methods[i].getName().equalsIgnoreCase("set" + name)) {
                        try {
                            //set sets the 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);
                    }
Copy the code

This process can be thought of as a simple set injection, similar to set injection in Spring;

What problem has the IoC solved

IoC addresses the coupling problem between objects

Instead of going to the new object ourselves, the IoC container (the Spring framework) helps us instantiate the object and manage it. We just ask the IoC container which object we need to use

Inversion of control

Control: refers to the reversal of authority over object creation (instantiation, management) : 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 dependency -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.6</version>
    </dependency>

    <! - dom4j dependence - >
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    <! Xpath expressions rely on XML elements to locate quickly.
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>1.1.6</version>
    </dependency>

Copy the code

code

portal