博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
maven scala plugin 实现jvmArgs,执行过程原理解析笔记
阅读量:5967 次
发布时间:2019-06-19

本文共 17468 字,大约阅读时间需要 58 分钟。

scala-maven-plugin 构建scala,springboot工程

我们在使用maven构建scala工程的时候, 要使用scala-maven-plugin插件,常规配置如下 :

org.scala-tools
maven-scala-plugin
pdata
com.pdata.PDataApplication
-Xmx1024m
-Dsword.autokey.port=9202

这样,就可以直接使用如下命令动态注入sword.autokey.prot的值, 启动应用:

mvn scala:run -Dlauncher=pdata

实现原理剖析

源码地址:

mvn scala:run -Dlauncher=pdata 执行过程讲解

自己动手编写Maven插件

参考: (Maven Plugin示例:自己动手编写Maven插件: )

Maven插件项目的POM有两个特殊的地方:

  1. 它的packaging必须为maven-plugin,这种特殊的打包类型能够控制Maven为其在生命周期阶段绑定插件处理相关的目标,例如在compile阶段,Maven需要为插件项目构建一个特殊插件描述符文件。

  1. maven-plugin-api依赖中包含了插件开发所必须得类。

首先,每个插件目标类,或者说MOJO,都必须继承AbstractMojo类并实现execute()方法,只有这样Maven才能识别该插件目标,并执行execute()方法中的行为。

mvn scala:run -Dlauncher=pdata 执行过程

项目运行run生命周期对应的处理类 scala_maven.ScalaRunMojo

package scala_maven;import org.apache.maven.toolchain.Toolchain;import org.codehaus.plexus.util.StringUtils;import scala_maven_executions.JavaMainCaller;import scala_maven_executions.JavaMainCallerByFork;import scala_maven_executions.MainHelper;/** * Run a Scala class using the Scala runtime * * @goal run * @requiresDependencyResolution test * @execute phase="test-compile" * @threadSafe */public class ScalaRunMojo extends ScalaMojoSupport {    /**     * The class to use when launching a scala program     *     * @parameter property="launcher"     */    protected String launcher;    /**     * Additional parameter to use to call the main class     * Using this parameter only from command line ("-DaddArgs=arg1|arg2|arg3|..."), not from pom.xml.     * @parameter property="addArgs"     */    protected String addArgs;    /**     * A list of launcher definition (to avoid rewriting long command line or share way to call an application)     * launchers could be define by :     * 
     *   <launchers>     *     <launcher>     *       <id>myLauncher</id>     *       <mainClass>my.project.Main</mainClass>     *       <args>     *         <arg>arg1</arg>     *       </args>     *       <jvmArgs>     *         <jvmArg>-Xmx64m</jvmArg>     *       </jvmArgs>     *     </launcher>     *     <launcher>     *       <id>myLauncher2</id>     *       ...     *       <><>     *     </launcher>     *   </launchers>     * 
* @parameter */ protected Launcher[] launchers; /** * Main class to call, the call use the jvmArgs and args define in the pom.xml, and the addArgs define in the command line if define. * * Higher priority to launcher parameter) * Using this parameter only from command line (-DmainClass=...), not from pom.xml. * @parameter property="mainClass" */ protected String mainClass; @Override protected void doExecute() throws Exception { JavaMainCaller jcmd = null; Toolchain toolchain = toolchainManager.getToolchainFromBuildContext("jdk", session); if (StringUtils.isNotEmpty(mainClass)) { jcmd = new JavaMainCallerByFork(this, mainClass, MainHelper.toMultiPath(project.getTestClasspathElements()), jvmArgs, args, forceUseArgFile, toolchain); } else if ((launchers != null) && (launchers.length > 0)) { if (StringUtils.isNotEmpty(launcher)) { for(int i = 0; (i < launchers.length) && (jcmd == null); i++) { if (launcher.equals(launchers[i].id)) { getLog().info("launcher '"+ launchers[i].id + "' selected => "+ launchers[i].mainClass ); jcmd = new JavaMainCallerByFork(this, launchers[i].mainClass, MainHelper.toMultiPath(project.getTestClasspathElements()), launchers[i].jvmArgs, launchers[i].args, forceUseArgFile, toolchain); } } } else { getLog().info("launcher '"+ launchers[0].id + "' selected => "+ launchers[0].mainClass ); jcmd = new JavaMainCallerByFork(this, launchers[0].mainClass, MainHelper.toMultiPath(project.getTestClasspathElements()), launchers[0].jvmArgs, launchers[0].args, forceUseArgFile, toolchain); } } if (jcmd != null) { if (StringUtils.isNotEmpty(addArgs)) { jcmd.addArgs(StringUtils.split(addArgs, "|")); } jcmd.run(displayCmd); } else { getLog().warn("Not mainClass or valid launcher found/define"); } }}

JavaMainCaller

package scala_maven_executions;import java.io.File;/** * This interface is used to create a call on a main method of a java class. * * The important implementations are JavaCommand and ReflectionJavaCaller * * @author J. Suereth * */public interface JavaMainCaller {    /** Adds an environemnt variable */    public abstract void addEnvVar(String key, String value);    /** Adds a JVM arg.  Note: This is not available for in-process "forks" */    public abstract void addJvmArgs(String... args);    /** Adds arguments for the process */    public abstract void addArgs(String... args);    /** Adds option (basically two arguments) */    public abstract void addOption(String key, String value);    /** Adds an option (key-file pair). This will pull the absolute path of the file */    public abstract void addOption(String key, File value);    /** Adds the key if the value is true */    public abstract void addOption(String key, boolean value);    /** request run to be redirected to maven/requester logger */    public abstract void redirectToLog();    // TODO: avoid to have several Thread to pipe stream    // TODO: add support to inject startup command and shutdown command (on :quit)    public abstract void run(boolean displayCmd) throws Exception;    /** Runs the JavaMain with all the built up arguments/options */    public abstract boolean run(boolean displayCmd, boolean throwFailure) throws Exception;    /**     * run the command without stream redirection nor waiting for exit     *     * @param displayCmd     * @return the spawn Process (or null if no process was spawned)     * @throws Exception     */    public abstract SpawnMonitor spawn(boolean displayCmd) throws Exception;}

ToolchainManager

这个接口是maven-core里面的.

Launcher领域对象模型

package scala_maven;public class Launcher {    protected String id;    protected String mainClass;    /**     * Jvm Arguments     *     * @parameter     */    protected String[] jvmArgs;    /**     * compiler additionnals arguments     *     * @parameter     */    protected String[] args;}

核心处理类ScalaMojoSupport, ScalaCompilerSupport

这个处理类中, 有对scala编译器和运行时依赖的实现处理.

基于 Zinc 的编译器:

com.typesafe.zinc:zinc:0.3.5

我们来看一下源码:

package com.typesafe.zincclass Compiler(scalac : sbt.compiler.AnalyzingCompiler, javac : xsbti.compile.JavaCompiler) extends scala.AnyRef {  def compile(inputs : com.typesafe.zinc.Inputs)(log : xsbti.Logger) : sbt.inc.Analysis = { /* compiled code */ }  def compile(inputs : com.typesafe.zinc.Inputs, cwd : scala.Option[java.io.File])(log : xsbti.Logger) : sbt.inc.Analysis = { /* compiled code */ }  def autoClasspath(classesDirectory : java.io.File, allScalaJars : scala.Seq[java.io.File], javaOnly : scala.Boolean, classpath : scala.Seq[java.io.File]) : scala.Seq[java.io.File] = { /* compiled code */ }  override def toString() : scala.Predef.String = { /* compiled code */ }}object Compiler extends scala.AnyRef {  val CompilerInterfaceId : java.lang.String = { /* compiled code */ }  val JavaClassVersion : java.lang.String = { /* compiled code */ }  val compilerCache : com.typesafe.zinc.Cache[com.typesafe.zinc.Setup, com.typesafe.zinc.Compiler] = { /* compiled code */ }  val residentCache : xsbti.compile.GlobalsCache = { /* compiled code */ }  val analysisCache : com.typesafe.zinc.Cache[com.typesafe.zinc.FileFPrint, scala.Option[scala.Tuple2[sbt.inc.Analysis, sbt.CompileSetup]]] = { /* compiled code */ }  def apply(setup : com.typesafe.zinc.Setup, log : xsbti.Logger) : com.typesafe.zinc.Compiler = { /* compiled code */ }  def getOrCreate(setup : com.typesafe.zinc.Setup, log : xsbti.Logger) : com.typesafe.zinc.Compiler = { /* compiled code */ }  def create(setup : com.typesafe.zinc.Setup, log : xsbti.Logger) : com.typesafe.zinc.Compiler = { /* compiled code */ }  def newScalaCompiler(instance : sbt.ScalaInstance, interfaceJar : java.io.File, log : xsbti.Logger) : sbt.compiler.AnalyzingCompiler = { /* compiled code */ }  def newJavaCompiler(instance : sbt.ScalaInstance, javaHome : scala.Option[java.io.File], fork : scala.Boolean) : xsbti.compile.JavaCompiler = { /* compiled code */ }  def createResidentCache(maxCompilers : scala.Int) : xsbti.compile.GlobalsCache = { /* compiled code */ }  def analysisStore(cacheFile : java.io.File) : sbt.inc.AnalysisStore = { /* compiled code */ }  def analysis(cacheFile : java.io.File) : sbt.inc.Analysis = { /* compiled code */ }  def analysisIsEmpty(cacheFile : java.io.File) : scala.Boolean = { /* compiled code */ }  def scalaInstance(setup : com.typesafe.zinc.Setup) : sbt.ScalaInstance = { /* compiled code */ }  def scalaLoader(jars : scala.Seq[java.io.File]) : java.net.URLClassLoader = { /* compiled code */ }  def scalaVersion(scalaLoader : java.lang.ClassLoader) : scala.Option[scala.Predef.String] = { /* compiled code */ }  def compilerInterface(setup : com.typesafe.zinc.Setup, scalaInstance : sbt.ScalaInstance, log : xsbti.Logger) : java.io.File = { /* compiled code */ }  def interfaceId(scalaVersion : scala.Predef.String) : java.lang.String = { /* compiled code */ }}

可以看出,编译的过程是,

  • scalac把scala代码编译成.class文件

  • ClassLoader对.class文件的读写操作, 寻找classpath下面的类, 加载到jvm执行引擎中

  • 最后在jvm中执行.class字节码.

增量编译: ScalaCompilerSupport.incrementalCompile

//// Incremental compilation//@SuppressWarnings("unchecked")protected int incrementalCompile(List
classpathElements, List
sourceRootDirs, File outputDir, File cacheFile, boolean compileInLoop) throws Exception, InterruptedException { List
sources = findSourceWithFilters(sourceRootDirs); if (sources.isEmpty()) { return -1; } if (incremental == null) { File libraryJar = getLibraryJar(); File compilerJar = getCompilerJar(); List
extraJars = getCompilerDependencies(); extraJars.remove(libraryJar); String sbtGroupId = SbtIncrementalCompiler.SBT_GROUP_ID; String xsbtiArtifactId = SbtIncrementalCompiler.XSBTI_ARTIFACT_ID; String compilerInterfaceArtifactId = SbtIncrementalCompiler.COMPILER_INTERFACE_ARTIFACT_ID; String compilerInterfaceClassifier = SbtIncrementalCompiler.COMPILER_INTERFACE_CLASSIFIER; String sbtVersion = findVersionFromPluginArtifacts(sbtGroupId, SbtIncrementalCompiler.COMPILER_INTEGRATION_ARTIFACT_ID); File xsbtiJar = getPluginArtifactJar(sbtGroupId, xsbtiArtifactId, sbtVersion); List
zincArgs = StringUtils.isEmpty(addZincArgs) ? new LinkedList
() : (List
) Arrays.asList(addZincArgs.split("\\|")); File interfaceSrcJar = getPluginArtifactJar(sbtGroupId, compilerInterfaceArtifactId, sbtVersion, compilerInterfaceClassifier); incremental = new SbtIncrementalCompiler(useZincServer, zincPort, libraryJar, compilerJar, extraJars, xsbtiJar, interfaceSrcJar, getLog(), zincArgs); } classpathElements.remove(outputDir.getAbsolutePath()); List
scalacOptions = getScalaOptions(); List
javacOptions = getJavacOptions(); Map
cacheMap = getAnalysisCacheMap(); try { incremental.compile(project.getBasedir(), classpathElements, sources, outputDir, scalacOptions, javacOptions, cacheFile, cacheMap, compileOrder); } catch (xsbti.CompileFailed e) { if (compileInLoop) { compileErrors = true; } else { throw e; } } return 1;}

调用的compile方法:

public void compile(File baseDir, List
classpathElements, List
sources, File classesDirectory, List
scalacOptions, List
javacOptions, File cacheFile, Map
cacheMap, String compileOrder) throws Exception { if (useServer) { zincCompile(baseDir, classpathElements, sources, classesDirectory, scalacOptions, javacOptions, cacheFile, cacheMap, compileOrder); } else { if (log.isDebugEnabled()) log.debug("Incremental compiler = " + compiler + " [" + Integer.toHexString(compiler.hashCode()) + "]"); List
classpath = pathsToFiles(classpathElements); Inputs inputs = Inputs.create(classpath, sources, classesDirectory, scalacOptions, javacOptions, cacheFile, cacheMap, compileOrder, defaultOptions(), true); if (log.isDebugEnabled()) Inputs.debug(inputs, logger); compiler.compile(inputs, logger); }}

如果useServer编译,执行zincCompile方法:

private void zincCompile(File baseDir, List
classpathElements, List
sources, File classesDirectory, List
scalacOptions, List
javacOptions, File cacheFile, Map
cacheMap, String compileOrder) throws Exception { List
arguments = new ArrayList
(extraArgs); arguments.add("-log-level"); arguments.add(logLevelToString(log)); arguments.add("-scala-compiler"); arguments.add(compilerJar.getAbsolutePath()); arguments.add("-scala-library"); arguments.add(libraryJar.getAbsolutePath()); arguments.add("-scala-extra"); List
extraPaths = new ArrayList
(); for (File extraJar : extraJars) { extraPaths.add(extraJar.getAbsolutePath()); } arguments.add(MainHelper.toMultiPath(extraPaths)); if (!classpathElements.isEmpty()) { arguments.add("-classpath"); arguments.add(MainHelper.toMultiPath(classpathElements)); } arguments.add("-d"); arguments.add(classesDirectory.getAbsolutePath()); for (String scalacOption : scalacOptions) { arguments.add("-S" + scalacOption); } for (String javacOption : javacOptions) { arguments.add("-C" + javacOption); } arguments.add("-compile-order"); arguments.add(compileOrder); arguments.add("-analysis-cache"); arguments.add(cacheFile.getAbsolutePath()); arguments.add("-analysis-map"); arguments.add(cacheMapToString(cacheMap)); for (File source : sources) { arguments.add(source.getAbsolutePath()); } int exitCode = zinc.run(arguments, baseDir, System.out, System.err); if (exitCode != 0) { xsbti.Problem[] problems = null; throw new sbt.compiler.CompileFailed(arguments.toArray(new String[arguments.size()]), "Compile failed via zinc server", problems); }}

ZincClient.run方法

/** * Java API for sending a zinc command to a currently running nailgun server. * All output goes to specified output streams. Exit code is returned. * @throws java.net.ConnectException if the zinc server is not available */@throws(classOf[java.net.ConnectException])def run(args: JList[String], cwd: File, out: OutputStream, err: OutputStream): Int =  send("zinc", args.asScala, cwd, out, err)/** * Send a command to a currently running nailgun server. * Possible commands are "zinc", "status", and "shutdown". * All output goes to specified output streams. Exit code is returned. * @throws java.net.ConnectException if the zinc server is not available */def send(command: String, args: Seq[String], cwd: File, out: OutputStream, err: OutputStream): Int = {  val socket  = new Socket(address, port)  val sockout = socket.getOutputStream  val sockin  = new DataInputStream(socket.getInputStream)  sendCommand(command, args, cwd, sockout)  val exitCode = receiveOutput(sockin, out, err)  sockout.close(); sockin.close(); socket.close()  exitCode}

ZincClient是

Client for talking directly to a nailgun server from another JVM.

增量编译是只编译那些源代码在上一次编译之后有修改的类,及那些受这些修改影响到的类,它可以大大减少 Scala 的编译时间。频繁编译代码的增量部分是非常有用的,因为在开发时我们经常要这样做。

Scala 插件现在通过集成 Zinc 来支持增量编译, 它是 sbt 增量 Scala 编译器的一个单机版本。

com.typesafe.zinc.Compiler.compile方法

/** * Run a compile. The resulting analysis is also cached in memory. */def compile(inputs: Inputs, cwd: Option[File])(log: Logger): Analysis = {  import inputs._  if (forceClean && Compiler.analysisIsEmpty(cacheFile)) Util.cleanAllClasses(classesDirectory)  val getAnalysis: File => Option[Analysis] = analysisMap.get _  val aggressive    = new AggressiveCompile(cacheFile)  val cp            = autoClasspath(classesDirectory, scalac.scalaInstance.allJars, javaOnly, classpath)  val compileOutput = CompileOutput(classesDirectory)  val globalsCache  = Compiler.residentCache  val progress      = None  val maxErrors     = 100  val reporter      = new LoggerReporter(maxErrors, log, identity)  val skip          = false  val incOpts       = incOptions.options  val compileSetup  = new CompileSetup(compileOutput, new CompileOptions(scalacOptions, javacOptions), scalac.scalaInstance.actualVersion, compileOrder, incOpts.nameHashing)  val analysisStore = Compiler.analysisStore(cacheFile)  val analysis      = aggressive.compile1(sources, cp, compileSetup, progress, analysisStore, getAnalysis, definesClass, scalac, javac, reporter, skip, globalsCache, incOpts)(log)  if (mirrorAnalysis) {    SbtAnalysis.printRelations(analysis, Some(new File(cacheFile.getPath() + ".relations")), cwd)  }  SbtAnalysis.printOutputs(analysis, outputRelations, outputProducts, cwd, classesDirectory)  analysis}

转载地址:http://zxqax.baihongyu.com/

你可能感兴趣的文章
Js屏蔽键盘输入的某些字符,用以部分代替正则表达式
查看>>
(1)H5实现音乐播放器【正在播放-歌词篇】
查看>>
python----设置默认编码
查看>>
leetcode 3. Longest Substring Without Repeating Characters
查看>>
高并发下的系统设计(偏数据库设计)
查看>>
人人都能够做深度学习应用:入门篇
查看>>
java设计模式学习 ----- 单例模式(Singleton)
查看>>
JavaScript 移动和触摸框架
查看>>
bzoj3524: [Poi2014]Couriers / bzoj2223: [Coci 2009]PATULJCI 主席树
查看>>
.net 下载文件几种方式
查看>>
SDWebImage之工具类
查看>>
linux下sort详解(sort对科学记数法的排序)
查看>>
linux C(hello world)三个数最大和三个数最新
查看>>
J2EE的13种核心技术
查看>>
Front_end - - BOM和DOM
查看>>
matlab练习程序(径向模糊1)
查看>>
显著性检测(saliency detection)评价指标之KL散度距离Matlab代码实现
查看>>
POJ3264Balanced Lineup(线段树)
查看>>
《程序是怎样跑起来的》读书笔记——第三章 计算机进行小数运算时出错的原因...
查看>>
Cookie技术
查看>>