@[TOC]

xml形式自定义实现Ioc容器

应用xml实现自定义简略的Ioc容器

前言

平时开发过程中,咱们都是应用Spring来进行开发,Spring外围的Ioc容器帮忙咱们去创建对象这一过程被称作管制反转也叫Ioc
在实例化一个对象时候,这个对象中用到一个对象类型的属性,容器把这个对象注入到实例化对象的过程被称作依赖注入简称DI

Ioc和DI说的是一个事件,针对的侧重点不同,IOC是站在容器角度创建对象,DI是站在应用的角度,注入应用对象;

没有IOC容器的时候

模仿银行转账例子

转账接口

public interface AccountDao {    Account queryAccountByCardNo(String cardNo) throws Exception;    int updateAccountByCardNo(Account account) throws Exception;}

接口实现类

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 {        // 从连接池获取连贯        // 革新为:从以后线程当中获取绑定的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;    }}

业务接口

public interface TransferService {    void transfer(String fromCardNo,String toCardNo,int money) throws Exception;}

实现类

public class TransferServiceImpl implements TransferService {    // 1 原始的new 办法创立dao接口实现类对象   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 {        // 设置申请体的字符编码        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. 调用service层办法            transferService.transfer(fromCardNo,toCardNo,money);            result.setStatus("200");        } catch (Exception e) {            e.printStackTrace();            result.setStatus("201");            result.setMessage(e.toString());        }        // 响应        resp.setContentType("application/json;charset=utf-8");        resp.getWriter().print(JsonUtils.object2Json(result));    }}

能够看进去在controller层 new业务层的对象,new 业务层的对象中曾经new进去dao层的实现类

业务层和dao层通过new关键字连接起来,耦合高

应用Ioc容器状况下

如图能够看到,Ioc容器讲过AB对象的示例存储起来,main函数应用AB对象的时候,间接在Ioc容器中获取;

基于这个了解,咱们能够实现本人的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>

创立dao层实现类和业务层实现类,并且过本人的Id从容器中获取;

创立BeanFactorys类来解析xml,实例化配置的对象

/** * 工厂Bean */public class BeanFactorys {    private final static Map<String, Object> iocMap = new HashMap<>();    static {        // 1 读取解析beans.xml  通过反射技术,生产bean对象,并将其存在map中        InputStream resourceAsStream = BeanFactorys.class.getClassLoader().getResourceAsStream("beans.xml");        //失去一个 文档对象        try {            Document read = new SAXReader().read(resourceAsStream);            //获取跟对象            Element rootElement = read.getRootElement();            /**             * xpath表达式 用法             *   // 从匹配抉择的以后节点抉择文档中的节点,而不思考他们的地位             *   / 从根节点获取             *  . 选取以后节点             *  .. 选取以后节点的父节点             *  @ 选取属性             *             */            // //示意读取任意地位的bean标签            List<Element> list = rootElement.selectNodes("//bean");            if (Objects.isNull(list) || list.size() == 0) {                throw new RuntimeException("无此bean标签");            }            list.forEach(x -> {                //获取Id                String id = x.attributeValue("id"); //accountDao                //获取权限定命名                String clasz = x.attributeValue("class"); //com.udeam.edu.dao.impl.JdbcAccountDaoImpl                System.out.println(id + " ---> " + clasz);                //通过反射创建对象                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权限定名,通过反射创建对象,存储在map中,id作为key;

而后咱们须要在实现类和管制层去替换这个new关键字创立的对象

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

然而这样子实现还是有点不优雅 还是有=连贯;

=号去掉

private AccountDao accountDao accountDao.queryAccountByCardNo(fromCardNo);

此时,没有赋值操作,这儿会默认null 空呈现空指针异样;
为此,咱们须要进行设置值,能够通过set,结构参数等设置值;

能够在xml中应用属性set设置值

在xml中定义一个 对象属性 property 设置名字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设置值            List<Element> prList = rootElement.selectNodes("//property");            prList.forEach(y -> {                //获取 property 属性name值                String name = y.attributeValue("name"); //   <property name="setAccountDao" ref = "accountDao"></property>                String ref = y.attributeValue("ref");                //获取父节点id                Element parent = y.getParent();                //获取父节点id                String id = parent.attributeValue("id");                //保护对象依赖关系                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设置对象                            methods[i].invoke(o, iocMap.get(ref));                        } catch (IllegalAccessException e) {                            e.printStackTrace();                        } catch (InvocationTargetException e) {                            e.printStackTrace();                        }                        //set之后从新赋值                        iocMap.put(id,o);                    }                }            });

而后TransferServiceImpl类中增加一个set办法设置咱们须要设置的值

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

xml上中获取这个set办法,而后利用set + 获取的property属性name值 进行判断,而后反射设置值

     if (methods[i].getName().equalsIgnoreCase("set" + name)) {                        try {                            //set设置对象                            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容器要即可

管制反转

管制:指的是对象创立(实例化、治理)的权力
反转:控制权交给外部环境了(spring框架、IoC容器)

用到的依赖

  <!-- mysql数据库驱动包 -->    <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表达式依赖 为了疾速定位xml元素-->    <dependency>      <groupId>jaxen</groupId>      <artifactId>jaxen</artifactId>      <version>1.1.6</version>    </dependency>

代码

传送门