前言
最近一些缘故软构实验,需要开发一些Java的命令行程序。然而习惯了使用MobileSuit的我觉得这些重复的输入输出、解析操作,实在是过于繁琐。
MobileSuit是什么呢?是一套把用户输入的命令直接映射到某个类的方法的框架。具体参见MobileSuitDocs。它可以极大的简化命令解析这种繁琐、高度重复,但是又毫无意义的工作。
经过思考与权衡,我不难得出,我直接写实验所需要的时间我移植MobileSuit的时间+使用MobileSuit完成实验的时间。
因此,我花了昨天接近一天的时间, 完成了移植和Maven打包发布的配置(现在在等审核)。以下是具体过程:
.NET到JAVA移植
经过这么长时间的发展,.NET Core无疑是比Java更有效率、更强大的平台,再加上我学习Java时间不过两个月,理解不太深入,因此.NET的很多特性,(凭借我的能力)难以在JVM上实现。因此,在进行移植时,我决定只移植最核心的一部分功能,而去除了一些高级功能(参数类型转换,并行和异步流支持,成员递归成员的函数调用等等)。
尽管是轻量级版本,JMobileSuitLite仍然是强大的指够用了
考虑到C#和Java代码的高度相似性,移植就是一个映射过程;实际上在移植时,我做了如下映射:
不难看出,前9个都是相当直接的映射。而最后两者则由于JVM的局限性并没有被移植。
那么移植的重点在于对attribute和out-argument的处理。
Attribute的处理
Java中没有Attribute,但是有与之非常相似的Annotation.
其语法如
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface SuitIgnore
{
}
其中Retention(RetentionPolicy.RUNTIME)表示这个Annotation会被保留到运行时。Inherited表示允许继承。
然而这样只能解决单重Attribute的问题,对于多重Attribute(即一个目标上加的多个Attribute)
如SuitAlias标签:
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public sealed class SuitAliasAttribute : Attribute
{
/// <summary>
/// Initialize a SuitAlias with its text.
/// </summary>
/// <param name="text">The alias.</param>
public SuitAliasAttribute(string text)
{
Text = text;
}
/// <summary>
/// The alias.
/// </summary>
public string Text { get; }
}
在Java中转换为
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface SuitAlias
{
/**
* @return The alias.
*/
String value();
}
会出现问题。为了解决这一问题,需要额外增加一个Annotation
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface SuitAliases
{
/**
* Container.
* @return SuitAliases
*/
SuitAlias[] value();
}
并修改SuitAlias
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(SuitAliases.class)
public @interface SuitAlias
{
/**
* @return The alias.
*/
String value();
}
这样,一个目标就可以添加多个SuitAlias了。在解析时,一个SuitAlias会被解释为SuitAlias注解,而SuitAliases会get到null;多个SuitAlias会被解释为一个SuitAliases,而SuitAlias会get到null。
out-argument的处理
C#中out关键字用于方法参数的声明,如:
bool TryParse(string expression, out int result){}
这个函数尝试解析expression,返回解析是否成功,然后把解析结果输出在result变量。
很遗憾,Java并没有这样的功能。因此我们只能在函数返回值上进行通融:把返回值改成一个元组。
Java也没有Tuple,不过这个很容易就能写一个(
那么映射过程就是:
public TraceBack Execute(string[] args, out object? returnValue);
映射为
Tuple<TraceBack,Object> Execute(String[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException;
小结
C#代码移植到Java,除了上面提到的两点以外,基本都是机械性的东西。因为C#的很多语法糖在Java(尤其是被迫使用的Java8)里都是没有的。不过只要有耐心,移植绝对不是难事。
使用Maven打包和发布
创建Maven的JIRA仓库
和C#统一使用nuget不同,java的包管理(貌似)很混乱,所以我就选择了一个maven(因为之前一直在用)。
要把jar包发布到Maven,供所有人进行下载,实际上是发布到maven的中央仓库里。在search.maven.org里,你可以搜到所有maven的jar包。
然而这个站点和nuget.org不同,完全找不到上传!(吐血
经过一番搜索与学习,最终我找到了发布maven jar包的正路。
首先,你得有一个待发布的maven项目。
然后,到jira创建账户(密码要求复杂,建议用Chrome生成的),并new一个issue。这个网站打开很慢,不过后面还有一个网站打开比它还慢,审核、同步也需要一段时间,所以,可能你传一个nuget包,上传+审核只需要几分钟,发布一个maven包却可能需要几个小时。 问题配置如图:
项目选Community Support - Open Source Project Repository Hosting (OSSRH),问题类型New Project,概要写项目名,描述就是项目描述~ Group ID一般写io.github.用户名就行 Project URL是项目地址,github链接贴上 SCM url是仓库地址,就是git clone时用的地址 usernames写自己和合作伙伴的JIRA用户名即可。 Already Synced to Central表示是否已经上传到中央仓库,当然是否啦~ 创建完Issue,你会被导航到这个页面
等待一会,管理员在comment中会给提示,然后issue状态变为waiting for response。
这时,只要按照人家的要求做,然后一定记得开放后评论就行,然后issue状态变回开放。
一般而言,再过几分钟,issue状 态会变为已解决,那么JIRA的一切都已经准备妥当了!车备好了,团长!
创建并上传GPG密钥
之后,我们需要生成一个gpg密钥用来给包签名,具体操作参考github给的教程
gpg要添加到path。
生成的gpg密钥要上传到OpenGPG服务器,才有效。 执行命令:
gpg --send-keys <key的ID>
来上传。key的ID就是gpg --list-secret-keys --keyid-format LONG
得到那 个.
然后运行命令
gpg --recv-keys <key的ID>
来进行检查,显示
就备好了。由于GPG服务器之间的同步需要时间,后面mvn deploy
的时候可能会出现认证失败的问题,等一段时间就好了。
配置maven设置
之后我们要配置maven的setting.xml,可以在maven home/conf里配置全局的,也可以在用户文件夹/.m2/里配置用户的。 一共有两处需要增加;一个是在servers标签下,增加:
<server>
<id>随便起一个名</id>
<username>jira用户名</username>
<password>jira密码</password>
</server>
记刚才随便起的名为"server名" 还有profiles下增加:
<profile>
<id>server名</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<gpg.executable>gpg</gpg.executable>
<gpg.passphrase>gpg密钥的密码</gpg.passphrase>
</properties>
</profile>
另外,由于国内下载maven包速度感人,还可以换maven源,在mirrors下面添加
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
这是阿里云的源,速度很不错。
配置项目
最后,我们配置Maven项目的POM.xml,格式如下,注意repository
那里可能需要修改,具体看Jira那边给的评论。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>项目名</name>
<groupId>就是JIRA上提issue用的那个</groupId>
<artifactId>项目识别id,需要是唯一的</artifactId>
<version>版本</version>
<packaging>jar</packaging>
<organization>
<!-- 如果有组织,可填写此字段 -->
<name>组织名</name>
<url>组织url</url>
</organization>
<url>项目url</url>
<inceptionYear>项目创建年份,用于copyright</inceptionYear>
<developers>
<developer>
<!-- 开发者字段,写自己就行咯 -->
<name>名字</name>
<email>邮箱</email>
<url>地址</url>
</developer>
</developers>
<description>项目描述</description>
<licenses>
<license>
<!-- 项目的license,比如MIT -->
<name>MIT</name>
<url>https://www.mit.edu/~amini/LICENSE.md</url>
</license>
</licenses>
<build>
<finalName>${project.organization.name}.${project.name}.${project.version}</finalName>
<defaultGoal>Package</defaultGoal>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- 打包Javadoc的插件,建议加上 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<encoding>UTF-8</encoding>
<charset>UTF-8</charset>
<docencoding>UTF-8</docencoding>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 打包源代码的插件,建议加上 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.3</version>
<extensions>true</extensions>
<configuration>
<serverId>server名</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<failOnError>false</failOnError>
<doclint>none</doclint>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>3.0.0-M1</version>
<configuration>
<tagNameFormat>v@{project.version}</tagNameFormat>
<autoVersionSubmodules>true</autoVersionSubmodules>
</configuration>
</plugin>
</plugins>
</build>
<distributionManagement>
<snapshotRepository>
<id>server名</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<repository>
<id>server名</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
</profile>
</profiles>
<scm>
<!-- git相关配置 -->
<url>项目github地址</url>
<connection>scm:git:+仓库地址(git clone时用的那个)</connection>
<developerConnection>scm:git:+仓库地址(git clone时用的那个)</developerConnection>
<tag>${project.version}</tag>
</scm>
</project>
上传,发布
之后,执行maven命令
mvn clean deploy -P release
就可以进行发布了。
在所有本地操作运行完后(一分钟),你可以到JIRA仓库里找到你上传的包,搜索groupID或者包名就行。然后,再过2小时左右,你可以在maven中心仓库搜索到你的包,此时,发布就算是完全成功了。
总结
移植花了一整天,发布花了大半天,总的来说,移植就是简单粗暴体力活,发布则需要更多耐心和细心,这一路上坑很多,网站打开速度也很慢。
但是,当你准备发布一个项目时,就应该为这些做好心理准备了吧。
所以说啊,不要停下来啊!
加油!奥利给!