通向架构师的道路(第二十天)万能框架spring (二)

ImportNew2018-03-16 17:58:24

(点击上方公众号,可快速关注)


来源:袁鸣凯,

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" "http://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"

 

"http://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"

 

"http://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技能

阅读原文

TAGS:DAO 层xml 文件工程文件