您的位置:首页 > 其它

希望大牛加入,共同为项目智能化管理jar包而努力

2016-12-09 16:54 211 查看
想听听大家对于我这个想法的一些看法,喷也好,赞也罢,希望留下您宝贵的建议! 我一直认为java程序员不需要自己去管理项目中依赖的jar,甚至不需要知道jar包的存在。什么,你不知道jar?好吧,下面就来说一说jar包。 就是一种被广泛使用的文件格式,比如你开发了一个牛逼闪闪的小程序,你们公司的其它人也想使用这个小程序,怎么办?一般的解决办法就是将这个程序打包成jar文件,发布到某个可供分享的地方(比如nexus),其它人如果使用maven或者gradle来管理项目的话,只需要加个jar的依赖配置即可,类似maven与Gradle的写法如下: Maven引入外部依赖写法:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.7.Final</version>
</dependency>

Gradle引入外部依赖写法:

dependencies {
compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'
}


看到了吧,当你的程序需要使用第三方封装好的功能时,可以通过引入jar包来实现。 我为什么感觉到没有必要呢?因为我觉得完全可以依赖于某个智能化工具来处理这件事情,也就是智能化引包。想想下面的一些问题吧。  假如我要在项目中添加邮件发送功能并且想通过第三方jar包来实现时,不但要知道相关功能在哪个jar包(幸亏maven也帮助我们引入了三方包的依赖),并且需要准确写出jar包的相关信息,比如唯一名称,版本号等。  假如我使用了hibernate的3.6.7.Final这个版本,那么在新需求开发时,项目还需要用到spring相关的功能,应该怎么做呢?搜索使用spring需要引入哪些依赖包吗?根据本人多年的经验,如果你不查一下与已有Hibernate相关包的兼容性,那起冲突的概率是非常大的。 PS:不要和我说Eclipse,IDEA不是有这个功能吗?我说的不是项目新建的时候,而是在项目开发过程中加入新的三方jar包。  你有遇到过ClassNotFoundException异常吗?我觉得大部分时候出现这种异常都是由于相关jar包引入不全,或者由于版本的问题,某个类并不存在而引起的。这种问题一般在运行项目时才会发现,想想为什么?很简单,你的源代码中肯定没有直接用这个类,项目不报错你当然在编写源代码时发现不了。  以前工作时项目经理让我把两个老项目改造成maven项目,以方便对项目进行jar包管理,项目打包、编译和部署。我查找了这个项目依赖的每一个jar包(包多,非常痛苦),然后转换为maven的<dependency>,那时候想了想,不就是把以前依赖的包转换为maven的写法,让maven来引入不就行了吗?其实事情远没有想像的那么简单,尤其是你的项目依赖的包很多时,非常容易引起冲突。 举个例子,好多的三方jar包会依赖log4j或者logback进行日志记录,maven就会帮助你引入这些三方jar包所依赖的包,那么就有可能引入一些版本不同的日志记录包,也就是项目中出现了好多相同的类,还需要配合<dependency>节点下的<exclude>来排除某些包。 记得曾经struts2的核心包暴露了一个巨大的安全隐患,而你又不幸的在项目中使用了这一版本的包,那么肯定还需要修改依赖配置,引入最新发布的补丁包(你知道补丁包具体的版本号是多少吗)。对于知道的人是这样,对于不知道的人也就那样了。  如果你经历过这些事情,那么假如现在有一个智能管理jar包的工具(暂时命名为autort,意为auto import)为你管理类似下面这一坨的东西,你愿不愿意用上一用呢?
<dependencies>
<dependency>
<groupId>org.apache.geronimo.ext.openejb</groupId>
<artifactId>javaee-api</artifactId>
<version>5.0.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
...
</dependencies>


使用时当你再想使用三方依赖的功能,如javamail的邮件发送功能时,可直接查找javamail api,在类中编写相关代码即可。如使用如下的类
HtmlEmail email = new HtmlEmail();
还需要手动键入import相关信息,如:
import org.apache.commons.mail.HtmlEmail;

运行autort后会引入与当前项目中其它jar包不冲突的,最新稳定版本的javamail相关包。添加上面的信息是为了告诉autort,我需要使用javamail相关的发送功能,如果你去掉了HtmlEmail类及相关引入,那么再次运行管理工具后,相关包也会去除。 不必担心在使用某个api接口时,当前javamail版本是否支持的问题。如果出现了找不到相关方法的提示时,只管调用这个方法,然后重新运行一下autort即可。  如果使用编程工具如Eclipse或IDEA的话项目可能显示红叉或者红色下划线,完全不必在意,需要保证的是你的项目没有语法错误,只是缺少了相关jar包而已。  听着还不错吧,不过要是实现起来可没那么容易。 首先能够想到的问题就一大堆,如何通过扫描用户的源代码准确retrieve出相关依赖信息(这个就够繁琐复杂的,已经快做了两个月了),即使找到了 各种依赖信息,又如何匹配出所有合适的jar包呢?肯定需要在服务端事先对jar包进行大规模的扫描,找出不同版本的不同依赖等等...............太多太多  不过经过2个月的思考,感觉越来越有点眉目了,最近准备好好出个文档,然后整理一下项目,多添加点注释,然后就开源啦!希望感兴趣的朋友一起加入,共同为解放程序员的双手而努力! 最后来一点干货吧,也不枉对这个项目不感兴趣的朋友白来一趟。 Java在调用Get()方法时给其传递了一串字符串引用,如下:  
String a = Get(cn.autort.core.Expression.a);

如何正确的分析这串字符串引用是一个关键。autort必须知道这串字符串引用代表的真正意思。可能你会毫不犹豫地说是在cn.autort.core包下的Expression类中的静态变量a。
如果用户编写代码时有良好的编码规范,也许你可以马上对这个字符串进行正确的分解。但是总有一些情况下,这个字符串代表的意思并不是这样。 它可以代表连续的变量引用,cn、autort、core、Expression与a全部为变量
它可以代表所有的类引用,就是内部类嵌套内部类的情况
它可以代表很多,但是有些规则还是需要知道的,那就是变量后面不可能直接跟类名和包名,包后不可能直接跟变量名。 那么如何在某些情况下正确分解出这个字符串代表的信息。我们还是需要基于已有的包信息和类信息进行分解。autort需要扫描所有的包和类,并进行有组织的存储,以便快速进行判断。在这里可以使用Trie树来解决。 
如果你不了解什么是Trie树,可以看一看这篇文章。传送门:http://www.cnblogs.com/beiyeqingteng/p/5625540.html 我要做的只是稍加更改一下Trie树,让它适应需求即可。 
class Node {
String term;    // the character in the node
boolean isEnd;  // whether the end of the words
int count;      // the number of words sharing this character
LinkedList<Node> childList; // the child list

Map<String,Object> map = null; // store class name

public Node(String term) {
this.childList = new LinkedList<Node>();
this.isEnd = false;
this.term = term;
this.count = 0;
}

public Node subNode(String term) {
if (childList != null) {
for (Node eachChild : childList) {
if (eachChild.term == term) {
return eachChild;
}
}
}
return null;
}

public void put(String key,Object value){
if(map==null){
map = new HashMap<String,Object>();
}
map.put(key, value);
}

public Object get(String key){
return map.get(key);
}

}

这是Node结点,每个包名为一个节点。如cn、core等。需要指出的是map中存储的数据。key为类名,如果类中有类的话,以逗号隔开。例如A.B,值就是对应的具体的语法树了。

public class Trie {
private Node root;

public Trie() {
root = new Node("root");  // 第一个节点的字符串为空
}

public Node getRoot(){
return root;
}

public Node insert(String[] terms) {
if (searchNode(terms)!=null)
return searchNode(terms);

Node current = root;
for (int i = 0; i < terms.length; i++) {
Node child = current.subNode(terms[i]);
if (child != null) {
current = child;
} else {
current.childList.add(new Node(terms[i]));
current = current.subNode(terms[i]);
}
current.count++;
}
current.isEnd = true;
return current;
}

public Node searchNode(String[] terms) {
Node current = root;
for (int i = 0; i < terms.length; i++) {
if (current.subNode(terms[i]) == null){
return null;
}else{
current = current.subNode(terms[i]);
}
}
if(current.isEnd){
return current;
}
return null;
}

public int[] searchParts(String[] terms) {
int[] result = new int[]{-1,-1};
int matchedIndex = -1;
Node current = root;
for (int i = 0; i < terms.length; i++) {
if (current.subNode(terms[i]) == null){
break;
}else{
current = current.subNode(terms[i]);
if(current.isEnd){
matchedIndex = i;
}
}
}

result[0] = matchedIndex;

StringBuffer buffer = new StringBuffer();
for(int j=matchedIndex+1;j<terms.length;j++){
buffer.append(terms[j]);
Object obj = current.get(buffer.toString());
if(obj!=null){
result[1] = j;
}
if(j!=terms.length-1){
buffer.append(".");
}
}
return result;
}

}

提供了插入和搜索的方法,并不复杂。不过需要兼容那些没有包路径的类。提供了个小Demo测试一下。建立Trie树并存储包和类的相关信息:Trie trie = new Trie();
Node a1 = trie.insert(new String[]{"cn","autort","core"});
a1.put("Compilation", "ReferenceASTNode");
a1.put("Compilation.A", "ReferenceASTNode"); // 嵌套类Compilation.A

Node a2 = trie.insert(new String[]{"cn","autort","core","expression"});
a2.put("Assignment", "ReferenceASTNode");
a2.put("FieldAccess", "ReferenceASTNode");
int[] result1 = trie.searchParts(new String[]{"cn","autort","core","Compilation","A","B","a"});
System.out.println(result1[0]+"/"+result1[1]);

int[] result2 = trie.searchParts(new String[]{"cn","autort","core","expression","Compilation"});
System.out.println(result2[0]+"/"+result2[1]);

int[] result3 = trie.searchParts(new String[]{"ClassA"});
System.out.println(result3[0]+"/"+result3[1]);

运行的结果如下:  
2/4   // cn.autort.core为包名,Compilation.A为类名,剩下的B和a就是类内的变量名了
3/-1  // cn.autort.core.expression为包中,Compilation无论做为包名还是类名都不存在
-1/0  //  无包名,ClassA为类名


以后有时间的话就多码点字,讲一讲autort技术实现上的一些原理和细节。 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐