zoukankan      html  css  js  c++  java
  • 如何做自己的服务监控?spring boot 1.x服务监控揭秘

    1.准备

      下载可运行程序:http://www.mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/

      2.添加服务监控依赖

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
                <scope>provided</scope>
            </dependency>

    3.启动spring boot项目

    console 截图如下:

    4.servlet和filter

      4.1 使用ServletRegistrationBean注册dispatcherServlet

    /**
     * A {@link ServletContextInitializer} to register {@link Servlet}s in a Servlet 3.0+
     * container. Similar to the {@link ServletContext#addServlet(String, Servlet)
     * registration} features provided by {@link ServletContext} but with a Spring Bean
     * friendly design.
     * <p>
     * The {@link #setServlet(Servlet) servlet} must be specified before calling
     * {@link #onStartup}. URL mapping can be configured used {@link #setUrlMappings} or
     * omitted when mapping to '/*' (unless
     * {@link #ServletRegistrationBean(Servlet, boolean, String...) alwaysMapUrl} is set to
     * {@code false}). The servlet name will be deduced if not specified.
     *
     * @param <T> the type of the {@link Servlet} to register
     * @author Phillip Webb
     * @since 1.4.0
     * @see ServletContextInitializer
     * @see ServletContext#addServlet(String, Servlet)
     */

    总结:类似于ServletContext#addServlet(String, Servlet)

    查看所有注册的bean

    http://127.0.0.1:8080/beans

    并把返回的json 格式化 视图查看,在线工具(http://www.bejson.com/jsonviewernew/)

    注册的流程:

    spring-boot-autoconfigure模块spring.facotories的属性org.springframework.boot.autoconfigure.EnableAutoConfiguration=DispatcherServletAutoConfiguration

            @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
            @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
            public DispatcherServletRegistrationBean dispatcherServletRegistration(
                    DispatcherServlet dispatcherServlet) {
                DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
                        dispatcherServlet, this.webMvcProperties.getServlet().getPath());
                registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
                registration.setLoadOnStartup(
                        this.webMvcProperties.getServlet().getLoadOnStartup());
                if (this.multipartConfig != null) {
                    registration.setMultipartConfig(this.multipartConfig);
                }
                return registration;
            }

    4.2 使用FilterRegistrationBean注册各种filter

    /**
     * A {@link ServletContextInitializer} to register {@link Filter}s in a Servlet 3.0+
     * container. Similar to the {@link ServletContext#addFilter(String, Filter) registration}
     * features provided by {@link ServletContext} but with a Spring Bean friendly design.
     * <p>
     * The {@link #setFilter(Filter) Filter} must be specified before calling
     * {@link #onStartup(ServletContext)}. Registrations can be associated with
     * {@link #setUrlPatterns URL patterns} and/or servlets (either by {@link #setServletNames
     * name} or via a {@link #setServletRegistrationBeans ServletRegistrationBean}s. When no
     * URL pattern or servlets are specified the filter will be associated to '/*'. The filter
     * name will be deduced if not specified.
     *
     * @param <T> the type of {@link Filter} to register
     * @author Phillip Webb
     * @since 1.4.0
     * @see ServletContextInitializer
     * @see ServletContext#addFilter(String, Filter)
     * @see DelegatingFilterProxyRegistrationBean
     */

    总结:类似于ServletContext#addFilter(String, Filter)

     spring-boot-actuator-autoconfigure模块spring.facotories的属性org.springframework.boot.autoconfigure.EnableAutoConfiguration=WebMvcMetricsAutoConfiguration,......

    5.RequestMappingHandlerAdapter查找controller注解

    使用

    2019-01-16 09:47:07.715  INFO 8468 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@66ac5762: startup date [Wed Jan 16 09:47:06 CST 2019]; root of context hierarchy

    定义:

    /**
     * An {@link AbstractHandlerMethodAdapter} that supports {@link HandlerMethod}s
     * with their method argument and return type signature, as defined via
     * {@code @RequestMapping}.
     *
     * <p>Support for custom argument and return value types can be added via
     * {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}.
     * Or alternatively, to re-configure all argument and return value types,
     * use {@link #setArgumentResolvers} and {@link #setReturnValueHandlers}.
     *
     * @author Rossen Stoyanchev
     * @author Juergen Hoeller
     * @since 3.1
     * @see HandlerMethodArgumentResolver
     * @see HandlerMethodReturnValueHandler
     */

    内部实现源码

    private void initControllerAdviceCache() {
            if (getApplicationContext() == null) {
                return;
            }
            if (logger.isInfoEnabled()) {
                logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
            }
    
            List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
            AnnotationAwareOrderComparator.sort(beans);
    
            List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();
    
            for (ControllerAdviceBean bean : beans) {
                Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
                if (!attrMethods.isEmpty()) {
                    this.modelAttributeAdviceCache.put(bean, attrMethods);
                    if (logger.isInfoEnabled()) {
                        logger.info("Detected @ModelAttribute methods in " + bean);
                    }
                }
                Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
                if (!binderMethods.isEmpty()) {
                    this.initBinderAdviceCache.put(bean, binderMethods);
                    if (logger.isInfoEnabled()) {
                        logger.info("Detected @InitBinder methods in " + bean);
                    }
                }
                if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
                    requestResponseBodyAdviceBeans.add(bean);
                    if (logger.isInfoEnabled()) {
                        logger.info("Detected RequestBodyAdvice bean in " + bean);
                    }
                }
                if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
                    requestResponseBodyAdviceBeans.add(bean);
                    if (logger.isInfoEnabled()) {
                        logger.info("Detected ResponseBodyAdvice bean in " + bean);
                    }
                }
            }
    
            if (!requestResponseBodyAdviceBeans.isEmpty()) {
                this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
            }
        }

    6.使用RequestMappingHandlerMapping查找controller映射路径

    2019-01-16 09:47:07.758  INFO 8468 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.mkyong.WelcomeController.welcome(java.util.Map<java.lang.String, java.lang.Object>)

    定义:

    /**
     * Creates {@link RequestMappingInfo} instances from type and method-level
     * {@link RequestMapping @RequestMapping} annotations in
     * {@link Controller @Controller} classes.
     *
     * @author Arjen Poutsma
     * @author Rossen Stoyanchev
     * @author Sam Brannen
     * @since 3.1
     */

    作用:使用注解@RequestMapping在controller类内创建一个类型或者方法级别的RequestMappingInfo实例

    7.EndpointHandlerMapping映射的监控项

    2019-01-15 14:19:30.985  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
    2019-01-15 14:19:30.986  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/health || /health.json],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(java.security.Principal)
    2019-01-15 14:19:30.987  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)
    2019-01-15 14:19:30.987  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
    2019-01-15 14:19:30.988  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
    2019-01-15 14:19:30.988  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/heapdump || /heapdump.json],methods=[GET],produces=[application/octet-stream]}" onto public void org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint.invoke(boolean,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException,javax.servlet.ServletException
    2019-01-15 14:19:30.989  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/beans || /beans.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
    2019-01-15 14:19:30.990  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
    2019-01-15 14:19:30.992  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)
    2019-01-15 14:19:30.992  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/env || /env.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
    2019-01-15 14:19:30.995  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/info || /info.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
    2019-01-15 14:19:30.996  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
    2019-01-15 14:19:30.996  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()

    其中,

    /trace 通过EndpointMvcAdapter.invoke()触发TraceEndpoint的invoke方法

    /health通过HealthMvcEndpoint.invoke()触发

    /metrics/{name:.*}通过MetricsMvcEndpoint.value()触发

    /metrics通过EndpointMvcAdapter.invoke()触发MetricsEndpoint的invoke方法

    /dump通过EndpointMvcAdapter.invoke()触发DumpEndpoint的invoke方法

    /heapdump通过HeapdumpMvcEndpoint.invoke()触发

    /beans通过EndpointMvcAdapter.invoke()触发BeansEndpoint的invoke方法

    /autoconfig通过EndpointMvcAdapter.invoke()触发AutoconfigEndpoint的invoke方法

    /env/{name:.*}通过EnvironmentMvcEndpoint.value()方法触发

    /info通过EndpointMvcAdapter.invoke()触发InfoEndpoint的invoke方法

    /mappings通过通过EndpointMvcAdapter.invoke()触发RequestMappingEndpoint的invoke方法

    小结:

    restful请求实现分两种,一种通过EndpointMvcAdapter.invoke()触发

    而EndpointMvcAdapter.invoke()通过注解@GetMapping实现了restful服务

    /**
     * Adapter class to expose {@link Endpoint}s as {@link MvcEndpoint}s.
     *
     * @author Dave Syer
     * @author Andy Wilkinson
     */
    public class EndpointMvcAdapter extends AbstractEndpointMvcAdapter<Endpoint<?>> {
    
        /**
         * Create a new {@link EndpointMvcAdapter}.
         * @param delegate the underlying {@link Endpoint} to adapt.
         */
        public EndpointMvcAdapter(Endpoint<?> delegate) {
            super(delegate);
        }
    
        @Override
        @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
        @ResponseBody
        public Object invoke() {
            return super.invoke();
        }
    
    }

    另一种,通过继承MvcEndpoint的invoke方法来触发 

    例如HealthMvcEndpoint

        @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
        @ResponseBody
        public Object invoke(Principal principal) {
            if (!getDelegate().isEnabled()) {
                // Shouldn't happen because the request mapping should not be registered
                return getDisabledResponse();
            }
            Health health = getHealth(principal);
            HttpStatus status = getStatus(health);
            if (status != null) {
                return new ResponseEntity<Health>(health, status);
            }
            return health;
        }

    7.1 EndpointHandlerMapping的定义

    /**
     * {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getId()}.
     * The semantics of {@code @RequestMapping} should be identical to a normal
     * {@code @Controller}, but the endpoints should not be annotated as {@code @Controller}
     * (otherwise they will be mapped by the normal MVC mechanisms).
     * <p>
     * One of the aims of the mapping is to support endpoints that work as HTTP endpoints but
     * can still provide useful service interfaces when there is no HTTP server (and no Spring
     * MVC on the classpath). Note that any endpoints having method signatures will break in a
     * non-servlet environment.
     *
     * @author Phillip Webb
     * @author Christian Dupuis
     * @author Dave Syer
     */

    7.2 层次结构

    7.3 查看对应的bean的生成

          {
            "bean": "endpointHandlerMapping",
            "aliases": [
              
            ],
            "scope": "singleton",
            "type": "org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping",
            "resource": "class path resource [org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.class]",
            "dependencies": [
              
            ]
          }

     7.4 获取流程

        @Bean
        @ConditionalOnMissingBean
        public EndpointHandlerMapping endpointHandlerMapping() {
            Set<? extends MvcEndpoint> endpoints = mvcEndpoints().getEndpoints(); //1
            CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties);
            EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints,
                    corsConfiguration);                                          //2
            boolean disabled = this.managementServerProperties.getPort() != null  
                    && this.managementServerProperties.getPort() == -1;           
            mapping.setDisabled(disabled);
            if (!disabled) {
                mapping.setPrefix(this.managementServerProperties.getContextPath()); //3
            }
            if (this.mappingCustomizers != null) {
                for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
                    customizer.customize(mapping);   //4
                }
            }
            return mapping;
        }

    7.4.1 MvcEndpoints获取endpoint定义

        @Override
        public void afterPropertiesSet() throws Exception {
            Collection<MvcEndpoint> existing = BeanFactoryUtils
                    .beansOfTypeIncludingAncestors(this.applicationContext, MvcEndpoint.class)
                    .values();
            this.endpoints.addAll(existing);
            this.customTypes = findEndpointClasses(existing);
            @SuppressWarnings("rawtypes")
            Collection<Endpoint> delegates = BeanFactoryUtils
                    .beansOfTypeIncludingAncestors(this.applicationContext, Endpoint.class)
                    .values();
            for (Endpoint<?> endpoint : delegates) {
                if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) {
                    EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint);
                    String path = determinePath(endpoint,
                            this.applicationContext.getEnvironment());
                    if (path != null) {
                        adapter.setPath(path);
                    }
                    this.endpoints.add(adapter);
                }
            }
        }

    7.4.2 定义映射关系

    其内部实现源码:

        private String getPath(Object handler) {
            if (handler instanceof String) {
                handler = getApplicationContext().getBean((String) handler);
            }
            if (handler instanceof MvcEndpoint) {
                return ((MvcEndpoint) handler).getPath();
            }
            return "";
        }

    7.4.3 增加contextpath

    7.4.4 自定义EndpointHandlerMappingCustomizer

    8.其它通过

        @Bean
        @ConditionalOnBean(EnvironmentEndpoint.class)
        @ConditionalOnEnabledEndpoint("env")
        public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
            return new EnvironmentMvcEndpoint(delegate);
        }
    
        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnEnabledEndpoint("heapdump")
        public HeapdumpMvcEndpoint heapdumpMvcEndpoint() {
            return new HeapdumpMvcEndpoint();
        }
    
        @Bean
        @ConditionalOnBean(HealthEndpoint.class)
        @ConditionalOnEnabledEndpoint("health")
        public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate,
                ManagementServerProperties managementServerProperties) {
            HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate,
                    isHealthSecure(), managementServerProperties.getSecurity().getRoles());
            if (this.healthMvcEndpointProperties.getMapping() != null) {
                healthMvcEndpoint
                        .addStatusMapping(this.healthMvcEndpointProperties.getMapping());
            }
            return healthMvcEndpoint;
        }
    
        @Bean
        @ConditionalOnBean(MetricsEndpoint.class)
        @ConditionalOnEnabledEndpoint("metrics")
        public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {
            return new MetricsMvcEndpoint(delegate);
        }
    
        @Bean
        @ConditionalOnEnabledEndpoint("logfile")
        @Conditional(LogFileCondition.class)
        public LogFileMvcEndpoint logfileMvcEndpoint() {
            return new LogFileMvcEndpoint();
        }
    
        @Bean
        @ConditionalOnBean(ShutdownEndpoint.class)
        @ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false)
        public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
            return new ShutdownMvcEndpoint(delegate);
        }

     以health为例

        @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
        @ResponseBody
        public Object invoke(Principal principal) {
            if (!getDelegate().isEnabled()) {
                // Shouldn't happen because the request mapping should not be registered
                return getDisabledResponse();
            }
            Health health = getHealth(principal);
            HttpStatus status = getStatus(health);
            if (status != null) {
                return new ResponseEntity<Health>(health, status);
            }
            return health;
        }

    调用HealthEndpoint

        /**
         * Invoke all {@link HealthIndicator} delegates and collect their health information.
         */
        @Override
        public Health invoke() {
            return this.healthIndicator.health();
        }

     总结:

    spring boot提供http请求的方式可以分两种:

    1.通过查找@Controller注解中的@RequestMapping来形成HandlerMapping

    2.直接通过@RequestMapping来形成HandlerMapping如actuator模块,这里面又分成两种:

       2.1  一种集中式的通过继承@RequestMapping来实现如通过EndpointMvcAdapter.invoke()触发

       2.2  另一种通过直接的@RequestMapping注解实现

    3.spring boot1.x监控的实现

      /trace 通过EndpointMvcAdapter.invoke()触发TraceEndpoint的invoke方法

      /health通过HealthMvcEndpoint.invoke()触发

      /metrics/{name:.*}通过MetricsMvcEndpoint.value()触发

      /metrics通过EndpointMvcAdapter.invoke()触发MetricsEndpoint的invoke方法

      /dump通过EndpointMvcAdapter.invoke()触发DumpEndpoint的invoke方法

      /heapdump通过HeapdumpMvcEndpoint.invoke()触发

      /beans通过EndpointMvcAdapter.invoke()触发BeansEndpoint的invoke方法

      /autoconfig通过EndpointMvcAdapter.invoke()触发AutoconfigEndpoint的invoke方法

      /env/{name:.*}通过EnvironmentMvcEndpoint.value()方法触发

      /info通过EndpointMvcAdapter.invoke()触发InfoEndpoint的invoke方法

      /mappings通过通过EndpointMvcAdapter.invoke()触发RequestMappingEndpoint的invoke方法。

     4. 这些实现都定义在spring.factories

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    org.springframework.boot.actuate.autoconfigure.AuditAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.CacheStatisticsAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.InfoContributorAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.MetricsDropwizardAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.MetricsChannelAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.MetricExportAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,
    org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration
    
    org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=
    org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration,
    org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration
  • 相关阅读:
    JQuery上传插件Uploadify使用详解
    c#.net 生成清晰缩略图的解决方案
    PS图片上传图片 同时生成微缩图
    无限极”分类数据表设计的简单再总结
    Web开发者必备的12款超赞jQuery插件
    mssql中对于bit类型字段的更新
    SQL Server存储多语言数据的几种方法
    正则表达式测试工具RegexTester
    .Net 2.0 正则表达式里的$在Multiline模式下的精确含意
    终于搞定了终端服务客户端三个月重装一次的问题
  • 原文地址:https://www.cnblogs.com/davidwang456/p/10272999.html
Copyright © 2011-2022 走看看