造一个方形轮子文章目录:造一个方形的轮子
01、解决遗留问题
上一篇的最后说要把BeansInitUtil类代码优化一下,先来弄一下这个吧,顺便把加入AOP导致多遍历了一遍目录的问题解决一下。
调整的有点多,就不放代码了,写一下主要改动的文件及改动内容吧:
com.jisuye.core包:
BeansMap.java 全方法静态化调用静态的个各map容器
BeanObject.java 添加了BeanClass记录bean类型,在加载AOP切面时使用
ControllerObject.java 添加了beanKey字段,将获取Bean从初始化阶段放到调用阶段,这么做是为了去掉添加AOP功能后多的一遍目录遍历
ControllerObject.java 部分代码:
1 | /** |
com.jisuye.util包:
BeansInitUtil.java 调整顺序把AOP放到IOC初始化之后DI初始化之前,减少了一次目录遍历
BeansInitUtil.java 放部分代码:
1 | public static void init(Class clazz){ |
02、编译打包问题整理
在真正开始写编译打包功能之前,我一直觉得这应该是一个比较简单的功能,实现应该也没什么难度,事实是我的脸现在依然很疼。。。
起初我想使用maven自带的打包插件,打一个jar包出来,看了一下要指定主方法所在类,看起来有点麻烦,而且打出来看包是不包含依赖的,不可以直接运行,当然有其它的插件能够实现把依赖jar包打包进去的功能,但还要是配置主类,于是我想自己定一个插件吧(这大概是打脸的开始)。
只实现自定义插件很容易,但要实现我的功能,第一个功能就是指定Main-Class 看了一下,编译完的文件里并没有MANIFEST.MF清单文件,只有最终打出来的jar包里才有,于是第一个问题就是:
如何向jar包里的文件写入内容?
看了spring-boot-maven-plugin 的源码,发现他是在package完成后跟着执行了自己的repackage 流程,将package打出来的jar包,复制了一份。于是我也按这个思路实现了一下,在复制的过程中判断是不是MANIFEST.MF 如果是的话向文件尾追加Main-Class配置 ,Main-Class指定的main方法所在类是通过遍历目录文件找到带有@SquareApplication 注解的类获得的。Main-Class的问题解决了,再来看一下没有打包依赖的问题。
如何将依赖的jar打包到一起?
有了上边的经验,这次和想直接把依赖的jar找出来,在复制的过程中保存进去这样应该就可以了,事实是我做到了,保存到了jar包的lib目录下,结果依赖还是找不到,后来查了一下,在jar内部的文件,引用的时候都是通过***.jar!/a/b/c 这样的方式标记的,Java中自带的ClassLoader只能接受一个“!”的这种路径,也就是可以加载jar包里的类,但对jar包里的jar包,也就是路径上要带两个“!” 这种就无能为力了,SpringBoot 是自己实现了一个ClassLoader,重写加载路径方法,搞定的,简单实现的话我参考了一下maven-assembly-plugin 他是将依赖jar包里的内容全部解压到当前jar里,这样就不会出来找不到依赖的类的问题(依然简单粗暴)。
编译打包的问题解决了,打出了可以执行的jar包,然后就发现我的程序实现有很大的问题,之前大量使用的File加载反射类,是没有问题的,但是放到Jar包中就不行了,因为File不能直接使用***.jar!/a/b
这种形式的路径。也就不能使用File进行目录遍历。
如何在Jar包中遍历路径?
这个问题我能想到的办法就是在程序中特殊处理了,判断一下当前是在jar包中还是在程序中,如果是在jar包中,则加载JarFile 获取Jar包中的条目,遍历是否需要加载就可以了。
03、编译打包插件
这里先看一下核心的代码,全部代码文章最后会给出下载地址。
BuildMojo.java:
1 | package com.jisuye; |
代码中有注释,基本就是按前边说的思路实现了一下。这块除了对maven插件不太熟悉,别的都还好,多看看前辈们写的插件,习惯就好了。
04、Square框架修改
如上文所说,打包这后,原来的代码问题很大,改了一些地方。
ClassesPathUtil.java:
1 | //... |
这里要加上判断是在jar包中的情况,要特殊处理。
LoadApplicationYmlUtil.java:
1 | // ... |
这里也要判断如果是在jar包里就不能使用File的方式获取文件流,可以通过ClassLoader.getSystemResourceAsStream()方法获得。
BeanInitUtil.java:
1 | // ... |
添加了initJar方法,从jar包中遍历初始化Bean。
其它的一些联动修改,可以在源码中查看。
05、测试项目
新创建一个square-demo项目,pom.xml文件中引入我的们框架及打包工具(需要先在框架及打包工具目录下执行mvn clean install)
1 | <dependencies> |
创建一个测试的HelloController.java:
1 | package com.jisuye.controller; |
启动程序T.java:
1 | package com.jisuye; |
添加application.yml:
1 | server: |
好的,现在我们试一下开发中启动程序,支持T.main()方法,查看控制台输出:
1 | 18:58:04.793 [main] INFO com.jisuye.core.SquareApplication - |
程序启动成功,在浏览器访问:http://localhost:8765/square-demo/hello?name=ixx
返回:hello ixx
到目前为止都没有问题,然后我们来试一下打包,在square-demo项目目录下执行mvn clean package
,查看输出:
1 | .... |
build success! 我们来看一下target目录:
1 | C:\Users\admin\idea\square-demo>ls target |
有一个square-demo-1.0-SNAPSHOT.jar
和一个square-demo-1.0-SNAPSHOT.jar.old
,跟我们插件实现效果一样,.old是package打出来的而.jar是我们repackage过的,我们来启动看一下,执行java -jar target/square-demo-1.0-SNAPSHOT.jar
1 | ..... |
可以看到也启动成功了,现在访问一下
在浏览器访问:http://localhost:8765/square-demo/hello?name=ixx
返回:hello ixx
06、遗留问题
编译打包基本完成了,但还有很多问题:
1、没有实现静态文件目录,public 的复制
2、没有处Square理框架的依赖关系
3、现有方式加载Bean有可能加载多余的类
问题留给下一篇….
这一篇写出来感觉没多少东西但在做的过程中确实是出现太多跟预想的不一样的结果,往往是为了解决一个问题,中间要走错很多路,找时间得好好看看Maven的实现原理。
本篇代码地址: https://github.com/iuv/square/tree/square9
打包插件地址: https://github.com/iuv/square-maven-plugin
演示项目地址: https://github.com/iuv/square-demo
本文链接: http://blog.jisuye.com/2019/08/15/square9/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!