2013年8月8日
- <%
- WebApplicationContext context = (WebApplicationContext)this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
- TestService service = (TestService)context.getBean("testService");
- %>
posted @
2013-08-08 14:48 邦 阅读(194) |
评论 (0) |
编辑 收藏
2012年9月24日
一、模版修改
在导出表时,powerdesigner默认为我们提供了很多的模版,在工具栏中选择【Report--->Report Template】即可看到所有的默认模版。如图一:
图一 模版列表
这里我们为了导出powerdesigner中创建的表,在工具栏中选择【Report--->Reports】(快捷键Ctrl+E),然后创建一个New Report,如下图二所以,选择Standard Physical Report,这里选择的标准的模版,点击OK确定。
图二 创建新的Report
从工具栏【Report--->Print Preview】或者点击图标同样可以预览导出到word的效果了。效果如图三所示:
图三 导出到word中预览效果
从【图三】中的工具栏里面HTM和RTF两种导出格式。如果导出到word选择RTF格式即可,但是我们从【图三】中看出红色标出的部分,1 是word的页眉,同样还有页脚信息 ,2 是一些列的属性清单。
图四 设置页眉、页脚
在【图四】中,选择Header/Footer后,删掉Header里面的%MODULE% %MODELNAME%,删掉Footer里面的%APPNAME% %DATE% 页数 %PAGE%,User_defined footer就会自动勾上,这样就去掉了页眉、页脚了
还有其他的比如表前面的自增序号,第一页中的创建者、版本、日期信息也不需要,我们可以进行配置去掉,如下【图五】【图六】
图四
图五 Properties配置
在图五中,在默认配置中(General)勾上No paragraph numbering即可取消表前面的自增序号,在Title Page中选择 No Title Page就不会生成第一页中关于创建者、版本、日期等信息。
在【图三】中,预览看到的内容太多,就必须删除一些我们不需要的内容,经过删减之后,如下【图六】所示
图六
在【图六】中右键单击【List of Table Columns - 表<%PARENT%>列说明】,从菜单中选择 Edit Title...,就可以编辑成现在我们已看到的。然后,在右键菜单中选择Layout...,选择我们所需要显示的内容,Code表示列名称,一般用英文单词或拼音字母表示,Name一般用中文描述,Data Type 表示数据类型,Comment表示字段备注或字段说明等。
到此基本上已经完成了powerdesigner模版的修改
二、导出表
然后点击[Report---->Generate RTF]导出到word中,最后我们看看导出到word的效果,如下图:
图七 导出到word效果
三、保存模版
通过上文,我们完成了自己所需的模版,然后就保存,以后可以直接使用即可,在工具栏中[Report--->Create Template From Section],然后Ctrl+S就会要求保存,取名保存即可。
posted @
2012-09-24 18:12 邦 阅读(565) |
评论 (0) |
编辑 收藏
2012年8月2日
用jTDS通过JDBC连:
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<version>1.2.4</version>
</dependency>
public static List<String> executeQuery(String sql,String columns) {
try {
Connection conn = getConnection();
Statement st = conn.createStatement();
ResultSet set = st.executeQuery(sql);
List<String> result = new ArrayList<String>();
while (set.next()) {
String[] columnList = columns.split(",");
for(String str:columnList){
result.add(set.getString(str));
}
}
set.close();
st.close();
conn.close();
return result;
} catch (SQLException e) {
throw new IllegalArgumentException(e);
}
}
public static Connection getConnection() {
try {
Class.forName("net.sourceforge.jtds.jdbc.Driver");
String url = "jdbc:jtds:sqlserver://localhost:1433;DatabaseName=jdl";
String username = "sa";
String password = "";
Connection conn = DriverManager.getConnection(url, username,
password);
return conn;
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
posted @
2012-08-02 15:55 邦 阅读(5129) |
评论 (0) |
编辑 收藏
2012年7月11日
在js中的url进行encodeURIComponent编码:dd_ddtj_sub.jsp?shrxm="+encodeURIComponent(shrxm1)+"&lxdh="+encodeURIComponent(lxdh1)
后台service进行encode编码和decode解码:shrxm= java.net.URLEncoder.encode(shrxm, "UTF-8");
shrxm= java.net.URLDecoder.decode(shrxm,"UTF-8");
posted @
2012-07-11 17:01 邦 阅读(265) |
评论 (0) |
编辑 收藏
2012年7月10日
1)、下载MongoDB
http://downloads.mongodb.org/win32/mongodb-win32-i386-2.0.4.zip
2)、设置MongoDB目录
将其解压到 d:\,再重命名为mongodb,路径为d:\mongodb
3)、设置数据文件路径
在d:盘建一个data文件夹,在data文件夹中新建db文件夹,路径d:\data\db
4)、启动MongoDB服务
进入 cmd 提示符控制台,
D:\mongodb\bin\mongod.exe --dbpath=d:\data\db
<!--[if !supportLists]-->1. <!--[endif]-->Mon Apr 16 08:50:54
<!--[if !supportLists]-->2. <!--[endif]-->Mon Apr 16 08:50:54 warning: 32-bit servers don't have journaling enabled by def
<!--[if !supportLists]-->3. <!--[endif]-->ault. Please use --journal if you want durability.
<!--[if !supportLists]-->4. <!--[endif]-->Mon Apr 16 08:50:54
<!--[if !supportLists]-->5. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] MongoDB starting : pid=5084 port=27017 dbpat
<!--[if !supportLists]-->6. <!--[endif]-->h=d:\data\db 32-bit host=PC-201012302214
<!--[if !supportLists]-->7. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten]
<!--[if !supportLists]-->8. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] ** NOTE: when using MongoDB 32 bit, you are
<!--[if !supportLists]-->9. <!--[endif]-->limited to about 2 gigabytes of data
<!--[if !supportLists]-->10. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] ** see http://blog.mongodb.org/post/13
<!--[if !supportLists]-->11. <!--[endif]-->7788967/32-bit-limitations
<!--[if !supportLists]-->12. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] ** with --journal, the limit is lower
<!--[if !supportLists]-->13. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten]
<!--[if !supportLists]-->14. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] db version v2.0.4, pdfile version 4.5
<!--[if !supportLists]-->15. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] git version: 329f3c47fe8136c03392c8f0e548506
<!--[if !supportLists]-->16. <!--[endif]-->cb21f8ebf
<!--[if !supportLists]-->17. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] build info: windows sys.getwindowsversion(ma
<!--[if !supportLists]-->18. <!--[endif]-->jor=6, minor=0, build=6002, platform=2, service_pack='Service Pack 2') BOOST_LIB
<!--[if !supportLists]-->19. <!--[endif]-->_VERSION=1_42
<!--[if !supportLists]-->20. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] options: { dbpath: "d:\data\db" }
<!--[if !supportLists]-->21. <!--[endif]-->Mon Apr 16 08:50:54 [websvr] admin web console waiting for connections on port 2
<!--[if !supportLists]-->22. <!--[endif]-->8017
<!--[if !supportLists]-->23. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] waiting for connections on port 27017
MongoDB服务端的默认连接端口:27017
5)、将MongoDB作为 Windows 服务随机启动
先创建D:\mongodb\logs\mongodb.log文件,用于存储MongoDB的日志文件, 再安装系统服务:
D:\mongodb\bin\mongod --dbpath=d:\data\db --logpath=d:\mongodb\log
s\mongodb.log --install
all output going to: d:\mongodb\logs\mongodb.log
Creating service MongoDB.
Service creation successful.
Service can be started from the command line via 'net start "MongoDB"'.
D:\>net start mongodb
Mongo DB 服务已经启动成功。
D:>
注意:如果需要卸载服务,执行命令:sc delete MongoDB
6)、客户端连接验证
新打开一个CMD输入:d:\mongodb\bin\mongo,如果出现下面提示,那么您就可以开始MongoDB之旅了:
<!--[if !supportLists]-->1. <!--[endif]-->d:\mongodb\bin\mongo
<!--[if !supportLists]-->2. <!--[endif]-->MongoDB shell version: 2.0.4
<!--[if !supportLists]-->3. <!--[endif]-->connecting to: test
<!--[if !supportLists]-->4. <!--[endif]-->>
7)、查看MongoDB日志
查看D:\mongodb\logs\mongodb.log文件,即可对MongoDB的运行情况进行查看或排错。
posted @
2012-07-10 13:24 邦 阅读(628) |
评论 (0) |
编辑 收藏
2012年5月3日
方案一:
希望实现 当鼠标离开一个DIV的时候触发一个事件处理函数 于是用onmouseout 结果却发现它的触发是不是也太敏感了 原因现在也没有弄清楚 IE下好像是因为区分mouseout时的fromElement还是toElement ,IE 5.5以上的onmouseleave事件就比较好用 偏FF又不支持这个事件 只有自己想办法手工判断了。
<SCRIPT> /*** * 参数e 是对象传递的触发事件 FF下想访问event对象必须传递event参数 * 参数o 是目标DIV对象 */ function fun(e,o) { /* FF 下判断鼠标是否离开DIV */ if(window.navigator.userAgent.indexOf("Firefox")>=1) { var x = e.clientX + document.body.scrollLeft; var y = e.clientY + document.body.scrollTop ; var left = o.offsetLeft; var top = o.offsetTop; var w = o.offsetWidth; var h = o.offsetHeight; if(y < top || y > (h + top) || x > left + w || x<left ) { alert("mouseout"); } }
/* IE */ if(o.contains(event.toElement ) == false ) alert("mouseout"); } </SCRIPT>
<DIV onmouseout=fun(event,this)>content</DIV> |
需要注意 在取鼠标的值的时候 一定要加上滚动条已经拖动过的内容e.clientY + document.body.scrollTop 如果只是e.clientY得到是个错误的值 当然如果高宽都很小 是看不出来问题。 取一个对象的高和宽 也可以使用 clientHeight clientWidth 属性 以后遇到FF IE不兼容的时候要多看看FF的开发手册 http://developer.mozilla.org/en/docs/DOM:element.offsetLeft
方案二:(与一相似)
js的onmouseout有很奇怪的一个问题。例如
<div onmouseout="alert(123)">
<a href="#">test</a>
</div>
我们预期只有当鼠标从div中移开的时候才会触发onmouseout事件,可是,事实上,当我们移到div中的元素时,例如本例中的a标签时,就会触发onmousout事件。也就是说,移到对象的子对象上,也算onmouseout了。这往往会让我们预期的效果达不到。今天的工作就遇到了这个问题。在blueidea上搜了一下,找了解决办法。兼容IE和FF。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>阿当制作</title>
</head>
<body>
<script type="text/javascript">
function test(obj, e) {
if (e.currentTarget) {
if (e.relatedTarget != obj) {
if (obj != e.relatedTarget.parentNode) {
alert(1);
}
}
} else {
if (e.toElement != obj) {
if (obj != e.toElement.parentNode) {
alert(1);
}
}
}
}
</script>
<div onmouseout="test(this, event)" style="width:100px;height:100px;border:1px #666 solid">
<span style="margin:5px;width:100%;height:100%;border:1px #ff0000 solid">faddsf</span>
</div>
</body>
</html>
今天发现JQ中关于这个问题,已经有了一个好的解决办法了.呵呵,jquery中定义了一种事件叫做"mouseleave",用这个事件做事件句柄的话,就可以解决这个问题了.越来越发现jquery是个好东西了.
方案三:
,jQuery V1.2.2推荐用bind("mouseleave",function(){})来代替以前的mouseover方法
用bind("mouseenter",function(){})来代替mouseout,同样也针对以前的hover方法,要看详细的说明点这个地址:http://docs.jquery.com/Release:jQuery_1.2.2
$(document).ready(function() {
$("#a1").bind("mouseleave", function(){
$('<div style="color:red;">out</div>')
.insertAfter($(this));
});
});
posted @
2012-05-03 16:08 邦 阅读(223) |
评论 (0) |
编辑 收藏
2012年3月19日
1. 为查询缓存优化你的查询
大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操作表而直接访问缓存结果了。
这里最主要的问题是,对于程序员来说,这个事情是很容易被忽略的。因为,我们某些查询语句会让MySQL不使用缓存。请看下面的示例:
1 // 查询缓存不开启
2 $r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");
3
4 // 开启查询缓存
5 $today = date("Y-m-d");
6 $r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");
上面两条SQL语句的差别就是 CURDATE() ,MySQL的查询缓存对这个函数不起作用。所以,像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存,因为这些函数的返回是会不定的易变的。所以,你所需要的就是用一个变量来代替MySQL的函数,从而开启缓存。
2. 当只要一行数据时使用 LIMIT 1
当你查询表的有些时候,你已经知道结果只会有一条结果,但因为你可能需要去fetch游标,或是你也许会去检查返回的记录数。
在这种情况下,加上 LIMIT 1 可以增加性能。这样一样,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。
下面的示例,只是为了找一下是否有“中国”的用户,很明显,后面的会比前面的更有效率。(请注意,第一条中是Select *,第二条是Select 1)
01 // 没有效率的:
02 $r = mysql_query("SELECT * FROM user WHERE country = 'China'");
03 if (mysql_num_rows($r) > 0) {
04 // ...
05 }
06
07 // 有效率的:
08 $r = mysql_query("SELECT 1 FROM user WHERE country = 'China' LIMIT 1");
09 if (mysql_num_rows($r) > 0) {
10 // ...
11 }
3. 千万不要 ORDER BY RAND()
想打乱返回的数据行?随机挑一个数据?真不知道谁发明了这种用法,但很多新手很喜欢这样用。但你确不了解这样做有多么可怕的性能问题。
如果你真的想把返回的数据行打乱了,你有N种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是:MySQL会不得不去执行RAND()函数(很耗CPU时间),而且这是为了每一行记录去记行,然后再对其排序。就算是你用了Limit 1也无济于事(因为要排序)
下面的示例是随机挑一条记录
1 // 千万不要这样做:
2 $r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");
3
4 // 这要会更好:
5 $r = mysql_query("SELECT count(*) FROM user");
6 $d = mysql_fetch_row($r);
7 $rand = mt_rand(0,$d[0] - 1);
8
9 $r = mysql_query("SELECT username FROM user LIMIT $rand, 1");
4. 避免 SELECT *
从数据库里读出越多的数据,那么查询就会变得越慢。并且,如果你的数据库服务器和WEB服务器是两台独立的服务器的话,这还会增加网络传输的负载。
所以,你应该养成一个需要什么就取什么的好的习惯。
1 // 不推荐
2 $r = mysql_query("SELECT * FROM user WHERE user_id = 1");
3 $d = mysql_fetch_assoc($r);
4 echo "Welcome {$d['username']}";
5
6 // 推荐
7 $r = mysql_query("SELECT username FROM user WHERE user_id = 1");
8 $d = mysql_fetch_assoc($r);
9 echo "Welcome {$d['username']}";
5. 永远为每张表设置一个ID
我们应该为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。
就算是你 users 表有一个主键叫 “email”的字段,你也别让它成为主键。使用 VARCHAR 类型来当主键会使用得性能下降。另外,在你的程序中,你应该使用表的ID来构造你的数据结构。
而且,在MySQL数据引擎下,还有一些操作需要使用主键,在这些情况下,主键的性能和设置变得非常重要,比如,集群,分区……
在这里,只有一个情况是例外,那就是“关联表”的“外键”,也就是说,这个表的主键,通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比如:有一个“学生表”有学生的ID,有一个“课程表”有课程ID,那么,“成绩表”就是“关联表”了,其关联了学生表和课程表,在成绩表中,学生ID和课程ID叫“外键”其共同组成主键。
6. 使用 ENUM 而不是 VARCHAR
ENUM 类型是非常快和紧凑的。在实际上,其保存的是 TINYINT,但其外表上显示为字符串。这样一来,用这个字段来做一些选项列表变得相当的完美。
如果你有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,你知道这些字段的取值是有限而且固定的,那么,你应该使用 ENUM 而不是 VARCHAR。
MySQL也有一个“建议”(见第十条)告诉你怎么去重新组织你的表结构。当你有一个 VARCHAR 字段时,这个建议会告诉你把其改成 ENUM 类型。使用 PROCEDURE ANALYSE() 你可以得到相关的建议。
7. 尽可能的使用 NOT NULL
除非你有一个很特别的原因去使用 NULL 值,你应该总是让你的字段保持 NOT NULL。这看起来好像有点争议,请往下看。
首先,问问你自己“Empty”和“NULL”有多大的区别(如果是INT,那就是0和NULL)?如果你觉得它们之间没有什么区别,那么你就不要使用NULL。(你知道吗?在 Oracle 里,NULL 和 Empty 的字符串是一样的!)
不要以为 NULL 不需要空间,其需要额外的空间,并且,在你进行比较的时候,你的程序会更复杂。当然,这里并不是说你就不能使用NULL了,现实情况是很复杂的,依然会有些情况下,你需要使用NULL值。
8. 把IP地址存成 UNSIGNED INT
很多程序员都会创建一个 VARCHAR(15) 字段来存放字符串形式的IP而不是整形的IP。如果你用整形来存放,只需要4个字节,并且你可以有定长的字段。而且,这会为你带来查询上的优势,尤其是当你需要使用这样的WHERE条件:IP between ip1 and ip2。
我们必需要使用UNSIGNED INT,因为 IP地址会使用整个32位的无符号整形。
而你的查询,你可以使用 INET_ATON() 来把一个字符串IP转成一个整形,并使用 INET_NTOA() 把一个整形转成一个字符串IP
9. 拆分大的 DELETE 或 INSERT 语句
如果你需要在一个在线的网站上去执行一个大的 DELETE 或 INSERT 查询,你需要非常小心,要避免你的操作让你的整个网站停止相应。因为这两个操作是会锁表的,表一锁住了,别的操作都进不来了。
如果你把你的表锁上一段时间,比如30秒钟,那么对于一个有很高访问量的站点来说,这30秒所积累的访问进程/线程,数据库链接,打开的文件数,可能不仅仅会让你泊WEB服务Crash,还可能会让你的整台服务器马上宕机。
所以,如果你有一个大的处理,你定你一定把其拆分,使用 LIMIT 条件是一个好的方法。下面是一个示例:
01 while (1) {
02 //每次只做1000条
03 mysql_query("DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000");
04 if (mysql_affected_rows() == 0) {
05 // 没得可删了,退出!
06 break;
07 }
08 // 每次都要休息一会儿
09 usleep(50000);
10 }
10. 越小的列会越快
对于大多数的数据库引擎来说,硬盘操作可能是最重大的瓶颈。所以,把你的数据变得紧凑会对这种情况非常有帮助,因为这减少了对硬盘的访问。
如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有理由使用 INT 来做主键,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。
posted @
2012-03-19 14:50 邦 阅读(491) |
评论 (0) |
编辑 收藏
首先看一下分页的基本原理:
mysql> explain SELECT * FROM message ORDER BY id DESC LIMIT 10000, 20\G
***************** 1. row **************
id: 1
select_type: SIMPLE
table: message
type: index
possible_keys: NULL
key: PRIMARY
key_len: 4
ref: NULL
rows: 10020
Extra:
1 row in set (0.00 sec)
limit 10000,20的意思扫描满足条件的10020行,扔掉前面的10000行,返回最后的20行,问题就在这里,如果是limit 100000,100,需要扫描100100行,在一个高并发的应用里,每次查询需要扫描超过10W行,性能肯定大打折扣。
一种”clue”的做法,给翻页提供一些”线索”,比如还是SELECT * FROM message ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,如果我们只提供”上一页”、”下一页”这样的跳转(不提供到第N页的跳转),那么在处理”上一页”的时候SQL语句可以是:
SELECT * FROM message WHERE id > 9527 ORDER BY id ASC LIMIT 20;
处理”下一页”的时候SQL语句可以是:
SELECT * FROM message WHERE id < 9500 ORDER BY id DESC LIMIT 20;
不管翻多少页,每次查询只扫描20行。
缺点是只能提供”上一页”、”下一页”的链接形式,但是有些人非常喜欢”<上一页 1 2 3 4 5 6 7 8 9 下一页>”这样的链接方式,怎么办呢?
如果LIMIT m,n不可避免的话,要优化效率,只有尽可能的让m小一下,我们扩展前面的”clue”做法,还是SELECT * FROM message ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,比如要跳到第8页,我看的SQL语句可以这样写:
SELECT * FROM message WHERE id > 9527 ORDER BY id ASC LIMIT 20,20;
跳转到第13页:
SELECT * FROM message WHERE id < 9500 ORDER BY id DESC LIMIT 40,20;
原理还是一样,记录住当前页id的最大值和最小值,计算跳转页面和当前页相对偏移,由于页面相近,这个偏移量不会很大,这样的话m值相对较小,大大减少扫描的行数。其实传统的limit m,n,相对的偏移一直是第一页,这样的话越翻到后面,效率越差,而上面给出的方法就没有这样的问题。
posted @
2012-03-19 14:46 邦 阅读(845) |
评论 (0) |
编辑 收藏
2012年3月6日
1.1何为maven坐标
Mavne的一大功能是管理项目依赖,为了能自动化的解析任何一个Java构件,maven就必须将它们唯一标识,这就依赖管理的底层基础—-坐标。
Maven的世界中拥有数量非常巨大的构件,也就是平时用的一些jar、war等文件,在Maven为这些构件引入坐标概念之前,我们无法使用任何一种方式来唯一标识所有这些构件。因此maven定义了这样组规则:世界上任何一个构件都可以使用Maven坐标唯一标识,maven坐标元素包括groupId、artifactId、version、packaging、classifier。现在,只要我们提供正确的坐标元素,maven就能找到对应的组件。比如说,当需要使用Java5平台上TestNG的5.8版本时,就告诉Maven:“groupId=org.testng;artifactId=testng;version=5.9;classifier=jdk15
”,maven就会从仓库中寻找相应的构件供我们使用。Maven是从哪里下载构件的呢?maven内置了一个中央仓库的地址(http://repol.maven.org/maven2),该中央仓库包含了世界上大部分流行的开源项目组件,maven会在需要的时候去那里下载。
1.2坐标详解
先看一组坐标定义,如下:
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus-indexer</artifactId>
<version>2.0.0</version>
<packaging>jar</packaging>
这是nexus-indexer的坐标定义,nexus-indexer是一个对maven仓库编纂索引并提供搜索功能的类库,它是Nexus项目的一个子模块。下面解释一下各个坐标元素:
- groupId:定义当前maven项目隶属的实际目录。首先,maven项目和实际项目不一定是一对一的关系。其次,groupId不应该对应项目隶属的组织或公司。最后,groupId的表示方式与Java包名的表示方式类似,通常与域名反向一一对应。
- artifactId:该元素定义实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为前缀。
- version:该元素定义Maven项目当前所处版本。
- packaging:该元素定义Maven项目的打包方式。首先。打包方式通常与所生成构件的文件扩展名对应。其次,打包方式会影响到构件的生命周期,比如jar打包和war打包会使用不同的命令。最后,当不定义packaging的时候,Maven会使用默认值jar。
- classifier:该元素用来帮助定义构建输出的一些附属构件。附属构件与主构件对应,如上例中的主构件是nexus-indexer-2.0.0.jar,该项目可能会通过使用一些插件生成如nexus-indexer-2.0.0-javadoc.jar、nexus-indexer-2.0.0-sources.jar这样一些附属构件。
上述五个元素中,groupId、artifactId、version是必须定义的,packaging是可选的(默认为jar),而classifier是不能直接定义的。
理解清楚Maven坐标之后,我们就能开始讨论Maven的依赖管理了。
1.3依赖的配置
一个依赖声明可以包含如下的一些元素:
<project>
…
<dependencies>
<dependency>
<groupId>…</groupId>
<artifactId>…</artifactId>
<version>…</version>
<type>…</type>
<scope>…</scope>
<optional>…</optional>
<exclusions>
<exclusion>
…
</exclusion>
…
</exclusions>
</dependency>
…
</dependencies>
…
</project>
Dependencies可以包含一个或者多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:
- groupId、artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。
- type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值为jar。
- scope:依赖的范围。
- optional:标记依赖是否可选。
- exclusions:用来排除传递性依赖。
1.4依赖范围
首先需要知道,Maven在编译项目主代码的时候需要使用一套classpath。其次,Maven在编译和执行测试的时候会使用另外一套classpath。最后,实际运行Maven项目的时候,又会使用一套classpath。
依赖范围就是用来控制依赖与这三种classpath(编译classpaht、测试classpath、运行classpath)的关系,Maven有以下几种依赖范围:
- compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。
- test:测试依赖范围。使用此范围依赖的Maven依赖,只对测试classpath有效,在编译主代码或者运行项目的时候将无法使用此类依赖
- provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath都有效,但是在运行时无效
- runtime:运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。
- system:系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构件的不可移植,因此应该谨慎使用。SystemPath元素可以引用环境变量,如:
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/tr.jar</systemPath>
</dependency>
- import:导入依赖范围。该依赖范围不会对三种classpath产生实际的影响。
1.5传递性依赖
何为传递性依赖?现在举一个例子:项目中有一个compile范围的spring-core依赖,spring-core有一个compile范围的commons-logging依赖,那么commons-logging就会成为该项目的compile范围依赖,commons-logging是该项目的一个传递依赖。
Maven会解析各个直接依赖的POM,将那些必要的间接依赖以传递性依赖的形式引入到当前项目中。
依赖范围不仅可以控制依赖与三种classpath的关系,还对传递性依赖产生影响。假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围,如表,最左边一列表示第一直接依赖范围,最上面一行表示第二直接依赖范围,中间的交叉单元格则表示传递性依赖范围
|
compile |
test |
provided |
runtime |
compile |
compile |
—— |
—— |
runtime |
test |
test |
—— |
—— |
test |
provided |
provided |
—— |
provided |
provided |
runtime |
runtime |
—— |
—— |
runtime |
仔细观察一下表可以发现这样的规律:当第二直接依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致;当第二直接依赖的范围是test的时候,依赖不会得以传递;当第二直接依赖的范围为provided的时候,只传递第一直接依赖的范围为provided的依赖,且传递性依赖的范围同样为provided;当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile除外,此时传递性依赖的范围为runtime。
1.6依赖调解
Maven引入的传递性依赖机制,一方面大大简化和方便了依赖声明,另一方面,大部分情况下我们只需要关心项目的直接依赖是什么,而不用考虑这些直接依赖会引入什么传递依赖。但是有时候,当传递性依赖造成问题的时候,我们就需要清楚的知道该传递性依赖是从哪条依赖路径引入的。
例如A->B->C->X(1.0)、A->D->X(2.0),X是A的传递性依赖,但是两条依赖路径上有两个版本的X,那个X会被Maven解析使用呢?Maven依赖调解的第一原则是:路径最近者优先。依赖调解第一原则不能解决所有问题,比如:A->B->Y(1.0)、A->C->Y(2.0),Y(1.0)和Y(2.0)的依赖路径长度是一样的,到底谁会解析?在Maven2.0.9开始,Maven定义了依赖调解的第二原则:第一声明者优先。
posted @
2012-03-06 09:24 邦 阅读(1311) |
评论 (0) |
编辑 收藏