??xml version="1.0" encoding="utf-8" standalone="yes"?> 配置安全{略Ӟ有两个概念需要清楚的区分 Q用户和角色Q简单的说用户ؓ使用计算机的人,可以是个人或l织。角色是一个抽象的概念Q泛指职务或者权限。例如,张三Q李四,王五三个人,有职员、主和l理三个职务Q权限)Q张三是用户Q张三可以是ȝ职务Q代表张三这个用户含有主的权利?/p>
不同的servlet容器所提供的用户和角色理机制是不相同的。我使用的是Tomcat服务器,它提供的用户和角色管理机制文件是在其安装目录下的conf目录中的tomcat-users.xml文gQ可以在q个文g里完成对用户和角色的~辑。例如: q个文g定义?个角?tomcat和role1)?名用?tomcat、both和role1)。你可以在tomcat-users.xml文g里定义Q意多个用户和角色?/p>
使用Struts 2保护应用E序的资?/p>
Struts 2应用E序的安全策略是通过部vweb.xml文g中的security-constraint元素实现的,该元素的语法定义Q?/p> 该语法说明了Qsecurity-constraint元素可以有一个可选的display-name子元素,臛_一个web-resource-collection子元素,一个可选的auth-constraint子元素和一个可选的user-data-constraint子元素?/p>
web-resource-collection子元素是用来列出打算保护的Web资源Q具体的做法是ؓq些资源讄URL限制Q它是通过讄web-resource-collection元素包含的子元素实现的: ◆ web-resource-nameQ是与受保护资源相关联的名称。该子元素ؓ必须元素?/p>
◆ descriptionQ对l定资源的描q。这个子元素为可选元素?/p>
◆ url-patternQ用来设|URL表达式,与这个URL表达式相匚w的URL地址指向的资源将受到保护。该子元素ؓ臛_有一个,为必d素?/p>
◆ http-methodQ用来表明哪些HTTPҎ受到限Ӟ例如讄为GET那么所有的GETh将受到限制。该元素为可选元素?/p>
auth-constraint元素用于指定可以讉K该资源用戯色集合。如果没有指定auth-constraint元素Q就安全约束应用于所有角艌Ӏ它包含下面几个子元素: ◆ descriptionQ描q。该元素是可选元素?/p>
◆ role-nameQ可以访问保护资源的用户角色。该元素可以有多个?/p>
◆ user-data-constraint元素用来讄怎样保护在客L和Web容器之间传递的数据?/p>
◆ descriptionQ?描述。可选元素?/p>
◆ transport-guarantee Q该元素有以下几个?/p>
1. NONEQ这意味着应用不需要传输保证?/p>
2. INTEGRALQ意味着服务器和客户端之间的数据必须以某U方式发送,而且在传送中数据不能被篡攏V?/p>
3. CONFIDENTIALQ这意味着传输的数据必d密?/p>
配置完毕security-constraint元素的基本信息,大致Z面的格式Q?/p> q个security-constraint元素的效果ؓQ只要与表达?*.action"匚w的请求不是来自拥?myeclipseWeb"权限的用PWeb容器׃L它。在q里q可以用http-method元素Q阻断特定方法的hQ因为没有用会L所有方法提交的h?/p>
讄完安全策略后Q还需要设|让用户有机会提供证明,证明自己有权限访问这个受限资源的登陆Ҏ。允怋用的登陆Ҏ使用login-config元素讄Q下面ؓlogin-config元素的语法定义: login-config子元素的描述如下Q? 讄完登陆方法后Q还应该使用security-role元素Q注册允许用来访问受保护资源所有角艌Ӏ在该元素内部用一个role-name子元素来注册一个角艌Ӏ例如: 注册了一?myeclipseWeb"的角艌Ӏ?/p>
演示CZQ用BASIC登陆Ҏ验证用户w䆾 1.我用的Servlet容器是TomcatQ找到它的目录下conf目录中的tomcat-users.xml文g打开内容如下Q?/p> 我用的IDE是myEclipse9.0Q它配置好Tomcat下的tomcat-users.xml文g内容如上Q我直接使用它了Q你也可以添加自q角色和用戗该文g定义?个角色和3个用P每一个用户都pq角色(或者说权限Q可以有多重权限)?/p>
2.创徏Web目Q找到web.xmlQ配|它Q它支持Struts 2q且启动Struts 2的安全策?/p> 3. 创徏接收一个字D信息的动作c: 4. 创徏struts.xml配置文gQ声明动?/p> 5. 创徏输入面input.jsp和结果页面index.jsp input.jsp: index.jsp 6.试效果Q在览器输入:http://localhost:8081/SecureTest/input.jspQ得到如?a style="text-decoration: none" >自力式温度调节阀界面Q输?Tom"Q点?submit"按钮Q查看效果: 看到了登陆框了吧Q此时我们要讉K的资源是一个受限资源所以要求权限验?a style="text-decoration: none" >装式球阀Q还记得我们的用戯吧,查看用户表输入用户信息查看结果: 输入"webservices"?webservices-pwd"的用户信息: 提示了一?403"错误Q这是因然用户信息正,但是"webservices"用户的没?myeclipseWeb"权限?/p>
q次输入一个不存在的用户信息: q次获得了一?401"错误Q这是登陆失败的提示l果Q这里会因浏览器的不同而需要不同次数的p|登陆才会得到q个l果?/p>
接下来输入一个正的用户Qƈ且拥?webservices"权限的用户信息: 点击"定"Q获得如下结果: 可以看到Q我们成功的讉K了受保护的资源。若要传中文字,解决Ҏ我已l在前面"配置Struts2"时介l过了,需要修改Struts 2默认的编码方式还需要修攚w面的~码方式Q都改ؓ"GBK"?/p>
◆ auth-method指定用来验证用户w䆾的方法。它的gؓ下面的一个:BASIC、DIGEST、FORM?CLIENT-CERT
◆ realm-name指定HTTP Basic验证中在标准登陆框中昄的一条提C?
◆ form-login-config元素是在<auth-method>元素gؓ"FORM"时用的。它是指定基于表单的d中应该用的d面和出错页面。如果没有用基于表单的验证Q则忽略q些元素。这个元素的定义如下Q其中form-login-page用于指定昄d面的资源\径, form-error-page则用于指定用L录失败时昄出错面的资源\径?/p>
基本参数说明Q?/p>
-clientQ?server
q两个参数用于设|虚拟机使用何种q行模式Qclient模式启动比较快,但运行时性能和内存管理效率不如server模式Q通常用于客户端应用程序。相反,server模式启动比client慢,但可获得更高的运行性能?/p>
?windows上,~省的虚拟机cd为client模式Q如果要使用server模式Q就需要在启动虚拟机时?server参数Q以获得更高性能Q对服务器端应用Q推荐采用server模式Q尤其是多个CPU的系l。在LinuxQSolaris上缺省采用server模式?/p>
-hotspot
含义与client相同Qjdk1.4以前使用的参敎ͼjdk1.4开始不再用,代之以client.
-classpathQ?cp
虚拟机在q行一个类Ӟ需要将其装入内存,虚拟机搜索类的方式和序如下Q?/p>
Bootstrap classesQExtension classesQUser classes.
Bootstrap 中的路径是虚拟机自带的jar或zip文gQ虚拟机首先搜烦q些包文Ӟ用System.getPropertyQ?sun.boot.class.path"Q可得到虚拟机搜索的包名?/p>
Extension是位于jre\lib\ext目录下的jar文gQ虚拟机在搜索完Bootstrap后就搜烦该目录下的jar文g。用System. getPropertyQ?java.ext.dirs“Q可得到虚拟Z用Extension搜烦路径?/p>
User classes搜烦序为当前目录、环境变?CLASSPATH?classpath.
-classpath告知虚拟机搜索目录名、jar文名、zip文名,之间用分P分隔?/p>
例如当你自己开发了公共cdƈ包装成一个common.jar包,在用common.jar中的cLQ就需要用-classpath common.jar 告诉虚拟Zcommon.jar中查找该c,否则虚拟机就会抛出java.lang.NoClassDefFoundError异常Q表明未扑ֈcd义?/p>
在运行时可用System.getPropertyQ?#8220;java.class.path”Q得到虚拟机查找cȝ路径?/p>
使用-classpath后虚拟机不再用CLASSPATH中的cL索\径,如果-classpath和CLASSPATH都没有设|,则虚拟机使用当前路径Q。)作ؓcL索\径?/p>
推荐使用-classpath来定义虚拟机要搜索的c\径,而不要用环境变?CLASSPATH的搜索\径,以减多个项目同时用CLASSPATH时存在的潜在冲突。例如应?要用a1.0.jar中的cGQ应?要?a2.0.jar中的cGQa2.0.jar是a1.0.jar的升U包Q当a1.0.jarQa2.0.jar都在CLASSPATH中,虚拟机搜索到W一个包中的cG时就停止搜烦Q如果应?应用2的虚拟机都从CLASSPATH中搜索,׃有一个应用得不到正确版本的类G.
-D<propertyName>=value
在虚拟机的系l属性中讄属性名/值对Q运行在此虚拟机之上的应用程序可用System.getPropertyQ?#8220;propertyName”Q得到value的倹{?/p>
如果value中有I格Q则需要用双引号将该值括hQ如-Dname=“space string”?/p>
该参数通常用于讄pȝU全局变量|如配|文件\径,应ؓ该属性在E序中Q何地斚w可访问?/p>
-verbose[Qclass|gc|jni]
在输备上昄虚拟行信息?/p>
verbose和verboseQclass含义相同Q输拟机装入的类的信息,昄的信息格式如下:
[Loaded java.io.FilePermission$1 from shared objects file]
当虚拟机报告cL不到或类冲突时可用此参数来诊断来查看虚拟Z装入cȝ情况?/p>
-verboseQgc在虚拟机发生内存回收时在输出讑֤昄信息Q格式如下:
[Full GC 268K->168KQ?984KQ, 0.0187390 secs]
该参数用来监视虚拟机内存回收的情c?/p>
-verboseQjni在虚拟机调用nativeҎ时输备显CZ息,格式如下Q?/p>
[Dynamic-linking native method HelloNative.sum …… JNI]
该参数用来监视虚拟机调用本地Ҏ的情况,在发生jni错误时可断提供便利?/p>
-version
昄可运行的虚拟机版本信息然后退出。一台机器上装有不同版本的JDK?/p>
-showversion
昄版本信息以及帮助信息?/p>
-ea[Q?lt;packagename>……|Q?lt;classname>]
-enableassertions[Q?lt;packagename>……|Q?lt;classname>]
从JDK1.4开始,java可支持断a机制Q用于诊断运行时问题。通常在测试阶D断言有效Q在正式q行时不需要运行断a。断a后的表达式的值是一个逻辑|为true时断a不运行,为false时断aq行Q抛出java.lang.AssertionError错误?/p>
上述参数q来设|虚拟机是否启动断言机制Q缺省时虚拟机关闭断a机制Q用-ea可打开断言机制Q不?lt;packagename>?classname时运行所有包和类中的断言Q如果希望只q行某些包或cM的断aQ可包名或cd加到-ea之后。例如要启动包com.foo.util 中的断言Q可用命?–eaQcom.foo.util .
-da[Q?lt;packagename>……|Q?lt;classname>]
-disableassertions[Q?lt;packagename>……|Q?lt;classname>]
用来讄虚拟机关闭断a处理Qpackagename和classname的用方法和-ea相同?/p>
-esa | -enablesystemassertions
讄虚拟机显C系l类的断a?/p>
-dsa | -disablesystemassertions
讄虚拟机关闭系l类的断a?/p>
-agentlibQ?lt;libname>[=<options>]
该参数是JDK5新引入的Q用于虚拟机装蝲本地代理库?/p>
Libname为本C理库文g名,虚拟机的搜烦路径为环境变量PATH中的路径QoptionsZl本地库启动时的参数Q多个参C间用逗号分隔。在Windowsq_上虚拟机搜烦本地库名为libname.dll的文Ӟ?Unix上虚拟机搜烦本地库名为libname.so的文Ӟ搜烦路径环境变量在不同系l上有所不同QLinux、SunOS、IRIX上ؓ LD_LIBRARY_PATHQAIX上ؓLIBPATHQHP-UX上ؓSHLIB_PATH.
例如可?agentlibQhprof来获取虚拟机的运行情况,包括CPU、内存、线E等的运行数据,q可输出到指定文件中Q可?agentlibQhprof=help来得C用帮助列表。在jre\bin目录下可发现hprof.dll文g?/p>
-agentpathQ?lt;pathname>[=<options>]
讄虚拟机按全\径装载本地库Q不再搜索PATH中的路径。其他功能和agentlib相同?/p>
-javaagentQ?lt;jarpath>[=<options>]
虚拟机启动时装入java语言讑֤代理。Jarpath文g中的mainfest文g必须有Agent-Class属性。代理类要实现public static void premainQString agentArgsQ?Instrumentation instQ方法。当虚拟机初始化Ӟ按代理cȝ说明序调用premainҎ?/p>
参见Qjava.lang.instrument
扩展参数说明
-Xmixed
讄-client模式虚拟机对使用频率高的方式q行Just-In-Time~译和执行,对其他方法用解释方式执行。该方式是虚拟机~省模式?/p>
-Xint
讄-client模式下运行的虚拟Z解释方式执行cȝ字节码,不将字节码编译ؓ本机码?/p>
-XbootclasspathQpath
-Xbootclasspath/aQpath
-Xbootclasspath/pQpath
改变虚拟载缺省系l运行包rt.jar而从-Xbootclasspath中设定的搜烦路径中装载系l运行类。除非你自己能写一个运行时Q否则不会用到该参数?/p>
/aQ将在缺省搜索\径后加上path 中的搜烦路径?/p>
/pQ在~省搜烦路径前先搜烦path中的搜烦路径?/p>
-Xnoclassgc
关闭虚拟机对class的垃圑֛收功能?/p>
-Xincgc
启动增量垃圾攉器,~省是关闭的。增量垃圾收集器能减偶然发生的长时间的垃圾回收造成的暂停时间。但浆?/font>增量垃圾攉器和应用E序q发执行Q因此会占用部分CPU在应用程序上的功能?/p>
-XloggcQ?lt;file>
虚拟机每次垃圾回收的信息写到日志文件中Q文件名由file指定Q文件格式是qxӞ内容?verboseQgc输出内容相同?/p>
-Xbatch
虚拟机的~省q行方式是在后台~译cM码,然后在前台执行代码,使用-Xbatch参数关闭虚拟机后台~译Q在前台~译完成后再执行?/p>
-Xms<size>
讄虚拟机可用内存堆的初始大,~省单位为字节,该大ؓ1024的整数倍ƈ且要大于1MBQ可用kQKQ或mQMQؓ单位来设|较大的内存数。初始堆大小?MB.
例如Q?Xms6400KQ?Xms256M
-Xmx<size>
讄虚拟机内存堆的最大可用大,~省单位为字节。该值必Mؓ1024整数倍,q且要大?MB.可用kQKQ或mQMQؓ单位来设|较大的内存数。缺省堆最大gؓ64MB.
例如Q?Xmx81920KQ?Xmx80M
当应用程序申请了大内存运行时虚拟机抛出java.lang.OutOfMemoryErrorQ?Java heap space错误Q就需要?Xmx讄较大的可用内存堆c?/p>
-Xss<size>
讄U程栈的大小Q缺省单位ؓ字节。与-XmxcMQ也可用K或M来设|较大的倹{通常操作pȝ分配l线E栈的缺省大ؓ1MB.
另外也可在java中创建线E对象时讄栈的大小Q构造函数原型ؓThreadQThreadGroup groupQ?Runnable targetQ?String nameQ?long stackSizeQ?/p>
-Xprof
输出CPUq行时的诊断信息?/p>
-Xfuture
对类文gq行严格格式查,以保证类代码W合cM码规范。ؓ保持向后兼容Q虚拟机~省不进行严格的格式查?/p>
-Xrs
减少虚拟Z操作pȝ的信PsingalsQ的使用。该参数通常用在虚拟Z后台服务方式q行时用(如ServletQ?/p>
-XcheckQjni
调用JNI函数时进行附加的查,特别地虚拟机校验传递给JNI函数参数的合法性,在本C码中遇到非法数据Ӟ虚拟机将报一个致命错误而终止。用该阀参数后将造成性能下降?/p>
Ergonomics相关的逻辑大都在hotspot/src/share/vm/runtime/arguments.cpp中,值得留意的是使用了FLAG_SET_ERGOQ)的地斏V?/p>
于是我们可以留意一下几个版本的HotSpot对UseCompressedOops参数的处理的差异Q?/p>
HotSpot 16Q?/p>
C++代码
#ifdef _LP64 // Check that UseCompressedOops can be set with
the max heap size allocated // by ergonomics. if (MaxHeapSize <= max_heap_for_compressed_oops()) { if (FLAG_IS_DEFAULT(UseCompressedOops)) { // Turn off until bug is fixed. // the following line to return it to default status. // FLAG_SET_ERGO(bool, UseCompressedOops, true); } // ... } #endif // _LP64
HotSpot 17:
C++代码
#ifndef ZERO #ifdef _LP64 // Check that UseCompressedOops can be set with
the max heap size allocated // by ergonomics. if (MaxHeapSize <= max_heap_for_compressed_oops()) { #ifndef COMPILER1 if (FLAG_IS_DEFAULT(UseCompressedOops) && !UseG1GC) { // Disable Compressed Oops by default. Uncomment
next line to enable it. // FLAG_SET_ERGO(bool, UseCompressedOops, true); } } #endif // ... #endif // _LP64 #endif // !ZERO
HotSpot 19 / HotSpot 20:
C++代码
#ifndef ZERO #ifdef _LP64 // Check that UseCompressedOops can be set with
the max heap size allocated // by ergonomics. if (MaxHeapSize <= max_heap_for_compressed_oops()) { #ifndef COMPILER1 if (FLAG_IS_DEFAULT(UseCompressedOops) && !UseG1GC) { FLAG_SET_ERGO(bool, UseCompressedOops, true); } #endif } // ... #endif // _LP64 #endif // !ZERO
Q注QHotSpot VM的版本号与JDK的版本号之间的关p,请参考另一笔讎ͼSun/Oracle JDK、OpenJDK、HotSpot VM版本之间的对应关p)
可以看到QUseCompressedOops参数从HotSpot 19开始终于开始受ergonomics控制Q会在下q条件满的时候默认开?a style="text-decoration: none" >道力?/font>Q?/p>
1、是64位系l(#ifdef _LP64Qƈ且不是client VMQ?ifndef COMPILER1Q; 2、Java堆的最大大不大于一个阈|MaxHeapSize <= max_heap_for_compressed_oopsQ)Q; 3、没有通过。hotspotrc或命令行参数手动讑֮qUseCompressedOops参数的| 4、没有用Garbage-First QG1Q?GC. q是看回代码QHotSpot 20Q?/p>
C++代码 Q注Q其?Quint64_tQmax_juintQ?+ 1Q?的g被称为NarrowOopHeapMaxQ也是2?2ơ方Q?x100000000Q?/p>
ObjectAlignmentInBytes?4位HotSpot上默认ؓ8Q?/p>
HeapWord在globalDefinitions.hpp里定义,大小跟一个char*一P HeapWordSize在同一个文仉定义Q等于sizeofQHeapWordQ,?4位系l上gؓ8Q?/p>
LogHeapWordSize也在同一文g里,?4位系l上定义?Q?/p>
跟踪一下里面几个参数的计算Q在64位HotSpot上有Q?/p>
C++代码 于是Q前面提到的W?个条件在64位HotSpot VM上默认是Q?/p>
C++代码 osQ:vm_page_sizeQ)是操作系l的虚拟内存的分大,在Linux上等于sysconfQ_SC_PAGESIZEQ的|在x86_64上的Linux默认分页大小?KB. MaxHeapSize的值基本上{于-Xmx参数讄的|会根据分大、对齐等因素做调_?/p>
MaxPermSize是perm gen讄的最大大?/p>
q下可以认Q在我现在用的环境里Q当包括perm gen在内的GC堆大在32GB - 4KB以下的时候,使用64位的JDK 6 update 23或更高版本就会自动开启UseCompressedOops功能
W???炚w很直观,于是W?点就是个关键点了Q阈值是多大Q?
"ObjectAlignmentInBytes value is too small");
"ObjectAlignmentInBytes value is incorrect");
OopEncodingHeapMax = 0x800000000 // 32GB
Java中的?/strong>
每当启用一个线E时QJVM׃ؓ他分配一个Java栈,栈是以为单位保存当前线E的q行状态。某个线E正在执行的ҎUCؓ当前ҎQ当前方法用的栈UCؓ当前帧,当前Ҏ所属的cȝ为当前类Q当前类的常量池UCؓ当前帔R池。当U程执行一个方法时Q它会跟t当前常量池?/p>
每当U程调用一个JavaҎӞJVM׃在该U程对应的栈中压入一个Q这个自然成了当前。当执行q个ҎӞ它用这个来存储参数、局部变量、中间运结果等{?/p>
Java栈上的所有数据都是私有的。Q何线E都不能讉K另一个线E的栈数据。所以我们不用考虑多线E情况下栈数据访问同步的情况?/p>
像方法区和堆一PJava栈和帧在内存中也不必是连l的,帧可以分布在q箋的栈里,也可以分布在堆里
Java栈的l成元素——栈
栈׃部分l成Q局部变量区、操作数栈、数据区。局部变量区和操作数栈的大小要视对应的方法而定Q他们是按字长计的。但调用一个方法时Q它从类型信息中得到此方法局部变量区和操作数栈大,q据此分配栈内存Q然后压入Java栈?/p>
局部变量区 局部变量区被组lؓ以一个字长ؓ单位、从0开始计数的数组Q类型ؓshort、byte和char的值在存入数组前要被{换成int|而long?double在数l中占据q箋的两,在访问局部变量中的long或doubleӞ只需取出q箋两项的第一的索引值即?如某个long值在局部变量区中占据的索引??,取值时Q指令只需取烦引ؓ3的long值即可?/p>
下面q个例子,好让大家对局部变量区有更深刻的认识。这个图来自《深入JVM》:
- public static int runClassMethod(int i,long l,float f,double d,Object o,byte b) {
- return 0;
- }
- public int runInstanceMethod(char c,double d,short s,boolean b) {
- return 0;
- }
上面代码片的Ҏ参数和局部变量在局部变量区中的存储l构如下图:
上面q个图没什么好说的Q大家看看就会懂。但是,在这个图里,有一炚w要注意:
runInstanceMethod的局部变量区W一Ҏ个referenceQ引用)Q它指定的就是对象本w的引用Q也是我们常用的this,但是在runClassMethodҎ中,没这个引用,那是因ؓrunClassMethod是个静态方法?
操作数栈和局部变量区一P操作数栈也被l织成一个以字长为单位的数组。但和前者不同的是,它不是通过索引来访问的Q而是通过入栈和出栈来讉K的。可把操作数栈理解ؓ存储计算Ӟ临时数据的存储区域。下面我们通过一D늮短的E序片段外加一q图片来了解下操作数栈的作用?/p>
int a = 100;
int b = 98;
int c = a+b;
从图中可以得出:操作数栈其实是个时数据存储区域,它是通过入栈和出栈来q行操作的?/p>
帧数据区除了局部变量区和操作数栈外QJava栈q需要一些数据来支持帔R池解析、正常方法返回以及异常派发机制。这些数据都保存在Java栈的数据Z?br />当JVM执行到需要常量池数据的指令时Q它都会通过帧数据区中指向常量池的指针来讉K它?/p>
除了处理帔R池解析外Q里的数据q要处理JavaҎ的正常结束和异常l止。如果是通过return正常l束Q则当前栈从Java栈中弹出Q恢?a style="text-decoration: none" >ȝ钢液下܇发v调用的方法的栈。如果方法又q回|JVM会把q回值压入到发v调用Ҏ的操作数栈?/p>
Z处理JavaҎ中的异常情况Q数据必须保存一个对此方法异常引用表的引用。当异常抛出ӞJVMlcatch块中的代码。如果没发现Q方法立即终止,然后JVM用区数据的信息恢复发v调用的方法的帧。然后再发v调用Ҏ的上下文重新抛出同样的异常?/p>
栈的整个l构
在前面就描述q:栈是由栈帧组成,每当U程调用一个JavaҎӞJVM׃在该U程对应的栈中压入一个Q而是由局部变量区、操作数栈和帧数据区l成。那在一个代码块中,栈到底是什么Ş式呢Q下面是我从《深入JVM》中摘抄的一个例子,大家可以看看Q?/p>
代码片段Q?/p>
执行q程中的三个快照Q?/p>
上面所l的图,只想说明两g事情Q我们也可用此来理解Java中的栈:
1、只有在调用一个方法时Q才为当前栈分配一个Q然后将该压入栈?/p>
2、中存储了对应Ҏ的局部数据,Ҏ执行完,对应的则从栈中弹出Qƈ把返回结果存储在调用Ҏ的的操作数栈中?/p>
Q?Q?模糊"cLӞ加大反编译器反编译源代码文g的难度。然而,可以修改反编译器Q之能够处理这些模p类文g。所以仅仅依?模糊cL?来保证代码的安全是不够的?/p>
Q?Q流行的加密工具Ҏ文gq行加密Q比如PGPQPretty Good PrivacyQ或GPGQGNU Privacy GuardQ。这Ӟ最l用户在q行应用之前必须先进行解密。但解密之后Q最l用户就有了一份不加密的类文gQ这和事先不q行加密没有什么差别?/p>
Q?Q加密类文gQ在q行中JVM用定制的c装载器QClass LoaderQ解密类文g。Javaq行时装入字节码的机刉含地意味着可以对字节码q行修改。JVM每次装入cL件时都需要一个称为ClassLoader的对象,q个对象负责把新的类装入正在q行的JVM。JVMlClassLoader一个包含了待装入类Q例如java.lang.ObjectQ名字的字符Ԍ然后由ClassLoader负责扑ֈcLӞ装入原始数据Qƈ把它转换成一个Class对象?/p>
用户下蝲的是加密q的cLӞ在加密类文g装入之时q行解密Q因此可以看成是一U即时解密器。由于解密后的字节码文g永远不会保存到文件系l,所以窃密者很隑־到解密后的代码?/p>
׃把原始字节码转换成Class对象的过E完全由pȝ负责Q所以创建定制ClassLoader对象其实q不困难Q只需先获得原始数据,接着可以进行包含解密在内的M转换?/p>
Java密码体系和Java密码扩展
Java密码体系(JCA)和Java密码扩展(JCE)的设计目的是为Java提供与实现无关的加密函数API。它们都用factoryҎ来创建类的例E,然后把实际的加密函数委托l提供者指定的底层引擎,引擎中ؓcL供了服务提供者接口在Java中实现数据的加密/解密Q是使用其内|的JCE(Java加密扩展)来实现的。Java开发工具集1.1为实现包括数字签名和信息摘要在内的加密功能,推出了一U基于供应商的新型灵zd用编E接口。Java密码体系l构支持供应商的互操?同时支持g和Y件实现?/p>
Java密码学结构设计遵循两个原?
(1)法的独立性和可靠性?/p>
(2)实现的独立性和怺作用性?/p>
法的独立性是通过定义密码服务cL获得。用户只需了解密码法的概?而不用去兛_如何实现q些概念。实现的独立性和怺作用性通过密码服务提供器来实现。密码服务提供器是实C个或多个密码服务的一个或多个E序包。Y件开发商Ҏ一定接?各U算法实现后,打包成一个提供器,用户可以安装不同的提供器。安装和配置提供?可将包含提供器的ZIP和JAR文g攑֜CLASSPATH?再编辑Java安全属性文件来讄定义一个提供器。Javaq行环境Sun版本? 提供一个缺省的提供器Sun?/p>
下面介绍DES法及如何利用DES法加密和解密类文g的步骤?/p>
DES法?/p>
DESQData Encryption StandardQ是发明最早的最q泛使用的分l对U加密算法。DES法的入口参数有三个QKey、Data、Mode。其中Key?个字节共64位,是DES法的工作密钥;Data也ؓ8个字?4位,是要被加密或被解密的数据QMode为DES的工作方式,有两U:加密或解密?/p>
DES法工作程如下Q若Mode为加密模式,则利用Key Ҏ据Dataq行加密Q?生成Data的密码Ş式(64位)作ؓDES的输出结果;如Mode密模式,则利用Key对密码Ş式的数据Dataq行解密Q还原ؓData的明码Ş式(64位)作ؓDES的输出结果。在上v针型阀通信|络的两端,双方U定一致的KeyQ在通信的源点用KeyҎ心数据进行DES加密Q然后以密码形式在公共通信|(如电话网Q中传输到通信|络的终点,数据到达目的地后Q用同样的Key对密码数据进行解密,便再C明码形式的核心数据。这P便保证了核心数据在公共通信|中传输的安全性和可靠性?/p>
也可以通过定期在通信|络的源端和目的端同时改用新的KeyQ便能更q一步提高数据的保密性?br /> 利用DES法加密的步?/p>
Q?Q生成一个安全密钥。在加密或解密Q何数据之前需要有一个密钥。密钥是随同被加密的应用E序一起发布的一D|据,密钥代码如下所C?/p>
【生成一个密钥代码?/p>
view plaincopy to clipboardprint?
// 生成一个可信Q的随机数?br />SecureRandom sr = new SecureRandom();
// 为我们选择的DES法生成一个KeyGenerator对象
KeyGenerator kg = KeyGenerator.getInstance ("DES" );
Kg.init (sr);
// 生成密钥
Secret Key key = kg.generateKey();
// 密钥数据保存ؓ文g供以后用,其中key FilenameZ存的文g?br />Util.writeFile (key Filename, key.getEncoded () );
// 生成一个可信Q的随机数?br />SecureRandom sr = new SecureRandom();
// 为我们选择的DES法生成一个KeyGenerator对象
KeyGenerator kg = KeyGenerator.getInstance ("DES" );
Kg.init (sr);
// 生成密钥
Secret Key key = kg.generateKey();
// 密钥数据保存ؓ文g供以后用,其中key FilenameZ存的文g?br />Util.writeFile (key Filename, key.getEncoded () );
Q?Q加密数据。得到密钥之后,接下来就可以用它加密数据。如下所C?/p>
【用密钥加密原始数据?/p>
view plaincopy to clipboardprint?
// 产生一个可信Q的随机数?br />SecureRandom sr = new SecureRandom();
//从密钥文件key Filename中得到密钥数?br />Byte rawKeyData = Util.readFile (key Filename);
// 从原始密钥数据创建DESKeySpec对象
DESKeySpec dks = new DESKeySpec (rawKeyData);
// 创徏一个密钥工厂,然后用它把DESKeySpec转换成Secret Key对象
SecretKeyFactory key Factory = SecretKeyFactory.getInstance("DES" );
Secret Key key = keyFactory.generateSecret( dks );
// Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance( "DES" );
// 用密钥初始化Cipher对象
cipher.init( Cipher.ENCRYPT_MODE, key, sr );
// 通过ȝ文g获取需要加密的数据
Byte data = Util.readFile (filename);
// 执行加密操作
Byte encryptedClassData = cipher.doFinal(data );
// 保存加密后的文gQ覆盖原有的cL件?br />Util.writeFile( filename, encryptedClassData );
// 产生一个可信Q的随机数?br />SecureRandom sr = new SecureRandom();
//从密钥文件key Filename中得到密钥数?br />Byte rawKeyData = Util.readFile (key Filename);
// 从原始密钥数据创建DESKeySpec对象
DESKeySpec dks = new DESKeySpec (rawKeyData);
// 创徏一个密钥工厂,然后用它把DESKeySpec转换成Secret Key对象
SecretKeyFactory key Factory = SecretKeyFactory.getInstance("DES" );
Secret Key key = keyFactory.generateSecret( dks );
// Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance( "DES" );
// 用密钥初始化Cipher对象
cipher.init( Cipher.ENCRYPT_MODE, key, sr );
// 通过ȝ文g获取需要加密的数据
Byte data = Util.readFile (filename);
// 执行加密操作
Byte encryptedClassData = cipher.doFinal(data );
// 保存加密后的文gQ覆盖原有的cL件?br />Util.writeFile( filename, encryptedClassData );
Q?Q解密数据。运行经q加密的E序ӞClassLoader分析q解密类文g。操作步骤如下所C?/p>
【用密钥解密数据?/p>
view plaincopy to clipboardprint?
// 生成一个可信Q的随机数?br />SecureRandom sr = new SecureRandom();
// 从密钥文件中获取原始密钥数据
Byte rawKeyData = Util.readFile( keyFilename );
// 创徏一个DESKeySpec对象
DESKeySpec dks = new DESKeySpec (rawKeyData);
// 创徏一个密钥工厂,然后用它把DESKeySpec对象转换成Secret Key对象
SecretKeyFactory key Factory = SecretKeyFactory.getInstance( "DES" );
SecretKey key = keyFactory.generateSecret( dks );
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance( "DES" );
// 用密钥初始化Cipher对象
Cipher.init( Cipher.DECRYPT_MODE, key, sr );
// 获得l过加密的数?br />Byte encrypted Data = Util.readFile (Filename);
//执行解密操作
Byte decryptedData = cipher.doFinal( encryptedData );
// 然后解密后的数据{化成原来的类文g?br />// 生成一个可信Q的随机数?br />SecureRandom sr = new SecureRandom();
// 从密钥文件中获取原始密钥数据
Byte rawKeyData = Util.readFile( keyFilename );
// 创徏一个DESKeySpec对象
DESKeySpec dks = new DESKeySpec (rawKeyData);
// 创徏一个密钥工厂,然后用它把DESKeySpec对象转换成Secret Key对象
SecretKeyFactory key Factory = SecretKeyFactory.getInstance( "DES" );
SecretKey key = keyFactory.generateSecret( dks );
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance( "DES" );
// 用密钥初始化Cipher对象
Cipher.init( Cipher.DECRYPT_MODE, key, sr );
// 获得l过加密的数?br />Byte encrypted Data = Util.readFile (Filename);
//执行解密操作
Byte decryptedData = cipher.doFinal( encryptedData );
// 然后解密后的数据{化成原来的类文g?/p>
上qC码与自定义的c装载器l合可以做到边解密边运行,从而vC护源代码的作用?/p>
l束?/p>
加密/解密是数据传输中保证数据安全性和完整性的常用ҎQJava语言因其q_无关性,在Internet上的应用非常之广泛。用DES法加密Java源码在一定程度上能保护Y件的产权?/p>
UserDao 接口Q?/p>
- /**
- * q程接口 必须l承与Remote对象
- * @author spring sky
- * date: 2012q???nbsp;10:55:05
- * Email:vipa1888@163.com
- * QQ:840950105
- */
- public interface UserDao extends Remote{
- /**
- * 单的试Ҏ
- * @param name
- */
- public void sayName(String name) throws RemoteException;
- }
UserDaoImpl实现c?/p>
- /**
- *
- * 接口的实现类 必须l承UnicastRemoteObject(单一q程对象) 实现UserDao自己的接?
- * @author spring sky
- * date: 2012q???nbsp;10:56:05
- * Email:vipa1888@163.com
- * QQ:840950105
- */
- public class UserDaoImpl extends UnicastRemoteObject implements UserDao {
- public UserDaoImpl() throws RemoteException {
- }
- @Override
- public void sayName(String name) {
- if(name!=null&&!name.equals(""))
- {
- System.out.println("我的名字是:"+name);
- }else{
- System.err.println("名字不ؓI?...");
- }
- }
- }
对外的提供一个服务,服务中已l共享了urll外界访?/p>
- /**
- * 使用mainҎ启动一个服务,用于外界环境讉K
- * @author spring sky
- * date:2012q???nbsp;10:57:37
- * Email:vipa1888@163.com
- * QQ:840950105
- */
- public class StartService {
- private static final String IP = "127.0.0.1";
- private static final int PORT = 9999;
- private static final String REMOTE_NAME = "userDao";
- private static final String REMOTE_URL = "rmi://"+IP+":"+PORT+"/"+REMOTE_NAME;
- public static void main(String[] args) {
- try {
- UserDao userDao = new UserDaoImpl(); //实例化对?
- LocateRegistry.createRegistry(PORT); //注册端口
- Naming.bind(REMOTE_URL, userDao); //l定q程服务对象
- System.out.println("q程"+REMOTE_NAME+"启动成功....");
- } catch (RemoteException e) {
- System.err.println("q程对象出错");
- e.printStackTrace();
- } catch (MalformedURLException e) {
- System.err.println("URL出错?);
- e.printStackTrace();
- } catch (AlreadyBoundException e) {
- System.err.println("l定的对象已l存在了");
- e.printStackTrace();
- }
- }
- }
上面是服务端的代码,如果启动没有M问题Q就可以做客L讉K了,其实上v旋塞阀客户端的讉K更加的简单,只需要远E的接口cd查询rmi中的url可以了Q?/p>
代码如下Q?/p>
- /**
- * q程Ҏ调用试
- * @author spring sky
- * date:2012q???nbsp;11:12:46
- * Email:vipa1888@163.com
- * QQ:840950105
- * name:x?
- */
- public class TestRemote {
- public static void main(String[] args) {
- try {
- //在rmi服务中查询userdao的对?
- UserDao userDao = (UserDao) Naming.lookup("rmi://127.0.0.1:9999/userDao");
- //调用q程服务的方?
- userDao.sayName("spring sky");
- } catch (MalformedURLException e) {
- System.err.println("URL出错");
- e.printStackTrace();
- } catch (RemoteException e) {
- System.err.println("q程对象出错");
- e.printStackTrace();
- } catch (NotBoundException e) {
- System.err.println("没有扑ֈl定的对?);
- e.printStackTrace();
- }
- }
- }
以上是所有的rmiq程调用代码了!q行l果如下Q?/p>
好了Q本Z只是单的了解了rmiQ如果以后有目做rmi可以深入了Q?呵呵 Q在q里我突然感觉,想web service也应该和他一L原理的把Q?/p>
1、如何判断横竖屏切换
横竖屏切换由手机pȝ自动完成的,在J2ME中只能检到q种变换Q判断横竖屏切换的方法有2U:
A)U程中每ơ检法(轮询方式)
q种方式是在E序的线E中每隔一定的旉间隔Q就获得一ơ屏q的宽度和高度,然后判断宽度和高度是否变化,如果宽度和高度和已有的宽度和高度相比发生了变化,至于是变化成横屏q是竖屏则只需要比较宽高就可以实现。在判断到横竖屏切换Ӟ执行逻辑代码卛_?/p>
使用q种方式׃每次U程循环都需要检,所以执行效率不高,但是׃一些{屏手Z支持sizeChanged的方法回调,所以用这U方式的通用性是最高的Q是l常被用的Ҏ?/p>
B)sizeChanged?中断方式)
q种方式是指如果转屏手机对于MIDP支持比较规范Q则当横竖屏切换时会自动调用CanvascM的sizeChangedҎQ这样就需要在Canvas的子cM覆盖该方法即可,在该Ҏ内部书写横竖屏切换的逻辑处理代码卛_?/p>
使用q种方式׃是系l自动调用,所以执行效率很好,但是׃部分手机实现的不规范Q所以通用性没有第一U方式高?/p>
说明Q高U用L面自动切换,不需要书写处理的代码?/p>
2、如何处理横竖屏切换
在检到横竖屏切换以后,需要对于横竖屏切换q行处理了,处理的方式一般有也有两种Q?/p>
A)单提C法
q种处理方式时指只实CU屏q模式下的逻辑Q而在另外一U屏q方式下昄提示。例如只实现竖屏模式的界面,在横屏模式下Q只出现提示Q?#8220;请在竖屏模式下用本E序”?/p>
使用q种方式E序实现比较单,只需要实C套界面即可,适合于比较简单的E序?/p>
B)实现两套UI?/strong>
q种处理方式时指在一个程序中实现两套界面Q一套横屏一套竖屏。可以在E序中设|一个screenType的参敎ͼ在每个节目的l制上v늣阀Ҏ中根据该参数判断是横屏还是竖屏,然后执行不同的绘制代码即可?/p>
使用q种方式E序实现比较友好Q但是程序开发和试的工作量都要E微大一点,而且横竖屏切换时数据的变换也比较ȝ?/p>
3、其它说?/strong>
在实现横竖屏切换Ӟ有些知识会帮助你实现一些很实用的功能:
在Nokia手机上禁止横竖屏切换Q?/strong>
在S60 5th 及以上版本中Q可以通过在jad或manifest文g中通过指定如下属性来指定E序q行时的屏幕cdQ?/p>
竖屏QNokia-MIDlet-App-OrientationQportrait
横屏QNokia-MIDlet-App-OrientationQlandscape
不之处希望大家U极指正、交和完善Q?/p>
但是我们一般都是用Java ME开发,开发出来的E序是jar格式Q可以运行在大部分型L手机上。可是BlackBerry是不支持jar的,它支持的是cod格式。所以如果你想你开发的Java MEE序q行在BlackBerry上,必d把它转ؓcod格式?/p>
要把一般Java MEE序转ؓcod可以在程序开发编译过E序中{Q也可以Ҏ包好的jar转?/p>
◆对已l打包好的jar文g转ؓBlackBerrycod格式:q入jar文g所在目录,在cmd(命o?中进入相应目录,输入下边命o可以了Q引用rapc import="C:\Program Files\Research In Motion\BlackBerry JDE 4.2.1\lib\net_rim_api.jar" codename=TestBB jad=TestBB.jad TestBB.jar
(注:q里rapc ?C:\Program Files\Research In Motion\BlackBerry JDE 4.2.1\bin\ 目录里的 rapc.exe, 如果环境变量没有讄Q这里就要用l对路径QC:\Program Files\Research In Motion\BlackBerry JDE 4.2.1\bin\rapc, 当然你可以把它写成一?bat文g来执行。而网上你可以搜烦C个名为Java2Cod.exe的小E序可以直接转换?
◆在JDE中编译程?无论你是用BlackBerry API或者Java ME/midp API来开发Java MEE序Q都可以在JDE上编译。最l他会自动打包成cod文g?/p>
◆在其IDE中编译打包:因ؓ我是用NetBeans来开发的Q所以本人只能提供NetBeans开发BlackBerryE序的见解。而本人是?NetBeans中用Java ME polish来整合开发的。下边介lJava ME polish中开发BlackBerryE序
Q可以参?a rel="nofollow">http://www.j2mepolish.org/docs/platform-blackberry.htmlQ?/p>
一、在Java ME polish安装目录里找到platforms.xml,在里边的BlackBerry部分中,加上<capability name="build.Finalizer" value="jar2cod" />Q我用的是Preview 2.0版本Q里边这一句是注释掉,所以要把它activateQ也可以在devices.xml中在你想用的emulator 属性里加上q一句。如果没有这一句编译时׃会自动把jar转换为cod文g?Q?/p>
polish里支持的版本最新系4.2.0,如果你安装BlackBerry版本最新的?.2.1/4.3.0,哪么你要?{BlackBerry目录}/lib/net_rim_API.jar copy?{Java MEpolish}/import里,在platforms.xml的BlackBerry中修?lt;capability name="build.BootClassPath" value="net_rim_API.jar" />
二、在工程的build.xml中添加blackberry.home属性(你安装的BlackBerry JDE目录Q?/p>
Java代码
三、如果你~译后打包的jad文g中缺MicroEdition-Configuration和MicroEdition-Profile两个属性是不能成功转成cod文g的。这可以在工E的build.xml中的<build>属性里?lt;jad>元素中添加:
Java代码
四、如果你用了h器的QobfuscatorQ,要把它unactivate,不要用它Q因为BlackBerry中的rapc转换q程中会对程序淆的?/p>
Java代码
五、在工程的build.xml中的<Java MEpolish>属性requirements用你想编译的BlackBerryemulator,如:
Java代码
如果你的E序是一般的Java MEE序Q那么就可以~译q行了。但如果你是用BlackBerry API开发的上v柱塞阀Q就要就把build.xml?lt;build>元素中的<midlet>,改ؓ<main>Q同时要dpolish.classes.midlet-1的variable。如Q?/p>
Java代码
(注:polish.classes.midlet-1的value? "Q中间有一个空格的Q我试过了,如果没有q个定义变量或者这个g间没有空|jar是不能{为cod格式?
q是我初接触BlackBerry时的一点心得。我用BlackBerry API在NetBeans+Java ME polish的环境下写过一个test appQ是能成功运行的Q希望对初接触BlackBerry的朋友有帮助?/p>
在我的项目中要用C个聚cȝ法,Affinity PropagationQAPQ,由多伦多大学的Brendan J. Frey发表?007q。相比其他的聚类法QAP法的聚cȝ果更加准?/p>
在AP的官方网站公布了AP法的动态链接库Q我的目标就是实现在Java工程中调用这个动态链接库?/p>
在网上查了资料,发现Q如果仅仅是惌用Windows的Native APIq是比较省事的,q里我主要针对第三方dll的调用?/p>
下面q入正题?/p>
q里主要用的Ҏ是JNI。在|上查资料时看到很多用JNI非常的复杂,不仅要看很多的文,而且要非常熟悉C/C++~程。恐怕有很多人在看到诸如此类的评论时已经军_l道用其他方法了。但是,假如你要实现的功能ƈ不复杂(单的参数传递,获取q回值等{)Q我q是支持使用q个Ҏ的?/p>
Java Native InterfaceQ简UJNIQ是Javaq_的一部分Q可用于让Java和其他语a~写的代码进行交互。下面是从网上摘取的JNI工作C意图?/p>
下面׃D具体的例子说明一下用步骤:
1Q?~写一个类Q声明nativeҎ
- public class APCluster {
- public native int[] CallAPClusterDll( int arg_Int,
- double[] arg_DoubleArray,
- boolean arg_boolean);
- static
- {
- System.loadLibrary("APClusterDllMedium");
- }
- }
上面是APCluster.java文gQ定义了一个APClusterc,其中有一个方法CallAPClusterDll()Q需要传递三U不同类型的参数Qƈ且返回一个整型数l?/p>
注意Q这里只需要声明这个方法,q不需要实玎ͼ具体实现在APClusterDllMedium中?/p>
APClusterDllMedium像中介一PJava通过调用q个中介Dll中的CallAPClusterDllҎQ间接调用真正的W三方Dll?/p>
2Q编译生?h文g
W一步:
javac APCluster.java 生成APCluster.class
W二步:
javah APCluster 生成APCluster.h头文Ӟ内容如下Q?/p>
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class APCluster */
- #ifndef _Included_APCluster
- #define _Included_APCluster
- #ifdef __cplusplus
- extern "C" {
- #endif10 /*
- * Class: APCluster
- * Method: CallAPClusterDll
- * Signature: (I[DZ)[I
- */
- JNIEXPORT jintArray JNICALL Java_APCluster_CallAPClusterDll
- (JNIEnv *, jobject, jint, jdoubleArray, jboolean);
- #ifdef __cplusplus
- }
- #endif21
- #endif
注意QAPCluster.hq个头文件的内容是不能修改的Q否则JNI会找不到相对应的CallAPClusterDll()的实现?/p>
3Q创建C/C++工程Q实现CallAPClusterDll()Ҏ?/strong>
创徏一个C/C++工程Q工E名为APClusterDllMediumQ其实,生成的dll名ؓAPClusterDllMedium卛_Q,导入APCluster.hq个头文Ӟq创Z个CPP文gQ实?h文g中的Ҏ?/p>
׃我创建的工程是win32控制台程序,所以最后默认生成的?exe文gQ所以还要做一步工E属性修改,让它生成.dll后缀文g?/p>
打开Project Property ->GeneralQ做以下修改Q?/p>
下面是实现 JNIEXPORT jintArray JNICALL Java_APCluster_CallAPClusterDll (JNIEnv *, jobject, jint, jdoubleArray, jboolean); q个Ҏ了。先贴代码再慢慢解释吧?/p>
- #include "APCluster.h"
- #include <stdio.h>
- #include <windows.h>
- #ifdef __cplusplus
- extern "C" {
- #endif
- typedef int* (__stdcall *APCLUSTER32)(double*, unsigned int, bool);
- JNIEXPORT jintArray JNICALL Java_APCluster_CallAPClusterDll
- (JNIEnv *env, jobject _obj, jint _arg_int, jdoubleArray _arg_doublearray, jboolean _arg_boolean)
- {
- HMODULE dlh = NULL;
- APCLUSTER32 apcluster32;
- if (!(dlh=LoadLibrary("apclusterwin.dll"))) //W三方DLL位置
- {
- printf("LoadLibrary() failed: %d\n", GetLastError());
- }
- if (!(apcluster32 = (APCLUSTER32)GetProcAddress(dlh, "apcluster32"))) //具体调用apcluster32Ҏ
- {
- printf("GetProcAddress() failed: %d\n", GetLastError());
- }
- int m_int = _arg_int; //cd转换
- double* m_doublearray = env->GetDoubleArrayElements(_arg_doublearray, NULL);
- bool m_boolean = _arg_boolean;
- int* ret = (*apcluster32)(m_doublearray, m_int, m_boolean); /* actual function call */
- jintArray result = env->NewIntArray(_arg_int);
- env->SetIntArrayRegion(result, 0, _arg_int, (const jint*)ret);
- FreeLibrary(dlh); /* unload DLL and free memory */
- if(ret)
- {
- free(ret);
- }
- return result;
- }
- #ifdef __cplusplus
- }
- #endif
aQ首先ؓ?include <jni.h>Q必L加JNI所在的目录?/p>
打开Project Property -> C/C++ -> General -> Additional Include Directoriesd相应目录Q?/p>
bQ在APCluster.h文g中自动生成的函数Q只标识了函数参数类型,Z引用q些参数Q自pv一个相应的名字Q?/p>
JNIEXPORT jintArray JNICALL Java_APCluster_CallAPClusterDll
(JNIEnv *env, jobject _obj, jint _arg_int, jdoubleArray _arg_doublearray, jboolean _arg_boolean) ......
cQ声明函数指针,是你要调用的第三方dll中函数的cd?/p>
dQLoadLibraryQ导入真正的W三方DllQƈ扑ֈ要调用的Ҏ的函数地址?/p>
把这个函数地址赋值给函数指针Q接下来可以通过q个函数指针调用真正的apcluster函数了!
eQ类型{换:
读读jni.h文gq道jdouble和double其实是一个东西,jboolean是unsigned charcdQjni.h中是q么声明的:
- typedef unsigned char jboolean;
- typedef unsigned short jchar;
- typedef short jshort;
- typedef float jfloat;
- typedef double jdouble;
但是数组cd没有这么简单,获取数组要用类型相对应的env->GetTypeArrayElement(jTypeArray...)?/p>
最后,要返回一个jintcd的数l,p新创Z个此cd的数l,再ؓ其赋|
- jintArray result = env->NewIntArray(_arg_int);
- env->SetIntArrayRegion(result, 0, _arg_int, (const jint*)ret);
其中Q_arg_int代表的是创徏数组的长度?/p>
最后return result?/p>
4QBuildq个工程?/strong>
BuildQ生成相应的APCluster.dll文gQ将q个dll攑ֈjava工程目录下?/p>
5Q编写测试javaE序Q调用dll库?/strong>
以下为测试程序,Test.javaQ?/p>
- public class Test
- {
- public static void main(String[] args)
- {
- double arg_doublearray[] = {0.1, 0.2, 0.3};
- int arg_int = 3;
- boolean arg_boolean = true;
- int[] result = new APCluster().CallAPClusterDll(arg_int, arg_doublearray, arg_boolean);
- .....
- }
- }
到此Qjava调用W三方dll基本完成了?/p>
本文也主要是介绍大概的操作流E,至于具体应该使用哪些API只有去研究官方文了?/p>
另外q有一些需要注意的问题Q比?4位的E序去调?2位的dll会报错啊{等...q些都是l节问题了?/p>
最后,个h认ؓQ自己动手实践还是很重要Q网上都说这个复杂那个难Q但是至于难q是不难Q还是要实践了才知道...不能不去试...