序
本文次要钻研一下arthas的spring-boot-starter
ArthasConfiguration
arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/ArthasConfiguration.java
@ConditionalOnProperty(name = "spring.arthas.enabled", matchIfMissing = true)@EnableConfigurationProperties({ ArthasProperties.class })public class ArthasConfiguration { private static final Logger logger = LoggerFactory.getLogger(ArthasConfiguration.class); @Autowired ConfigurableEnvironment environment; /** * <pre> * 1. 提取所有以 arthas.* 结尾的配置项,再对立转换为Arthas配置 * 2. 防止某些配置在新版本里反对,但在ArthasProperties里没有配置的状况。 * </pre> */ @ConfigurationProperties(prefix = "arthas") @ConditionalOnMissingBean(name="arthasConfigMap") @Bean public HashMap<String, String> arthasConfigMap() { return new HashMap<String, String>(); } @ConditionalOnMissingBean @Bean public ArthasAgent arthasAgent(@Autowired @Qualifier("arthasConfigMap") Map<String, String> arthasConfigMap, @Autowired ArthasProperties arthasProperties) throws Throwable { arthasConfigMap = StringUtils.removeDashKey(arthasConfigMap); ArthasProperties.updateArthasConfigMapDefaultValue(arthasConfigMap); /** * @see org.springframework.boot.context.ContextIdApplicationContextInitializer#getApplicationId(ConfigurableEnvironment) */ String appName = environment.getProperty("spring.application.name"); if (arthasConfigMap.get("appName") == null && appName != null) { arthasConfigMap.put("appName", appName); } // 给配置全加上前缀 Map<String, String> mapWithPrefix = new HashMap<String, String>(arthasConfigMap.size()); for (Entry<String, String> entry : arthasConfigMap.entrySet()) { mapWithPrefix.put("arthas." + entry.getKey(), entry.getValue()); } final ArthasAgent arthasAgent = new ArthasAgent(mapWithPrefix, arthasProperties.getHome(), arthasProperties.isSlientInit(), null); arthasAgent.init(); logger.info("Arthas agent start success."); return arthasAgent; }}
ArthasConfiguration注册了arthasConfigMap及arthasAgent两个bean
ArthasAgent
arthas-agent-attach/src/main/java/com/taobao/arthas/agent/attach/ArthasAgent.java
public class ArthasAgent { private static final int TEMP_DIR_ATTEMPTS = 10000; private static final String ARTHAS_CORE_JAR = "arthas-core.jar"; private static final String ARTHAS_BOOTSTRAP = "com.taobao.arthas.core.server.ArthasBootstrap"; private static final String GET_INSTANCE = "getInstance"; private static final String IS_BIND = "isBind"; private String errorMessage; private Map<String, String> configMap = new HashMap<String, String>(); private String arthasHome; private boolean slientInit; private Instrumentation instrumentation; public ArthasAgent() { this(null, null, false, null); } //...... public void init() throws IllegalStateException { // 尝试判断arthas是否已在运行,如果是的话,间接就退出 try { Class.forName("java.arthas.SpyAPI"); // 加载不到会抛异样 if (SpyAPI.isInited()) { return; } } catch (Throwable e) { // ignore } try { if (instrumentation == null) { instrumentation = ByteBuddyAgent.install(); } // 查看 arthasHome if (arthasHome == null || arthasHome.trim().isEmpty()) { // 解压出 arthasHome URL coreJarUrl = this.getClass().getClassLoader().getResource("arthas-bin.zip"); if (coreJarUrl != null) { File tempArthasDir = createTempDir(); ZipUtil.unpack(coreJarUrl.openStream(), tempArthasDir); arthasHome = tempArthasDir.getAbsolutePath(); } else { throw new IllegalArgumentException("can not getResources arthas-bin.zip from classloader: " + this.getClass().getClassLoader()); } } // find arthas-core.jar File arthasCoreJarFile = new File(arthasHome, ARTHAS_CORE_JAR); if (!arthasCoreJarFile.exists()) { throw new IllegalStateException("can not find arthas-core.jar under arthasHome: " + arthasHome); } AttachArthasClassloader arthasClassLoader = new AttachArthasClassloader( new URL[] { arthasCoreJarFile.toURI().toURL() }); /** * <pre> * ArthasBootstrap bootstrap = ArthasBootstrap.getInstance(inst); * </pre> */ Class<?> bootstrapClass = arthasClassLoader.loadClass(ARTHAS_BOOTSTRAP); Object bootstrap = bootstrapClass.getMethod(GET_INSTANCE, Instrumentation.class, Map.class).invoke(null, instrumentation, configMap); boolean isBind = (Boolean) bootstrapClass.getMethod(IS_BIND).invoke(bootstrap); if (!isBind) { String errorMsg = "Arthas server port binding failed! Please check $HOME/logs/arthas/arthas.log for more details."; throw new RuntimeException(errorMsg); } } catch (Throwable e) { errorMessage = e.getMessage(); if (!slientInit) { throw new IllegalStateException(e); } } }}
ArthasAgent的init办法先尝试加载java.arthas.SpyAPI,若SpyAPI.isInited()为true则间接返回;之后执行ByteBuddyAgent.install();对于arthasHome为null则尝试读取arthas-bin.zip文件,接着创立AttachArthasClassloader,加载com.taobao.arthas.core.server.ArthasBootstrap,执行其getInstance办法,再对实例执行isBind
ArthasEndPointAutoConfiguration
arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/endpoints/ArthasEndPointAutoConfiguration.java
@ConditionalOnProperty(name = "spring.arthas.enabled", matchIfMissing = true)public class ArthasEndPointAutoConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnAvailableEndpoint public ArthasEndPoint arthasEndPoint() { return new ArthasEndPoint(); }}
ArthasEndPointAutoConfiguration则创立ArthasEndPoint
ArthasEndPoint
arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/endpoints/ArthasEndPoint.java
@Endpoint(id = "arthas")public class ArthasEndPoint { @Autowired(required = false) private ArthasAgent arthasAgent; @Autowired(required = false) private HashMap<String, String> arthasConfigMap; @ReadOperation public Map<String, Object> invoke() { Map<String, Object> result = new HashMap<String, Object>(); if (arthasConfigMap != null) { result.put("arthasConfigMap", arthasConfigMap); } String errorMessage = arthasAgent.getErrorMessage(); if (errorMessage != null) { result.put("errorMessage", errorMessage); } return result; }}
ArthasEndPoint提供了一个读办法返回arthasConfigMap
小结
arthas的spring-boot-starter有两个主动配置,别离是ArthasConfiguration及ArthasEndPointAutoConfiguration,其中ArthasConfiguration注册了arthasConfigMap及arthasAgent两个bean,而ArthasEndPointAutoConfiguration则创立ArthasEndPoint。