Scala URLClassLoader没有重新加载类文件

时间:2018-12-11 10:51:56

标签: java scala akka

我正在运行一个scala项目,需要执行一些规则。这些规则将在运行时从scala类文件中动态添加或删除。

因此,我希望每当修改规则类时,都应重新加载它以获取更改,而无需停止执行过程。

我使用runtime.getruntime.exec()对其进行编译

和URL类加载器从类中获取修改后的代码

exec()运行正常。并且即使在使用URL Class Loader的情况下,目标文件夹中的类也会被修改。

但是它给了我与项目开始时一样的结果。它没有给我修改代码。

下面是我正在使用的代码。

package RuleEngine

import akka.actor._
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import akka.util.Timeout

import scala.io.StdIn
import Executor.Compute
import scala.concurrent.{Await, ExecutionContextExecutor}
import scala.concurrent.duration._


object StatsEngine {

def main(args: Array[String]) {

implicit val system: ActorSystem = ActorSystem("StatsEngine")
implicit val materializer: ActorMaterializer = ActorMaterializer()
implicit val executionContext: ExecutionContextExecutor = system.dispatcher

implicit val timeout = Timeout(10 seconds)
val computeDataActor = system.actorOf(Props[Compute],"ComputeData")


val route = {
  post {
    path("computedata"/) {
        computeDataActor ! "Execute"
        complete("done")
      }
    }
  }


val bindingFuture = Http().bindAndHandle(route , "localhost", 9000)

println(s"Server online at http://localhost:9000/\nPress RETURN to stop...")
 }
}

这是我创建Akka HTTP来创建API的主要目标文件

它将调用下面的代码的computeDataActor。

package Executor

import java.io.File
import java.net.URLClassLoader

import CompiledRules.RulesList
import akka.actor.Actor

class Compute extends Actor{

  def exceuteRule(): Unit ={
    val rlObj = new RulesList
    rlObj.getClass.getDeclaredMethods.map(name=>name).foreach(println)

    val prcs = Runtime.getRuntime().exec("scalac /home/hduser/MStatsEngine/Test/RuleListCollection/src/main/scala/CompiledRules/RuleList.scala -d  /home/hduser/MStatsEngine/Test/RuleListCollection/target/scala-2.11/classes/")
    prcs.waitFor()

    val fk = new File("/home/hduser/MStatsEngine/Test/RuleListCollection/target/scala-2.11/classes/").toURI.toURL

    val classLoaderUrls = Array(fk)

    val urlClassLoader = new URLClassLoader(classLoaderUrls)

    val beanClass = urlClassLoader.loadClass("CompiledRules.RulesList")

    val constructor = beanClass.getConstructor()

    val beanObj = constructor.newInstance()

    beanClass.getDeclaredMethods.map(x=>x.getName).foreach(println)

  }
  override def receive: Receive ={
    case key:String => {
      exceuteRule()
    }
  }
}

导入规则,如下所述。

package CompiledRules

class RulesList{

  def R1 : Any = {
    return "executing R1"
  }

  def R2 : Any = {return "executing R2"}
//  def R3 : Any = {return "executing R3"}
  //def R4 : Any = {return "executing R4"}
  def R5 : Any = {return "executing R5"}
}//Replace

因此,当我执行代码并调用API时,我将得到输出

R1
R2
R5

现在,在不停止项目的情况下,我将取消注释R3和R4。我会再次调用API, 当我再次执行代码时,使用

runtime.getruntime.exec()

它将编译文件并更新target中的类

因此,我使用URLClassLoader来获取修改代码的新对象。

但是不幸的是,我在项目开始时总是得到相同的结果

R1
R2
R5

下面是完整项目的链接 Source Code

1 个答案:

答案 0 :(得分:0)

val beanClass = urlClassLoader.loadClass("CompiledRules.RulesList")
    val constructor = beanClass.getConstructor()
    val beanObj = constructor.newInstance()

只是创建newInstance的已加载类。

Java的内置类加载器始终在加载类之前检查是否已经加载了一个类。 loadClass

protected Class<?> loadClass(String name,
             boolean resolve)
                  throws ClassNotFoundException

使用指定的二进制名称加载类。此方法的默认实现按以下顺序搜索类: 调用findLoadedClass(String)检查类是否已经加载。

在父类加载器上调用loadClass方法。如果父级为null,则使用虚拟机内置的类加载器。

调用findClass(String)方法来查找类。

要重新加载类,您将必须按照此link

的方式实现自己的ClassLoader子类。