造一个方形轮子文章目录:造一个方形的轮子
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 国际许可协议 进行许可。转载请注明出处!
网友评论