自定义Mybatis框架(XML)

我们已经通过案例体验到了mybatis的魅力。现在我们来看它的测试类,有几个对象我们需要搞清楚他们的作用,进而需要理解mybatis的整个工作流程和执行原理。

  • Resources

加载配置文件,有一种是使用类加载进行加载,我们通过这个类的类加载器进行资源的加载。

  • SqlSessionFactoryBuilder

构建SqlSessionFactory工厂对象需要的对象。采用了构建者模式,屏蔽了对象构建的细节。

  • SqlSessionFactory

创建SqlSession对象所用。使用工厂模式创建,目的就是解耦合。

  • SqlSession

创建代理对象,调用接口里面的方法。使用了代理模式。 

下面我们就自己来手写mybatis框架,体验其工作原理。


1.1 流程分析

1.2 搭建环境 

拷贝上一个工程代码的依赖,去掉mybatis的依赖。

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.10</version>
    </dependency>
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.6.1</version>
    </dependency>
    <dependency>
        <groupId>jaxen</groupId>
        <artifactId>jaxen</artifactId>
        <version>1.1.6</version>
    </dependency>
</dependencies>

我们需要两个工具类

一个是XMLConfigBuilder类,主要是用来解析XMl配置文件。

第二个是DataSourceUtil类,主要是用来获取数据库连接对象。

第三个是Executor类,主要是用来获取数据结果集,封装我们想要的数据。

1.3 编码实现  

  •  编写资源加载类。使用类加载器加载配置资源。
public class Resources {
    //根据文件名称,加载类路径下面的配置文件
    public static InputStream getResourceAsStream(String filePath){
        return Resources.class.getClassLoader().getResourceAsStream(filePath);
    }
}
  • 编写SqlSessionFactoryBulider类

作用:加载配置资源,并将配置资源封装成Configuration对象,并且将该资源对象传到工厂对象中。

public class SqlSessionFactoryBuilder {

    /**
     * 构建SqlSessionFactory对象
     * @param in
     * @return
     */
    public SqlSessionFactory build(InputStream in){
        Configuration configuration = XMLConfigBuilder.loadConfiguration(in);
        return  new DefaultSqlSessionFactory(configuration);

    }
}

此时没有配置类?那我们就创建。  

  • 创建Configuration配置类
public class Configuration {

    private String  driver;
    private String url;
    private String username;
    private String password;

    private Map<String, Mapper> mappers = new HashMap<String, Mapper>();

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Map<String, Mapper> getMappers() {
        return mappers;
    }

    public void setMappers(Map<String, Mapper> maps) {
        mappers.putAll(maps);
    }

}

配置类里面的Mapper是用来干嘛的?前面我们分析过,Mapper是用来封装sql语句和查询结果集的实体全限定名的。

public class Mapper {

    private String queryString;
    private String resultType;

    public String getQueryString() {
        return queryString;
    }

    public void setQueryString(String queryString) {
        this.queryString = queryString;
    }

    public String getResultType() {
        return resultType;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }
}

也没有工厂?那我们就创建工厂。

  • 创建SqlSessionFactory接口
public interface SqlSessionFactory {

    //获取SQLSession对象
    public SqlSession openSession();
}

由于工厂的类型也可以多样化定义,所以我们把工厂定义为接口,以后想设计什么样的工厂,我们只需要实现这个接口就可以了。  

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private Configuration cfg;

    public DefaultSqlSessionFactory(Configuration cfg) {
        this.cfg = cfg;
    }

    /**
     * 获取一个SqlSession对象
     * @return
     */
    public SqlSession openSession() {
        return new DefaultSqlSession(cfg);
    }
}

这里设计构造函数的作用是把SqlSessionFactoryBuilder类里面已经加载好的Configuration对象传入DefaultSqlSessionFactory里面来。  

  • 定义SqlSession对象
public interface SqlSession {

    //获取代理对象
    public <T> T getMapper(Class<T> tClass);

     //释放资源
    void close();
}

为了提高这个方法的可重用性,这个方法定义为泛型方法。

由于这里面获取代理对象的方式有多种方式可以实现,所以SqlSession我们也可以定义为接口。以后你想用什么方式获取代理对象,只需要实现这个接口即可。

我们创建这个接口的实现类 DefaultSqlSession。

public class DefaultSqlSession implements SqlSession {

    private Configuration cfg;
    private Connection conn;

    public DefaultSqlSession(Configuration cgf){
        this.cfg = cgf;
        this.conn = DataSourceUtil.getConnection(cfg);
    }


    /*
    * 创建代理对象
    */
    public <T> T getMapper(Class<T> tClass) {
        return (T)Proxy.newProxyInstance(tClass.getClassLoader(),
                new Class[]{tClass},
                new ProxyFactory(cfg.getMappers(),conn));
    }

    public void close() {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

 getMapper是在产生代理对象。其中的第三个参数,这个ProxyFactory我们实现了InvocationHandler接口。目的就是为了对接口的方法进行增强!!!而我们之前分析的selectList就是增强的方法!!!然而Executor类里面的selectList方法执行需要两个参数。分别是Map<String,Mapper> mappers,和Connection conn。所以需要将这个两个参数准备好。而Map<String,Mapper> mappers是定义在Configuration类里面的。可以通过Configuration直接获取。而Configuration对象从SqlSessionFactoryBuilder就一直传递过来啦。所以我们只需要在DefaultSqlSession构造函数里面初始化连接对象即可。

 

  • 定义ProxyFactory类
public class ProxyFactory implements InvocationHandler {

    private Map<String, Mapper> mappers;
    private Connection conn;
    public ProxyFactory(Map<String, Mapper> mappers, Connection conn){
       this.mappers = mappers;
       this.conn = conn;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1.获取方法名
        String methodName = method.getName();
        //2.获取方法所在类的名称
        String className = method.getDeclaringClass().getName();
        //3.组合key
        String key = className+"."+methodName;
        //4.获取mappers中的Mapper对象
        Mapper mapper = mappers.get(key);
        //5.判断是否有mapper
        if(mapper == null){
            throw new IllegalArgumentException("传入的参数有误");
        }
        //6.调用工具类执行查询所有
        return new Executor().selectList(mapper,conn);
    }
}

 

1.4总结

下面我们通过一幅图来对上面的案例进行总结:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Austine Reaves

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值