本文次要钻研一下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。