造一个方形的轮子10--集成第三方

造一个方形轮子文章目录:造一个方形的轮子

01、解决遗留问题

这一篇没有太多的解决上一篇遗留的问题,静态文件暂时没有处理,继续算做遗留问题,处理了一下Square框架的依赖,修改了mysql数据库驱动的scope配置:

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
            <scope>provided</scope>
        </dependency>

02、集成Mybatis

集成第三方,找了一个比较常用的框架,持久层的Mybatis,起初觉得这东西用起来很方便,没有太细的考虑过,如果要集成他要怎么做,一上手写还是发现了很多问题。

集成的大体思路是:

1、添加square-mybatis项目,做成依赖,处理Mybatis的相关配置

2、demo项目添加Mybatis和square-mybatis依赖

3、添加mapper的xml配置及对应的接口

4、关联使Mybatis框架生效

这里有几个问题:

1、square-mybatis里的代码如果在square框架里加载(因为有Bean操作)

2、Mybatis的mapper.xml文件放在哪?如何加载?

3、打包后怎么保证Mybatis框架生效

这几个问题其实是做之前有大体思路,但做下来发现有些跟写之前想的确实不一样,下边我会尽量记录一下遇到的问题,及解决过程

03、square框架加载square-mybatis

最开始我在square框架里添加了一个@config 注解及一个SquareConfig.java,作用就是使用这个注解标记的 类同时继承SquareConfig接口,实现config()方法,会在square框架加载过程中处理依赖之前,执行这个config()方法,目的是把配置的Mybatis框架的Mapper注入到Bean容器中,好在接下来的initDI()方法中使用,保证依赖类的存在,但是写完后发现因为这个类在 square-mybatis项目里,是以依赖的形式提供给demo的,所以demo项目启动过程中扫描不到jar包里的文件。

所以最终将square-mybatis中的Conifg类实现成了静态方法,在demo中添加了InitConfig.java来实现调用功能,下边看代码:

square-mybatis项目的MybatisConfig.java:

package com.jisuye;
//import ...
/**
 * 集成Mybatis配置类
 * @author ixx
 * @date 2019-09-08
 */
public class MybatisConfig {
    private static Logger logger = LoggerFactory.getLogger(MybatisConfig.class);
    public static void init(){
        // 获取DataSource
        DataSource dataSource = DbUtil.getDataSource();
        // 使用代码构造Mybatis配置
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("development", transactionFactory, dataSource);
        Configuration configuration = new Configuration(environment);
        URL mapperUrl = ClassLoader.getSystemResource("mapper/");
        String path = mapperUrl.getPath();
        logger.info("mapperUrl path :{}", path);
        // 判断当前启动是在否jar包中
        if(path.indexOf("!/")>0){
            getXmlByJar(path, configuration);
        } else {
            getXmlByFile(path, configuration);
        }
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
        try {
            SqlSession session = sqlSessionFactory.openSession();
            // 循环所有Bean处理Config
            for(Class clzz : BeansMap.getClassList()) {
                Annotation[] annotations = clzz.getAnnotations();
                for (Annotation annotaion : annotations) {
                    // 如果是Mybatis的Mapper则注入到容器里
                    if(annotaion instanceof Mapper){
                        Object mapper = session.getMapper(clzz);
                        BeanObject tmpBeanObject = new BeanObject(clzz, mapper);
                        tmpBeanObject.setObject(mapper);
                        BeansMap.put(clzz.getName(), tmpBeanObject);
                    }
                }
            }
        } catch (Exception e){
            logger.error("init mybatis config error!!", e);
        }
    }

    /**
     * 本地调试获取xml配置文件
     * @param path
     * @param configuration
     */
    private static void getXmlByFile(String path, Configuration configuration){
        File mapperDir = null;
        try {
            mapperDir = new File(path);
            if(mapperDir.exists()){
                File[] files = mapperDir.listFiles();
                logger.info("files size :{}", files.length);
                for (File file : files) {
                    logger.info("file path:{}", file.getPath());
                    logger.info("file abspath:{}", file.getAbsolutePath());
                    try {
                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
                                new FileInputStream(file), configuration,
                                file.getPath(),
                                configuration.getSqlFragments());
                        xmlMapperBuilder.parse();
                    } catch (Exception e) {
                        e.printStackTrace(); // 出现错误抛出异常
                    }
                }
            }
        } catch (Exception e) {
            logger.error("load mapper xml file error!", e);
        }
    }

    /**
     * 处理jar包中的xml配置文件(打包后启动)
     */
    private static void getXmlByJar(String path, Configuration configuration){
        logger.info("xml get by path:{}", path);
        path = path.substring(0, path.indexOf("!/")).replace("file:", "");
        logger.info("xml get by jar path:{}", path);
        try {
            JarFile jarFile = new JarFile(path);
            Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
            while (jarEntryEnumeration.hasMoreElements()){
                JarEntry jarEntry = jarEntryEnumeration.nextElement();
                String name = jarEntry.getName();
                // logger.info("file name ====={}", name);
                if(name.startsWith("mapper/") && name.endsWith(".xml")){
                    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
                            ClassLoader.getSystemResourceAsStream(name), configuration,
                            name,
                            configuration.getSqlFragments());
                    xmlMapperBuilder.parse();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这个类处理时遇到的问题比较多,首先最开始没找到设置mapper.xml文件的地方,就用了Mybatis默认的mapper.xml跟对应的接口在同一个目录下,结果发现这样需要在pom文件中配置resource引入.xml文件 不然编译后找不到xml文件,而且还要添加一个配置,指定Mybatis扫描的package路径.后来翻看了mybatis-spring的代码找到了遍历加载xml配置文件的方法,又遇到打包后在jar包里加载不到的问题,好在之前处理打包插件,弄过jar文件,直接在做个判断如果在jar里启动的,特殊处理一下,最后就可以了。

04、square项目调整

添加Config相关注解及接口:

package com.jisuye.annotations;
/**
 * Config注解
 * @author ixx
 * @date 2019-09-06
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Config {
     String value() default "";
}
package com.jisuye.core;
/**
 * 定义config接口,使用@config注解的类需要继承
 * @author ixx
 * @date 2019-09-08
 */
public interface SquareConfig {
    void config();
}

BeanInitUtil.java添加initConfig()方法:

        //...
        public static void init(Class clazz){
        String path = clazz.getResource("").getPath();
        // ....
        // 处理aop类
        initAop();
        // 处理config
        initConfig();
        // 处理依赖注入
        initDI();
    }
        private static void initConfig(){
        // 循环所有Bean处理Config
        Set<String> keySet = BeansMap.keySet();
        String[] keys = new String[keySet.size()];
        keySet.toArray(keys);
        List<Object> list = new ArrayList<>();
        for (String key : keys) {
            BeanObject beanObject = BeansMap.get(key);
            if(list.contains(beanObject)){
                continue;
            }
            list.add(beanObject);
            for (Annotation annotaion : beanObject.getAnnotaions()) {
                // 如果是配置则调用config()方法
                if(annotaion instanceof Config){
                    try {
                        Method method = beanObject.getBeanClass().getMethod("config");
                        method.invoke(beanObject.getObject());
                    } catch (Exception e) {
                        log.error("execute config method error!!", e);
                    }
                }
            }
        }
    }
        //...

DbUtil原来只有自己使用,现在集成Mybatis后获取 DataSource部分可以放在这个类里,于是添加了一个dataSource 参数,及对应的getDataSource()方法,首次调用如果为空会去初始化:

package com.jisuye.util;
// import ...
/**
 * 数据库操作工具
 * @author ixx
 * @date 2019-07-01
 */
public class DbUtil {
    private static final Logger log = LoggerFactory.getLogger(DbUtil.class);
    private static Connection connection;
    private static DataSource dataSource;

    /** 初始化方法*/
    public static void init(){
        try {
            //...
            HikariDataSource ds = new HikariDataSource();
            //...
            connection = ds.getConnection();
            dataSource = ds;
        } catch (Exception e) {
            log.error("mysql connection init error..", e);
            throw new SquareException("mysql connection init error....");
        }
    }

    // ...
    public static DataSource getDataSource(){
        if (dataSource == null) {
            init();
        }
        return dataSource;
    }
}

05、修改square-demo项目,测试集成

首先添加pom依赖:

                <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>com.jisuye</groupId>
            <artifactId>square-mybatis</artifactId>
            <version>0.1-SNAPSHOT</version>
        </dependency>

添加InitConfig.java配置类,只是用来调用MybatisConfig.init()方法。

package com.jisuye.config;
// import ...
@Config
public class InitConfig implements SquareConfig {
    @Override
    public void config() {
        MybatisConfig.init();
    }
}

添加AbcMapper.java接口

package com.jisuye.mapper;
//import ...
/**
 * 测试mapper
 */
@Mapper
public interface AbcMapper {
    List<AbcEntity> selectAbc(int id);
}

在resource下添加mapper目录,添加AbcMapper.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jisuye.mapper.AbcMapper">
    <resultMap id="abc" type="com.jisuye.entity.AbcEntity">
        <result column="id" javaType="Integer" property="id" />
        <result column="name" javaType="java.lang.String" property="name" />
        <result column="age" javaType="Integer" property="age" />
    </resultMap>
    <select id="selectAbc" resultMap="abc">
    select * from abc where id = #{id}
  </select>
</mapper>

application.yml中添加数据库相关配置

server:
  port: 8765
  servlet:
    context-path: /square-demo
square:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&autoReconnect=true
    username: root
    password: 123456

HelloController 添加调用Mapper的方法:

package com.jisuye.controller;
//import ...
@Controller("/")
public class HelloController {
    @Resource
    private AbcMapper abcMapper;
    @GetMapping("/id")
    public String selectAbc(@RequestParam("id") int id) {
        List<AbcEntity> list = abcMapper.selectAbc(id);
        if(list != null && list.size()>0){
            return "success! name is : " + list.get(0).getName();
        } else {
            return "error! select by db.";
        }
    }
    @GetMapping("/hello")
    public String hello(@RequestParam("name") String name){
        return "hello "+name;
    }
}

首先在IDE中启动程序测试,程序启动后访问:http://localhost:8765/square-demo/id?id=1

返回结果:success! name is : ixx

查看日志输出:

19:42:45.594 [http-nio-8765-exec-1] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [HikariProxyConnection@2030223697 wrapping com.mysql.cj.jdbc.ConnectionImpl@d088afc]
19:42:45.604 [http-nio-8765-exec-1] DEBUG com.jisuye.mapper.AbcMapper.selectAbc - ==>  Preparing: select * from abc where id = ? 
19:42:45.643 [http-nio-8765-exec-1] DEBUG com.jisuye.mapper.AbcMapper.selectAbc - ==> Parameters: 1(Integer)
19:42:45.681 [http-nio-8765-exec-1] DEBUG com.jisuye.mapper.AbcMapper.selectAbc - <==      Total: 1
19:42:45.681 [http-nio-8765-exec-1] INFO com.jisuye.core.DispatcherServlet - exec method :selectAbc
19:42:45.681 [http-nio-8765-exec-1] INFO com.jisuye.core.DispatcherServlet - response:success! name is : ixx

ok !本地测试通过,再试一下打包。

在square-demo目录下执行mvn clean package 构建完毕后执行java -jar target/square-demo-1.0-SNAPSHOT.jar

等程序启动成功后访问:http://localhost:8765/square-demo/id?id=1

返回结果:success! name is : ixx

控制台也有上边同样的日志输出,说明打包启动也OK了。

06、遗留问题

集成这篇还算完事吧,至少该实现的都实现了,当然也相当于会对Mybatis插件做了些定制如果集成其它的功能应该还需要做一定的抽象工作。
上一篇遗留的最主要的问题就是静态文件的问题了,不知道下一篇会不会解决。。。

本篇代码地址: https://github.com/iuv/square/tree/square10
spring-mybatis地址: https://github.com/iuv/square-mybatis
演示项目地址: https://github.com/iuv/square-demo

本文作者: ixx
本文链接: http://jianpage.com/2019/09/09/square10
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,104评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,816评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,697评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,836评论 1 298
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,851评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,441评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,992评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,899评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,457评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,529评论 3 341
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,664评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,346评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,025评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,511评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,611评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,081评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,675评论 2 359

推荐阅读更多精彩内容