造一个方形的轮子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 国际许可协议 进行许可。转载请注明出处!

推荐阅读更多精彩内容