两页面传值request_getParameter()为null值

Posted on

两页面传值request_getParameter()为null值

写词条,拿礼品—中国移动Wiki嘉年华 浅谈并行编程中的任务分解模式 CSDN社区

搜索 收藏 打印 关闭

CSDN社区 > Java > Web 开发

两页面传值request.getParameter()为null值

楼主sole_lodestar(弱势群体应该怎么办)2005-01-19 21:36:02 在 Java / Web 开发 提问

为什么在第二个页面使用request.getParameter()得不到上一页面提交过来的数据?

第一个页面:

<%@ page contentType="text/html;charset=GBK" errorPage="../include/error.jsp"%>

import_data




会计帐号:


密码:


请选择要更新的表


请选择要导入的文件:





第二个页面:

<%@ page contentType="text/html;charset=GBK" %>
<%@ page language="java" import="java.sql./" %>
<%@ page import="java.io.File" %>
<%@ page import="java.util.Date" %>
<%@ page import="java.io./
" %>
<%@ page import="java.lang./" %>
<%@ page import="java.util./
" %>
<%@ page import="jxl./*" %>
<%
//定义所有变量
String userName = ""; //会计帐号
String userPass = ""; //密码
String myFile = ""; //数据源文件
String myTable = ""; //所要更新的表

//取得提交数据
userName = request.getParameter("userName");
userPass = request.getParameter("userPass");
myFile = request.getParameter("myFile");
myTable = request.getParameter("myTable");
………………
问题点数:20、回复次数:11Top

1 楼jFresH_MaN(十一月的萧邦-夜曲)回复于 2005-01-19 21:42:56 得分 20

因为里面有一个上传文件,如果还有一般的字符串请求就得不到了
Top

2 楼sole_lodestar(弱势群体应该怎么办)回复于 2005-01-19 21:45:09 得分 0

那该怎么解决呢?Top

3 楼sole_lodestar(弱势群体应该怎么办)回复于 2005-01-19 21:49:52 得分 0

中间曾经把form的name属性改为form1,就可以了。可后来对第二页面修改后,又得不到值了。不知道为什么Top

4 楼jFresH_MaN(十一月的萧邦-夜曲)回复于 2005-01-19 21:50:34 得分 0

单独提交上传文件
不过现在一般都是使用jspsmartupload来上传文件,那样就没问题了Top

5 楼sole_lodestar(弱势群体应该怎么办)回复于 2005-01-19 21:54:33 得分 0

我并不是要上传文件,而是要在第二页面读取该文件内容。只要把我要上传的文件的完整路径(包括文件名)提交给第二个页面就可以了。Top

6 楼sole_lodestar(弱势群体应该怎么办)回复于 2005-01-19 21:55:23 得分 0

我并不是要上传文件,而是要在第二页面读取该文件内容。只要把我选中的文件的完整路径(包括文件名)提交给第二个页面就可以了。Top

7 楼jFresH_MaN(十一月的萧邦-夜曲)回复于 2005-01-19 21:58:24 得分 0

这样啊?
那你把ENCTYPE="multipart/form-data"去掉Top

8 楼sole_lodestar(弱势群体应该怎么办)回复于 2005-01-19 22:00:38 得分 0

嗯,对了。什么原因?
谢谢Top

9 楼xuerenrenmin()回复于 2005-01-19 22:03:30 得分 0

把第一个页面改为
<%@ page contentType="text/html;charset=GBK" errorPage="../include/error.jsp"%>


import_data




会计帐号:


密码:


请选择要更新的表


请选择要导入的文件:





应当没有问题了Top

10 楼jFresH_MaN(十一月的萧邦-夜曲)回复于 2005-01-19 22:03:40 得分 0

原来提交得方式就把那个文件作为流来传输了,普通字符串就传不过去了!
去掉之后默认都是传递字符串,所以只把那个文本框里面得字符串提交过去!Top

11 楼sole_lodestar(弱势群体应该怎么办)回复于 2005-01-19 22:05:38 得分 0

送分啰Top

相关问题

关键词

得分解答快速导航

相关链接

广告也精彩

反馈

请通过下述方式给我们反馈 反馈

x 提问

jsp

Posted on

jsp:include与include的区别

(题外话:引入的文件如果是jsp则应定义为///.jspf文件,如果其他文件可定义为///.inc文件,即include file。而且<%@ inlucde page="" %>除了可以引jspf还可以引servlet——很重要) 近日做一项目要用到JSP动态包含JSP,本想肯定很简单,但不想这么复杂,而且目前还没有求到好的答案,问题如下: /#文件:one.jsp 1

2 3 <%!

String var1=

"China"

; %> /#文件 two.jsp 1

2 3

4 <%!

String var1=

"America"

; String var2=

"England"

;

%> /#文件 three.jsp 1

2 3

4 5

6 7

8 9

10 11

12 <%

int j=1; if

(j==1){

%> <%@ include file=

"one.jsp"

%>

<% }

else

{

%> <%@ include file=

"two.jsp"

%>

<%}%> <%=var1%>

<%=var2%> 执行three.jsp会出什么结果? a.编译错误 b.显示China England 很多人理所当然的觉得肯定是a,因为j=1所以只包含one.jsp,two.jsp不会包含进来,但答案是b,上机测试就知道。 为什么? 因为@include要先于jsp的其他代码执行,所以两个文件都会被包含进来! ---------------------------------------- 如果你说用jsp:include可以解决问题,好,把three.jsp改成如下: #文件 three.jsp 1

2 3

4 5

6 7

8 9

10 11 <%

int j=1; String includeFile=

""

;

if

(j==1){ includeFile =

"one.jsp"

;

}

else

{ includeFile =

"two.jsp"

;

} %>

′ /> <%=var1%> ---------------------------------------- 结果是什么? a.编译错误 b.显示 China 是b吗,不,是a,编译错误!提示var1未定义。 为什么?因为jsp:include是动态包含,相当于把包含文件与被包含文件分开编译。 ---------------------------------------- 现把include的问题总结如下: 【引用 gfzhx(小小)的话】 动态包含相当于作了一个页面跳转,也就是相当于重新访问了另一个servlet,所以当然会提示变量没有定义,你想一想,一个类中申明了一个变量,第二个类直接是用这个变量,你说可以吗?其实你的情况和这个例子很像,这就是动态包含,而静态包含你可以看一看jsp编译后的java文件就知道了,它是直接将你包含的页面直接包含进去,然后再编译的。所以你的问题采用静态包含就可以了。不过不管怎么,还是不推荐采用这种形式,会给程序造成很多问题,至少比较难以维护了,可以说是一种不好的编程风格。建议采用其他方法解决问题。 【引用 xiao_yuer(小鱼儿)的话】 要使用引入文件中定义的变量,只能用@include指令。 也就是<%@ include file="one.jsp" %>,但这在一般情况下都不是动态的,是在jsp页面第一次编译时,把它导入的。而jsp编译后,这两个文件再作修改很多jsp服务器都不会侦测到,因为包含这两的jsp的jsp文件本身并没有发生变化。但很奇怪,weblogic6好像可以。你可以试试,不过不要抱太大希望,因为你这种要求不是很合理。向你这种情况,完全应该引入一个java类,这个类中定义一些变量(按你的说法都应该算是常量了,jsp取出来直接用而不会修改它再存回去),然后再jsp中得到那个类的实例,来进行处理。那样如果你要修改这些常量的值,就修改java类,而不用修改jsp. 【自己的:-))】 @include包含是静态包含,是把被包含文件加入到包含文件中然后进行编译,所以这种包含与解释执行的语言很象(例如php),而且JSP中@开头的语句都要先于其他语句执行,所以如上,用if.else来判断然后包含是不行的,所以以前如果是做PHP这种解释语言的人会觉得不适应。 jsp:include是既可以静态包含又可以动态包含,与@include不同的是,jsp:include没有@include那样的优先权,即不是现于其他语句执行的,所以jsp:include可以又选择性的包含。不过更重要的一点是,用jsp:include相当于编译两个不同的文件,所以如果被包含文件中仅仅是显示某些东西(例如被包含文件是纯HTML)的话,这种情况下,用jsp:include和@include来包含文件的效果是一样的,但如果要用jsp:include来显示被包含文件中定义的变量就不行了(为什么?见上面的引用吧,就不赘述了)。 【感谢】 gfzhx(小小)、xiao_yuer(小鱼儿) 来源: [http://www.wudongqi.com/article/529.htm](http://www.wudongqi.com/article/529.htm)

switch之enum

Posted on

switch之enum

switch之enum

2011-01-14 23:46 springtableclassstring编译器byte

记得曾经去一家公司面试,那时啥也不懂,面试我的那个人好像呆过IBM,数据结构、编译原理这些都很NB。

问答环节

他:java switch中能支持什么类型?

我:byte short char int ,jdk1.5出来了enum,同样也支持enum

他:为什么能支持byte short char int 而long不行?

我:这个可能是设计问题

他:其实jvm执行class文件的时候,byte short char int这些都是当int类型来执行的,long不能直接转换成int,编译阶段就通不过了。

我:我那个时候不太理解他说的那个意思,只能点点头

他:好,那接着讨论switch为什么支持enum,刚才也讨论过switch其实都是int类型,也只支持int,那enum不是int类型,是个对象,那为什么支持呢!

我:那个时候我就蒙了(心里想着,你这家伙,就胡扯),但我讲不出理由,就直接说不知道

他:其实在switch中enum也是int类型

我:心想----我不知道你说的是对还是错,你怎么说都行

自从那以后,哥去研究虚拟机,java指令

好,废话不多说了,现在来看一下代码,代码比较简单!

[java] view plaincopy

  1. //////////////////////////////////////*
  2. /////////////////////////////////////*
  3. 源代码
  4. /////////////////////////////////////*
  5. /////////////////////////////////////*/
  6. public static void testSwitchInt() {
  7. int intElement = 3;
  8. switch (intElement) {
  9. case 3:
  10. System.out.println("3");
  11. break;
  12. default:
  13. System.out.println("int DEFAULT");
  14. break;
  15. }
  16. }
  17. //int 类型反编译跟源代码是一样的
  18. //////////////////////////////////////*
  19. /////////////////////////////////////*
  20. 用javap工具看class指令
  21. /////////////////////////////////////*
  22. /////////////////////////////////////*/
  23. //case 的值本来就是int,这没什么好说的
  24. public static void testSwitchInt();
  25. Code:
  26. Stack=2, Locals=1, Args_size=0
  27. 0: iconst_3 //解释:加载int常量3
  28. 1: istore_0 //解释:保存int类型到局部变量表index为0的位置(其实保存的就是3)
  29. 2: iload_0 //加载局部变量表index为0的位置的int变量,用于switch里面
  30. 3: lookupswitch{ //1
  31. 3: 20;
  32. default: 31 }
  33. 20: getstatic /#9; //Field java/lang/System.out:Ljava/io/PrintStream;
  34. 23: ldc /#13; //String 3
  35. 25: invokevirtual /#11; //Method java/io/PrintStream.println:(Ljava/lang/St
  36. ring;)V
  37. 28: goto 39
  38. 31: getstatic /#9; //Field java/lang/System.out:Ljava/io/PrintStream;
  39. 34: ldc /#14; //String int DEFAULT
  40. 36: invokevirtual /#11; //Method java/io/PrintStream.println:(Ljava/lang/St
  41. ring;)V
  42. 39: return


  43. //////////////////////////////////////*
  44. /////////////////////////////////////*
  45. 源代码
  46. /////////////////////////////////////*
  47. /////////////////////////////////////*/
  48. public static void testSwitchChar() {
  49. int charElement = 'a'; //ascii对应的是97,编译器直接把这个值编译成97,case里面也是这样的
  50. switch (charElement) {
  51. case 'a':
  52. System.out.println("a");
  53. break;
  54. default:
  55. System.out.println("char DEFAULT");
  56. break;
  57. }
  58. }
  59. //////////////////////////////////////*
  60. /////////////////////////////////////*
  61. 编译后的代码
  62. /////////////////////////////////////*
  63. /////////////////////////////////////*/
  64. public static void testSwitchChar()
  65. {
  66. int charElement = 97;
  67. switch(charElement)
  68. {
  69. case 97: // 'a'
  70. System.out.println("a");
  71. break;
  72. default:
  73. System.out.println("char DEFAULT");
  74. break;
  75. }
  76. }
  77. //////////////////////////////////////*
  78. /////////////////////////////////////*
  79. 用javap工具看class指令
  80. /////////////////////////////////////*
  81. /////////////////////////////////////*/
  82. //case 的值本来就是char类型,但被编译器处理成int
  83. public static void testSwitchChar();
  84. Code:
  85. Stack=2, Locals=1, Args_size=0
  86. 0: bipush 97 //解释:加载int常量97,a的ascii码
  87. 2: istore_0 //接下来和上面都一样的
  88. 3: iload_0
  89. 4: tableswitch{ //97 to 97
  90. 97: 24;
  91. default: 35 }
  92. 24: getstatic /#46; //Field java/lang/System.out:Ljava/io/PrintStream;
  93. 27: ldc /#68; //String a
  94. 29: invokevirtual /#53; //Method java/io/PrintStream.println:(Ljava/lang/St
  95. ring;)V
  96. 32: goto 43
  97. 35: getstatic /#46; //Field java/lang/System.out:Ljava/io/PrintStream;
  98. 38: ldc /#70; //String char DEFAULT
  99. 40: invokevirtual /#53; //Method java/io/PrintStream.println:(Ljava/lang/St
  100. ring;)V
  101. 43: return
  102. byte short 也是同理,都会编译成int


  103. /////////////////////////上面的例子都比较好理解,enum大家可能也会有点疑惑/////////////////////////////////
  104. //////////////////////////////////////*
  105. /////////////////////////////////////*
  106. 源代码
  107. /////////////////////////////////////*
  108. /////////////////////////////////////*/
  109. enum EnumTest {
  110. WINTER, SUMMER, SPRING, AUTUMN;
  111. }
  112. public static void testSwitchEnum() {
  113. EnumTest enumElement = EnumTest.AUTUMN;
  114. switch (enumElement) {
  115. case AUTUMN:
  116. System.out.println("AUTUMN");
  117. break;
  118. default:
  119. System.out.println("enum DEFAULT");
  120. break;
  121. }
  122. }
  123. //////////////////////////////////////*
  124. /////////////////////////////////////*
  125. enum类编译后的代码
  126. /////////////////////////////////////*
  127. /////////////////////////////////////*/
  128. //enum其实也就是个普通的类,继承Enum
  129. public final class EnumTest extends Enum
  130. {
  131. private EnumTest(String s, int i)
  132. {
  133. super(s, i);
  134. //*调用父类的构造函数
  135. protected Enum(String name, int ordinal) {
  136. this.name = name; //名称
  137. this.ordinal = ordinal; 元素位置
  138. }
  139. /*/
  140. }
  141. public static EnumTest[] values()
  142. {
  143. EnumTest aenumtest[];
  144. int i;
  145. EnumTest aenumtest1[];
  146. System.arraycopy(aenumtest = ENUM$VALUES, 0, aenumtest1 = new EnumTest[i = aenumtest.length], 0, i);
  147. return aenumtest1;
  148. }
  149. public static EnumTest valueOf(String s)
  150. {
  151. return (EnumTest)Enum.valueOf(meiju/EnumTest, s);
  152. }
  153. public static final EnumTest WINTER;
  154. public static final EnumTest SUMMER;
  155. public static final EnumTest SPRING;
  156. public static final EnumTest AUTUMN;
  157. private static final EnumTest ENUM$VALUES[];
  158. static
  159. {
  160. //enum的位置的排好的,想数组一样,enum元素最终都保存在ENUM$VALUES数组
  161. WINTER = new EnumTest("WINTER", 0);
  162. SUMMER = new EnumTest("SUMMER", 1);
  163. SPRING = new EnumTest("SPRING", 2);
  164. AUTUMN = new EnumTest("AUTUMN", 3);
  165. ENUM$VALUES = (new EnumTest[] {
  166. WINTER, SUMMER, SPRING, AUTUMN
  167. });
  168. }
  169. }
  170. //////////////////////////////////////*
  171. /////////////////////////////////////*
  172. testSwitchEnum方法编译后的代码
  173. /////////////////////////////////////*
  174. /////////////////////////////////////*/
  175. 用到enum元素,所以会在当前类中多生成一个$SWITCH_TABLE$meiju$EnumTest()方法和$SWITCH_TABLE$meiju$EnumTest[]变量,用于switch
  176. static int[] $SWITCH_TABLE$meiju$EnumTest()
  177. {
  178. $SWITCH_TABLE$meiju$EnumTest;
  179. if($SWITCH_TABLE$meiju$EnumTest == null) goto _L2; else goto _L1
  180. _L1:
  181. return;
  182. _L2:
  183. JVM INSTR pop ;
  184. int ai[] = new int[EnumTest.values().length];
  185. try
  186. {
  187. ai[EnumTest.AUTUMN.ordinal()] = 4;
  188. }
  189. catch(NoSuchFieldError _ex) { }
  190. try
  191. {
  192. ai[EnumTest.SPRING.ordinal()] = 3;
  193. }
  194. catch(NoSuchFieldError _ex) { }
  195. try
  196. {
  197. ai[EnumTest.SUMMER.ordinal()] = 2;
  198. }
  199. catch(NoSuchFieldError _ex) { }
  200. try
  201. {
  202. ai[EnumTest.WINTER.ordinal()] = 1;
  203. }
  204. catch(NoSuchFieldError _ex) { }
  205. return $SWITCH_TABLE$meiju$EnumTest = ai;
  206. }
  207. private static int $SWITCH_TABLE$meiju$EnumTest[];//保存的是enum的index
  208. public static void testSwitchEnum()
  209. {
  210. EnumTest enumElement = EnumTest.AUTUMN;
  211. //这个就是上面所用到的变量
  212. switch($SWITCH_TABLE$meiju$EnumTest()[enumElement.ordinal()])
  213. {
  214. case 4: // '/004' 因为enum类的元素其实就是个常量,在编译阶段就能确定值,在源代码的case AUTUMN: 其实也就被他所在的ordinal()给替换掉了,其实就是索引
  215. System.out.println("AUTUMN");
  216. break;
  217. default:
  218. System.out.println("enum DEFAULT");
  219. break;
  220. }
  221. }
  222. //////////////////////////////////////*
  223. /////////////////////////////////////*
  224. 用javap工具看class指令
  225. /////////////////////////////////////*
  226. /////////////////////////////////////*/
  227. public static void testSwitchEnum();
  228. Code:
  229. Stack=2, Locals=1, Args_size=0
  230. 0: getstatic /#6; //Field meiju/EnumTest.AUTUMN:Lmeiju/EnumTest;
  231. 3: astore_0
  232. 4: getstatic /#7; //Field meiju/SwitchEnum$1.$SwitchMap$meiju$EnumTest
  233. :[I
  234. 7: aload_0
  235. 8: invokevirtual /#8; //Method meiju/EnumTest.ordinal:()I
  236. 11: iaload
  237. 12: lookupswitch{ //1
  238. 1: 32;
  239. default: 43 }
  240. 32: getstatic /#9; //Field java/lang/System.out:Ljava/io/PrintStream;
  241. 35: ldc /#10; //String AUTUMN
  242. 37: invokevirtual /#11; //Method java/io/PrintStream.println:(Ljava/lang/St
  243. ring;)V
  244. 40: goto 51
  245. 43: getstatic /#9; //Field java/lang/System.out:Ljava/io/PrintStream;
  246. 46: ldc /#12; //String enum DEFAULT
  247. 48: invokevirtual /#11; //Method java/io/PrintStream.println:(Ljava/lang/St
  248. ring;)V
  249. 51: return

事实证明当时他不是忽悠我,确实是这样的:)

来源: [http://blog.csdn.net/wzju64676266/article/details/6140871](http://blog.csdn.net/wzju64676266/article/details/6140871)

java操作word文档(jacob

Posted on

java操作word文档(jacob,poi)

项目需要,用户从系统里面下载word文档,该文档进行了填写限制和加密,用户只能在固定位置填写内容。现要求系统验证上传的附件是否从系统上下载下来的。

思路:系统上面的文档都加入一个固定书签,用户上传文档的时候,检验文档里是否包含这个书签。

采用jacob操作word文档

JACOB(java -com bridge)是一个 JAVA到微软的COM接口的桥梁。使用JACOB允许任何JVM访问COM对象,从而使JAVA应用程序能够调用COM对象。

下载地址:http://sourceforge.net/projects/jacob-project/

其中jacob-1.16.1-x64.dll 是用于64位机器上的,jacob-1.16.1-x86.dll用于32位的。

该dll放于 C:\Windows\system32 目录下。jacob.jar放于应用lib底下

测试代码 Java代码 收藏代码

  1. ActiveXComponent word = null;
  2. try {
  3. word = new ActiveXComponent("Word.Application");
  4. System.out.println("jacob当前版本:"+word.getBuildVersion());
  5. }catch(Exception e ){
  6. e.printStackTrace();
  7. }

ActiveXComponent word = null;

try { word = new ActiveXComponent("Word.Application");

    System.out.println("jacob当前版本:"+word.getBuildVersion());

}catch(Exception e ){

     e.printStackTrace();

}

下面再贴出网上常见的代码+自己整理的几个方法(模糊查询书签等)

注意插入书签+书签值的方法,要先插入书签值再选中书签值,之后插入书签。这样根据书签名才能取得书签值。否则根据网络上很多方法,都取不到书签值或者取到空。因为书签值可以是一个点也可以是一大段内容。 Java代码 收藏代码

  1. import java.io.File;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import com.gdcn.bpaf.common.helper.StringHelper;
  5. import com.jacob.activeX.ActiveXComponent;
  6. import com.jacob.com.ComThread;
  7. import com.jacob.com.Dispatch;
  8. import com.jacob.com.Variant;
  9. ///
  10. / /
  11. /*

    Description: {jacob操作word类}

  12. /*
  13. /*

    Copyright: Copyright (c) 2011

  14. /*
  15. /*

    CreateDate: 2012-6-28

  16. /*
  17. /* @author Beny
  18. /* @version 1.0
  19. /*/
  20. public class JacobHelper {
  21. // word文档
  22. private Dispatch doc;
  23. // word运行程序对象
  24. private ActiveXComponent word;
  25. // 所有word文档集合
  26. private Dispatch documents;
  27. // 选定的范围或插入点
  28. private Dispatch selection;
  29. private boolean saveOnExit = true;
  30. public JacobHelper(boolean visible) throws Exception {
  31. ComThread.InitSTA();//线程启动
  32. if (word == null) {
  33. word = new ActiveXComponent("Word.Application");
  34. word.setProperty("Visible", new Variant(visible)); // 不可见打开word
  35. word.setProperty("AutomationSecurity", new Variant(3)); // 禁用宏
  36. }
  37. if (documents == null)
  38. documents = word.getProperty("Documents").toDispatch();
  39. }
  40. ///
  41. /* 设置退出时参数
  42. /*
  43. /* @param saveOnExit
  44. /* boolean true-退出时保存文件,false-退出时不保存文件
  45. /*/
  46. public void setSaveOnExit(boolean saveOnExit) {
  47. this.saveOnExit = saveOnExit;
  48. }
  49. ///
  50. /* 创建一个新的word文档
  51. /*
  52. /*/
  53. public void createNewDocument() {
  54. doc = Dispatch.call(documents, "Add").toDispatch();
  55. selection = Dispatch.get(word, "Selection").toDispatch();
  56. }
  57. ///
  58. /* 打开一个已存在的文档
  59. /*
  60. /* @param docPath
  61. /*/
  62. public void openDocument(String docPath) {
  63. // closeDocument();
  64. doc = Dispatch.call(documents, "Open", docPath).toDispatch();
  65. selection = Dispatch.get(word, "Selection").toDispatch();
  66. }
  67. ///
  68. /* 只读方式打开一个加密的文档
  69. /*
  70. /* @param docPath-文件全名
  71. /* @param pwd-密码
  72. /*/
  73. public void openDocumentOnlyRead(String docPath, String pwd)
  74. throws Exception {
  75. // closeDocument();
  76. doc = Dispatch.callN(
  77. documents,
  78. "Open",
  79. new Object[] { docPath, new Variant(false), new Variant(true),
  80. new Variant(true), pwd, "", new Variant(false) })
  81. .toDispatch();
  82. selection = Dispatch.get(word, "Selection").toDispatch();
  83. }
  84. ///
  85. /* 打开一个加密的文档
  86. /* @param docPath
  87. /* @param pwd
  88. /* @throws Exception
  89. /*/
  90. public void openDocument(String docPath, String pwd) throws Exception {
  91. // closeDocument();
  92. doc = Dispatch.callN(
  93. documents,
  94. "Open",
  95. new Object[] { docPath, new Variant(false), new Variant(false),
  96. new Variant(true), pwd }).toDispatch();
  97. selection = Dispatch.get(word, "Selection").toDispatch();
  98. }
  99. ///
  100. /* 从选定内容或插入点开始查找文本
  101. /*
  102. /* @param toFindText
  103. /* 要查找的文本
  104. /* @return boolean true-查找到并选中该文本,false-未查找到文本
  105. /*/
  106. @SuppressWarnings("static-access")
  107. public boolean find(String toFindText) {
  108. if (toFindText == null || toFindText.equals(""))
  109. return false;
  110. // 从selection所在位置开始查询
  111. Dispatch find = word.call(selection, "Find").toDispatch();
  112. // 设置要查找的内容
  113. Dispatch.put(find, "Text", toFindText);
  114. // 向前查找
  115. Dispatch.put(find, "Forward", "True");
  116. // 设置格式
  117. Dispatch.put(find, "Format", "True");
  118. // 大小写匹配
  119. Dispatch.put(find, "MatchCase", "True");
  120. // 全字匹配
  121. Dispatch.put(find, "MatchWholeWord", "false");
  122. // 查找并选中
  123. return Dispatch.call(find, "Execute").getBoolean();
  124. }
  125. ///
  126. /* 把选定选定内容设定为替换文本
  127. /*
  128. /* @param toFindText
  129. /* 查找字符串
  130. /* @param newText
  131. /* 要替换的内容
  132. /* @return
  133. /*/
  134. public boolean replaceText(String toFindText, String newText) {
  135. if (!find(toFindText))
  136. return false;
  137. Dispatch.put(selection, "Text", newText);
  138. return true;
  139. }
  140. ///
  141. /* 全局替换文本
  142. /*
  143. /* @param toFindText
  144. /* 查找字符串
  145. /* @param newText
  146. /* 要替换的内容
  147. /*/
  148. public void replaceAllText(String toFindText, String newText) {
  149. while (find(toFindText)) {
  150. Dispatch.put(selection, "Text", newText);
  151. Dispatch.call(selection, "MoveRight");
  152. }
  153. }
  154. ///
  155. /* 在当前插入点插入字符串
  156. /*
  157. /* @param newText
  158. /* 要插入的新字符串
  159. /*/
  160. public void insertText(String newText) {
  161. Dispatch.put(selection, "Text", newText);
  162. }
  163. ///
  164. /* 设置当前选定内容的字体
  165. /*
  166. /* @param boldSize
  167. /* @param italicSize
  168. /* @param underLineSize
  169. /* 下划线
  170. /* @param colorSize
  171. /* 字体颜色
  172. /* @param size
  173. /* 字体大小
  174. /* @param name
  175. /* 字体名称
  176. /* @param hidden
  177. /* 是否隐藏
  178. /*/
  179. public void setFont(boolean bold, boolean italic, boolean underLine,
  180. String colorSize, String size, String name,boolean hidden) {
  181. Dispatch font = Dispatch.get(selection, "Font").toDispatch();
  182. Dispatch.put(font, "Name", new Variant(name));
  183. Dispatch.put(font, "Bold", new Variant(bold));
  184. Dispatch.put(font, "Italic", new Variant(italic));
  185. Dispatch.put(font, "Underline", new Variant(underLine));
  186. Dispatch.put(font, "Color", colorSize);
  187. Dispatch.put(font, "Size", size);
  188. Dispatch.put(font, "Hidden", hidden);
  189. }
  190. ///
  191. /* 文件保存或另存为
  192. /*
  193. /* @param savePath
  194. /* 保存或另存为路径
  195. /*/
  196. public void save(String savePath) {
  197. Dispatch.call(Dispatch.call(word, "WordBasic").getDispatch(),
  198. "FileSaveAs", savePath);
  199. }
  200. ///
  201. /* 文件保存为html格式
  202. /*
  203. /* @param savePath
  204. /* @param htmlPath
  205. /*/
  206. public void saveAsHtml(String htmlPath) {
  207. Dispatch.invoke(doc, "SaveAs", Dispatch.Method, new Object[] {
  208. htmlPath, new Variant(8) }, new int[1]);
  209. }
  210. ///
  211. /* 关闭文档
  212. /*
  213. /* @param val
  214. /* 0不保存修改 -1 保存修改 -2 提示是否保存修改
  215. /*/
  216. public void closeDocument(int val) {
  217. Dispatch.call(doc, "Close", new Variant(val));//注 是documents而不是doc
  218. documents = null;
  219. doc = null;
  220. }
  221. ///
  222. /* 关闭当前word文档
  223. /*
  224. /*/
  225. public void closeDocument() {
  226. if (documents != null) {
  227. Dispatch.call(documents, "Save");
  228. Dispatch.call(documents, "Close", new Variant(saveOnExit));
  229. documents = null;
  230. doc = null;
  231. }
  232. }
  233. public void closeDocumentWithoutSave() {
  234. if (documents != null) {
  235. Dispatch.call(documents, "Close", new Variant(false));
  236. documents = null;
  237. doc = null;
  238. }
  239. }
  240. ///
  241. /* 保存并关闭全部应用
  242. /*
  243. /*/
  244. public void close() {
  245. closeDocument(-1);
  246. if (word != null) {
  247. // Dispatch.call(word, "Quit");
  248. word.invoke("Quit", new Variant[] {});
  249. word = null;
  250. }
  251. selection = null;
  252. documents = null;
  253. ComThread.Release();//释放com线程。根据jacob的帮助文档,com的线程回收不由java的垃圾回收器处理
  254. }
  255. ///
  256. /* 打印当前word文档
  257. /*
  258. /*/
  259. public void printFile() {
  260. if (doc != null) {
  261. Dispatch.call(doc, "PrintOut");
  262. }
  263. }
  264. ///
  265. /* 保护当前档,如果不存在, 使用expression.Protect(Type, NoReset, Password)
  266. /*
  267. /* @param pwd
  268. /* @param type
  269. /* WdProtectionType 常量之一(int 类型,只读):
  270. /* 1-wdAllowOnlyComments 仅批注
  271. /* 2-wdAllowOnlyFormFields 仅填写窗体
  272. /* 0-wdAllowOnlyRevisions 仅修订
  273. /* -1-wdNoProtection 无保护,
  274. /* 3-wdAllowOnlyReading 只读
  275. /*
  276. /*/
  277. public void protectedWord(String pwd,String type) {
  278. String protectionType = Dispatch.get(doc, "ProtectionType").toString();
  279. if (protectionType.equals("-1")) {
  280. Dispatch.call(doc, "Protect", Integer.parseInt(type), new Variant(true),pwd);
  281. }
  282. }
  283. ///
  284. /* 解除文档保护,如果存在
  285. /*
  286. /* @param pwd
  287. /* WdProtectionType 常量之一(int 类型,只读):
  288. /* 1-wdAllowOnlyComments 仅批注
  289. /* 2-wdAllowOnlyFormFields 仅填写窗体
  290. /* 0-wdAllowOnlyRevisions 仅修订
  291. /* -1-wdNoProtection 无保护,
  292. /* 3-wdAllowOnlyReading 只读
  293. /*
  294. /*/
  295. public void unProtectedWord(String pwd) {
  296. String protectionType = Dispatch.get(doc, "ProtectionType").toString();
  297. if (!protectionType.equals("0")&&!protectionType.equals("-1")) {
  298. Dispatch.call(doc, "Unprotect", pwd);
  299. }
  300. }
  301. ///
  302. /* 返回文档的保护类型
  303. /* @return
  304. /*/
  305. public String getProtectedType(){
  306. return Dispatch.get(doc, "ProtectionType").toString();
  307. }
  308. ///
  309. /* 设置word文档安全级别
  310. /*
  311. /* @param value
  312. /* 1-msoAutomationSecurityByUI 使用“安全”对话框指定的安全设置。
  313. /* 2-msoAutomationSecurityForceDisable
  314. /* 在程序打开的所有文件中禁用所有宏,而不显示任何安全提醒。 3-msoAutomationSecurityLow
  315. /* 启用所有宏,这是启动应用程序时的默认值。
  316. /*/
  317. public void setAutomationSecurity(int value) {
  318. word.setProperty("AutomationSecurity", new Variant(value));
  319. }
  320. ///
  321. /* 在word中插入标签 labelName是标签名,labelValue是标签值
  322. /* @param labelName
  323. /* @param labelValue
  324. /*/
  325. public void insertLabelValue(String labelName,String labelValue) {
  326. Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();
  327. boolean isExist = Dispatch.call(bookMarks, "Exists", labelName).getBoolean();
  328. if (isExist == true) {
  329. Dispatch rangeItem1 = Dispatch.call(bookMarks, "Item", labelName).toDispatch();
  330. Dispatch range1 = Dispatch.call(rangeItem1, "Range").toDispatch();
  331. String bookMark1Value = Dispatch.get(range1, "Text").toString();
  332. System.out.println("书签内容:"+bookMark1Value);
  333. } else {
  334. System.out.println("当前书签不存在,重新建立!");
  335. //TODO 先插入文字,再查找选中文字,再插入标签
  336. this.insertText(labelValue);
  337. // this.find(labelValue);//查找文字,并选中
  338. this.setFont(true, true,true,"102,92,38", "20", "",true);
  339. Dispatch.call(bookMarks, "Add", labelName, selection);
  340. Dispatch.call(bookMarks, "Hidden", labelName);
  341. }
  342. }
  343. ///
  344. /* 在word中插入标签 labelName是标签名
  345. /* @param labelName
  346. /*/
  347. public void insertLabel(String labelName) {
  348. Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();
  349. boolean isExist = Dispatch.call(bookMarks, "Exists", labelName).getBoolean();
  350. if (isExist == true) {
  351. System.out.println("书签已存在");
  352. } else {
  353. System.out.println("建立书签:"+labelName);
  354. Dispatch.call(bookMarks, "Add", labelName, selection);
  355. }
  356. }
  357. ///
  358. /* 查找书签
  359. /* @param labelName
  360. /* @return
  361. /*/
  362. public boolean findLabel(String labelName) {
  363. Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();
  364. boolean isExist = Dispatch.call(bookMarks, "Exists", labelName).getBoolean();
  365. if (isExist == true) {
  366. return true;
  367. } else {
  368. System.out.println("当前书签不存在!");
  369. return false;
  370. }
  371. }
  372. ///
  373. /* 模糊查找书签,并返回准确的书签名称
  374. /* @param labelName
  375. /* @return
  376. /*/
  377. public String findLabelLike(String labelName) {
  378. Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();
  379. int count = Dispatch.get(bookMarks, "Count").getInt(); // 书签数
  380. Dispatch rangeItem = null;
  381. String lname = "";
  382. for(int i=1;i<=count;i++){
  383. rangeItem = Dispatch.call(bookMarks, "Item", new Variant(i)).toDispatch();
  384. lname = Dispatch.call(rangeItem, "Name").toString();//书签名称
  385. if(lname.startsWith(labelName)){//前面匹配
  386. // return lname.replaceFirst(labelName, "");//返回后面值
  387. return lname;
  388. }
  389. }
  390. return "";
  391. }
  392. ///
  393. /* 模糊删除书签
  394. /* @param labelName
  395. /*/
  396. public void deleteLableLike(String labelName){
  397. Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();
  398. int count = Dispatch.get(bookMarks, "Count").getInt(); // 书签数
  399. Dispatch rangeItem = null;
  400. String lname = "";
  401. for(int i=1;i<=count;i++){
  402. rangeItem = Dispatch.call(bookMarks, "Item", new Variant(i)).toDispatch();
  403. lname = Dispatch.call(rangeItem, "Name").toString();//书签名称
  404. if(lname.startsWith(labelName)){//前面匹配
  405. Dispatch.call(rangeItem, "Delete");
  406. count--;//书签已被删除,书签数目和当前书签都要相应减1,否则会报错:集合找不到
  407. i--;
  408. }
  409. }
  410. }
  411. ///
  412. /* 获取书签内容
  413. /* @param labelName
  414. /* @return
  415. /*/
  416. public String getLableValue(String labelName){
  417. if(this.findLabel(labelName)){
  418. Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();
  419. Dispatch rangeItem1 = Dispatch.call(bookMarks, "Item", labelName).toDispatch();
  420. Dispatch range1 = Dispatch.call(rangeItem1, "Range").toDispatch();
  421. Dispatch font = Dispatch.get(range1, "Font").toDispatch();
  422. Dispatch.put(font, "Hidden", new Variant(false)); //显示书签内容
  423. String bookMark1Value = Dispatch.get(range1, "Text").toString();
  424. System.out.println("书签内容:"+bookMark1Value);
  425. // font = Dispatch.get(range1, "Font").toDispatch();
  426. // Dispatch.put(font, "Hidden", new Variant(true)); //隐藏书签内容
  427. return bookMark1Value;
  428. }
  429. return "";
  430. }
  431. public static void main(String[] args) throws Exception {
  432. }
  433. }

import java.io.File;

import java.util.HashMap; import java.util.Map;

import com.gdcn.bpaf.common.helper.StringHelper;

import com.jacob.activeX.ActiveXComponent; import com.jacob.com.ComThread;

import com.jacob.com.Dispatch; import com.jacob.com.Variant;

/// / /

/

Description: {jacob操作word类}

/

/

Copyright: Copyright (c) 2011

/

/

CreateDate: 2012-6-28

/

/ @author Beny / @version 1.0

/*/

public class JacobHelper { // word文档

private Dispatch doc;


// word运行程序对象
private ActiveXComponent word;


// 所有word文档集合

private Dispatch documents;


// 选定的范围或插入点
private Dispatch selection;


private boolean saveOnExit = true;


public JacobHelper(boolean visible) throws Exception {

    ComThread.InitSTA();//线程启动
    if (word == null) {

        word = new ActiveXComponent("Word.Application");
        word.setProperty("Visible", new Variant(visible)); // 不可见打开word

        word.setProperty("AutomationSecurity", new Variant(3)); // 禁用宏
    }

    if (documents == null)
        documents = word.getProperty("Documents").toDispatch();

}


//*/*
 /* 设置退出时参数

 /*
 /* @param saveOnExit

 /*            boolean true-退出时保存文件,false-退出时不保存文件
 /*/

public void setSaveOnExit(boolean saveOnExit) {
    this.saveOnExit = saveOnExit;

}


//*/*
 /* 创建一个新的word文档

 /*
 /*/

public void createNewDocument() {
    doc = Dispatch.call(documents, "Add").toDispatch();

    selection = Dispatch.get(word, "Selection").toDispatch();
}


//*/*

 /* 打开一个已存在的文档
 /*

 /* @param docPath
 /*/

public void openDocument(String docPath) {

// closeDocument();

    doc = Dispatch.call(documents, "Open", docPath).toDispatch();
    selection = Dispatch.get(word, "Selection").toDispatch();

}


//*/*
 /* 只读方式打开一个加密的文档

 /*
 /* @param docPath-文件全名

 /* @param pwd-密码
 /*/

public void openDocumentOnlyRead(String docPath, String pwd)
        throws Exception {

// closeDocument(); doc = Dispatch.callN(

            documents,
            "Open",

            new Object[] { docPath, new Variant(false), new Variant(true),
                    new Variant(true), pwd, "", new Variant(false) })

            .toDispatch();
    selection = Dispatch.get(word, "Selection").toDispatch();

}
//*/*

 /* 打开一个加密的文档
 /* @param docPath

 /* @param pwd
 /* @throws Exception

 /*/
public void openDocument(String docPath, String pwd) throws Exception {

// closeDocument(); doc = Dispatch.callN(

            documents,
            "Open",

            new Object[] { docPath, new Variant(false), new Variant(false),
                    new Variant(true), pwd }).toDispatch();

    selection = Dispatch.get(word, "Selection").toDispatch();
}


//*/*

 /* 从选定内容或插入点开始查找文本
 /*

 /* @param toFindText
 /*            要查找的文本

 /* @return boolean true-查找到并选中该文本,false-未查找到文本
 /*/

@SuppressWarnings("static-access")
public boolean find(String toFindText) {

    if (toFindText == null || toFindText.equals(""))
        return false;

    // 从selection所在位置开始查询
    Dispatch find = word.call(selection, "Find").toDispatch();

    // 设置要查找的内容
    Dispatch.put(find, "Text", toFindText);

    // 向前查找
    Dispatch.put(find, "Forward", "True");

    // 设置格式
    Dispatch.put(find, "Format", "True");

    // 大小写匹配
    Dispatch.put(find, "MatchCase", "True");

    // 全字匹配
    Dispatch.put(find, "MatchWholeWord", "false");

    // 查找并选中
    return Dispatch.call(find, "Execute").getBoolean();

}


//*/*
 /* 把选定选定内容设定为替换文本

 /*
 /* @param toFindText

 /*            查找字符串
 /* @param newText

 /*            要替换的内容
 /* @return

 /*/
public boolean replaceText(String toFindText, String newText) {

    if (!find(toFindText))
        return false;

    Dispatch.put(selection, "Text", newText);
    return true;

}


//*/*
 /* 全局替换文本

 /*
 /* @param toFindText

 /*            查找字符串
 /* @param newText

 /*            要替换的内容
 /*/

public void replaceAllText(String toFindText, String newText) {
    while (find(toFindText)) {

        Dispatch.put(selection, "Text", newText);
        Dispatch.call(selection, "MoveRight");

    }
}


//*/*

 /* 在当前插入点插入字符串
 /*

 /* @param newText
 /*            要插入的新字符串

 /*/
public void insertText(String newText) {

    Dispatch.put(selection, "Text", newText);
}





//*/*

 /* 设置当前选定内容的字体
 /*

 /* @param boldSize
 /* @param italicSize

 /* @param underLineSize
 /*            下划线

 /* @param colorSize
 /*            字体颜色

 /* @param size
 /*            字体大小

 /* @param name
 /*            字体名称

 /* @param hidden
 /*            是否隐藏

 /*/
public void setFont(boolean bold, boolean italic, boolean underLine,

        String colorSize, String size, String name,boolean hidden) {
    Dispatch font = Dispatch.get(selection, "Font").toDispatch();

    Dispatch.put(font, "Name", new Variant(name));
    Dispatch.put(font, "Bold", new Variant(bold));

    Dispatch.put(font, "Italic", new Variant(italic));
    Dispatch.put(font, "Underline", new Variant(underLine));

    Dispatch.put(font, "Color", colorSize);
    Dispatch.put(font, "Size", size);

    Dispatch.put(font, "Hidden", hidden);
}




//*/*
 /* 文件保存或另存为

 /*
 /* @param savePath

 /*            保存或另存为路径
 /*/

public void save(String savePath) {
    Dispatch.call(Dispatch.call(word, "WordBasic").getDispatch(),

            "FileSaveAs", savePath);
}


//*/*

 /* 文件保存为html格式
 /*

 /* @param savePath
 /* @param htmlPath

 /*/
public void saveAsHtml(String htmlPath) {

    Dispatch.invoke(doc, "SaveAs", Dispatch.Method, new Object[] {
            htmlPath, new Variant(8) }, new int[1]);

}


//*/*
 /* 关闭文档

 /*
 /* @param val

 /*            0不保存修改 -1 保存修改 -2 提示是否保存修改
 /*/

public void closeDocument(int val) {
    Dispatch.call(doc, "Close", new Variant(val));//注 是documents而不是doc

    documents = null;
    doc = null;

}


//*/*
 /* 关闭当前word文档

 /*
 /*/

public void closeDocument() {
    if (documents != null) {

        Dispatch.call(documents, "Save");
        Dispatch.call(documents, "Close", new Variant(saveOnExit));

        documents = null;
        doc = null;

    }
}


public void closeDocumentWithoutSave() {

    if (documents != null) {
        Dispatch.call(documents, "Close", new Variant(false));

        documents = null;
        doc = null;

    }
}


//*/*

 /* 保存并关闭全部应用
 /*

 /*/
public void close() {

    closeDocument(-1);
    if (word != null) {

// Dispatch.call(word, "Quit"); word.invoke("Quit", new Variant[] {});

        word = null;
    }

    selection = null;
    documents = null;

    ComThread.Release();//释放com线程。根据jacob的帮助文档,com的线程回收不由java的垃圾回收器处理


}
//*/*

 /* 打印当前word文档
 /*

 /*/
public void printFile() {

    if (doc != null) {
        Dispatch.call(doc, "PrintOut");

    }
}


//*/*

 /* 保护当前档,如果不存在, 使用expression.Protect(Type, NoReset, Password)
 /*

 /* @param pwd
 /* @param type

 /*            WdProtectionType 常量之一(int 类型,只读):
 /*            1-wdAllowOnlyComments  仅批注

 /*            2-wdAllowOnlyFormFields 仅填写窗体
 /*            0-wdAllowOnlyRevisions 仅修订

 /*            -1-wdNoProtection 无保护,
 /*            3-wdAllowOnlyReading 只读

 /*
 /*/

public void protectedWord(String pwd,String type) {
    String protectionType = Dispatch.get(doc, "ProtectionType").toString();

    if (protectionType.equals("-1")) {
        Dispatch.call(doc, "Protect", Integer.parseInt(type), new Variant(true),pwd);

    }
}


//*/*

 /* 解除文档保护,如果存在
 /*

 /* @param pwd
 /*            WdProtectionType 常量之一(int 类型,只读):

 /*            1-wdAllowOnlyComments  仅批注
 /*            2-wdAllowOnlyFormFields 仅填写窗体

 /*            0-wdAllowOnlyRevisions 仅修订
 /*            -1-wdNoProtection 无保护,

 /*            3-wdAllowOnlyReading 只读
 /*

 /*/
public void unProtectedWord(String pwd) {

    String protectionType = Dispatch.get(doc, "ProtectionType").toString();
    if (!protectionType.equals("0")&&!protectionType.equals("-1")) {

        Dispatch.call(doc, "Unprotect", pwd);
    }

}
//*/*

 /* 返回文档的保护类型
 /* @return

 /*/
public String getProtectedType(){

    return Dispatch.get(doc, "ProtectionType").toString();
}


//*/*

 /* 设置word文档安全级别
 /*

 /* @param value
 /*            1-msoAutomationSecurityByUI 使用“安全”对话框指定的安全设置。

 /*            2-msoAutomationSecurityForceDisable
 /*            在程序打开的所有文件中禁用所有宏,而不显示任何安全提醒。 3-msoAutomationSecurityLow

 /*            启用所有宏,这是启动应用程序时的默认值。
 /*/

public void setAutomationSecurity(int value) {
    word.setProperty("AutomationSecurity", new Variant(value));

}






//*/*

 /* 在word中插入标签 labelName是标签名,labelValue是标签值
 /* @param labelName

 /* @param labelValue
 /*/

public  void insertLabelValue(String labelName,String labelValue) {


   Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();
   boolean isExist = Dispatch.call(bookMarks, "Exists", labelName).getBoolean();

    if (isExist == true) {
        Dispatch rangeItem1 = Dispatch.call(bookMarks, "Item", labelName).toDispatch();

        Dispatch range1 = Dispatch.call(rangeItem1, "Range").toDispatch();
        String bookMark1Value = Dispatch.get(range1, "Text").toString();

          System.out.println("书签内容:"+bookMark1Value);
    } else {

        System.out.println("当前书签不存在,重新建立!");
        //TODO 先插入文字,再查找选中文字,再插入标签

        this.insertText(labelValue);

// this.find(labelValue);//查找文字,并选中

        this.setFont(true, true,true,"102,92,38", "20", "",true);
         Dispatch.call(bookMarks, "Add", labelName, selection);

         Dispatch.call(bookMarks, "Hidden", labelName);
    }

}
//*/*

 /* 在word中插入标签 labelName是标签名
 /* @param labelName

 /*/
public  void insertLabel(String labelName) {


   Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();

   boolean isExist = Dispatch.call(bookMarks, "Exists", labelName).getBoolean();
    if (isExist == true) {

          System.out.println("书签已存在");
    } else {

        System.out.println("建立书签:"+labelName);
         Dispatch.call(bookMarks, "Add", labelName, selection);

    }
}   

//*/*
 /* 查找书签

 /* @param labelName
 /* @return

 /*/
public boolean findLabel(String labelName) {

   Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();
   boolean isExist = Dispatch.call(bookMarks, "Exists", labelName).getBoolean();

   if (isExist == true) {
          return true;

    } else {
        System.out.println("当前书签不存在!");

        return false;
    }

}
//*/*

 /* 模糊查找书签,并返回准确的书签名称
 /* @param labelName

 /* @return
 /*/

public String findLabelLike(String labelName) {
   Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();

   int count = Dispatch.get(bookMarks, "Count").getInt(); // 书签数
   Dispatch rangeItem = null;

   String lname = "";
   for(int i=1;i<=count;i++){

       rangeItem = Dispatch.call(bookMarks, "Item", new Variant(i)).toDispatch();
       lname = Dispatch.call(rangeItem, "Name").toString();//书签名称

       if(lname.startsWith(labelName)){//前面匹配

// return lname.replaceFirst(labelName, "");//返回后面值

           return lname;
       }

   }
   return "";

}
//*/*

 /* 模糊删除书签
 /* @param labelName

 /*/
public void deleteLableLike(String labelName){

    Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();
   int count = Dispatch.get(bookMarks, "Count").getInt(); // 书签数

   Dispatch rangeItem = null;
   String lname = "";

   for(int i=1;i<=count;i++){
       rangeItem = Dispatch.call(bookMarks, "Item", new Variant(i)).toDispatch();

       lname = Dispatch.call(rangeItem, "Name").toString();//书签名称
       if(lname.startsWith(labelName)){//前面匹配

           Dispatch.call(rangeItem, "Delete");
           count--;//书签已被删除,书签数目和当前书签都要相应减1,否则会报错:集合找不到

           i--;
       }

   }
}

//*/*
 /* 获取书签内容

 /* @param labelName
 /* @return

 /*/
public String getLableValue(String labelName){

    if(this.findLabel(labelName)){
        Dispatch bookMarks = Dispatch.call(doc, "Bookmarks").toDispatch();

        Dispatch rangeItem1 = Dispatch.call(bookMarks, "Item", labelName).toDispatch();
        Dispatch range1 = Dispatch.call(rangeItem1, "Range").toDispatch();

        Dispatch font = Dispatch.get(range1, "Font").toDispatch();
        Dispatch.put(font, "Hidden", new Variant(false)); //显示书签内容

        String bookMark1Value = Dispatch.get(range1, "Text").toString();
          System.out.println("书签内容:"+bookMark1Value);

// font = Dispatch.get(range1, "Font").toDispatch(); // Dispatch.put(font, "Hidden", new Variant(true)); //隐藏书签内容

          return bookMark1Value;
    }

    return "";
}




public static void main(String[] args) throws Exception {


}   

}

采用jacob方式操作文档,经常会出现卡机的现象,所以最后采用poi方式来操作书签。若单纯的操作书签用poi方式还是比较简单的,但要操作表格、文档格式之类的还是用jacob功能比较强大。 Java代码 收藏代码

  1. InputStream input = null;
  2. File docFile = new File( fileName );
  3. HWPFDocument document = null;
  4. try{
  5. input = new FileInputStream(docFile);//加载 doc 文档
  6. document = new HWPFDocument(input);//文件流方式创建hwpf
  7. Bookmarks bookmarks = document.getBookmarks();//文档书签
  8. for(int i=0,length=bookmarks.getBookmarksCount();i<length;i++){
  9. bookmarkName = bookmarks.getBookmark(i).getName();
  10. //.....
  11. }
  12. }catch( Exception e){
  13. }finally{
  14. if( null != input )
  15. input.close();
  16. }

InputStream input = null;

File docFile = new File( fileName ); HWPFDocument document = null;

try{ input = new FileInputStream(docFile);//加载 doc 文档

document = new HWPFDocument(input);//文件流方式创建hwpf
Bookmarks bookmarks =  document.getBookmarks();//文档书签

for(int i=0,length=bookmarks.getBookmarksCount();i<length;i++){
    bookmarkName = bookmarks.getBookmark(i).getName();

    //.....
}

}catch( Exception e){

}finally{

if( null != input )
        input.close();

}

聊聊并发(三)——JAVA线程池的分析和使用

Posted on

聊聊并发(三)——JAVA线程池的分析和使用

分享到

百度分享

聊聊并发(三)——JAVA线程池的分析和使用

作者 方腾飞 发布于 十一月 15, 2012 | 14 评论

1. 引言

合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。但是要做到合理的利用线程池,必须对其原理了如指掌。

2. 线程池的使用

线程池的创建

我们可以通过ThreadPoolExecutor来创建一个线程池。 new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);

创建一个线程池需要输入几个参数:

  • corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
  • runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。

  • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。

  • LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
  • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
  • PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
  • maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
  • ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
  • RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。

  • AbortPolicy:直接抛出异常。

  • CallerRunsPolicy:只用调用者所在线程来运行任务。
  • DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  • DiscardPolicy:不处理,丢弃掉。
  • 当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。
  • keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
  • TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。

向线程池提交任务

我们可以使用execute提交的任务,但是execute方法没有返回值,所以无法判断任务是否被线程池执行成功。通过以下代码可知execute方法输入的任务是一个Runnable类的实例。 threadsPool.execute(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } });

我们也可以使用submit 方法来提交任务,它会返回一个future,那么我们可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成,而使用get(long timeout, TimeUnit unit)方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。

Future future = executor.submit(harReturnValuetask); try { Object s = future.get(); } catch (InterruptedException e) { // 处理中断异常 } catch (ExecutionException e) { // 处理无法执行任务异常 } finally { // 关闭线程池 executor.shutdown(); }

线程池的关闭

我们可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池,它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

只要调用了这两个关闭方法的其中一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于我们应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow。

3. 线程池的分析

流程分析:线程池的主要工作流程如下图:

从上图我们可以看出,当提交一个新任务到线程池时,线程池的处理流程如下:

  1. 首先线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务。满了,则进入下个流程。
  2. 其次线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。
  3. 最后线程池判断整个线程池是否已满?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务。

源码分析。上面的流程分析让我们很直观的了解了线程池的工作原理,让我们再通过源代码来看看是如何实现的。线程池执行任务的方法如下: public void execute(Runnable command) { if (command == null) throw new NullPointerException(); //如果线程数小于基本线程数,则创建线程并执行当前任务 if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) { //如线程数大于等于基本线程数或线程创建失败,则将当前任务放到工作队列中。 if (runState == RUNNING && workQueue.offer(command)) { if (runState != RUNNING || poolSize == 0) ensureQueuedTaskHandled(command); } //如果线程池不处于运行中或任务无法放入队列,并且当前线程数量小于最大允许的线程数量, 则创建一个线程执行任务。 else if (!addIfUnderMaximumPoolSize(command)) //抛出RejectedExecutionException异常 reject(command); // is shutdown or saturated } }

工作线程。线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,还会无限循环获取工作队列里的任务来执行。我们可以从Worker的run方法里看到这点:

public void run() { try { Runnable task = firstTask; firstTask = null; while (task != null || (task = getTask()) != null) { runTask(task); task = null; } } finally { workerDone(this); } }

4. 合理的配置线程池

要想合理的配置线程池,就必须首先分析任务特性,可以从以下几个角度来进行分析:

  1. 任务的性质:CPU密集型任务,IO密集型任务和混合型任务。
  2. 任务的优先级:高,中和低。
  3. 任务的执行时间:长,中和短。
  4. 任务的依赖性:是否依赖其他系统资源,如数据库连接。

任务性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务配置尽可能小的线程,如配置Ncpu+1个线程的线程池。IO密集型任务则由于线程并不是一直在执行任务,则配置尽可能多的线程,如2/*Ncpu。混合型的任务,如果可以拆分,则将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐率要高于串行执行的吞吐率,如果这两个任务执行时间相差太大,则没必要进行分解。我们可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。

优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级高的任务先得到执行,需要注意的是如果一直有优先级高的任务提交到队列里,那么优先级低的任务可能永远不能执行。

执行时间不同的任务可以交给不同规模的线程池来处理,或者也可以使用优先级队列,让执行时间短的任务先执行。

依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,如果等待的时间越长CPU空闲时间就越长,那么线程数应该设置越大,这样才能更好的利用CPU。

建议使用有界队列,有界队列能增加系统的稳定性和预警能力,可以根据需要设大一点,比如几千。有一次我们组使用的后台任务线程池的队列和线程池全满了,不断的抛出抛弃任务的异常,通过排查发现是数据库出现了问题,导致执行SQL变得非常缓慢,因为后台任务线程池里的任务全是需要向数据库查询和插入数据的,所以导致线程池里的工作线程全部阻塞住,任务积压在线程池里。如果当时我们设置成无界队列,线程池的队列就会越来越多,有可能会撑满内存,导致整个系统不可用,而不只是后台任务出现问题。当然我们的系统所有的任务是用的单独的服务器部署的,而我们使用不同规模的线程池跑不同类型的任务,但是出现这样问题时也会影响到其他任务。

5. 线程池的监控

通过线程池提供的参数进行监控。线程池里有一些属性在监控线程池的时候可以使用

  • taskCount:线程池需要执行的任务数量。
  • completedTaskCount:线程池在运行过程中已完成的任务数量。小于或等于taskCount。
  • largestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过。如等于线程池的最大大小,则表示线程池曾经满了。
  • getPoolSize:线程池的线程数量。如果线程池不销毁的话,池里的线程不会自动销毁,所以这个大小只增不+ getActiveCount:获取活动的线程数。

通过扩展线程池进行监控。通过继承线程池并重写线程池的beforeExecute,afterExecute和terminated方法,我们可以在任务执行前,执行后和线程池关闭前干一些事情。如监控任务的平均执行时间,最大执行时间和最小执行时间等。这几个方法在线程池里是空方法。如: protected void beforeExecute(Thread t, Runnable r) { }

6. 参考资料

  • Java并发编程实战。
  • JDK1.6源码

作者介绍

方腾飞,花名清英,淘宝资深开发工程师,关注并发编程,目前在广告技术部从事无线广告联盟的开发和设计工作。个人博客:http://ifeve.com 微博:http://weibo.com/kirals 欢迎通过我的微博进行技术交流。

感谢张龙对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

相关内容

您好,陌生人!

您需要 注册一个InfoQ账号 或者 登录 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

社区评论 Watch Thread

拜读,有一些困惑望解答 by 史 雨鑫 Posted 16/11/2012 03:19 Re: 拜读,有一些困惑望解答 by 方 腾飞 Posted 16/11/2012 05:06

Re: 拜读,有一些困惑望解答 by 萧 欢 Posted 18/11/2012 12:57 Re: 拜读,有一些困惑望解答 by 方 腾飞 Posted 18/11/2012 02:43

能谈谈thread pool和多核CPU的关系吗? 怎样能保证thread pool的配置,让其能够最大化的压栈多核cpu by biao jiang Posted 20/11/2012 11:02 Re: 能谈谈thread pool和多核CPU的关系吗? 怎样能保证thread pool的配置,让其能够最大化的压栈多核cpu by 萧 欢 Posted 20/11/2012 02:26

Re: 能谈谈thread pool和多核CPU的关系吗? 怎样能保证thread pool的配置,让其能够最大化的压栈多核cpu by 老 bull Posted 22/11/2012 11:48 Re: 能谈谈thread pool和多核CPU的关系吗? 怎样能保证thread pool的配置,让其能够最大化的压栈多核cpu by 方 腾飞 Posted 26/11/2012 00:00

使用的一些问题? by Qiao stephen Posted 25/02/2013 02:23 Re: 使用的一些问题? by vince vince Posted 16/04/2013 02:45

不知道是不是错误 by 高 鹏翔 Posted 29/03/2013 01:53 Re: 不知道是不是错误 by 方 腾飞 Posted 18/05/2013 01:25

昨晚再次阅读了该文,有几个问题需要解答 by 王 明军 Posted 27/05/2013 09:11 Re: 昨晚再次阅读了该文,有几个问题需要解答 by 方 腾飞 Posted 13/06/2013 03:38

拜读,有一些困惑望解答 16/11/2012 03:19 by 史 雨鑫

腾飞,线程池分析小节中,“队列是否满了”->”线程池是否满了“过程中,假设使用ArrayBlockingQueue队列,文中提到如果队列满了,并且线程池没满,则会新建线程处理此任务,那问题是此任务有可能会在队列任务被执行前执行,这样就不满足”先进先出“原则了吧?望解答。

Re: 拜读,有一些困惑望解答 16/11/2012 05:06 by 方 腾飞

此任务是提交给线程池执行的新任务,不在队列里。队列里的任务存储的都是以前提交的任务,它们需要等待线程空闲时来执行。

Re: 拜读,有一些困惑望解答 18/11/2012 12:57 by 萧 欢

你的理解是对的。如果在创建线程池的时候,指定的corePoolSize

Re: 拜读,有一些困惑望解答 18/11/2012 02:43 by 方 腾飞

是的。之前没理解你的问题。

能谈谈thread pool和多核CPU的关系吗? 怎样能保证thread pool的配置,让其能够最大化的压栈多核cpu 20/11/2012 11:02 by biao jiang

能谈谈thread pool和多核CPU的关系吗? 怎样能保证thread pool的配置,让其能够最大化的压栈多核cpu

Re: 能谈谈thread pool和多核CPU的关系吗? 怎样能保证thread pool的配置,让其能够最大化的压栈多核cpu 20/11/2012 02:26 by 萧 欢

这个应该挺难的。只能说,理论上一个CPU分配一个线程是最优的情况,然而这只是理论上。线程池和CPU核数的关系也与线程池的任务模型息息相关,是CPU密集型还是I/O密集型。最简单的方式就是动态递增的增加线程池数目然后跑一段时间程序,观察系统吞吐率,寻找最适合你的那个高层点

Re: 能谈谈thread pool和多核CPU的关系吗? 怎样能保证thread pool的配置,让其能够最大化的压栈多核cpu 22/11/2012 11:48 by 老 bull

Java 1.2之后线程都是采用内核级线程(进程)实现的,一个JAVA用户线程最终会对应到一个内核线程上面托管,而内核线程在CPU上的调度是依赖操作系统的,这个问题可以从操作系统层面上解决或者寻求答案。

Re: 能谈谈thread pool和多核CPU的关系吗? 怎样能保证thread pool的配置,让其能够最大化的压栈多核cpu 26/11/2012 00:00 by 方 腾飞

文中有提到如果是CPU密集型任务,使用Ncpu+1个线程可以很好的压榨CPU。详细的可以参见“合理的配置线程池”章节。

使用的一些问题? 25/02/2013 02:23 by Qiao stephen

如果我在一个应用中,存在多个小服务,每个服务都开启一个线程,请问那么是在整个应用中开启一个线程池还是每个小服务开启 一个线程池呢?如果是每个小服务开启一个,各个线程池之间又是怎么管理呢? 是不是使用一个线程池来管理上面的多个线程池呢?

不知道是不是错误 29/03/2013 01:53 by 高 鹏翔

◦DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。 这个策略,我看的时候有点疑惑,进入代码里面看了下,应该是丢弃待执行的任务队列的对首,但是这个队列是FIFO的,所以我觉得应该是丢弃最远的一个任务,不知道是否理解的有误解,忘指正

Re: 使用的一些问题? 16/04/2013 02:45 by vince vince

应该是在应用中建一个线程池,把每个服务要执行的方法体封装成Runnable对象提交到线程池里执行吧,线程池顾名思义,就是一个线程库,事先为你建好,要用的时候丢给它,它给你返回结果,至于优势嘛开头说得很清楚了~这么做可以简化客户端代码复杂度,让他只关心自己的线程体逻辑,而线程本身的生命周期等都不需要自己操心~

Re: 不知道是不是错误 18/05/2013 01:25 by 方 腾飞

的确是丢弃最远的任务,源码如下 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } }

昨晚再次阅读了该文,有几个问题需要解答 27/05/2013 09:11 by 王 明军

1、文中提到了线程池的关闭,那么线程池在什么情况下执行关闭呢? 2、什么是CPU密集型任务? 谢谢!有个问题通过阅读评论已经解决,是丢弃任务那个问题。

Re: 昨晚再次阅读了该文,有几个问题需要解答 13/06/2013 03:38 by 方 腾飞

1:不需要使用的时候就关闭。比如服务器暂停使用。 2:CPU密集型任务,如压缩和解压缩,这种需要CPU不停的计算的任务。

关闭

**by

发布于

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p 当有人回复此评论时请E-mail通知我

关闭 主题 您的回复

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p 当有人回复此评论时请E-mail通知我 关闭

深度内容

Juergen Fesslmeier谈端到端的JavaScript开发

Juergen Fesslmeier 七月 02, 2013

设计模式自动化

Gael Fraiteur and Yan Cui 七月 01, 2013

设计指尖上的世界:移动用户界面一瞥

Forrest Shull 六月 28, 2013

成功的根本—集成的ALM工具

Dave West 六月 28, 2013

书评:验收测试驱动开发实践指南

Manuel Pais 六月 26, 2013

跨终端的web

舒文亮 六月 26, 2013

语言 & 开发

Juergen Fesslmeier谈端到端的JavaScript开发

MobileCloud for TFS支持测试Windows Phone,Android,iOS及BlackBerry应用

百度技术沙龙第39期回顾:前端快速开发实践(含资料下载)

架构 & 设计

内存与本机代码的性能

设计模式自动化

连接设备编程 过程 & 实践

成功的根本—集成的ALM工具

ThoughtWorks全球CEO郭晓谈软件人才的招聘与培养

书评:验收测试驱动开发实践指南

运维 & 基础架构

在传统企业中引入DevOps

安全性——“DevOpS”中的S

书评:验收测试驱动开发实践指南 企业架构

设计指尖上的世界:移动用户界面一瞥

Stratos 2.0已发布,支持所有运行时环境和30个IaaS

让1.5亿移动端用户第一时间获取消息

语言 & 开发

Juergen Fesslmeier谈端到端的JavaScript开发

MobileCloud for TFS支持测试Windows Phone,Android,iOS及BlackBerry应用

百度技术沙龙第39期回顾:前端快速开发实践(含资料下载) 架构 & 设计

内存与本机代码的性能

设计模式自动化

连接设备编程

过程 & 实践

成功的根本—集成的ALM工具

ThoughtWorks全球CEO郭晓谈软件人才的招聘与培养

书评:验收测试驱动开发实践指南 运维 & 基础架构

在传统企业中引入DevOps

安全性——“DevOpS”中的S

书评:验收测试驱动开发实践指南

企业架构

设计指尖上的世界:移动用户界面一瞥

Stratos 2.0已发布,支持所有运行时环境和30个IaaS

让1.5亿移动端用户第一时间获取消息 Close E-mail 密码

使用Google账号登录 使用Microsoft账号登录 忘记密码? InfoQ账号使用的E-mail 发送邮件

重新登录 重新发送激活信息 重新发送

重新登录 没有用户名?

点击注册