2021-07-01_Spring自定义RequestMapping学习笔记

20210701_Spring自定义RequestMapping学习笔记

1概述

通过对spring,spring-web,hystrix等相关源码的阅读,结合自己的理解,产生此篇文章。具体涉及内容如下:

  1. springmvc父子容器

    基于内嵌tomcat实现spring父子容器(MySpringContainer、MySpringMvcContainer)的加载

  2. 重写RequestMapping,自定义请求映射MyHystrixRequestMapping

  3. 针对自定义MyHystrixRequestMapping注解的类及方法,借助spring-aop切面,实现aop请求拦截

  4. 对代理的方法进行Hystrix限流、熔断、隔离、降级方法的处理

  5. 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">
     * &#064;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的传递问题

https://www.cnblogs.com/grey-wolf/p/12859084.html

2Hystrix 入门教程 - 基础篇

https://www.techgrow.cn/posts/2ed0fea6.html

https://www.bilibili.com/video/BV1TK4y1S7fv

3豪猪Hystrix:普通Javaweb结合AOP使用

https://blog.csdn.net/fly910905/article/details/87863388

4Hystrix+AOP的完美结合

https://blog.csdn.net/CCCdingding/article/details/91878824

5SpringMVC接收Get请求参数

https://blog.csdn.net/weixin_43965912/article/details/105239740

6hystrix源码解析——FallbackMethod是如何接收异常的

https://blog.csdn.net/u012107143/article/details/106139215

7自定义SpringMVC中的*RequestMappingHandlerMapping

http://monkeywie.cn/2020/06/22/custom-springmvc-requestmappinghandlermapping/

https://monkeywie.cn/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容