wb.ouyang

毕竟几人真得鹿,不知终日梦为鱼

导航

模拟spring的IoC

1、新建一个web项目,jdk版本为1.8.0_111,使用 Jsp + Servlet + Model 实现MVC模式,并使用BeanFactory工厂 + xml配置文件 + 反射 来解耦合

  整个web项目的结构如下图:

  

  1.1、编写BeanFactory工厂

package com.oy;

import java.util.HashMap;
import java.util.Map;

public class BeanFactory {

    private static BeanFactory beanFactory = new BeanFactory();

    // 用于封装bean实例
    private Map<String, Object> map = new HashMap<>();

    private BeanFactory() {}

    public static BeanFactory getInstance() {
        return beanFactory;
    }

    /**
     * 获取参数name指定的实例,而且每次调用返回的是同一个实例。
     * @param name
     * @return
     */
    public Object getBean(String name) {
        Bean bean = (Bean) map.get(name);
        if (bean == null) {
            // System.out.println("map.get(" + name + "),返回null");
            return null;
        }
        return bean.getBeanInstance();
    }

    /**
     * 根据类名反射创建一个新的实例
     * @param name
     * @return
     */
    public Object getNewBean(String name) {
        Bean bean = (Bean) map.get(name);
        try {
            return Class.forName(bean.getbeanClassName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    public Map<String, Object> getMap() {
        return map;
    }
}
package com.oy;

public class Bean {
    private String beanName;
    private String beanClassName;
    private Object beanInstance;

    public String getBeanName() {
        return beanName;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    public String getbeanClassName() {
        return beanClassName;
    }

    public void setbeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
    }

    public Object getBeanInstance() {
        return beanInstance;
    }

    public void setBeanInstance(Object beanInstance) {
        this.beanInstance = beanInstance;
    }

    @Override
    public String toString() {
        return "Bean [beanName=" + beanName + ", beanClassName=" + beanClassName + ", beanInstance=" + beanInstance
                + "]";
    }
}

 

  1.2、为了在服务器启动时就创建bean实例,写一个监听器 BeanFactoryListener implements ServletContextListener

package com.oy;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class BeanFactoryListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent sce) {
        ServletContext sc = sce.getServletContext();
        if (sc.getAttribute("beanFactory") != null) {
            throw new RuntimeException("beanFactory已经存在!");
        }
        BeanFactory beanFactory = BeanFactory.getInstance();

        // 把所有bean加载进beanFactory
        loadBeans(sc, beanFactory);

        // 把beanFactory添加到ServletContext容器中
        // System.out.println("把beanFactory添加到ServletContext容器中");
        // System.out.println(beanFactory.getMap());
        sc.setAttribute("beanFactory", beanFactory);
        // System.out.println(sc.getAttribute("beanFactory"));
    }

    private void loadBeans(ServletContext sc, BeanFactory beanFactory) {
        String contextConfigLocation = sc.getInitParameter("contextConfigLocation");
        String config = contextConfigLocation.split(":")[1];
        InputStream inputStream = BeanFactoryListener.class.getClassLoader().getResourceAsStream(config);

        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(inputStream);
            Element root = document.getRootElement();
            @SuppressWarnings("unchecked")
            List<Element> list = root.elements();

            // 只有map指向beanFactory里面的map,这样每次读一个<bean>就加载一个bean;
            // 然后,后面加载的bean就可以使用前面已经加载的bean
            beanFactory.setMap(new HashMap<String, Object>());
            Map<String, Object> map = beanFactory.getMap();

            Bean bean = null;
            for (Element e : list) {
                String beanName = e.attributeValue("id");
                String beanClassName = e.attributeValue("class");
                bean = new Bean();
                bean.setBeanName(beanName);
                bean.setbeanClassName(beanClassName);
                bean.setBeanInstance(Class.forName(beanClassName).newInstance());
                map.put(beanName, bean);
            }
            // 如果等所有bean都添加进map后再把map设置给beanFactory,会造成后面加载的bean无法使用前面加载的bean
            // beanFactory.setMap(map);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

  1.3、beans.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="bookDao" class="com.oy.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.oy.service.impl.BookServiceImpl"/>
</beans>

 

  1.4、web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
    
    <!-- 监听器:初始化beanFactory -->
    <listener>
        <listener-class>com.oy.BeanFactoryListener</listener-class>
    </listener>
    
    <!-- beanFactory的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:beans.xml</param-value>
    </context-param>
    
    <servlet>
        <servlet-name>BookServlet</servlet-name>
        <servlet-class>com.oy.servlet.BookServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>BookServlet</servlet-name>
        <url-pattern>/book</url-pattern>
    </servlet-mapping>
</web-app>

 

2、编写web、service和dao层代码进行测试:

  BookServlet类:

package com.oy.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.oy.BeanFactory;
import com.oy.entity.Book;
import com.oy.service.BookService;

public class BookServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private BookService bookService = (BookService) BeanFactory.getInstance().getBean("bookService");

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String method = (String) request.getParameter("method");
        if ("add".equals(method)) {
            add(request, response);
        }
    }

    public void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("调用servlet层的add()方法。。。");
        Book book = new Book();
        book.setName((String) request.getParameter("name"));
        bookService.add(book);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

 

  service层接口和实现类:

package com.oy.service;

import com.oy.entity.Book;

public interface BookService {
    void add(Book book);
}
package com.oy.service.impl;

import com.oy.BeanFactory;
import com.oy.dao.BookDao;
import com.oy.entity.Book;
import com.oy.service.BookService;

public class BookServiceImpl implements BookService {
    private BookDao bookDao = (BookDao) BeanFactory.getInstance().getBean("bookDao");

    public void add(Book book) {
        System.out.println("调用service层的add()方法。。。");
        if (bookDao != null) {
            bookDao.add(book);
        } else {
            System.out.println("bookDao为null");
        }
    }
}

 

  dao层接口和实现类:

package com.oy.dao;

import com.oy.entity.Book;

public interface BookDao {
    void add(Book book);
}
package com.oy.dao.impl;

import java.util.ArrayList;
import java.util.List;

import com.oy.dao.BookDao;
import com.oy.entity.Book;

public class BookDaoImpl implements BookDao {
    private List<Book> bookList = new ArrayList<>();

    public void add(Book book) {
        System.out.println("调用dao层的add()方法。。。");
        int size = bookList.size();
        book.setId(size + 1);
        bookList.add(book);
        System.out.println("bookList: " + bookList);
    }
}

 

3、项目部署及测试

  3.1、服务器:apache-tomcat-9.0.13。

  3.2、浏览器输入 http://localhost:8080/05_book/book?method=add&name=张三,回车后,再输入 http://localhost:8080/05_book/book?method=add&name=李四,回车。

    控制台打印结果:

    调用servlet层的add()方法。。。
    调用service层的add()方法。。。
    调用dao层的add()方法。。。
    bookList: [Book [id=1, name=张三]]
    调用servlet层的add()方法。。。
    调用service层的add()方法。。。
    调用dao层的add()方法。。。
    bookList: [Book [id=1, name=张三], Book [id=2, name=李四]]

 

 4、注意事项

  根据beans.xml配置文件创建bean实例时,创建顺序是从上到下。如果改变<bean>的顺序,比如像下面这样:

    <beans>
        <bean id="bookService" class="com.oy.service.impl.BookServiceImpl"/>
        <bean id="bookDao" class="com.oy.dao.impl.BookDaoImpl"/>
    </beans>

  则创建bookService实例时依赖注入失败,因为其依赖一个bookDao实例,但是此时容器中还没有bookDao实例。

posted on 2019-03-13 16:35  wenbin_ouyang  阅读(183)  评论(0)    收藏  举报