问题很简单。如何在另一个java进程中启动main方法?现在我这样做:
startOptions = new String[] {"java", "-jar", "serverstart.jar"};
new ProcessBuilder(startOptions).start();
但他们要求我不要使用外部.jar文件。 serverstart.jar显然有一个main方法,但是可以在不调用.jar文件的情况下在另一个进程中调用main方法吗?
我在考虑这样的事情:
new ProcessBuilder(ServerStart.main(startOptions)).start();
但我不知道是否存在这样的事情。
亲切的问候,
答案 0 :(得分:7)
从java创建一个新的“java”进程不,因为两个进程不能共享一个JVM 。 (见question and the accepted answer)。
如果您可以创建新的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);
}
}
}
答案 1 :(得分:7)
假设一个带有新类加载器的新线程是不够的(我会投票支持这个解决方案),我知道你需要创建一个独特的进程来调用类中的main方法而不必将其声明为“jar main方法” “在清单文件中 - 因为你不再拥有一个独特的serverstart.jar。
在这种情况下,您可以简单地调用java -cp $yourClassPath your.package.ServerStart
,就像在没有(或者不想使用)清单Main-Class时运行任何Java应用程序一样。
答案 2 :(得分:1)
我建议从java调用shell脚本并使用它来启动新进程(如果你不能只使用另一个线程)。
答案 3 :(得分:0)
我将在这里回答如何在不使用spring的情况下创建多进程应用程序:)。 使用spring,您可以通过xml config来实现。 多线程是另一个故事,这是多进程
创建一个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();
}
答案 4 :(得分:-2)
您可以使用Reflection(java.lang.reflect包)执行此操作。
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);
}