这个问题很简单。如何在另一个 Java 进程中启动主方法?现在我这样做:
startOptions = new String[] {"java", "-jar", "serverstart.jar"};
new ProcessBuilder(startOptions).start();
但是他们要求我不要使用外部.jar文件来执行此操作。serverstart.jar 显然有一个 main 方法,但是是否可以在另一个进程中调用该 main 方法,而无需调用 .jar 文件?
我在想这样的事情:
new ProcessBuilder(ServerStart.main(startOptions)).start();
但我不知道是否存在这样的事情。
假设带有新类加载器的新线程是不够的(不过我会投票支持这个解决方案),我知道你需要创建一个不同的进程来调用类中的 main 方法,而无需在清单文件中将其声明为"jar main 方法"——因为你不再有单独的 serverstart.jar了。
在这种情况下,您可以简单地调用 java -cp $yourClassPath your.package.ServerStart
,就像在没有(或不想使用)清单主类时运行任何 Java 应用程序一样。
从java创建新的"java"进程是不可能的,因为两个进程不能共享一个JVM。(请参阅此问题和接受的答案)。
如果您可以接受创建新Thread
而不是Process
则可以使用自定义ClassLoader
来实现。它尽可能接近一个新的过程。所有静态字段和最终字段都将重新初始化!
另请注意,"ServerStart
类(对于下面的示例)必须位于当前正在执行的 JVM 的类路径中):
public static void main(String args[]) throws Exception {
// start the server
start("ServerStart", "arg1", "arg2");
}
private static void start(final String classToStart, final String... args) {
// start a new thread
new Thread(new Runnable() {
public void run() {
try {
// create the custom class loader
ClassLoader cl = new CustomClassLoader();
// load the class
Class<?> clazz = cl.loadClass(classToStart);
// get the main method
Method main = clazz.getMethod("main", args.getClass());
// and invoke it
main.invoke(null, (Object) args);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
这是自定义类加载器:
private static class CustomClassLoader extends URLClassLoader {
public CustomClassLoader() {
super(new URL[0]);
}
protected java.lang.Class<?> findClass(String name)
throws ClassNotFoundException {
try{
String c = name.replace('.', File.separatorChar) +".class";
URL u = ClassLoader.getSystemResource(c);
String classPath = ((String) u.getFile()).substring(1);
File f = new File(classPath);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte buff[] = new byte[(int) f.length()];
dis.readFully(buff);
dis.close();
return defineClass(name, buff, 0, buff.length, (CodeSource) null);
} catch(Exception e){
throw new ClassNotFoundException(e.getMessage(), e);
}
}
}
我建议从java调用shellscript并使用它来启动新进程(如果您根本无法忍受另一个线程)。
我将在这里回答如何在没有弹簧:)的情况下创建多进程应用程序。使用spring,您可以通过xml配置来做到这一点。多线程是另一回事,这是多进程
创建一个 JavaProces 类,如下所示。您可以在您的环境中存储此类的对应 XML/JSON。然后从Runtime.getRuntime().exec(processRunnerString);
开始您的过程,
您应该首先找到java.exe
、vm args
,然后分别设置-classpath
mainClass
和args
。
最后你会得到类似java JRE\java.exe-classpath的东西。 .;*;lib* AClass arg1 - Dprop=val
您可以使用JMX
与其他进程进行通信。
import java.util.Dictionary;
import java.util.List;
public class JavaProcess {
private String mainClass;
private Dictionary<String, String> vmParameters;
private List<String> classPath;
private List<String> mainArgs;
public String getMainClass() {
return mainClass;
}
public void setMainClass(String mainClass) {
this.mainClass = mainClass;
}
public Dictionary<String, String> getVmParameters() {
return vmParameters;
}
public void setVmParameters(Dictionary<String, String> vmParameters) {
this.vmParameters = vmParameters;
}
public List<String> getClassPath() {
return classPath;
}
public void setClassPath(List<String> classPath) {
this.classPath = classPath;
}
public List<String> getMainArgs() {
return mainArgs;
}
public void setMainArgs(List<String> mainArgs) {
this.mainArgs = mainArgs;
}
}
MainRunner 应用程序,您可以从 配置文件。我刚刚在这里创建了一个虚拟进程,以防万一 错误我停止它从进程的回调。
//process
JavaProcess jp = new JavaProcess();
//java class
jp.setMainClass("com.hmg.vidapter.run.DriverLauncher");
//main args[]
List<String> mainArgsList = new ArrayList<String>();
mainArgsList.add("ABC1 ARG2 ARG3 ARGN");
jp.setMainArgs(mainArgsList);
//-classpath
List<String> classPath = new ArrayList<String>();
classPath.add("*");
classPath.add("libs\*");
classPath.add("repo\*");
jp.setClassPath(classPath);
//-Dvm args.
Dictionary<String, String> vmArgs = new Hashtable<String, String>();
vmArgs.put("-Dcom.sun.management.jmxremote", "");
vmArgs.put("-Dcom.sun.management.jmxremote.authenticate=false", "");
vmArgs.put("-Dcom.sun.management.jmxremote.port=1453", "");
vmArgs.put("-Dcom.sun.management.jmxremote.ssl=false", "");
jp.setVmParameters(vmArgs);
String params = JSONUtils.convertToJSON(jp);
System.out.println(params);
StringBuilder sb = new StringBuilder(""" + getJavaExecutablePath()+ """);
sb.append(" ");
Enumeration<String> vmaEnum = vmArgs.keys();
while (vmaEnum.hasMoreElements()) {
String key = vmaEnum.nextElement();
sb.append(key + " ");
String val=vmArgs.get(key);
if(val!=null && !val.isEmpty())
{
sb.append(val + " ");
}
}
sb.append(" -classpath ");
List<String> cps = jp.getClassPath();
for (String cp : cps) {
sb.append(cp+";");
}
sb.append(" ");
sb.append(jp.getMainClass());
sb.append(" ");
List<String> mainArgs = jp.getMainArgs();
for (String ma : mainArgs) {
sb.append(ma+" ");
}
System.out.println(sb.toString());
Process p = Runtime.getRuntime().exec(sb.toString());
//write output
InputStreamReader isrO = new InputStreamReader(p.getInputStream());
BufferedReader brO = new BufferedReader(isrO);
String callBackO = brO.readLine();
if (callBackO!=null)
{
System.out.println("Application Output: " + callBackO);
}
//write errput
InputStreamReader isr = new InputStreamReader(p.getErrorStream());
BufferedReader br = new BufferedReader(isr);
String callBack = br.readLine();
if (callBack!=null)
{
System.err.println("Application Error: "+ callBack);
//you can do whatever you want if you don't wanna stop it
p.destroyForcibly();
}
从java.home env.变量中确定java.exe的位置。
private static String getJavaExecutablePath(){
String javaHome = System.getProperty("java.home");
File f = new File(javaHome);
f = new File(f, "bin");
f = new File(f, "java.exe");
return f.getAbsolutePath();
}
Reflection(java.lang.reflect package)来执行此操作。
public static void main(String[] args) throws Exception {
Class c = Class.forName("ServerStart");
Class[] argTypes = { args.getClass() };
Method m = c.getMethod("main", argTypes);
Object passedArgv[] = { args };
m.invoke(null, passedArgv);
}