設計模式的應用-工廠方法實現3層模型解耦

在web應用中常見的3層架構:控制層(Controller)、業務邏輯(Service)、數據持久層(DAO),控制層調用業務邏輯層,業務邏輯層調用數據層。web

這樣:sql

3層架構)數據庫

解耦,變成這樣:緩存

解耦後的3層架構

首先新建DAO工廠類dao.factory.DaoFactory, 用於建立DAO對象:安全

/**
 * dao工廠
 * 
 * 注意:工廠類會在類加載的時候爲每種DAO只會建立一個dao對象,因此DAO實現類中
 *       不能經過對象包含數據庫鏈接,應該經過ThreadLoacl<Connection>實現每線程一個鏈接
 * @author
 *
 */

public class DaoFactory {

    /* 保存全部的Dao對象 */
    private static final HashMap<Class<?>,Object> DAO_MAP = new HashMap<>();
    
    static {
        try {
            init();
        } catch(Exception e) {
            // 日誌記錄
        }
    }

    private static void init() {
        DAO_MAP.put(ManagerDao.class, new ManagerDaoImpl());
        DAO_MAP.put(OrderDao.class, new OrderDaoImpl());
        DAO_MAP.put(SchoolDao.class, new SchoolDaoImpl());
        DAO_MAP.put(UserDao.class, new UserDaoImpl());
    }

    
    @SuppressWarnings("unchecked")
    public static<T> T getDao(Class<T> clazz) {
        // 注:clazz類對應的對象不存在,應該拋出異常
        return (T) DAO_MAP.get(clazz);
    }
}

而後在業務邏輯層實現類中,注入Dao對象,以下:mybatis

/**
 * 用戶服務類
 */
public class UserServiceImpl implements UserService {

    // 經過工廠方式注入
    private UserDao userDao = DaoFactory.getDao(UserDao.class);

    ...
}

遵循開閉原則,新建DAO實現類,只須要工修改廠類便可。多線程

注:能夠給DAO實現類添加自定義註解的方式,而後DAO工廠類經過反射,掃描出有指定註解的類,進行注入。架構

注意多線程問題:app

Dao層和Service層均爲「實際單例」緩存到map中,Service中持有dao成員變量,Servlet控制層持有service成員變量,因爲Dao的使用的數據庫鏈接來自於ThreadLocal變量,因此是線程安全的類,service只持有線程安全的類對象,因此也是線程安全的,同理上推。ide

然而,在我使用mybatis的過程當中,出現了錯誤!提示「Executor 已經關閉」,mybatis官方資料顯示mapper和sqlSession具備一樣的聲明週期,均是線程不安全的。此時對象之間的關係是Servlet包含service變量,service變量持有mapper對象,mapper對象對應的sqlSession會在一次請求後關閉(一個線程執行後), 此時再次執行會提示類時數據庫鏈接以及關閉的問題!!!

在過濾器中關閉的SqlSession:

/**
 * 系統服務過濾器
 * 過濾字符編碼
 * @author
 *
 */
public class OESServiceFilter implements Filter {

    @Override
    public void destroy() {
        
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        
        try{
            chain.doFilter(req, resp);
        }finally{
            // 關閉SqlSession
            MybatisUtil.closeSqlSession();
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        
    }

    
}
相關文章
相關標籤/搜索