谈谈如何在运行时获取「环境」信息

开发 开发工具
软件工程师,特别是开发客户端产品,App这一类,都免不了需要判断当前所处环境。比如客户端产品要判断是Windows/Linux系统,x86还是x64等等。 App则需要判断安卓、iOS的版本,当前环境是否WIFI之类的。

软件工程师,特别是开发客户端产品,App这一类,都免不了需要判断当前所处环境。比如客户端产品要判断是Windows/Linux系统,x86还是x64等等。 App则需要判断安卓、iOS的版本,当前环境是否WIFI之类的。

[[233843]]

对于 Java 应用, 无论 Web 还是 桌面应用,也会遇到需要判断当前所使用的 JDK 版本,当前应用对应的操作系统等等。

比如我们在几年前做应用服务器集群管理与监控时,需要判断应用服务器所属物理机器的CPU以及内存的使用率、对于服务器实例进行操作,对于不同的JVM 实现,采用不同的attach机制等等。当时是使用 Sigar 进行这些硬件信息的获取。

由于提供的是All in One 的版本,所以需要判断操作系统类型,来判断加载 Sigar 的Windows支持 dll 文件还是Linux 支持 so 文件。

当然获取操作系统类型、版本都较容易,直接通过System 的getProperty再加上对应的名称就能拿到,比如「os.name」,「os.arch」等。对于 JVM 的厂商,则可以通过「java.vm.vendor」来得到,完整的可以通过System.getProperties全部拿到。

如果习惯使用JMX 读取Platform 的 MBean,也可以通过 JMX来获取,便捷操作类似这样:

  1. OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean(); 
  2. System.out.println(os.getName()); 

本质上也都是一样的。

那除了这种方式,还有哪些方式可以获取到呢?一般开源软件又是怎么做的呢?

我们来看 Tomcat 内部怎么实现的。

我们发现, Tomcat 内部,对于通过 System 的 properties获取到的,基本都是用来打印 Log 和输出使用,对于应用内的控制,基本没看到使用。

那 Tomcat在判断版本时,是怎么做的呢?为什么不直接用这个呢?

首先看怎么做的。

前面的文章提到过, Tomcat 对于内存泄漏做了一些努力,比如先Hold 一块内存这种(Tomcat与内存泄露处理),也比如防止内存泄漏的PreventionListener,会先将可能共用的 class加载到Common classLoader里。

那这里加载到 Common classLoader这些常用 class时,就需要判断当前是Java 哪个版本,因为有些 class 是某些版之后才出现的。

具体实现是这个样子:

  1. // Trigger a call to sun.awt.AppContext.getAppContext(). This 
  2. // will pin the system class loader in memory but that shouldn't 
  3. // be an issue. 
  4. if (appContextProtection && !JreCompat.isJre8Available()) { 
  5.     ImageIO.getCacheDirectory(); 
  6.  
  7. // Trigger the creation of the AWT (AWT-Windows, AWT-XAWT, 
  8. // etc.) thread 
  9. if (awtThreadProtection && !JreCompat.isJre9Available()) { 
  10.     java.awt.Toolkit.getDefaultToolkit(); 

什么时候 Available了?

  1. static { 
  2.     // This is Tomcat 8 with a minimum Java version of Java 7. The latest 
  3.     // Java version the optional features require is Java 9. 
  4.     // Look for the highest supported JVM first 
  5.     if (Jre9Compat.isSupported()) { 
  6.         instance = new Jre9Compat(); 
  7.         jre9Available = true
  8.         jre8Available = true
  9.     } 
  10.     else if (Jre8Compat.isSupported()) { 
  11.         instance = new Jre8Compat(); 
  12.         jre9Available = false
  13.         jre8Available = true
  14.     } else { 
  15.         instance = new JreCompat(); 
  16.         jre9Available = false
  17.         jre8Available = false
  18.     } 

具体是不是支持是直接通过加载特定版JDK 对应的 class 来判断

  1. static { 
  2.     Method m1 = null
  3.     try { 
  4.         // The class is Java6+... 
  5.         Class<?> c1 = Class.forName("javax.net.ssl.SSLParameters"); 
  6.         // ...but this method is Java8+ 
  7.         m1 = c1.getMethod("setUseCipherSuitesOrder", boolean.class); 
  8.     } catch (SecurityException e) { 
  9.         // Should never happen 
  10.     } catch (NoSuchMethodException e) { 
  11.         // Expected on Java < 8 
  12.     } catch (ClassNotFoundException e) { 
  13.         // Should never happen 
  14.     } 
  15.     setUseCipherSuitesOrderMethod = m1

通过这种加载 class 的方式,和 我们前面通过 System.property获取,有啥区别呢?

要知道, System 的 Property 是个系统属性,是可配置的,也就是说,谁都可以进行setProperty的操作。如果不巧被别人改了,那你的程序可能就会出现不符合你预期的行为。

而加载类的形式,并不受其他人的影响。是最真实的一线声音。

这样的判断不方式,不仅 Tomcat 自己,其他框架也有在用。我们来看 Spring Boot。

Boot 执行的时候,有时候会判断当前应用是否需要支持 Web, 类似于是否加载了 Spring MVC 这种。

  1. private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", 
  2.     "org.springframework.web.context.ConfigurableWebApplicationContext" }; 
  3.      
  4.         private boolean deduceWebEnvironment() { 
  5.             for (String className : WEB_ENVIRONMENT_CLASSES) { 
  6.                 if (!ClassUtils.isPresent(className, null)) { 
  7.                     return false; 
  8.                 } 
  9.             } 
  10.             return true; 
  11.         } 

然后在启动时候,会调用deduceWebEnvironment 方法,从而决定一些执行的逻辑。

还有其他方式么? 如何不想自己手工处理,可以使用一些工具类,比如 Apache 的 commons-lang, 提供了一个 SystemUtils 可以直接。例如判断操作系统和 Java 版本

  1. if (SystemUtils.IS_JAVA_1_7) { 
  2.     System.out.println("Hello 1.7"); 

工具类是怎么做的呢?

  1. public static final String JAVA_VERSION = getSystemProperty("java.version"); 
  2. private static String getJavaVersionTrimmed() { 
  3.     if(JAVA_VERSION != null) { 
  4.         for(int i = 0; i < JAVA_VERSION.length(); ++i) { 
  5.             char ch = JAVA_VERSION.charAt(i); 
  6.             if(ch >= 48 && ch <= 57) { 
  7.                 return JAVA_VERSION.substring(i); 
  8.             } 
  9.         } 
  10.     } 
  11.  
  12.     return null; 

说到底,也还是System.getProperty.

所以,在检测环境的时候,我们可以通过System.getProperty,也可以通过 Tomcat 和 Spring 这种加载 class 的方式。或者干脆直接用工具类。 你还用过啥办法呢?

【本文为51CTO专栏作者“侯树成”的原创稿件,转载请通过作者微信公众号『Tomcat那些事儿』获取授权】

戳这里,看该作者更多好文

责任编辑:赵宁宁 来源: 51CTO专栏
相关推荐

2015-09-09 10:10:35

运行时改变图标

2020-07-15 11:17:04

云计算云安全云原生

2015-07-20 15:44:46

Swift框架MJExtension反射

2021-08-18 08:32:09

代码运行时间示波器

2023-04-03 13:01:14

UbuntuCRI-O

2024-03-21 09:15:58

JS运行的JavaScrip

2020-04-06 11:47:44

Linux命令脚本

2018-04-08 14:27:45

Linuxuptime系统运行时间

2022-10-08 00:00:00

V8channel对象

2009-06-17 15:46:36

Java运行时本机内存

2019-07-12 09:30:12

DashboardDockerDNS

2021-09-11 15:38:23

容器运行镜像开放

2022-10-08 00:06:00

JS运行V8

2023-11-21 16:31:51

C++语言

2024-01-08 08:24:02

PythonSys 模块工具

2014-09-02 10:39:53

Go语言C语言

2009-11-02 12:25:30

2013-11-26 16:49:55

Android开发运行时KitKat

2023-07-28 10:42:43

2024-03-20 10:46:00

云原生容器
点赞
收藏

51CTO技术栈公众号