SpringBoot启动成功后调用接口、方法

简介: SpringBoot启动成功后调用接口、方法

问题


  线上问题遇到一个接口第一次访问特别慢的问题,后来说是因为该接口加了某注解,所以第一次请求比较慢,初步解决办法就是启动后先请求一次就好了。


代码


模拟测试接口


  @RequestMapping("/hello")
  public String hello() {
    return LocalDateTime.now().toString();
  }


核心接口CommandLineRunner


package com.example.autorequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
/**
 * @author chaird
 * @create 2020-07-31 22:43
 */
@Component
@Slf4j
public class StartAutoRequestService implements CommandLineRunner {
  @Override
  public void run(String... args) throws Exception {
    String httpUrl = "http://localhost:8082/hello";
    // 链接
    HttpURLConnection connection = null;
    InputStream is = null;
    BufferedReader br = null;
    StringBuffer result = new StringBuffer();
    try {
      // 创建连接
      URL url = new URL(httpUrl);
      connection = (HttpURLConnection) url.openConnection();
      // 设置请求方式
      connection.setRequestMethod("GET");
      // 设置连接超时时间
      connection.setReadTimeout(15000);
      // 开始连接
      connection.connect();
      // 获取响应数据
      if (connection.getResponseCode() == 200) {
        // 获取返回的数据
        is = connection.getInputStream();
        if (null != is) {
          br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
          String temp = null;
          while (null != (temp = br.readLine())) {
            result.append(temp);
          }
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (null != br) {
        try {
          br.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (null != is) {
        try {
          is.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      // 关闭远程连接
      connection.disconnect();
    }
    System.out.println(result.toString());
  }
}

原理


首先在自己实现CommandLineRunner接口的类的run方法里打个断点,看一下调用栈,这样好知道在哪打断点


5.png


在上图箭头处的方法的第一行打断点

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
          new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
            //断点走到这,此时控制台已经打印出Tomcat started on port(s): 8082 (http) with context path ''
            //断点走到这,此时控制台已经打印出Tomcat started on port(s): 8082 (http) with context path ''
           //断点走到这,此时控制台已经打印出Tomcat started on port(s): 8082 (http) with context path ''
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
            //开始调用实现CommandLineRunner接口的方法
            //开始调用实现CommandLineRunner接口的方法
            //开始调用实现CommandLineRunner接口的方法
      callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
    }
    try {
      listeners.running(context);
    }
    catch (Throwable ex) {
    }
    return context;
  }

进入上图的callRunners方法


  private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    //拿到实现CommandLineRunner接口的实现类
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<>(runners)) {
      if (runner instanceof ApplicationRunner) {
        callRunner((ApplicationRunner) runner, args);
      }
      if (runner instanceof CommandLineRunner) {
           //调用方法,此时在跟进去就进入自己写的方法里了
        callRunner((CommandLineRunner) runner, args);
      }
    }
  }
目录
相关文章
|
安全 NoSQL Java
SpringBoot接口安全:限流、重放攻击、签名机制分析
本文介绍如何在Spring Boot中实现API安全机制,涵盖签名验证、防重放攻击和限流三大核心。通过自定义注解与拦截器,结合Redis,构建轻量级、可扩展的安全防护方案,适用于B2B接口与系统集成。
1469 3
|
算法 网络协议 Java
Spring Boot 的接口限流算法
本文介绍了高并发系统中流量控制的重要性及常见的限流算法。首先讲解了简单的计数器法,其通过设置时间窗口内的请求数限制来控制流量,但存在临界问题。接着介绍了滑动窗口算法,通过将时间窗口划分为多个格子,提高了统计精度并缓解了临界问题。随后详细描述了漏桶算法和令牌桶算法,前者以固定速率处理请求,后者允许一定程度的流量突发,更符合实际需求。最后对比了各算法的特点与适用场景,指出选择合适的算法需根据具体情况进行分析。
1156 56
Spring Boot 的接口限流算法
|
人工智能 前端开发 JavaScript
SpringBoot实现网页消息推送的5种方法
本文详细介绍了在SpringBoot中实现网页消息推送的几种主流方案,包括短轮询、长轮询、SSE(Server-Sent Events)、WebSocket以及STOMP。每种方案各有优缺点,适用于不同的场景需求。短轮询简单易实现但效率低;长轮询提升了实时性但仍有限制;SSE适合单向通信且轻量高效;WebSocket支持全双工通信,适合高实时性要求的场景;STOMP基于WebSocket,提供更高级的消息传递功能。通过对比分析,开发者可根据业务需求、性能要求及浏览器兼容性选择最适合的技术方案,同时可结合多种技术实现优雅降级,优化用户体验。
2351 57
|
Java API 网络架构
基于 Spring Boot 框架开发 REST API 接口实践指南
本文详解基于Spring Boot 3.x构建REST API的完整开发流程,涵盖环境搭建、领域建模、响应式编程、安全控制、容器化部署及性能优化等关键环节,助力开发者打造高效稳定的后端服务。
1644 1
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
1333 5
|
Java Spring 容器
两种Spring Boot 项目启动自动执行方法的实现方式
在Spring Boot项目启动后执行特定代码的实际应用场景中,可通过实现`ApplicationRunner`或`CommandLineRunner`接口完成初始化操作,如系统常量或配置加载。两者均支持通过`@Order`注解控制执行顺序,值越小优先级越高。区别在于参数接收方式:`CommandLineRunner`使用字符串数组,而`ApplicationRunner`采用`ApplicationArguments`对象。注意,`@Order`仅影响Bean执行顺序,不影响加载顺序。
1157 2
|
存储 算法 安全
SpringBoot 接口加密解密实现
【10月更文挑战第18天】
|
缓存 NoSQL Java
springboot怎么使用rides缓存方法的返回值 完整例子
通过上述步骤,我们成功地在 Spring Boot 项目中集成了 Redis 缓存,并通过注解的方式实现了方法返回值的缓存。这种方式不仅提高了系统的性能,还简化了缓存管理的复杂度。使用 Spring Boot 的缓存注解和 Redis,可以轻松地实现高效、可靠的缓存机制。
485 23
|
监控 Java Spring
SpringBoot:SpringBoot通过注解监测Controller接口
本文详细介绍了如何通过Spring Boot注解监测Controller接口,包括自定义注解、AOP切面的创建和使用以及具体的示例代码。通过这种方式,可以方便地在Controller方法执行前后添加日志记录、性能监控和异常处理逻辑,而无需修改方法本身的代码。这种方法不仅提高了代码的可维护性,还增强了系统的监控能力。希望本文能帮助您更好地理解和应用Spring Boot中的注解监测技术。
685 16