当程序有多个启动入口时,需要根据不同的启动类来决定方法是否执行,此时就需要获取启动类。
首先根据系统参数 sun.java.command 来获取启动类,如果以jar包方式启动,则获取到的就是jar包名称,此时需要从线程栈中获取main方法或者从jar包的元数据中获取。
java代码如下:
java">
import lombok.extern.slf4j.Slf4j;import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;@Slf4j
public class MainClassUtil {private static String MAIN_CLASS ;/*** 忽略的第三方引导类启动*/private static final Set<String> IGNORE_MAIN_CLASS = new HashSet<>(Arrays.asList("org.springframework.boot.loader.JarLauncher", "org.springframework.boot.loader.WarLauncher", "org.springframework.boot.loader.PropertiesLauncher"));/*** 查找启动类* @return 找到返回具体的类名称,否则返回空*/public static String getStartMainClass() {if (MAIN_CLASS != null && !MAIN_CLASS.isEmpty()) {return MAIN_CLASS;}// 系统参数String clazz = System.getProperty("sun.java.command");if (clazz.contains(".jar")) {// 通过堆栈跟踪clazz = fromStackTrace();// 通过 MANIFEST.MF 查找if(clazz == null || clazz.isEmpty()){clazz = fromJarFile();}}MAIN_CLASS = clazz;log.info("start class :{}",MAIN_CLASS);return clazz;}/*** 从jar文件 META-INF/MANIFEST.MF 获取* @return 启动类,未找到返回空*/private static String fromJarFile() {String javaCmd = System.getProperty("sun.java.command");String jarName = javaCmd.substring(0,javaCmd.indexOf(".jar") + 4);String userDir = System.getProperty("user.dir");// 拼接全路径if(!jarName.contains("/") && !jarName.contains("\\")){jarName = userDir + File.separator + jarName;}String clazz = null;log.info("jarName-------{}",jarName);try(JarFile jarFile = new JarFile(jarName)){Manifest manifest = jarFile.getManifest();Attributes attributes = manifest.getMainAttributes();clazz = attributes.getValue("Main-Class");if(IGNORE_MAIN_CLASS.contains(clazz)){clazz = attributes.getValue("Start-Class");}}catch (Exception e){log.error("jar包解析异常",e);}return clazz;}/*** 栈内信息查找* @return 启动类,未找到返回空*/private static String fromStackTrace(){StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();List<String> mainMethod = new ArrayList<>();String methodName, cl;for (StackTraceElement stack : stackTrace) {methodName = stack.getMethodName();cl = stack.getClassName();if ("main".equals(methodName) && !IGNORE_MAIN_CLASS.contains(cl)) {try {if (hasStandardMain(cl)) {mainMethod.add(cl);}} catch (Exception ignore) {}}}log.info("包含main方法的类:{}",mainMethod);return mainMethod.isEmpty() ? null : mainMethod.get(mainMethod.size() - 1);}/*** 判断是否有main方法* @param clazz 类名* @return true 有 false 没有*/private static boolean hasStandardMain(String clazz) throws Exception {Class<?> aClass = Class.forName(clazz);Method main = aClass.getDeclaredMethod("main", String[].class);Class<?> returnType = main.getReturnType();int modifiers = main.getModifiers();return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && (returnType.equals(void.class) || returnType.equals(Void.class)) ;}
}