歡迎光臨
每天分享高質量文章

通向架構師的道路(第二十天)萬能框架spring (二)

(點選上方公眾號,可快速關註)


來源:袁鳴凱,

blog.csdn.net/lifetragedy/article/details/8122621

一、前言

上次講了Struts結合Spring並使用Spring的JdbcTemplate來搭建工程框架後我們面臨著jar庫無法管理,工程釋出不方便,jar包在工程內太佔空間,jar包衝突,管理,甚至漏包都問題。於是我們在講“萬能框架spring(二)”前,傳授了一篇番外篇,即講利用maven來管理我們的jar庫。

從今天開始我們將結合“萬能框架spring(一)”與番外篇maven來更進一步豐富我們的ssx框架,那麼今天講的是使用iBatis3結合SS來構建我們的ssi框架,我們把這個框架命名為beta吧。

二、SSI框架

還記得我們在第十八天中講到的我們的框架的架構圖嗎?上面這張是我們今天的架構圖,除了Struts,Spring層,我們需要變換的是DAO層即把原來的SQL這部分換成iBatis,我們在次使用的是iBatis版本3。

由於我們在第十八天中已經說了這樣的一個框架的好處其中就有:

層中相關技術的替換不影響到其它層面

所以對於我們來說我們需要改動的程式碼只有datasource.xml與dao層的2個介面兩個類,那我們就一起來看看這個基於全註解的SSi框架是怎麼樣搭起來的吧。

三、搭建SSI框架

3.1建立工程

我們還是使用maven來建立我們的工程

建完後照著翻外篇《第十九天》中的“四、如何讓Maven構建的工程在eclipse裡跑起來”對工程進行設定。

3.2 增加iBatis3的jar相關包

開啟pom.xml

第一步

找到“slf4j”,將它在pom中的描述改成如下內容:

    org.slf4j

    slf4j-api

    1.5.10

第二步

增加兩個jar包

    org.slf4j

    slf4j-log4j12

    1.5.10

 

    org.apache.ibatis

    ibatis-core

    3.0

3.3 開始配置ibatis與spring結合

開啟/src/main/resources/spring/datasource下的datasource.xml,增加如下幾行

 

   

 

   

 

 

 

 

 

   

 

此處,我們需要4個類,它們是:

org.sky.ssi.ibatis.IBatis3SQLSessionFactoryBean類

package org.sky.ssi.ibatis;

  

import java.io.IOException;

import java.io.Reader;

import javax.sql.DataSource;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;

import org.apache.ibatis.io.Resources;

import org.apache.ibatis.mapping.Environment;

import org.apache.ibatis.session.Configuration;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

import org.springframework.beans.factory.FactoryBean;

import org.springframework.beans.factory.InitializingBean;

import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;

 

 

/**

 

 *

 

 * IBatis3SQLSessionFactoryBean is responsible for integrating iBatis 3

 

 * with spring 3. Since all environment configurations have been moved to

 

 * spring, this class takes the responsibility to get environment information

 

 *  from spring configuration to generate SqlSessionFactory.

 

 * @author lifetragedy

 

 *

 

 */

 

public class IBatis3SQLSessionFactoryBean implements FactoryBean, InitializingBean{

 

    rivate String configLocation;   

 

    private DataSource dataSource;   

 

    private SqlSessionFactory sqlSessionFactory;   

 

    private boolean useTransactionAwareDataSource = true;   

 

    private String environmentId = “development”;

 

    public String getConfigLocation() {

 

        return configLocation;

 

    }

 

    public void setConfigLocation(String configLocation) {

 

        this.configLocation = configLocation;

 

    }

 

    public DataSource getDataSource() {

 

        return dataSource;

 

    }

 

    public void setDataSource(DataSource dataSource) {

 

        this.dataSource = dataSource;

 

    }

 

    public SqlSessionFactory getSqlSessionFactory() {

 

        return sqlSessionFactory;

 

    }

 

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {

 

        this.sqlSessionFactory = sqlSessionFactory;

 

    }

 

    public boolean isUseTransactionAwareDataSource() {

 

        return useTransactionAwareDataSource;

 

    }

 

    public void setUseTransactionAwareDataSource(

 

        boolean useTransactionAwareDataSource) {

 

            this.useTransactionAwareDataSource = useTransactionAwareDataSource;

 

    }

 

    public String getEnvironmentId() {

 

        return environmentId;

 

    }

 

    public void setEnvironmentId(String environmentId) {

 

        this.environmentId = environmentId;

 

    }

 

    

 

    public SqlSessionFactory getObject() throws Exception {   

 

        return this.sqlSessionFactory;   

 

    }   

 

 

    public Class getObjectType() {   

 

        return  SqlSessionFactory.class;   

 

    }

 

    

 

    public boolean isSingleton() {   

 

        return true;   

 

    }   

 

 

    public void afterPropertiesSet() throws Exception {   

 

        this.sqlSessionFactory = this.buildSqlSessionFactory(configLocation);   

 

    }

 

   

    protected SqlSessionFactory buildSqlSessionFactory(String configLocation)   

 

    throws IOException {   

 

    if (configLocation == null) {   

 

        throw new IllegalArgumentException(   

 

        “configLocation entry is required”);   

 

    }   

 

    DataSource dataSourceToUse = this.dataSource;   

 

    if (this.useTransactionAwareDataSource  && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) {   

 

        dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);   

 

    }   

 

 

    Environment environment = new Environment(environmentId, new IBatisTransactionFactory(dataSourceToUse), dataSourceToUse);   

 

    Reader reader = Resources.getResourceAsReader(configLocation);   

 

    XMLConfigBuilder parser = new XMLConfigBuilder(reader, null, null);   

 

    Configuration config = parser.parse();   

 

    config.setEnvironment(environment);   

 

    return new DefaultSqlSessionFactory(config);   

 

    }

 

}

org.sky.ssi.ibatis.IBatisDAOSupport

package org.sky.ssi.ibatis;

 

import javax.annotation.Resource;

import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.log4j.Logger;

 

 

/**

 

 * Base class for all DAO class. The subclass extends this class to get

 

 *

 

 * DAO implementation proxy.

 

 *

 

 * @author lifetragedy

 

 *

 

 * @param

 

 */

 

public class IBatisDAOSupport {

 

  

 

    protected Logger log = Logger.getLogger(this.getClass());

 

    @Resource

 

    private SqlSessionFactory ibatisSessionFactory;

 

    private T mapper;

 

    public SqlSessionFactory getSessionFactory() {

 

       return ibatisSessionFactory;

 

    }

 

 

 

    protected SqlSession getSqlSession() {

 

        return ibatisSessionFactory.openSession();

 

    }

 

 

 

    public T getMapper(Class clazz) {

 

        mapper = getSqlSession().getMapper(clazz);

 

        return mapper;

 

    }

 

 

 

    public T getMapper(Class clazz, SqlSession session) {

 

        mapper = session.getMapper(clazz);

 

        return mapper;

 

    }

 

 

 

    /**

 

     * close SqlSession

 

     */

 

    protected void closeSqlSession(SqlSession sqlSession) throws Exception {

 

        try {

 

                if (sqlSession != null) {

 

                    sqlSession.close();

 

                    sqlSession = null;

 

                }

 

        } catch (Exception e) {

 

        }

 

    }

 

}

org.sky.ssi.ibatis.IBatisTransaction

package org.sky.ssi.ibatis;

 

import java.sql.Connection;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.ibatis.transaction.Transaction;

import org.springframework.jdbc.datasource.DataSourceUtils;

 

public class IBatisTransaction implements Transaction{

 

    private DataSource dataSource;

 

    private Connection connection;

 

    public IBatisTransaction(DataSource dataSource, Connection con, boolean autoCommit){

 

        this.dataSource = dataSource;

 

        this.connection = con;

 

    }

 

 

 

    public Connection getConnection(){

 

        eturn connection;

 

    }

 

  

 

    public void commit()

 

        throws SQLException{                        }

 

  

 

    public void rollback()

 

        throws SQLException{                        }

 

  

 

    public void close()

 

        throws SQLException{

 

            if(dataSource != null && connection != null){

 

            DataSourceUtils.releaseConnection(connection, dataSource);

 

            }

 

    }

 

}

org.sky.ssi.ibatis.IBatisTransactionFactory

package org.sky.ssi.ibatis;

 

import java.sql.Connection;

import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.transaction.Transaction;

import org.apache.ibatis.transaction.TransactionFactory;

 

public class IBatisTransactionFactory implements TransactionFactory{

                      

 

     private DataSource dataSource;

 

   

     public IBatisTransactionFactory(DataSource dataSource){

 

        this.dataSource = dataSource;

 

     }

 

      

 

     public void setProperties(Properties properties){      }

 

      

 

     public Transaction newTransaction(Connection connection, boolean flag){

 

        return new IBatisTransaction(dataSource,connection,flag);

 

     } 

 

}

此三個類的作用就是在datasource.xml檔案中描述的,把spring與datasource.xml中的datasource和transaction連線起來,此處尤其是“IBatis3SQLSessionFactoryBean”的寫法,它透過spring中的“註入”特性,把iBatis的配置註入進spring並委託spring的context來管理iBatis(此屬網上沒有的資料,全部為本人在歷年工程中的經驗總結,並且已經在至少3個專案中進行了整合使用與相關測試)。

建立iBatis配置檔案

我們先在/src/main/resources目錄下建立一個叫sqlmap.xml的檔案,內容如下:

 


 

 

   

 

       

 

       

 

   

 

然後我們在/src/main/resources 目錄下建立index.xml與login.xml這2個xml檔案。

看到這兒,有人會問了:為什麼不把這兩個xml檔案也建立在spring目錄下?

原因很簡單:

在datasource.xml檔案內我們已經透過

 

   

 

   

 

這樣的方式把iBatis委託給了spring,iBatis的核心就是這個sqlmap.xml檔案了,而在這個sqlmap.xml檔案已經取用了login.xml與index.xml檔案了。

而我們的web.xml檔案裡有這麼一句:

 

    contextConfigLocation

 

    /WEB-INF/classes/spring/**/*.xml

 

因此如果我們再把ibatis/index.xml與ibatis/login.xml再建立到src/main/resources/spring目錄下,spring於是會在容器啟動時試圖載入這兩個xml檔案,然後一看這兩個xml檔案不是什麼spring的bean,直接拋錯,對吧?

其們等一會再來看login.xml檔案與index.xml檔案,我們先來搞懂iBatis呼叫原理.

3.4 iBatis呼叫原理

1)iBatis就是一個dao層,它又被稱為sqlmapping,它的sql是書寫在一個.xml檔案內的,在該xml檔案內會將相關的sql系結到相關的dao類的方法。

2)在呼叫結束時我們需要在finally塊中關閉相關的sql呼叫。

我們來看一個例子。

login.xml檔案

 

/span>

 

PUBLIC “-//ibatis.apache.org//DTD Mapper 3.0//EN”

 

“http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd”>

 

 

   

 

       

 

        SELECT student_no studentNo, student_name studentName from t_student

 

        ]]>

 

   

 

 

 

   

 

        insert into t_student(student_no, student_name)values(seq_student_no.nextval,#{stdName})

 

   

 

   

 

        delete from t_student where student_no=#{stdNo}

 

   

 

它指向了StudentDAO這個介面

StudentDAO.java

package org.sky.ssi.dao;

 

 

import org.sky.ssi.dbo.StudentDBO;

 

import org.sky.ssi.student.form.*;

 

import java.util.*;

 

 

public interface StudentDAO {

 

 

    public List getAllStudent() throws Exception;

 

    public void addStudent(Map paraMap) throws Exception;

 

    public void delStudent(Map paraMap) throws Exception;

 

}

StudentDAOImpl.java

package org.sky.ssi.dao.impl;

 

import java.util.List;

 

import java.util.Map;

 

import org.apache.commons.logging.Log;

 

import org.apache.commons.logging.LogFactory;

 

import org.apache.ibatis.session.SqlSession;

 

import org.sky.ssi.dao.StudentDAO;

 

import org.sky.ssi.ibatis.IBatisDAOSupport;

 

import org.sky.ssi.dbo.StudentDBO;

  

import org.springframework.stereotype.Repository;

 

 

@Repository

 

public class StudentDAOImpl extends IBatisDAOSupport implements StudentDAO {

 

 

    @Override

 

    public List getAllStudent() throws Exception {

 

        SqlSession session = this.getSqlSession();

 

        try {

 

            return this.getMapper(StudentDAO.class, session).getAllStudent();

 

        } catch (Exception e) {

 

            throw new Exception(e);

 

        } finally {

 

            this.closeSqlSession(session);

 

        }

 

    }

 

 

 

    public void addStudent(Map paraMap) throws Exception {

 

        SqlSession session = this.getSqlSession();

 

        try {

 

            this.getMapper(StudentDAO.class, session).addStudent(paraMap);

 

        } catch (Exception e) {

 

            throw new Exception(e);

 

        } finally {

 

            this.closeSqlSession(session);

 

        }

 

    }

 

 

 

    public void delStudent(Map paraMap) throws Exception {

 

        SqlSession session = this.getSqlSession();

 

        try {

 

            this.getMapper(StudentDAO.class, session).delStudent(paraMap);

 

        } catch (Exception e) {

 

            throw new Exception(e);

 

        } finally {

 

            this.closeSqlSession(session);

 

        }

 

    }

 

}

3.6 Service介面微微有些改變

為了演示給大家看 iBatis接受多個引數的例子因此我們把原來的如:login(String loginId, String loginPwd)這樣的方法改成了public int validLogin(Map paraMap) throws Exception;這樣的結構,請大家註意。

四、beta工程中的增加功能

4.1 增加了一個filter

在我們的web.xml檔案中

 

    LoginFilter

 

    org.sky.ssi.filter.LoginFilter

 

   

 

        exclude

 

        /jsp/login/login.jsp,

 

                     /login.do

 

       

 

   

 

 

 

    LoginFilter

 

    *.jsp

 

 

 

    LoginFilter

 

    /servlet/*

 

 

 

    LoginFilter

 

    *.do

 

有了這個filter我們就不用在我們的web工程中每一個action、每 個jsp裡進行“使用者是否登入”的判斷了,它會自動根據配置除去“exclude”中的相關web resource,全部走這個“是否登入”的判斷。

註意此處這個exclude是筆者自己寫的,為什麼要exclude?

如果你不exclude,試想一個使用者在login.jsp中填入相關的登入資訊後點一下login按鈕跳轉到了login.do,而這兩個web resource由於沒有被“排除”出“需要登入校驗”,因此每次你一呼叫login.jsp, login.do這個filter就都會強制要求你再跳轉到login.jsp,那麼我們一個使用者從login.jsp登入完後再跳回login.jsp再跳回,再跳回,如此重覆,進入死迴圈。

4.2 增加了一個自動記錄異常的日誌功能

在我們的applicationContext.xml檔案中

 

    id=”methodLoggerAdvisor”

 

    class=”org.sky.ssi.util.LoggerAdvice” >

 

 

 

   

 

        id=”originalBeanAspect”

 

        ref=”methodLoggerAdvisor” >

 

       

 

            id=”loggerPointCut”

 

            expression=”execution(* org.sky.ssi.service.impl.*.*(..))” />

 

       

 

            method=”aroundAdvice”

 

            pointcut-ref=”loggerPointCut” />

 

   

 

這樣,我們的dao層、service層、有錯就只管往外throw,框架一方面在接到相關的exception會進行資料庫事務的自動回滾外,還會自動把service層丟擲的exception記錄在log檔案中。

五、測試我們的工程

確認我們的StudentServiceImpl中刪除學生的delStudent方法內容如下:

public void delStudent(String[] stdNo) throws Exception {

 

  for (String s : stdNo) {

 

         Map paraMap = new HashMap();

 

         paraMap.put(“stdNo”, s);

 

         studentDAO.delStudent(paraMap);

 

         throw new Exception(“force system to throw a exception”);

 

  }

 

}

我們把beta工程新增入我們在eclipse中配好的j2eeserver中去並啟動起來。

在IE中輸入:http://localhost:8080/beta/index.do。 系統直接跳到login介面

我們輸入相關的使用者名稱寫密碼。

我們選中“13號學生高樂高”與“9號學生”,點“deletestudent”按鈕。

後臺拋錯了,檢視資料庫內的資料

資料還在,說明我們的iBatis的事務已經在spring中啟作用了.

再次更改StudentServiceImpl.java類中的delStudent方法,把“throw new Exception(“force system to throw a exception”);”註釋掉,再來執行

我們再次選 中9號和13號學生,點deletestudent按鈕,刪除成功,這個夠13的人終於被刪了,呵呵。

系列


看完本文有收穫?請轉發分享給更多人

關註「ImportNew」,提升Java技能

贊(0)

分享創造快樂