當前位置:
首頁 > 知識 > 通向架構師的道路(第二十天)萬能框架spring (二)

通向架構師的道路(第二十天)萬能框架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中的描述改成如下內容:





<dependency>

    <groupId>org.slf4j</groupId>


    <artifactId>slf4j-api</artifactId>


    <version>1.5.10</version>


</dependency>



第二步




增加兩個jar包





<dependency>


    <groupId>org.slf4j</groupId>


    <artifactId>slf4j-log4j12</artifactId>


    <version>1.5.10</version>


</dependency>


 


<dependency>


    <groupId>org.apache.ibatis</groupId>


    <artifactId>ibatis-core</artifactId>


    <version>3.0</version>


</dependency>




3.3 開始配置ibatis與spring結合




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





<bean id="iBatisSessionFactory" class="org.sky.ssi.ibatis.IBatis3SQLSessionFactoryBean" scope="singleton">


 


    <property name="configLocation" value="sqlmap.xml"></property>


 


    <property name="dataSource" ref="dataSource"></property>


 


</bean>


 


<bean id="iBatisDAOSupport" class="org.sky.ssi.ibatis.IBatisDAOSupport">


 


</bean>


 


<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">


 


    <property name="dataSource" ref="dataSource" />


 


</bean>




此處,我們需要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 <p>


 


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


 


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


 


 *  from spring configuration to generate SqlSessionFactory.


 


 * @author lifetragedy


 


 *


 


 */


 


public class IBatis3SQLSessionFactoryBean implements FactoryBean<SqlSessionFactory>, 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<SqlSessionFactory> 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


 


 * <p>


 


 * DAO implementation proxy.


 


 *


 


 * @author lifetragedy


 


 *


 


 * @param <T>


 


 */


 


public class IBatisDAOSupport<T> {


 


  


 


    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<T> clazz) {


 


        mapper = getSqlSession().getMapper(clazz);


 


        return mapper;


 


    }


 


 


 


    public T getMapper(Class<T> 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的文件,內容如下:





<?xml version="1.0" encoding="UTF-8"?>


 


<!DOCTYPE configuration PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN" "//ibatis.apache.org/dtd/ibatis-3-config.dtd">


 


<configuration>


 


    <mappers>


 


        <mapper resource="ibatis/index.xml" />


 


        <mapper resource="ibatis/login.xml" />


 


    </mappers>


 


</configuration>




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







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




原因很簡單:




在datasource.xml文件內我們已經通過





<bean id="iBatisSessionFactory" class="org.sky.ssi.ibatis.IBatis3SQLSessionFactoryBean" scope="singleton">


 


    <property name="configLocation" value="sqlmap.xml"></property>


 


    <property name="dataSource" ref="dataSource"></property>


 


</bean>




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




而我們的web.xml文件里有這麼一句:





<context-param>


 


    <param-name>contextConfigLocation</param-name>


 


    <param-value>/WEB-INF/classes/spring/**/*.xml</param-value>


 


</context-param>




因此如果我們再把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文件





<?xml version="1.0" encoding="UTF-8"?>


 


<!DOCTYPE mapper


 


PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"


 


"//ibatis.apache.org/dtd/ibatis-3-mapper.dtd">


 


<mapper namespace="org.sky.ssi.dao.LoginDAO">


 


    <select id="validLogin" resultType="int" parameterType="java.util.Map">


 


        <![CDATA[


 


        SELECT count(1) from t_login where login_id= #{loginId} and login_pwd=#{loginPwd}


 


        ]]>


 


    </select>


 


</mapper>




該DAO指向了一個介面org.sky.ssi.dao.LoginDAO,該dao接受一個sql,並且接受一個Map類型的參數。




那麼我們來看該DAO




LoginDao.java





package org.sky.ssi.dao;


 


import java.util.Map;


 


public interface LoginDAO {


 


    public int validLogin(Map<String, Object> paraMap) throws Exception;


 


}




LoginImpl.java





package org.sky.ssi.dao.impl;


 


 


import java.util.Map;


 


import org.apache.ibatis.session.SqlSession;


 


import org.sky.ssi.dao.LoginDAO;


 


import org.sky.ssi.ibatis.IBatisDAOSupport;


 


import org.springframework.stereotype.Repository;


  


 


@Repository


 


public class LoginDAOImpl extends IBatisDAOSupport<LoginDAO> implements LoginDAO {


 


    public int validLogin(Map<String, Object> paraMap) throws Exception {


 


        SqlSession session = this.getSqlSession();


 


        try {


 


            return this.getMapper(LoginDAO.class, session).validLogin(paraMap);


 


        } catch (Exception e) {


 


            log.error(e.getMessage(), e);


 


            throw new Exception(e);


 


        } finally {


 


            this.closeSqlSession(session);


 


        }


 


    }


 


}




很簡單吧,一切邏輯都在xml文件內。




一定記得不要忘了在finally塊中關閉相關的sql調用啊,要不然將來工程出了OOM的錯誤不要怪我啊.




3.5 index模塊




Index.xml文件





<?xml version="1.0" encoding="UTF-8"?>


 


<!DOCTYPE mapper


 


PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"


 


"//ibatis.apache.org/dtd/ibatis-3-mapper.dtd">


 


<mapper namespace="org.sky.ssi.dao.StudentDAO">


 


    <select id="getAllStudent" resultType="org.sky.ssi.dbo.StudentDBO">


 


        <![CDATA[


 


        SELECT student_no studentNo, student_name studentName from t_student


 


        ]]>


 


    </select>


 


 


 


    <update id="addStudent" parameterType="java.util.Map">


 


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


 


    </update>


 


    <update id="delStudent" parameterType="java.util.Map">


 


        delete from t_student where student_no=#{stdNo}


 


    </update>


 


</mapper>




它指向了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<StudentDBO> getAllStudent() throws Exception;


 


    public void addStudent(Map<String, Object> paraMap) throws Exception;


 


    public void delStudent(Map<String, Object> 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<StudentDAO> implements StudentDAO {


 


 


    @Override


 


    public List<StudentDBO> 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<String, Object> 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<String, Object> 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<String, Object> paraMap) throws Exception;這樣的結構,請大家注意。




四、beta工程中的增加功能




4.1 增加了一個filter




在我們的web.xml文件中





<filter>


 


    <filter-name>LoginFilter</filter-name>


 


    <filter-class>org.sky.ssi.filter.LoginFilter</filter-class>


 


    <init-param>


 


        <param-name>exclude</param-name>


 


        <param-value>/jsp/login/login.jsp,


 


                     /login.do


 


        </param-value>


 


    </init-param>


 


</filter>


 


<filter-mapping>


 


    <filter-name>LoginFilter</filter-name>


 


    <url-pattern>*.jsp</url-pattern>


 


</filter-mapping>


 


<filter-mapping>


 


    <filter-name>LoginFilter</filter-name>


 


    <url-pattern>/servlet/*</url-pattern>


 


</filter-mapping>


 


<filter-mapping>


 


    <filter-name>LoginFilter</filter-name>


 


    <url-pattern>*.do</url-pattern>


 


</filter-mapping>




有了這個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文件中





<bean


 


    id="methodLoggerAdvisor"


 


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


 


</bean>


 


<aop:config>


 


    <aop:aspect


 


        id="originalBeanAspect"


 


        ref="methodLoggerAdvisor" >


 


        <aop:pointcut


 


            id="loggerPointCut"


 


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


 


        <aop:around


 


            method="aroundAdvice"


 


            pointcut-ref="loggerPointCut" />


 


    </aop:aspect>


 


</aop:config>




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




五、測試我們的工程




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





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


 


  for (String s : stdNo) {


 


         Map<String, Object> paraMap = new HashMap<String, Object>();


 


         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的人終於被刪了,呵呵。




系列






  • 通向架構師的道路(第一天)之 Apache 整合 Tomcat



  • 通向架構師的道路(第二天)之 apache tomcat https 應用



  • 通向架構師的道路(第三天)之 apache 性能調優



  • 通向架構師的道路(第四天)之 Tomcat 性能調優



  • 通向架構師的道路(第五天)之 tomcat 集群 – 群貓亂舞



  • 通向架構師的道路(第六天)之漫談基於資料庫的許可權系統的設計



  • 通向架構師的道路(第七天)之漫談使用 ThreadLocal 改進你的層次的劃分



  • 通向架構師的道路(第八天)之 Weblogic 與 Apache 的整合與調優



  • 通向架構師的道路(第九天)之 Weblogic 的集群與配置



  • 通向架構師的道路 ( 第十天 ) 之 Axis2 Web Service ( 一 )



  • 通向架構師的道路 ( 第十一天 ) 之 Axis2 Web Service ( 二 )



  • 通向架構師的道路 ( 第十二天 ) 之 Axis2 Web Service ( 三 )



  • 通向架構師的道路 ( 第十三天 ) Axis2 Web Service 安全初步



  • 通向架構師的道路 ( 第十四天 ) Axis2 Web Service 安全之 rampart



  • 通向架構師的道路 ( 第十五天 ) IBM Websphere 的安裝與優化



  • 通向架構師的道路 ( 第十六天 ) IBM Websphere 與 IBM HttpServer 的集成



  • 通向架構師的道路 ( 第十七天 ) IBM Websphere 集群探秘 – WASND



  • 通向架構師的道路(第十八天)萬能框架 Spring ( 一 )




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


關注「ImportNew」,提升Java技能


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 ImportNew 的精彩文章:

演算法餘暉
如何編寫 bash completion script

TAG:ImportNew |