20210701_Spring自定义RequestMapping学习笔记
1概述
通过对spring,spring-web,hystrix等相关源码的阅读,结合自己的理解,产生此篇文章。具体涉及内容如下:
-
springmvc父子容器
基于内嵌tomcat实现spring父子容器(MySpringContainer、MySpringMvcContainer)的加载
-
重写RequestMapping,自定义请求映射MyHystrixRequestMapping
-
针对自定义MyHystrixRequestMapping注解的类及方法,借助spring-aop切面,实现aop请求拦截
-
对代理的方法进行Hystrix限流、熔断、隔离、降级方法的处理
-
hystrix熔断实时监控(通过tomcat SCI机制定义hystrix servlet事件推送)
2代码实战
2.1Maven文件配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kikop</groupId>
<artifactId>myspringbootspidemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>myspringbootspidemo</name>
<description>myspringbootspidemo project(todo)</description>
<properties>
<java.version>1.8</java.version>
<fastjson.version>1.2.29</fastjson.version>
</properties>
<dependencies>
<!--1.1引入内嵌 tomcat-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.17</version>
</dependency>
<!--1.2.tomcat-embed-jasper-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.17</version>
</dependency>
<!--2.fastjson-->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--3.begin_hystrix-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.15</version>
</dependency>
<!--3.1.hystrix core-->
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.12</version>
</dependency>
<!--3.2.推送hystrix event事件-->
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
<version>1.5.12</version>
</dependency>
<!--4.spring-boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2基于内嵌tomcat实现spring父子容器的加载
2.2.1定义父容器MySpringContainer
package com.kikop.myspringmvc;
import org.springframework.context.annotation.ComponentScan;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file MySpringContainer
* @desc 父容器
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
@ComponentScan("com.kikop")
public class MySpringContainer {
}
2.2.2定义子容器MySpringMvcContainer(可忽略)
package com.kikop.myspringmvc;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file Name: MySpringContainer
* @desc MySpringMvcContainer
* 可以什么都不需要,就如本文中这样,相当于没有
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
// 传统 xml web 配置,dispatcherServlet只扫描 Controller.class
//@ComponentScan(value = "com.kikop", includeFilters = {
// @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
//}, useDefaultFilters = false)
public class MySpringMvcContainer {
}
2.2.3自定义实现接口WebApplicationInitializerBySpringSPI
注意:
该接口实现了 WebApplicationInitializer,基于SpirngSSCI机制(对比Tomcat SCI)
[图片上传失败...(image-a821ab-1649426404419)]
// 文件内容:org.springframework.web.SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
package com.kikop.myspringmvc.init;
import com.kikop.myspringmvc.MySpringContainer;
import com.kikop.myspringmvc.MySpringMvcContainer;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file MySpringWebAppInitializer
* @desc 底层实现了 WebApplicationInitializer,由 SpringMvc SPI完成加载及相关重写方法的调用
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
public class MySpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// 1.父容器(必填)
// ==> AbstractDispatcherServletInitializer::super.onStartup()
@Override
protected Class<?>[] getRootConfigClasses() {
// return new Class[0];
return new Class<?>[]{MySpringContainer.class};
}
// 2.子容器child(可选)
// 2.1.getServletConfigClasses
// ==> AbstractDispatcherServletInitializer::registerDispatcherServlet()
@Override
protected Class<?>[] getServletConfigClasses() {
// return new Class[0];
return new Class<?>[]{MySpringMvcContainer.class};
}
// 2.2.Webapp mappings==>add Servlet
@Override
protected String[] getServletMappings() {
// return new String[0];
return new String[]{"/"};
}
}
2.2.4AppConfig
package com.kikop.config;
import com.kikop.service.UserServiceImpl;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
/**
* @author kikop
* @version 1.0
* @project Name: myspringbootspidemo
* @file Name: AppConfig
* @desc AppConfig
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
@Configuration
@ComponentScan("com.kikop")
public class AppConfig {
/**
* 借助 bean工厂后置处理器的实现代码的方式读取资源配置文件
*
* @return
*/
@Bean
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
// https://blog.csdn.net/a3060858469/article/details/80791202
PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
org.springframework.core.io.Resource resource = new ClassPathResource("mycfg.properties");
propertyPlaceholderConfigurer.setLocation(resource);
return propertyPlaceholderConfigurer;
}
@Bean
public UserServiceImpl userService() {
return new UserServiceImpl();
}
}
2.2.5重写扩展方法MyWebMvcConfigurationSupport
MyWebMvcConfigurationSupport配置类,定义了许多默认@Bean,
例如:public RequestMappingHandlerMapping requestMappingHandlerMapping()
package com.kikop.myspringmvc.config;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.kikop.myspringmvc.handlermapping.MyHystrixRequestMappingHandlerMapping;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.List;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file MyWebMvcConfigurationSupport
* @desc 本质等同于 @EnableWebMvc,用于响应的方法重写和扩展
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
@Configuration
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
/**
* 基于Json的消息转换器
*
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter =
new FastJsonHttpMessageConverter();
converters.add(fastJsonHttpMessageConverter);
}
}
2.2.6业务DemoController
package com.kikop.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file Name: AppConfig
* @desc AppConfig
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
@RestController
@RequestMapping("/demo")
public class DemoController {
// 测试配置文件加载
@Value("${sysname}")
private String sysname;
/**
* @param httpservletRequest
* @param httpServletResponse
* @return
*/
@RequestMapping(value = "/index", method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView index(HttpServletRequest httpservletRequest, HttpServletResponse httpServletResponse) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("index");
return modelAndView;
}
/**
* getVersion
*
* @param httpservletRequest
* @param httpServletResponse
* @return
*/
@RequestMapping(value = "/getVersion", method = {RequestMethod.GET, RequestMethod.POST})
public String getVersion(HttpServletRequest httpservletRequest, HttpServletResponse httpServletResponse) {
// http://localhost:9090/myspringbootspidemo/demo/getVersion
return "sysName:" + sysname + ",getVersion" + ":" + "v1.0";
}
/**
* getJsonVersion
*
* @param httpservletRequest
* @param httpServletResponse
* @return
*/
@ResponseBody
@RequestMapping(value = "/getJsonVersion", method = {RequestMethod.GET, RequestMethod.POST})
public Map<String, String> getJsonVersion(HttpServletRequest httpservletRequest, HttpServletResponse httpServletResponse) {
// http://localhost:9090/myspringbootspidemo/demo/getJsonVersion
// 需配置: configureMessageConverters,否则
// No converter found for return value of type: class java.util.HashMap
Map hashMap = new HashMap();
hashMap.put("version", "v1.0");
return hashMap;
}
}
2.2.7定义NestTomcatManager
package com.kikop.tomcat;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file Name: NestTomcatManagerEntry
* @desc 内嵌 tomcat封装类
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
public class NestTomcatManagerEntry {
public static void run() throws LifecycleException {
int conectorPort = 9090;
String contextPath = "/myspringbootspidemo";
String docBase = "D:\\workdirectory\\mapexperiment\\myspringbootspidemo\\";
// tomcat-embed-core-9.0.17-sources.jar!\org\apache\catalina\startup\Tomcat.java
Tomcat tomcat = new Tomcat();
// 1.配置connector
Connector connector = new Connector();
connector.setPort(conectorPort);
connector.setURIEncoding("UTF-8");
tomcat.getService().addConnector(connector);
// 2.注意点:
// 必须申明项目为 webapp项目,
// 等同于配置了web.xml
// 自动执行SPI查找机制
// myspringbootspidemo\src\main\resources\META-INF\services\javax.servlet.ServletContainerInitializer
tomcat.addWebapp(contextPath, docBase);
// 3.启动(执行 SPI查找机制)
tomcat.start();
// 4.并阻塞
tomcat.getServer().await();
}
}
2.2.8测试
package com.kikop;
import com.kikop.tomcat.NestTomcatManagerEntry;
import org.apache.catalina.LifecycleException;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file MySpringbootSPIDemoApplication
* @desc http://localhost:9090/myspringbootspidemo/myhystrix/fallBack
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
// 启动springboot自动装配
@SpringBootApplication
// MapperScan 第三方 Jar包
// @MapperScan(basepackage={"com.kikop"})
public class MySpringbootSPIDemoApplication {
public static void main(String[] args) throws LifecycleException {
NestTomcatManagerEntry.run();
}
}
2.3自定义请求映射MyHystrixRequestMapping并且兼容RequestMapping
2.3.1定义注解MyHystrixRequestMapping
package com.kikop.myhystrixcomponent.myannotation;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolKey;
import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.Mapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.lang.annotation.*;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file Name: MyHystrixRequestMapping
* @desc 支持熔断的请求映射, 类似 RequestMapping
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface MyHystrixRequestMapping {
/**
* Assign a name to this mapping.
* <p><b>Supported at the type level as well as at the method level!</b>
* When used on both levels, a combined name is derived by concatenation
* with "#" as separator.
*
* @see org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder
* @see org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy
*/
String name() default "";
/**
* The primary mapping expressed by this annotation.
* <p>This is an alias for {@link #path}. For example
* {@code @RequestMapping("/foo")} is equivalent to
* {@code @RequestMapping(path="/foo")}.
* <p><b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings inherit
* this primary mapping, narrowing it for a specific handler method.
*/
@AliasFor("path")
String[] value() default {};
/**
* The path mapping URIs (e.g. "/myPath.do").
* Ant-style path patterns are also supported (e.g. "/myPath/*.do").
* At the method level, relative paths (e.g. "edit.do") are supported
* within the primary mapping expressed at the type level.
* Path mapping URIs may contain placeholders (e.g. "/${connect}").
* <p><b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings inherit
* this primary mapping, narrowing it for a specific handler method.
*
* @see org.springframework.web.bind.annotation.ValueConstants#DEFAULT_NONE
* @since 4.2
*/
@AliasFor("value")
String[] path() default {};
/**
* The HTTP request methods to map to, narrowing the primary mapping:
* GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
* <p><b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings inherit
* this HTTP method restriction (i.e. the type-level restriction
* gets checked before the handler method is even resolved).
*/
RequestMethod[] method() default {};
/**
* The parameters of the mapped request, narrowing the primary mapping.
* <p>Same format for any environment: a sequence of "myParam=myValue" style
* expressions, with a request only mapped if each such parameter is found
* to have the given value. Expressions can be negated by using the "!=" operator,
* as in "myParam!=myValue". "myParam" style expressions are also supported,
* with such parameters having to be present in the request (allowed to have
* any value). Finally, "!myParam" style expressions indicate that the
* specified parameter is <i>not</i> supposed to be present in the request.
* <p><b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings inherit
* this parameter restriction (i.e. the type-level restriction
* gets checked before the handler method is even resolved).
* <p>Parameter mappings are considered as restrictions that are enforced at
* the type level. The primary path mapping (i.e. the specified URI value)
* still has to uniquely identify the target handler, with parameter mappings
* simply expressing preconditions for invoking the handler.
*/
String[] params() default {};
/**
* The headers of the mapped request, narrowing the primary mapping.
* <p>Same format for any environment: a sequence of "My-Header=myValue" style
* expressions, with a request only mapped if each such header is found
* to have the given value. Expressions can be negated by using the "!=" operator,
* as in "My-Header!=myValue". "My-Header" style expressions are also supported,
* with such headers having to be present in the request (allowed to have
* any value). Finally, "!My-Header" style expressions indicate that the
* specified header is <i>not</i> supposed to be present in the request.
* <p>Also supports media type wildcards (*), for headers such as Accept
* and Content-Type. For instance,
* <pre class="code">
* @RequestMapping(value = "/something", headers = "content-type=text/*")
* </pre>
* will match requests with a Content-Type of "text/html", "text/plain", etc.
* <p><b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings inherit
* this header restriction (i.e. the type-level restriction
* gets checked before the handler method is even resolved).
*
* @see org.springframework.http.MediaType
*/
String[] headers() default {};
/**
* The consumable media types of the mapped request, narrowing the primary mapping.
* <p>The format is a single media type or a sequence of media types,
* with a request only mapped if the {@code Content-Type} matches one of these media types.
* Examples:
* <pre class="code">
* consumes = "text/plain"
* consumes = {"text/plain", "application/*"}
* </pre>
* Expressions can be negated by using the "!" operator, as in "!text/plain", which matches
* all requests with a {@code Content-Type} other than "text/plain".
* <p><b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings override
* this consumes restriction.
*
* @see org.springframework.http.MediaType
* @see javax.servlet.http.HttpServletRequest#getContentType()
*/
String[] consumes() default {};
/**
* The producible media types of the mapped request, narrowing the primary mapping.
* <p>The format is a single media type or a sequence of media types,
* with a request only mapped if the {@code Accept} matches one of these media types.
* Examples:
* <pre class="code">
* produces = "text/plain"
* produces = {"text/plain", "application/*"}
* produces = MediaType.APPLICATION_JSON_UTF8_VALUE
* </pre>
* <p>It affects the actual content type written, for example to produce a JSON response
* with UTF-8 encoding, {@link org.springframework.http.MediaType#APPLICATION_JSON_UTF8_VALUE} should be used.
* <p>Expressions can be negated by using the "!" operator, as in "!text/plain", which matches
* all requests with a {@code Accept} other than "text/plain".
* <p><b>Supported at the type level as well as at the method level!</b>
* When used at the type level, all method-level mappings override
* this produces restriction.
*
* @see org.springframework.http.MediaType
*/
String[] produces() default {};
// 降级方法,在AOP中获取并解析处理
String fallbackMethod() default "";
String groupKey() default "";
String commandKey() default "";
HystrixCommandProperties.ExecutionIsolationStrategy strategety()
default HystrixCommandProperties.ExecutionIsolationStrategy.THREAD;
String threadPoolKey() default "";
}
2.3.2MyHystrixCommand
package com.kikop.myhystrixcomponent.hystrix;
import com.kikop.controller.MyHystrixController;
import com.kikop.util.SpringUtils;
import com.netflix.hystrix.HystrixCommand;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file MyHystrixCommand
* @desc 放行策略, 在 Aop:MyHystrixAdvice 中 创建
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
public class MyHystrixCommand extends HystrixCommand<Object> {
// 实现方式1:注入业务Service 暂未实现
// @Autowired
// private UserServiceImpl userServiceImpl;
// 实现方式2:MethodInvocation
private MethodInvocation methodInvocation;
// 降级的类
private String fallbackMethodName;
/**
* @param setter hystrix参数
* @param methodInvocation 代理的方法体,供 run调用
*/
public MyHystrixCommand(HystrixCommand.Setter setter, MethodInvocation methodInvocation, String fallbackMethod) {
super(setter);
this.methodInvocation = methodInvocation;
// this.targetClass = targetClass;
this.fallbackMethodName = fallbackMethod;
}
/**
* 处理 COMMAND_EXCEPTION的熔断
* return Observable.just(HystrixCommand.this.getFallback());
*
* @return
*/
@Override
protected String getFallback() {
Object fallbackResult = null;
Class<?> targetClass = methodInvocation.getThis().getClass();
if (null != targetClass) {
if (targetClass.getSimpleName().equalsIgnoreCase("MyHystrixController")) {
MyHystrixController targetBean = (MyHystrixController) SpringUtils.getBean(targetClass);
fallbackResult = targetBean.myFallbackMethod(methodInvocation.getArguments());
}
return (String) fallbackResult;
}
return (String) fallbackResult;
}
/**
* 处理 COMMAND_EXCEPTION的熔断
* return Observable.just(HystrixCommand.this.getFallback());
*
* @return
*/
protected String getFallbackByReflect() {
Object fallbackResult = null;
Class<?> targetClass = methodInvocation.getThis().getClass();
if (null != targetClass) {
Method fallbackMethod = null;
try {
fallbackMethod = targetClass.getMethod(this.fallbackMethodName,
Object[].class);
// Method fallbackMethod2 = ReflectionUtils.findMethod(targetClass, this.fallbackMethodName,
// Object[].class);
try {
fallbackResult = fallbackMethod.invoke(methodInvocation.getThis(),
new Object[]{methodInvocation.getArguments()});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return (String) fallbackResult;
}
return (String) fallbackResult;
}
/**
* 核心业务逻辑
*
* @return
*/
@Override
protected Object run() {
Object result = null;
try {
result = this.methodInvocation.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
// 正常返回值
return result;
}
}
2.3.3MyHystrixRequestMappingHandlerMapping
package com.kikop.myspringmvc.handlermapping;
import com.kikop.myhystrixcomponent.myannotation.MyHystrixRequestMapping;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringValueResolver;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.function.Predicate;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file HystrixRequestMappingHandlerMapping
* @desc 实现类似于 RequestMappingHandlerMapping 的功能
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
public class MyHystrixRequestMappingHandlerMapping extends RequestMappingHandlerMapping
implements EmbeddedValueResolverAware {
// embeddedValueResolver
// getPathPrefix 方法使用,必须再次定义该变量
@Nullable
private StringValueResolver embeddedValueResolver;
/**
* setEmbeddedValueResolver
*
* @param resolver
*/
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
// 我就是天才
super.setEmbeddedValueResolver(resolver);
}
/**
* 判断类型
* {@inheritDoc}
* <p>Expects a handler to have either a type-level @{@link Controller}
* annotation or a type-level @{@link RequestMapping} annotation.
*/
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class) || // 支持 Spring内置的 RequestMapping,判断逻辑是否要复用基类
AnnotatedElementUtils.hasAnnotation(beanType, MyHystrixRequestMapping.class)); // 自定义的 RequestMapping
}
@Nullable
String getPathPrefix(Class<?> handlerType) {
for (Map.Entry<String, Predicate<Class<?>>> entry : this.getPathPrefixes().entrySet()) {
if (entry.getValue().test(handlerType)) {
String prefix = entry.getKey();
if (this.embeddedValueResolver != null) {
prefix = this.embeddedValueResolver.resolveStringValue(prefix);
}
return prefix;
}
}
return null;
}
/**
* 根据方法和类
* 重写核心方法:getMappingForMethod
* Uses method and type-level @{@link RequestMapping} annotations to create
* the RequestMappingInfo.
*
* @return the created RequestMappingInfo, or {@code null} if the method
* does not have a {@code @RequestMapping} annotation.
* @see #getCustomMethodCondition(Method)
* @see #getCustomTypeCondition(Class)
*/
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
RequestMappingInfo typeInfo = null;
// 1.方法注解判断
// 得到 method方法的注解作为判断依据
MyRequestMappingType myRequestMappingType = checkAnnotationTypeForMethod(method);
if (myRequestMappingType == MyRequestMappingType.OFFICIAL) {
info = createRequestMappingInfoRepeat(method);
} else if (myRequestMappingType == MyRequestMappingType.CUSTOMER) {
info = createMyHystrixRequestMappingInfo(method);
}
if (info != null) {
// 2.类注解判断
// 得到 类注解作为判断依据
myRequestMappingType = checkAnnotationTypeForClass(handlerType);
if (myRequestMappingType == MyRequestMappingType.OFFICIAL) {
typeInfo = createRequestMappingInfoRepeat(handlerType);
} else if (myRequestMappingType == MyRequestMappingType.CUSTOMER) {
typeInfo = createMyHystrixRequestMappingInfo(handlerType);
}
// 3.合并
if (typeInfo != null) {
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).build().combine(info);
}
}
return info;
}
/**
* 获取类注解类型
*
* @param handlerType
* @return
*/
private MyRequestMappingType checkAnnotationTypeForClass(Class<?> handlerType) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(handlerType, RequestMapping.class);
MyHystrixRequestMapping myHystrixRequestMapping = AnnotatedElementUtils.findMergedAnnotation(handlerType, MyHystrixRequestMapping.class);
if (myHystrixRequestMapping != null) {
return MyRequestMappingType.CUSTOMER;
} else if (requestMapping != null) {
return MyRequestMappingType.OFFICIAL;
}
return MyRequestMappingType.UNKNOWD;
}
/**
* 获取方法注解类型
*
* @param method
* @return
*/
private MyRequestMappingType checkAnnotationTypeForMethod(Method method) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
MyHystrixRequestMapping myHystrixRequestMapping = AnnotatedElementUtils.findMergedAnnotation(method, MyHystrixRequestMapping.class);
if (myHystrixRequestMapping != null) {
return MyRequestMappingType.CUSTOMER;
} else if (requestMapping != null) {
return MyRequestMappingType.OFFICIAL;
}
return MyRequestMappingType.UNKNOWD;
}
/**
* 请求注解类型
*/
private enum MyRequestMappingType {
OFFICIAL(1, "spring内置RequestMapping"),
CUSTOMER(2, "自定义RequestMapping"),
UNKNOWD(3, "未知");
private int type;
private String desc;
MyRequestMappingType(int type, String desc) {
this.type = type;
this.desc = desc;
}
}
/**
* 主要这个方法在父类中是私有的
* 没法进行重写
* Delegates to {@link #createRequestMappingInfo(RequestMapping, RequestCondition)},
* supplying the appropriate custom {@link RequestCondition} depending on whether
* the supplied {@code annotatedElement} is a class or method.
*
* @see #getCustomTypeCondition(Class)
* @see #getCustomMethodCondition(Method)
*/
@Nullable
private RequestMappingInfo createMyHystrixRequestMappingInfo(AnnotatedElement element) {
// 1.element:方法或类
MyHystrixRequestMapping myHystrixRequestMapping = AnnotatedElementUtils.findMergedAnnotation(element,
MyHystrixRequestMapping.class);
// 2.获取类或方法的参数调节
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
// 3.转换 myHystrixRequestMapping 为spring内置的 requestMapping
RequestMapping requestMapping = convertRequestMapping2RequestMapping(myHystrixRequestMapping);
// 4.复用基类的方法:createRequestMappingInfo
return (myHystrixRequestMapping != null ? super.createRequestMappingInfo(requestMapping, condition) : null);
}
/**
* 主要是基类中该方法为私有,只能拷贝一遍了 kikop
* Delegates to {@link #createRequestMappingInfo(RequestMapping, RequestCondition)},
* supplying the appropriate custom {@link RequestCondition} depending on whether
* the supplied {@code annotatedElement} is a class or method.
*
* @see #getCustomTypeCondition(Class)
* @see #getCustomMethodCondition(Method)
*/
@Nullable
private RequestMappingInfo createRequestMappingInfoRepeat(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? super.createRequestMappingInfo(requestMapping, condition) : null);
}
/**
* convertRequestMapping2RequestMapping
* 转换自定义 RequestMapping
* 降级的相关参数这里不需处理
*
* @param myHystrixRequestMapping
* @return
*/
private RequestMapping convertRequestMapping2RequestMapping(MyHystrixRequestMapping myHystrixRequestMapping) {
return new RequestMapping() {
@Override
public String name() {
return myHystrixRequestMapping.name();
}
@Override
public String[] value() {
return myHystrixRequestMapping.value();
}
@Override
public String[] path() {
return myHystrixRequestMapping.path();
}
@Override
public RequestMethod[] method() {
return myHystrixRequestMapping.method();
}
@Override
public String[] params() {
return myHystrixRequestMapping.params();
}
@Override
public String[] headers() {
return myHystrixRequestMapping.headers();
}
@Override
public String[] consumes() {
return myHystrixRequestMapping.consumes();
}
@Override
public String[] produces() {
return myHystrixRequestMapping.produces();
}
@Override
public Class<? extends Annotation> annotationType() {
return RequestMapping.class;
}
};
}
}
2.3.4替换RequestMappingHandlerMapping
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
/**
* 重写 createRequestMappingHandlerMapping,实现自定义RequestMapping的扩展
*
* @return
*/
@Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
// 1.spring 自带的 HandlerMapping
// return super.createRequestMappingHandlerMapping();
// 2.改造后的 HandlerMapping
return new MyHystrixRequestMappingHandlerMapping();
}
}
2.3.5业务MyHystrixController
package com.kikop.controller;
import com.kikop.myhystrixcomponent.myannotation.MyHystrixRequestMapping;
import com.kikop.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file Name: MyHystrixController
* @desc http://localhost:9090/myspringbootspidemo/myhystrix/fallBack
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
@RestController
@RequestMapping("/myhystrix")
public class MyHystrixController {
@Autowired
private UserServiceImpl userServiceImpl;
/**
* getVersion
*
* @param httpservletRequest
* @param httpServletResponse
* @return
*/
@RequestMapping(value = "/getVersion", method = {RequestMethod.GET, RequestMethod.POST})
public String getVersion(HttpServletRequest httpservletRequest, HttpServletResponse httpServletResponse) {
// http://localhost:9090/myspringbootspidemo/myhystrix/getVersion
return "getVersion" + ":" + "v1.0";
}
@MyHystrixRequestMapping(value = "/myHystrixByReqAnnotationTest",
fallbackMethod = "myFallbackMethod",
commandKey = "myHystrixByReqAnnotationTest",
groupKey = "myHystrixByReqAnnotationTestGroup",
threadPoolKey = "myHystrixByReqAnnotationTestThread",
method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String myHystrixByReqAnnotationTest(@RequestParam(value = "name") String name) {
// http://localhost:9090/myspringbootspidemo/myhystrix/myHystrixByReqAnnotationTest?name=kikop
return name;
}
/**
* myHystrixByPathTest
* 降级测试
*
* @param name
* @return
*/
@MyHystrixRequestMapping(value = "/myHystrixByPathTest/{name}",
fallbackMethod = "myFallbackMethod",
commandKey = "myHystrixByPathTest",
groupKey = "myHystrixByPathTestGroup",
threadPoolKey = "myHystrixByPathTestThread",
method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String myHystrixByPathTest(@PathVariable String name) {
// http://localhost:9090/myspringbootspidemo/myhystrix/myHystrixByPathTest/kikop
System.out.println("------tomcat threadInfo:" + Thread.currentThread().getId());
String result = userServiceImpl.getResult(name);
return result;
}
/**
* 降级方法
* Controller中增加未通过
* 原则上要定义统一的返回响应类型
*
* @param reqParam
* @return
*/
@ResponseBody
public String myFallbackMethod(Object[] reqParam) {
// 在Hystrix的实现中,这就出现了“熔断器(CircuitBreaker)”的概念,
// 即当前的系统是否处于需要保护的状态。
//
// 当熔断器处于开启的状态时,所有的请求都不会真正的走之前的业务逻辑,
// 而是直接返回一个约定的信息,即 fallBack。通过这种快速失败原则保护我们的系统。
//
// return String.valueOf(reqParam[0]);
return "oh,my god,fallback!";
}
}
2.4借助spring-aop,实现请求代理
2.4.1MyHystrixAdvice
package com.kikop.myhystrixcomponent.aop;
import com.kikop.myhystrixcomponent.hystrix.MyHystrixCommand;
import com.kikop.myhystrixcomponent.myannotation.MyHystrixRequestMapping;
import com.netflix.hystrix.*;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotatedElementUtils;
import java.lang.reflect.Method;
import java.util.concurrent.ExecutionException;
/**
* @author kikop
* @version 1.0
* @project Name: myspringbootspidemo
* @file Name: MyHystrixAdvice
* @desc 业务增强器
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
public class MyHystrixAdvice implements MethodInterceptor {
// 核心AOP类: MethodInterceptor
/**
* invoke
*
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return doHystrixInvoke(invocation);
}
/**
* 具体的业务代理方法
* 判断Bean是否需要增加代理:
* 在 AbstractAutowireCapableBeanFactory.initializeBean::applyBeanPostProcessorsAfterInitialization
*
* @param methodInvocation
* @return
*/
private Object doHystrixInvoke(MethodInvocation methodInvocation) throws ExecutionException, InterruptedException {
// 1.getTargetClass
// Class<?> targetClass = findTargetClass(invocation.getThis());
// invocation.getThis(): com.kikop.controller.MyHystrixController@1bb460bc
Class<?> targetClass =
methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
// 2.找到原生的method
Method mostSpecificMethod = AopUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
// 3.获取方法上的注解实体:MyHystrixRequestMapping
MyHystrixRequestMapping myHystrixRequestMapping = AnnotatedElementUtils.findMergedAnnotation(
mostSpecificMethod, MyHystrixRequestMapping.class);
// 4.HystrixCommandGroupKey
HystrixCommandGroupKey hystrixCommandGroupKey = HystrixCommandGroupKey.Factory
.asKey(myHystrixRequestMapping.groupKey());
// 5.setter隔离策略
HystrixCommand.Setter setter = HystrixCommand.Setter
.withGroupKey(hystrixCommandGroupKey)
// 默认超时时间
.andCommandKey(HystrixCommandKey.Factory.asKey(myHystrixRequestMapping.commandKey()))
// 线程池隔离策略
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionIsolationStrategy(
myHystrixRequestMapping.strategety()))
// HystrixThreadPoolKey
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(myHystrixRequestMapping.threadPoolKey()));
// 6.构建 MyHystrixCommand
MyHystrixCommand controllerhystrixCommand = new MyHystrixCommand(setter, methodInvocation);
// 7.执行核心业务逻辑
// 7.1异步调用
// Future<Object> queue = controllerhystrixCommand.queue();
// Object book = queue.get();
// 7.2.同步调用
// 在执行execute的过程中,最终就会把这个command,
// 丢到线程池中,然后,command中的业务逻辑,就在线程池的线程中执行了
return controllerhystrixCommand.execute();
}
/**
* findTargetClass
*
* @param proxy
* @return
*/
public static Class<?> findTargetClass(Object proxy) {
if (AopUtils.isAopProxy(proxy)) {
AdvisedSupport advisedSupport = getAdvisedSupport(proxy);
}
return null;
}
/**
* getAdvisedSupport
*
* @param proxy
* @return
*/
private static AdvisedSupport getAdvisedSupport(Object proxy) {
return null;
}
}
2.4.2MyHystrixPointcut
package com.kikop.myhystrixcomponent.aop;
import com.kikop.myhystrixcomponent.myannotation.MyHystrixRequestMapping;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.core.annotation.AnnotatedElementUtils;
import java.lang.reflect.Method;
/**
* @author kikop
* @version 1.0
* @project Name: myspringbootspidemo
* @file Name: MyHystrixPointcut
* @desc 定义切点
* 判断类或方法上是否有指定的注解:MyHystrixRequestMapping
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
public class MyHystrixPointcut extends StaticMethodMatcherPointcut {
/**
* 判断类或方法上是否有指定的注解
* @param method
* @param targetClass
* @return
*/
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 找到原始类、方法对象
Method mostSpecificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// 判断类、方法
if(AnnotatedElementUtils.hasAnnotation(targetClass,MyHystrixRequestMapping.class) ||
AnnotatedElementUtils.hasAnnotation(mostSpecificMethod,MyHystrixRequestMapping.class)){
return true;
}
return false;
}
}
2.4.3MyHystrixPointcutAdvisor
package com.kikop.myhystrixcomponent.aop;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.stereotype.Component;
/**
* @author kikop
* @version 1.0
* @project Name: myspringbootspidemo
* @file Name: MyHystrixPointcutAdvisor
* @desc 定义切面
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
// 必须的注册到 Spring IOC
@Component
public class MyHystrixPointcutAdvisor extends AbstractPointcutAdvisor {
// 业务增强
private Advice advice;
// 业务拦截点
private Pointcut pointcut;
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this.advice;
}
public MyHystrixPointcutAdvisor() {
this.advice = new MyHystrixAdvice();
this.pointcut = new MyHystrixPointcut();
}
}
2.4.4生成切面代理类HystrixAutoConfig
package com.kikop.myhystrixcomponent.config;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file HystrixAutoConfig
* @desc 构建切面代理类
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
@Configuration
public class MyHystrixAopAutoConfig {
/**
* 为指定的注解类生成动态代理
* 构建切面Advisor代理类
* 构造 BeanPostProcessor,基于默认的 AbstractAdvisorAutoProxyCreator
*
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
// @Bean
// public InfrastructureAdvisorAutoProxyCreator infrastructureAdvisorAutoProxyCreator() {
// return new InfrastructureAdvisorAutoProxyCreator();
// }
}
// InfrastructureAdvisorAutoProxyCreator 需要加指定的注解条件
// * isEligibleAdvisorBean
// * BeanPostProcessor
2.5熔断实时监控(通过SCI机制定义hystrix事件推送)
2.5.1定义接口MyInsterestedLoadServlet
package com.kikop.myservlet;
import javax.servlet.ServletContext;
/**
* @author kikop
* @version 1.0
* @project Name: myspringbootspidemo
* @file Name: AppConfig
* @desc 定义感兴趣的接口
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
public interface MyInsterestedLoadServlet {
void loadOnStrap(ServletContext servletContext);
}
2.5.2接口实现MyHystrixMetricStreamServlet
定义hystrix.stream端点
package com.kikop.tomcat.myservlet.impl;
import com.kikop.tomcat.myservlet.IMyInsterestedLoadServlet;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file Name: MyHystrixMetricStreamServletImpl
* @desc 感兴趣的接口实现类:熔断降级页面监控
* @date 2021/5/24
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
public class MyHystrixMetricStreamServletImpl implements IMyInsterestedLoadServlet {
/**
* 创建一个Servlet并进行映射配置
* 自定义 hystrix 启动逻辑
* http://localhost:9090/myspringbootspidemo/hystrix.stream
* @param servletContext
*/
@Override
public void loadOnStrap(ServletContext servletContext) {
// 解决404问题:创建一个配置类,并注册一个Servlet
// http://localhost:9090/myspringbootspidemo/hystrix.stream
// 1.运行静态资源
// ServletRegistration.Dynamic defaultServlet=servletContext.addServlet(
// "defaultServlet",DefaultServlet.class);
// defaultServlet.setLoadOnStartup(1);
// defaultServlet.addMapping("*.css","*.gif","*.jpg","*.js");
// 2.改造原有 HystrixMetricsStreamServlet
// 需引入 hystrix-metrics-event-stream-1.5.12.jar
String urlPatterns = "/hystrix.stream";
ServletRegistration.Dynamic hystrixMetricsStreamServlet =
servletContext.addServlet("hystrixMetricsStreamServlet", HystrixMetricsStreamServlet.class);
hystrixMetricsStreamServlet.setLoadOnStartup(1);
hystrixMetricsStreamServlet.addMapping(urlPatterns);
}
}
2.5.3MyWebCollectorServletContainerInitializerByTomcatSpi
在当前Jar中配置MyWebCollector
package com.kikop.tomcat.collector;
import com.kikop.tomcat.myservlet.IMyInsterestedLoadServlet;
import org.springframework.lang.Nullable;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.annotation.HandlesTypes;
import java.lang.reflect.Modifier;
import java.util.Set;
/**
* @author kikop
* @version 1.0
* @project myspringbootspidemo
* @file Name: MyWebCollectorServletContainerInitializer
* @desc tomcat SCI接口收集器, 各个Jar包管理自己
* 定义 ServletContainerInitializer的实现类 MyWebCollectorServletContainerInitializer
* @date 2021/5/19
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
// 搜集项目中,SPI中我们感兴趣的接口:IMyInsterestedLoadServlet
@HandlesTypes(IMyInsterestedLoadServlet.class)
public class MyWebCollectorServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) {
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
// 得到:src\main\java\com\kikop\myservlet\impl\MyHystrixMetricStreamServletImpl.java
// MyHystrixMetricStreamServletImpl
// a.class.isAssignableFrom(b)
// 这个方法用于判断参数类(对应b)表示的类型能否转换为当前类对象(对应a)表示的类型。
// waitClass是否能够转换为 IMyInsterestedLoadServlet
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
IMyInsterestedLoadServlet.class.isAssignableFrom(waiClass)) {
try {
// 调用实现类中的方法:loadOnStrap
((IMyInsterestedLoadServlet) waiClass.newInstance()).loadOnStrap(servletContext);
} catch (Throwable ex) {
ex.printStackTrace();
//throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
}
}
2.5.4classpath配置
[图片上传失败...(image-c54bd1-1649426404420)]
// 文件内容
com.kikop.collector.MyWebCollector
3Hystrix源码剖析
3.1关于HystrixCommand
从下面的代码中我们可以看出,当出现getFailureType为:COMMAND_EXCEPTION,TIMEOUT时,才会走熔断降级fallBack方法。
其他的HystrixBadRequestException则不会。
所有首要我们想到要构造HystrixRuntimeException这个异常对象。
public Future<R> queue() {
if (f.isDone()) {
try {
f.get();
return f;
} catch (Exception var6) {
Throwable t = this.decomposeException(var6);
if (t instanceof HystrixBadRequestException) {
return f;
} else if (t instanceof HystrixRuntimeException) {
HystrixRuntimeException hre = (HystrixRuntimeException)t;
switch(hre.getFailureType()) {
case COMMAND_EXCEPTION:
case TIMEOUT:
return f;
default:
throw hre;
}
} else {
throw Exceptions.sneakyThrow(t);
}
}
} else {
return f;
}
3.2FailureType
public static enum FailureType {
// 这些异常或其子类会直接抛出(ExceptionNotWrappedByHystrix)
BAD_REQUEST_EXCEPTION,
// 触发fallbackMethod
COMMAND_EXCEPTION,
// 触发fallbackMethod
TIMEOUT,
// 触发fallbackMethod
SHORTCIRCUIT,
// 触发fallbackMethod
REJECTED_THREAD_EXECUTION,
// 触发fallbackMethod
REJECTED_SEMAPHORE_EXECUTION,
// 触发fallbackMethod
REJECTED_SEMAPHORE_FALLBACK;
private FailureType() {
}
}
3.3熔断策略
public static enum ExecutionIsolationStrategy {
THREAD,
SEMAPHORE;
private ExecutionIsolationStrategy() {
}
}
参考
1使用Hystrix的插件机制,解决在使用线程隔离时,threadlocal的传递问题
2Hystrix 入门教程 - 基础篇
3豪猪Hystrix:普通Javaweb结合AOP使用
4Hystrix+AOP的完美结合
5SpringMVC接收Get请求参数
https://blog.csdn.net/weixin_43965912/article/details/105239740
6hystrix源码解析——FallbackMethod是如何接收异常的
7自定义SpringMVC中的*RequestMappingHandlerMapping
http://monkeywie.cn/2020/06/22/custom-springmvc-requestmappinghandlermapping/
网友评论