{"id":21240581,"url":"https://github.com/a252937166/spring-boot-hystrix-demo","last_synced_at":"2026-05-20T06:09:09.872Z","repository":{"id":56042902,"uuid":"316919783","full_name":"a252937166/spring-boot-hystrix-demo","owner":"a252937166","description":"spring-boot整合hystrix示例","archived":false,"fork":false,"pushed_at":"2020-12-02T15:08:43.000Z","size":82,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-21T19:36:29.377Z","etag":null,"topics":["hystrix","spring-boot"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/a252937166.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-11-29T09:49:20.000Z","updated_at":"2020-12-02T15:08:46.000Z","dependencies_parsed_at":"2022-08-15T12:01:02.415Z","dependency_job_id":null,"html_url":"https://github.com/a252937166/spring-boot-hystrix-demo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a252937166%2Fspring-boot-hystrix-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a252937166%2Fspring-boot-hystrix-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a252937166%2Fspring-boot-hystrix-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a252937166%2Fspring-boot-hystrix-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/a252937166","download_url":"https://codeload.github.com/a252937166/spring-boot-hystrix-demo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243681003,"owners_count":20330155,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hystrix","spring-boot"],"created_at":"2024-11-21T00:52:01.134Z","updated_at":"2026-05-20T06:09:09.796Z","avatar_url":"https://github.com/a252937166.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/a252937166/spring-boot-hystrix-demo.svg?branch=main)](https://travis-ci.org/a252937166/spring-boot-hystrix-demo)\n[![codecov](https://codecov.io/gh/a252937166/spring-boot-hystrix-demo/branch/main/graph/badge.svg?token=USFMCL7WYR)](https://codecov.io/gh/a252937166/spring-boot-hystrix-demo)\n[![GitHub license](https://img.shields.io/github/license/a252937166/spring-boot-hystrix-demo)](https://github.com/a252937166/spring-boot-hystrix-demo/blob/main/LICENSE)\n\n# spring-boot-hystrix-demo\n\n参考了大多数文章，大多使用的是spring-cloud的整合方式，如果只是单独使用spring-boot的话，这种方式引用了太多无用的依赖，而且没有明明没有使用spring-cloud，pom中有个spring-cloud开头的依赖，有强迫症的我实在接受不了，所以花了些时间自己研究了一下如何快速简洁地单独整合hystrix。\n\n# maven\n\n```xml\n        \u003cdependency\u003e\n            \u003cgroupId\u003ecom.netflix.hystrix\u003c/groupId\u003e\n            \u003cartifactId\u003ehystrix-javanica\u003c/artifactId\u003e\n            \u003cversion\u003e1.5.2\u003c/version\u003e\n        \u003c/dependency\u003e\n```\nhystrix-javanica中包含了hystrix-core，并且提供了`@HystrixCommand`等注解，如果只hystrix-core是没法使用`@HystrixCommand`等注解的。\n\n# config\n\n```java\nimport com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class HystrixConfig {\n\n    @Bean\n    public HystrixCommandAspect hystrixCommandAspect() {\n        return new HystrixCommandAspect();\n    }\n}\n```\n我们看其源码：\n\n```java\n\n@Aspect\npublic class HystrixCommandAspect {\n    private static final Map\u003cHystrixCommandAspect.HystrixPointcutType, HystrixCommandAspect.MetaHolderFactory\u003e META_HOLDER_FACTORY_MAP;\n\n    public HystrixCommandAspect() {\n    }\n\n    @Pointcut(\"@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)\")\n    public void hystrixCommandAnnotationPointcut() {\n    }\n\n    @Pointcut(\"@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)\")\n    public void hystrixCollapserAnnotationPointcut() {\n    }\n\n    @Around(\"hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()\")\n    public Object methodsAnnotatedWithHystrixCommand(ProceedingJoinPoint joinPoint) throws Throwable {\n...\n\t}\n}\n\n```\n\n其中定义了`HystrixCommand`这个切入点，这就是`hystrix`最简单的原理，在有`@HystrixCommand`注解的方法前后进行增强处理。\n\n至此，第一步整合已经成功了，随便写一个方法测验，返回`fail`。\n```java\n\n@HystrixCommand(commandProperties = {\n//    @HystrixProperty(name = \"execution.isolation.thread.timeoutInMilliseconds\",value = \"5000\")\n    },fallbackMethod = \"fail1\"\n    )\n    public String t() throws InterruptedException {\n\n        Thread.sleep(5000);\n        return tService.t();\n    }\n    private String fail1() {\n        System.out.println(\"fail1\");\n        return \"fail1\";\n    }\n    \n```\n\n# 实现可读取配置文件\n\n此时，我们在配置文件中进行设置：\n\n```profile\nhystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000\n```\n\n在默认时间`1000ms`之后，还是返回`fail`，说明配置没有生效，但是如果我们直接引用`spring-cloud-starter-netflix-hystrix`依赖的话，配置是生效的，那么`spring-cloud-starter-netflix-hystrix`是如何读取的配置文件，并且修改的默认参数，这个就需要我们阅读源码，并且手动`debug`慢慢研究了。\n我大概花了3-4小时的时间，对两种jar包的引用方式作对比。这简单说明一下。\n我们的切入口是`HystrixPropertiesCommandDefault`这个类，每个有`HystrixCommand`注解的方法，第一次执行时，会通过这个类初始化配置参数。\n设置参数的入口是`HystrixCommandProperties`的`getProperty`方法：\n\n```java\n    private static HystrixProperty\u003cInteger\u003e getProperty(String propertyPrefix, HystrixCommandKey key, String instanceProperty, Integer builderOverrideValue, Integer defaultValue) {\n        return forInteger()\n                .add(propertyPrefix + \".command.\" + key.name() + \".\" + instanceProperty, builderOverrideValue)\n                .add(propertyPrefix + \".command.default.\" + instanceProperty, defaultValue)\n                .build();\n    }\n\n```\n\n然后一步一步debug，最终到`ConcurrentCompositeConfiguration`的`getList()`方法时发现：`spring-cloud-starter-netflix-hystrix`的`configList`比`hystrix-javanica`的`configList`多一个`ConfigurableEnvironmentConfiguration`。\n\n```java\n    @Override\n    public List getList(String key, List defaultValue)\n    {\n        List\u003cObject\u003e list = new ArrayList\u003cObject\u003e();\n\n        // add all elements from the first configuration containing the requested key\n        Iterator\u003cAbstractConfiguration\u003e it = configList.iterator();\n        if (overrideProperties.containsKey(key)) {\n            appendListProperty(list, overrideProperties, key);\n        }\n        ...\n\t}\n```\n\n![enter image description here](https://qiniu.ouyanglol.com/blog/springboot%E6%9C%80%E7%AE%80%E6%96%B9%E5%BC%8F%E6%95%B4%E5%90%88hystrix%E4%BB%A5%E5%8F%8A%E6%A0%B9%E6%8D%AE%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%AE%BE%E7%BD%AE%E9%BB%98%E8%AE%A4%E5%8F%82%E6%95%B01.png)\n\u003ccenter\u003e图(1)\u003c/center\u003e\n\n![enter image description here](https://qiniu.ouyanglol.com/blog/springboot%E6%9C%80%E7%AE%80%E6%96%B9%E5%BC%8F%E6%95%B4%E5%90%88hystrix%E4%BB%A5%E5%8F%8A%E6%A0%B9%E6%8D%AE%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%AE%BE%E7%BD%AE%E9%BB%98%E8%AE%A4%E5%8F%82%E6%95%B02.png)\n\u003ccenter\u003e图(2)\u003c/center\u003e\n\n经查询，`ConfigurableEnvironmentConfiguration`在`spring-cloud-netflix-archaius`，还是有`spring-cloud`的命名，不想直接引入，看其源码后，发现这个bean并不复杂，可以直接复制到我们的项目中，删除掉多余的代码就行了。\n\nArchaiusAutoConfiguration:\n```java\npackage com.ouyanglol.hytrixdemo.archaius;/*\n * Copyright 2013-2020 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\nimport com.netflix.config.AggregatedConfiguration;\nimport com.netflix.config.ConcurrentCompositeConfiguration;\nimport com.netflix.config.ConfigurationManager;\nimport com.netflix.config.DynamicProperty;\nimport com.netflix.config.DynamicPropertyFactory;\nimport com.netflix.config.DynamicURLConfiguration;\nimport org.apache.commons.configuration.AbstractConfiguration;\nimport org.apache.commons.configuration.ConfigurationBuilder;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.AutoConfigureOrder;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.env.ConfigurableEnvironment;\nimport org.springframework.core.env.Environment;\nimport org.springframework.util.ReflectionUtils;\n\nimport javax.annotation.PreDestroy;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n\n@Lazy(false)\n@Configuration(proxyBeanMethods = false)\n@ConditionalOnClass({ConcurrentCompositeConfiguration.class,\n        ConfigurationBuilder.class})\n@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)\npublic class ArchaiusAutoConfiguration {\n\n    private static final Log log = LogFactory.getLog(ArchaiusAutoConfiguration.class);\n\n    private static final AtomicBoolean initialized = new AtomicBoolean(false);\n\n    private static DynamicURLConfiguration defaultURLConfig;\n\n    @PreDestroy\n    public void close() {\n        if (defaultURLConfig != null) {\n            defaultURLConfig.stopLoading();\n        }\n        setStatic(ConfigurationManager.class, \"instance\", null);\n        setStatic(ConfigurationManager.class, \"customConfigurationInstalled\", false);\n        setStatic(DynamicPropertyFactory.class, \"config\", null);\n        setStatic(DynamicPropertyFactory.class, \"initializedWithDefaultConfig\", false);\n        setStatic(DynamicProperty.class, \"dynamicPropertySupportImpl\", null);\n        initialized.compareAndSet(true, false);\n    }\n\n    @Bean\n    public static ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration(ConfigurableEnvironment env, ApplicationContext context) {\n        Map\u003cString, AbstractConfiguration\u003e abstractConfigurationMap = context.getBeansOfType(AbstractConfiguration.class);\n        List\u003cAbstractConfiguration\u003e externalConfigurations = new ArrayList\u003c\u003e(abstractConfigurationMap.values());\n        ConfigurableEnvironmentConfiguration envConfig = new ConfigurableEnvironmentConfiguration(env);\n        configureArchaius(envConfig, env, externalConfigurations);\n        return envConfig;\n    }\n\n    protected static void configureArchaius(ConfigurableEnvironmentConfiguration envConfig, ConfigurableEnvironment env, List\u003cAbstractConfiguration\u003e externalConfigurations) {\n        if (initialized.compareAndSet(false, true)) {\n            ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();\n            if (externalConfigurations != null) {\n                for (AbstractConfiguration externalConfig : externalConfigurations) {\n                    config.addConfiguration(externalConfig);\n                }\n            }\n            config.addConfiguration(envConfig,\n                    ConfigurableEnvironmentConfiguration.class.getSimpleName());\n\n            defaultURLConfig = new DynamicURLConfiguration();\n\n            addArchaiusConfiguration(config);\n        } else {\n            // TODO: reinstall ConfigurationManager\n            log.warn(\n                    \"Netflix ConfigurationManager has already been installed, unable to re-install\");\n        }\n    }\n\n    private static void addArchaiusConfiguration(\n            ConcurrentCompositeConfiguration config) {\n        if (ConfigurationManager.isConfigurationInstalled()) {\n            AbstractConfiguration installedConfiguration = ConfigurationManager\n                    .getConfigInstance();\n            if (installedConfiguration instanceof ConcurrentCompositeConfiguration) {\n                ConcurrentCompositeConfiguration configInstance = (ConcurrentCompositeConfiguration) installedConfiguration;\n                configInstance.addConfiguration(config);\n            } else {\n                installedConfiguration.append(config);\n                if (!(installedConfiguration instanceof AggregatedConfiguration)) {\n                    log.warn(\n                            \"Appending a configuration to an existing non-aggregated installed configuration will have no effect\");\n                }\n            }\n        } else {\n            ConfigurationManager.install(config);\n        }\n    }\n\n    private static void setStatic(Class\u003c?\u003e type, String name, Object value) {\n        // Hack a private static field\n        Field field = ReflectionUtils.findField(type, name);\n        ReflectionUtils.makeAccessible(field);\n        ReflectionUtils.setField(field, null, value);\n    }\n\n\n    @Configuration(proxyBeanMethods = false)\n    @ConditionalOnProperty(value = \"archaius.propagate.environmentChangedEvent\",\n            matchIfMissing = true)\n    protected static class PropagateEventsConfiguration {\n\n        @Autowired\n        private Environment env;\n\n    }\n\n}\n\n```\n\nConfigurableEnvironmentConfiguration:\n```java\n/*\n * Copyright 2013-2020 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.ouyanglol.hytrixdemo.archaius;\n\nimport org.apache.commons.configuration.AbstractConfiguration;\nimport org.springframework.core.env.CompositePropertySource;\nimport org.springframework.core.env.ConfigurableEnvironment;\nimport org.springframework.core.env.EnumerablePropertySource;\nimport org.springframework.core.env.MutablePropertySources;\nimport org.springframework.core.env.PropertySource;\nimport org.springframework.core.env.StandardEnvironment;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * EnvironmentConfiguration wrapper class providing further configuration possibilities.\n *\n * @author Spencer Gibb\n */\npublic class ConfigurableEnvironmentConfiguration extends AbstractConfiguration {\n\n\tprivate final ConfigurableEnvironment environment;\n\n\tpublic ConfigurableEnvironmentConfiguration(ConfigurableEnvironment environment) {\n\t\tthis.environment = environment;\n\t}\n\n\t@Override\n\tprotected void addPropertyDirect(String key, Object value) {\n\n\t}\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\treturn !getKeys().hasNext(); // TODO: find a better way to do this\n\t}\n\n\t@Override\n\tpublic boolean containsKey(String key) {\n\t\treturn this.environment.containsProperty(key);\n\t}\n\n\t@Override\n\tpublic Object getProperty(String key) {\n\t\treturn this.environment.getProperty(key);\n\t}\n\n\t@Override\n\tpublic Iterator\u003cString\u003e getKeys() {\n\t\tList\u003cString\u003e result = new ArrayList\u003c\u003e();\n\t\tfor (Map.Entry\u003cString, PropertySource\u003c?\u003e\u003e entry : getPropertySources()\n\t\t\t\t.entrySet()) {\n\t\t\tPropertySource\u003c?\u003e source = entry.getValue();\n\t\t\tif (source instanceof EnumerablePropertySource) {\n\t\t\t\tEnumerablePropertySource\u003c?\u003e enumerable = (EnumerablePropertySource\u003c?\u003e) source;\n\t\t\t\tfor (String name : enumerable.getPropertyNames()) {\n\t\t\t\t\tresult.add(name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result.iterator();\n\t}\n\n\tprivate Map\u003cString, PropertySource\u003c?\u003e\u003e getPropertySources() {\n\t\tMap\u003cString, PropertySource\u003c?\u003e\u003e map = new LinkedHashMap\u003c\u003e();\n\t\tMutablePropertySources sources = (this.environment != null\n\t\t\t\t? this.environment.getPropertySources()\n\t\t\t\t: new StandardEnvironment().getPropertySources());\n\t\tfor (PropertySource\u003c?\u003e source : sources) {\n\t\t\textract(\"\", map, source);\n\t\t}\n\t\treturn map;\n\t}\n\n\tprivate void extract(String root, Map\u003cString, PropertySource\u003c?\u003e\u003e map,\n\t\t\tPropertySource\u003c?\u003e source) {\n\t\tif (source instanceof CompositePropertySource) {\n\t\t\tfor (PropertySource\u003c?\u003e nest : ((CompositePropertySource) source)\n\t\t\t\t\t.getPropertySources()) {\n\t\t\t\textract(source.getName() + \":\", map, nest);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tmap.put(root + source.getName(), source);\n\t\t}\n\t}\n\n}\n\n```\n\n至此，配置文件中的hystrix的相关配置就生效了。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa252937166%2Fspring-boot-hystrix-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fa252937166%2Fspring-boot-hystrix-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa252937166%2Fspring-boot-hystrix-demo/lists"}