我正在运行一个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
答案 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子类。