java世界有很多优秀的开源类库。如果想在.net世界使用,一种可以手动把java代码转换成C#。如NHibernate,Spring.net,Quartz.net等。但这种方式需要作者同时熟悉java和C#语言,并且要对原项目的架构有所熟悉。所以如果时间精力有限,但又想在.net环境下使用java的优秀类库,可以通过IKVM.NET这个工具把java项目的jar包编译成C#的程序集文件以供使用。本文通过以下步骤讲解shiro这个项目的转换过程。
1,下载最新的IKVM.NET。把zip包解压到某个路径下。如D:\ikvm-7.1.4532.2。
2,在环境变量里的path变量的值里添加:D:\ikvm-7.1.4532.2\bin。(别忘了前面加;号)
3,下载以下jar包:
commons-beanutils-core-1.8.3.jar commons-logging-api-1.1.1.jar slf4j-api-1.6.6.jar slf4j-jdk14-1.6.6.jar shiro-core-1.2.1.jarshiro-web-1.2.1.jar
4,在tomcat的lib文件夹下找到以下jar包:
el-api.jar jsp-api.jar servlet-api.jar
5,把这些包都放到同一个文件夹下如D:\shiro。
6,因为shiro项目依赖slf4j-api-1.6.6.jar和commons-beanutils-core-1.8.3.jar。而这两个jar包又依赖相应的jar包。经测试,得到如下编译顺序。打开cmd命令行。定位到D:\shiro。输入命令:
ikvmc -target:library commons-logging-api-1.1.1.jar
7,如图所示生成:commons-logging-api-1.1.1.dll。
8,输入命令:
ikvmc -target:library -reference:commons-logging-api-1.1.1.dll commons-beanutils-core-1.8.3.jar
9,生成:commons-beanutils-core-1.8.3.dll。
10,输入命令(注意是两个jar包):
ikvmc -target:library slf4j-api-1.6.6.jar slf4j-jdk14-1.6.6.jar
11,生成:slf4j-api-1.6.6.dll。
12,下面可以生成我们要在.net下使用的程序集了。输入命令:
ikvmc -target:library -reference:slf4j-api-1.6.6.dll -reference:commons-beanutils-core-1.8.3.dll shiro-core-1.2.1.jar
13,生成我们需要的:shiro-core-1.2.1.dll。
下面我们在VS2010里建个项目测试下生成的程序集是否可用。就以原shiro项目中的Quickstart例子来测试。
新建控制台应用程序Shiro.Quickstart,添加引用:IKVM.OpenJDK.Core.dll和shiro-core-1.2.1.dll。
添加Shiro.ini和log4j.properties文件,文件属性“复制到输出目录” 都设置为:始终复制。
文件program.cs内容如下:
using System; using System.Collections.Generic; using System.Text; using org.apache.shiro; using org.apache.shiro.authc; using org.apache.shiro.config; using org.apache.shiro.mgt; using org.apache.shiro.session; using org.apache.shiro.subject; using org.apache.shiro.util; using org.slf4j; namespace Shiro.Quickstart { class Program { [NonSerializedAttribute] private static readonly Logger log = LoggerFactory.getLogger("Quickstart"); static void Main(string[] args) { // The easiest way to create a Shiro SecurityManager with configured // realms, users, roles and permissions is to use the simple INI config. // We'll do that by using a factory that can ingest a .ini file and // return a SecurityManager instance: // Use the shiro.ini file at the root of the classpath // (file: and url: prefixes load from files and urls respectively): Factory factory = new IniSecurityManagerFactory(@"shiro.ini"); SecurityManager securityManager = (SecurityManager)factory.getInstance(); // for this simple example quickstart, make the SecurityManager // accessible as a JVM singleton. Most applications wouldn't do this // and instead rely on their container configuration or web.xml for // webapps. That is outside the scope of this simple quickstart, so // we'll just do the bare minimum so you can continue to get a feel // for things. SecurityUtils.setSecurityManager(securityManager); // Now that a simple Shiro environment is set up, let's see what you can do: // get the currently executing user: Subject currentUser = SecurityUtils.getSubject(); // Do some stuff with a Session (no need for a web or EJB container!!!) Session session = currentUser.getSession(); session.setAttribute("someKey", "aValue"); String value = (String)session.getAttribute("someKey"); if (value == "aValue") { log.info("Retrieved the correct value! [" + value + "]"); } // let's login the current user so we can check against roles and permissions: if (!currentUser.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); token.setRememberMe(true); try { currentUser.login(token); } catch (UnknownAccountException uae) { log.info("There is no user with username of " + token.getPrincipal()); } catch (IncorrectCredentialsException ice) { log.info("Password for account " + token.getPrincipal() + " was incorrect!"); } catch (LockedAccountException lae) { log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it."); } // ... catch more exceptions here (maybe custom ones specific to your application? catch (AuthenticationException ae) { //unexpected condition? error? } } //say who they are: //print their identifying principal (in this case, a username): log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); //test a role: if (currentUser.hasRole("schwartz")) { log.info("May the Schwartz be with you!"); } else { log.info("Hello, mere mortal."); } //test a typed permission (not instance-level) if (currentUser.isPermitted("lightsaber:weild")) { log.info("You may use a lightsaber ring. Use it wisely."); } else { log.info("Sorry, lightsaber rings are for schwartz masters only."); } //a (very powerful) Instance Level permission: if (currentUser.isPermitted("winnebago:drive:eagle5")) { log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + "Here are the keys - have fun!"); } else { log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!"); } //all done - log out! currentUser.logout(); System.Console.ReadKey(); } } }
按F5调试。运行成功。说明程序集基本可用。
结语:
但是,也应注意到debug下生成的所有文件达25M。而且在正式商用项目中性能也可能是一个问题。
这个只有自己在实际使用中权衡利弊了。