企业级spring-boot案例-Spring Boot 启动时的运行方法

2023-05-16

文章目录

    • 为什么我们需要在 spring boot 启动时运行代码?
    • spring boot中启动后不同的运行方式
    • 1.使用CommandLineRunner接口
      • 实现 CommandLineRunner 接口的示例
      • 创建 CommandLineRunner 接口的 bean 示例
    • 2.使用ApplicationRunner接口
      • 实现 ApplicationRunner 接口的示例
    • 3. Spring Boot中的应用事件
    • 4.方法上的@Postconstruct注解
    • 5. InitializingBean 接口
    • 6.@bean注解的init属性
    • 总结

企业级spring-boot案例系列文章上线了,涵盖了大部分企业级的spring-boot使用场景,会不定期进行更新,企业级spring-boot案例源码地址:https://gitee.com/JourWon/spring-boot-example,欢迎各位大佬一起学习和指正

企业级spring-boot案例
|----Spring Boot整合actuator,实现服务监控管理 spring-boot-actuator
|----Spring Boot集成事件发布及监听 spring-boot-applicationevent
|----Spring Boot整合异步线程池 spring-boot-async
|----Spring Boot整合自定义banner spring-boot-banner
|----Spring Boot整合本地缓存caffeine spring-boot-cache-caffeine
|----Spring Boot整合验证码captcha spring-boot-captcha
|----Spring Boot整合cors跨域资源共享 spring-boot-cors
|----Spring Boot整合jpa实现增删改查 spring-boot-data-jpa
|----Spring Boot整合PostgreSQL spring-boot-data-postgresql
|----Spring Boot整合Redis spring-boot-data-redis
|----Spring Boot整合Docker spring-boot-docker
|----Spring Boot设计模式之工厂模式 spring-boot-dp-factory
|----Spring Boot整合Druid数据库连接池 spring-boot-druid
|----Spring Boot整合dubbo spring-boot-dubbo
|----Spring Boot整合EasyExcel,实现Excel导入导出 spring-boot-easyexcel
|----Spring Boot整合邮件发送 spring-boot-email
|----Spring Boot整合全局异常处理,接口统一响应对象 spring-boot-exception-handler
|----Spring Boot整合flyway,数据库版本控制 spring-boot-flyway
|----Spring Boot整合git插件,将项目打包为tar.gz,并带上git版本号,然后通过sh脚本快速部署 spring-boot-git-commit-id-plugin
|----spring-boot快速入门-HelloWorld spring-boot-helloworld
|----Spring Boot整合HikariCP数据库连接池 spring-boot-hikaricp
|----Spring Boot整合https spring-boot-https
|----Spring Boot整合Jackson,实现数据脱敏 spring-boot-json-desensitization
|----Spring Boot整合Kafka spring-boot-kafka
|----Spring Boot整合Knife4j-API接口文档 spring-boot-knife4j
|----Spring Boot整合log4j2日志 spring-boot-log4j2
|----Spring Boot整合logback日志 spring-boot-logback
|----Spring Boot整合MyBatis spring-boot-mybatis
|----Spring Boot整合MyBatis,使用注解方式 spring-boot-mybatis-annotation
|----Spring Boot整合MyBatis逆向工程 spring-boot-mybatis-generator
|----Spring Boot整合通用mapper spring-boot-mybatis-mapper
|----Spring Boot整合MyBatis多数据源 spring-boot-mybatis-multi-datasource
|----Spring Boot整合MyBatis,使用pagehelper进行分页 spring-boot-mybatis-pagehelper
|----Spring Boot整合MyBatis脱敏插件,实现手机号等信息脱敏 spring-boot-mybatis-plugin-sensitive
|----Spring Boot整合Mybatis-Plus spring-boot-mybatis-plus
|----Spring Boot整合MyBatis Plus代码生成器 spring-boot-mybatis-plus-generator
|----Spring Boot整合MyBatis Plus多数据源 spring-boot-mybatis-plus-multi-datasource
|----Spring Boot整合pf4j,进行插件式编程拓展 spring-boot-pf4j
|----Spring Boot加载配置文件 spring-boot-properties
|----Spring Boot整合接口限流-guava单体方式 spring-boot-ratelimit-guava
|----Spring Boot整合接口限流-redis集群方式 spring-boot-ratelimit-redis
|----Spring Boot整合RestTemplate,实现服务间调用 spring-boot-resttemplate
|----Spring Boot整合retrofit,支持通过接口的方式发起HTTP请求 spring-boot-retrofit
|----Spring Boot启动时的运行方法 spring-boot-runner
|----Spring Boot整合定时任务scheduler spring-boot-scheduler
|----Spring Boot整合Screw,一键生成数据库文档 spring-boot-screw
|----Spring Boot整合Shiro spring-boot-shiro
|----Spring Boot整合Swagger3-API接口文档 spring-boot-swagger3
|----Spring Boot整合模板引擎Thymeleaf spring-boot-thymeleaf
|----Spring Boot整合undertow spring-boot-undertow
|----Spring Boot项目打包成war包 spring-boot-war
|----Spring Boot整合zip,压缩和解压文件 spring-boot-zip

在开发spring boot应用的时候,有时候我们需要在应用启动的时候运行一个方法或者一段代码。这段代码可以是任何范围,从记录某些信息到设置数据库、cron 作业等。我们不能只将此代码放在构造函数中,因为所需的变量或服务可能尚未初始化。这可能会导致空指针或其他一些异常。

为什么我们需要在 spring boot 启动时运行代码?

启动后调用方法

出于多种原因,我们需要在应用程序启动时运行方法,例如:

  • 记录重要的事情或消息说应用程序已启动
  • 处理数据库或文件、索引、创建缓存等。
  • 启动后台进程,如发送通知、从某个队列中获取数据等。

spring boot中启动后不同的运行方式

每种方式都有其自身的好处。让我们详细看看来决定我们应该使用哪个,

  1. 使用 CommandLineRunner 接口
  2. 使用 ApplicationRunner 接口
  3. Spring 启动应用程序事件
  4. 方法上的@Postconstruct 注解
  5. InitializingBean 接口
  6. @bean注解的初始化属性

1.使用CommandLineRunner接口

org.springframework.boot.CommandLineRunnerSpring Boot提供的一个接口,当你实现该接口并将之注入Spring IoC容器后,Spring Boot应用启动时就会执行其run方法。

实现 CommandLineRunner 接口的示例

@Slf4j
@Component
public class MyCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        log.info("启动预加载数据(MyCommandLineRunner)...{}", Arrays.toString(args));
    }

}

创建 CommandLineRunner 接口的 bean 示例

@Slf4j
@SpringBootApplication
public class SpringBootRunnerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootRunnerApplication.class, args);
        log.info("应用启动...");
    }

    @Bean
    public CommandLineRunner CommandLineRunnerBean() {
        return (args) -> {
            log.info("启动预加载数据(In CommandLineRunnerImpl)...{}", Arrays.toString(args));
        };
    }

}

我们可以使用命令行或 IDE 运行应用程序。让我们举一个例子,当我们使用“–status=running”作为参数运行应用程序时

mvn spring-boot:run -Dspring-boot.run.arguments="--spring.profile.active=test, --foo=bar, --foo=baz, --debug, runner"

或者

mvn package 
java -jar target/<FILENAME.JAR HERE> --spring.profile.active=test, --foo=bar, --foo=baz, --debug, runner

或者

java -jar spring-boot-runner-1.0.0.jar --spring.profile.active=test, --foo=bar, --foo=baz, --debug, runner

或者在 IDE 运行应用程序指定参数

在这里插入图片描述

这将产生以下日志输出:

2021-12-04 10:24:04.525  INFO 6964 --- [           main] c.j.s.boot.runner.MyCommandLineRunner    : 启动预加载数据(MyCommandLineRunner)...[--spring.profile.active=test, --foo=bar, --foo=baz, --debug, runner]
2021-12-04 10:24:04.526  INFO 6964 --- [           main] c.j.s.boot.SpringBootRunnerApplication   : 启动预加载数据(In CommandLineRunnerImpl)...[--spring.profile.active=test, --foo=bar, --foo=baz, --debug, runner]
2021-12-04 10:24:04.527  INFO 6964 --- [           main] c.j.s.boot.SpringBootRunnerApplication   : 应用启动...

正如我们所看到的,参数没有被解析,而是被解释为单个值"–spring.profile.active=test, --foo=bar, --foo=baz, --debug, runner"。

要以解析格式访问命令行参数,我们需要使用 ApplicationRunner 接口。

Spring Boot 在启动过程中添加了 CommandLineRunner 接口。因此在 commandlinerRunner 中抛出异常将强制 Spring boot 中止启动。

一个Spring Boot可以存在多个CommandLineRunner的实现,当存在多个时,你可以实现Ordered接口控制这些实现的执行顺序(Order 数值越小优先级越高)。

@Component
@Order(1)
public class CommandLineRunnerImpl implements CommandLineRunner {
    ........
}

2.使用ApplicationRunner接口

要访问解析的参数,我们需要使用 ApplicationRunner 接口,在Spring Boot 1.3.0引入了一个和CommandLineRunner功能一样的接口。CommandLineRunner接收可变参数String... args,而ApplicationRunner 接收一个封装好的对象参数ApplicationArguments。除此之外它们功能完全一样,甚至连方法名都一样。

它提供了访问参数的不同方法,如下所示

方法说明
String[] GetSourceArgs()被传递给应用程序的原始参数,返回这些参数的字符串数组
Set getOptionNames()获取选项名称的Set字符串集合,选项参数的前面都有"–"。如 --spring.profiles.active=dev --debug 将返回["spring.profiles.active","debug"]
List getNonOptionArgs()用来获取所有的无选项参数,不带"–"的参数
boolean containsOption(String name)用来判断是否包含某个选项的名称
List getOptionValues(String name)通过名称来获取该名称对应的选项值,我们可以在命令行中多次使用相同的键。如--foo=bar --foo=baz 将返回["bar","baz"]

实现 ApplicationRunner 接口的示例

让我们使用"–spring.profile.active=test, --foo=bar, --foo=baz, --debug, runner"参数运行下面的程序

@Slf4j
@Order(1)
@Component
public class MyApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("MyApplicationRunner run方法");
        boolean b1 = args.containsOption("spring.profile.active");
        boolean b2 = args.containsOption("abc");
        log.info("containsOption[spring.profile.active]:{},containsOption[abc]:{}", b1, b2);

        log.info("SourceArgs:{}", Arrays.toString(args.getSourceArgs()));
        log.info("NonOptionArgs:{}", args.getNonOptionArgs());
        log.info("OptionNames:{}", args.getOptionNames());

        log.info("Printing key and value in loop:");
        for (String key : args.getOptionNames()) {
            log.info("key:{}", key);
            log.info("val:{}", args.getOptionValues(key));
        }
    }

}

输出:

2021-12-04 11:02:53.584  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : MyApplicationRunner run方法
2021-12-04 11:02:53.584  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : containsOption[spring.profile.active]:true,containsOption[abc]:false
2021-12-04 11:02:53.586  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : SourceArgs:[--spring.profile.active=test, --foo=bar, --foo=baz, --debug, runner]
2021-12-04 11:02:53.586  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : NonOptionArgs:[runner]
2021-12-04 11:02:53.586  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : OptionNames:[spring.profile.active, debug, foo]
2021-12-04 11:02:53.586  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : Printing key and value in loop:
2021-12-04 11:02:53.586  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : key:spring.profile.active
2021-12-04 11:02:53.587  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : val:[test]
2021-12-04 11:02:53.587  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : key:debug
2021-12-04 11:02:53.587  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : val:[]
2021-12-04 11:02:53.587  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : key:foo
2021-12-04 11:02:53.587  INFO 15260 --- [           main] c.j.s.boot.runner.MyApplicationRunner    : val:[bar, baz]
2021-12-04 11:02:53.587  INFO 15260 --- [           main] c.j.s.boot.runner.MyCommandLineRunner    : MyCommandLineRunner run方法...[--spring.profile.active=test, --foo=bar, --foo=baz, --debug, runner]
2021-12-04 11:02:53.587  INFO 15260 --- [           main] c.j.s.boot.SpringBootRunnerApplication   : bean注解创建CommandLineRunner,参数[--spring.profile.active=test, --foo=bar, --foo=baz, --debug, runner]
2021-12-04 11:02:53.588  INFO 15260 --- [           main] c.j.s.boot.SpringBootRunnerApplication   : 应用启动...

CommandLineRunner 和 ApplicationRunner 具有类似的功能,例如

  • run() 方法中的异常将中止应用程序启动
  • 可以使用 Ordered 接口或 @Order 注解对多个 ApplicationRunner 进行排序

需要注意的最重要的一点是,CommandLineRunners 和 ApplicationRunners 之间共享 Order。这意味着 commandlinerRunner 和 applicationRunner 之间的执行顺序可能会混合。

CommandLineRunnerApplicationRunner 区别

  • 参数不一样。两个接口的实现方法一样,参数不一样,设置命令行参数:–spring.profile.active=test,ApplicatonRunner接口的方法参数ApplicationArguments(是个对象)比CommandLineRunner接口的方法参数(是个可以接收多个string的参数)功能更强大。ApplicatonRunner接口的方法参数ApplicationArguments既可以获取参数的字符串,也可以直接获取key;CommandLineRunner接口的方法参数只能获取参数的字符串。

  • 方法执行顺序不一样。ApplicationRunner接口的实现方法比CommandLineRunner接口的实现方法前执行(当然也可以设置@Order的值来决定谁先执行)

执行顺序使用源码分析

说到执行顺序,那么再进一步了解一下这两个方法是在什么时候执行的。这两个接口的实现执行的时机在于SpringApplication初始化之后,调用的run方法中被调用的。

public ConfigurableApplicationContext run(String... args) {
        // 创建 StopWatch 对象,用于统计 run 方法启动时长。
        StopWatch stopWatch = new StopWatch();
        // 启动统计。
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        // 配置 headless 属性。
        configureHeadlessProperty();
        // 获得 SpringApplicationRunListener 数组,
        // 该数组封装于 SpringApplicationRunListeners 对象的 listeners 中。
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 启动监听,遍历 SpringApplicationRunListener 数组每个元素,并执行。
        listeners.starting();
        try {
            //创建 ApplicationArguments 对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            // 加载属性配置,包括所有的配置属性(如:application.properties 中和外部的属性配置)
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            configureIgnoreBeanInfo(environment);
            // 打印 Banner
            Banner printedBanner = printBanner(environment);
            // 创建容器
            context = createApplicationContext();

            // 异常报告器
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[]{ConfigurableApplicationContext.class}, context);
            // 准备容器,组件对象之间进行关联
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            // 初始化容器
            refreshContext(context);
            // 初始化操作之后执行,默认实现为空。
            afterRefresh(context, applicationArguments);
            // 停止时长统计
            stopWatch.stop();
            // 打印启动日志
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            // 通知监听器:容器启动完成。
            listeners.started(context);
            // 调用 ApplicationRunner 和 CommandLineRunner 的运行方法。
            callRunners(context, applicationArguments);
        } catch (Throwable ex) {
            // 异常处理
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            // 通知监听器:容器正在运行。
            listeners.running(context);
        } catch (Throwable ex) {
            // 异常处理
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

我们可以看到,在try方法的最后,会执行一个callRunners的方法,在此方法中会对实现这两个接口的实现类进行调用。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<>();
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   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);
      }
   }
}

private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
   try {
      (runner).run(args);
   } catch (Exception ex) {
      throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
   }
}

private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
   try {
      (runner).run(args.getSourceArgs());
   } catch (Exception ex) {
      throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
   }
}

通过以上代码,我们也就了解到这两个接口的实现类的执行时机了。

3. Spring Boot中的应用事件

Spring 框架在不同的情况下会触发不同的事件。它还在启动过程中触发了许多事件。我们可以使用这些事件来执行我们的代码,例如 ApplicationReadyEvent 可以用于在 Spring Boot 应用程序启动后执行代码。

如果我们不需要命令行参数,这是在应用程序启动后执行代码的最佳方式。

@Slf4j
@Component
public class RunAfterStartup {

    @EventListener(ApplicationReadyEvent.class)
    public void runAfterStartup() {
        log.info("Spring Boot中的应用事件,Yaaah, I am running........");
    }

}

输出:

2021-12-04 11:08:23.434  INFO 7824 --- [           main] c.j.spring.boot.event.RunAfterStartup    : Spring Boot中的应用事件,Yaaah, I am running........

Spring Boot 中一些最重要的事件

  • ApplicationContextInitializedEvent :在准备好 ApplicationContext 并调用ApplicationContextInitializers 之后但在加载 bean 定义之前触发
  • ApplicationPreparedEvent :加载 bean 定义后触发
  • ApplicationStartedEvent :在刷新上下文之后,但在调用命令行和应用程序运行程序之前触发
  • ApplicationReadyEvent :在调用应用程序和命令行运行程序后触发
  • ApplicationFailedEvent : 如果启动时出现异常则触发

可以创建多个 ApplicationListeners。可以使用 @Order 注释或 Ordered 接口对它们进行排序。

该顺序与其他相同类型的 ApplicationListeners 共享,但不与 ApplicationRunnersCommandLineRunners 共享。

4.方法上的@Postconstruct注解

一个方法可以用@PostConstruct 注解来标记。每当一个方法被这个注解标记时,它会在依赖注入后立即被调用。

@PostConstruct 方法链接到特定的类,因此它应该只用于特定于类的代码。每个类只能有一个带有 postConstruct 注释的方法。

@Slf4j
@Component
public class PostContructImpl {

    public PostContructImpl() {
        log.info("PostContructImpl构造器");
    }

    @PostConstruct
    public void runAfterObjectCreated() {
        log.info("PostContruct runAfterObjectCreated方法");
    }

}

输出:

2021-12-04 11:18:52.563  INFO 14928 --- [           main] c.j.s.b.postconstruct.PostContructImpl   : PostContructImpl构造器
2021-12-04 11:18:52.564  INFO 14928 --- [           main] c.j.s.b.postconstruct.PostContructImpl   : PostContruct runAfterObjectCreated方法

需要注意的是,如果类被标记为惰性,则意味着该类是在请求时创建的,之后将执行标有@postConstruct 注解的方法。

用 postConstruct 注解标记的方法可以有任何名称,但不能有任何参数。它必须是空的,不应该是静态的。

请注意,@postConstruct 注解是 Java EE 模块的一部分,它在 Java 9 中被标记为已弃用并在 Java 11 中删除。我们仍然可以通过将 java.se.ee 添加到应用程序中来使用它。

5. InitializingBean 接口

InitializingBean 的工作方式与 postConstruct 注释完全相似。我们必须实现 InitializingBean 接口,而不是使用注解。然后我们需要覆盖 void afterPropertiesSet() 方法。InitializingBean 是 org.springframework.beans.factory 包的一部分。

@Slf4j
@Component
public class InitializingBeanImpl implements InitializingBean {

    public InitializingBeanImpl() {
        log.info("InitializingBeanImpl Constructor构造器");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("InitializingBeanImpl afterPropertiesSet方法");
    }

}

输出

2021-12-04 11:23:15.843  INFO 8012 --- [           main] c.j.s.b.i.InitializingBeanImpl           : InitializingBeanImpl Constructor构造器
2021-12-04 11:23:15.843  INFO 8012 --- [           main] c.j.s.b.i.InitializingBeanImpl           : InitializingBeanImpl afterPropertiesSet方法

您一定在想如果我们同时使用 @PostConstruct 注释和 InitializingBean 会发生什么。那么在这种情况下,@PostConstruct 方法将在 InitializingBean 的 afterPropertiesSet() 方法之前被调用。

@PostConstructafterPropertiesSet 区别

  1. afterPropertiesSet,顾名思义「在属性设置之后」,调用该方法时,该 bean 的所有属性已经被 Spring 填充。如果我们在某些属性上使用 @Autowired(常规操作应该使用构造函数注入),那么 Spring 将在调用afterPropertiesSet 之前将 bean 注入这些属性。但 @PostConstruct 并没有这些属性填充限制
  2. 所以 InitializingBean.afterPropertiesSet 解决方案比使用 @PostConstruct 更安全,因为如果我们依赖尚未自动注入的 @Autowired 字段,则 @PostConstruct 方法可能会遇到 NullPointerExceptions

6.@bean注解的init属性

我们可以使用@Bean 注释中的 initMethod 属性提供一个方法。这个方法会在 bean 初始化后调用。

initMethod 中提供的方法必须是 void 并且不应该有任何参数。这种方法甚至可以是私有的。

@Slf4j
public class BeanInitMethodImpl {

    public void runAfterObjectCreated() {
        log.info("BeanInitMethodImpl runAfterObjectCreated方法,yooooooooo......... someone called me");
    }

}

@Slf4j
@SpringBootApplication
public class SpringBootRunnerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootRunnerApplication.class, args);
        log.info("应用启动...");
    }

    @Bean(initMethod = "runAfterObjectCreated")
    public BeanInitMethodImpl getFunnyBean() {
        return new BeanInitMethodImpl();
    }

}

输出:

2021-12-04 11:26:09.865  INFO 2884 --- [           main] c.j.s.b.b.BeanInitMethodImpl             : BeanInitMethodImpl runAfterObjectCreated方法,yooooooooo......... someone called me

如果同一个类有InitializingBean 实现和@Bean 注解的initMethod 属性,则InitializingBean 的afterPropertiesSet 方法会在initMethod 之前被调用。

总结

最后,有时我们可能需要组合多个选项。然后他们将按以下顺序执行,

  • Constructor
  • PostContruct method
  • afterPropertiesSet method
  • Bean init Method
  • ApplicationStartedEvent
  • ApplicationRunner Or CommandLineRunner depends on Order
  • ApplicationReadyEvent
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

企业级spring-boot案例-Spring Boot 启动时的运行方法 的相关文章

  • 敏捷教练的十种能力

    1 具备神奇的 读懂一个房间 的能力 只要走进一个房间 xff0c 就能判断出不在的过程中 xff0c 房间里发生了什么事情 xff0c 能立即读出空气中蕴含的情绪 xff0c 从而判断是否一切正常 xff1b 2 关心人本身胜过关心产品
  • Java数组之二分法查找数

    数组的二分法查找数据 使用前提 xff1a 查找的数组必须是有序的 span class token keyword public span span class token keyword class span span class to
  • MQ 队列管理器常见错误解析

    消息管理器无法连接到目标队列管理器 请确保以下事项 xff1a 在 消息管理器 队列管理器定义中所定义的端口与通道侦听器使用的端口相匹配 WebSphere MQ 队列管理器通道侦听器已启动 WebSphere MQ 队列管理器命令服务器已
  • 【简单理解】ubuntu中的sudo和su

    参考 xff1a https blog csdn net liberty12345678 article details 87686284 https cloud tencent com developer article 1721753
  • 那些女程序员们的故事

    点击上方蓝字 关注我们 xff0c 和小伙伴一起聊技术 xff01 程序媛是程序员大军中一道美丽的风景线 xff0c 今天的这篇文章就选取了一些女程序员们的故事 xff0c 希望当所有人了解了他们的经历后 xff0c 能让这个 重男轻女 的
  • 直播分享丨前沿技术讲习班:知识图谱前沿技术与应用(CIPS ATT27)

    本文转载自公众号 xff1a 智源社区助手 作为大数据时代重要的知识表示方式 xff0c 知识图谱是人工智能领域构建和应用知识的新阶段 xff0c 它能够更好地实现大规模数据的认知与推理 同时 xff0c 知识图谱和深度学习相互协作 xff
  • 图谱实战 | 京东基于时序知识图谱的问答系统

    转载公众号 DataFunSummit 分享嘉宾 xff1a 商超博士 京东硅谷研究院 研究员 编辑整理 xff1a 张存旺 北航杭州创新研究院 出品平台 xff1a DataFunTalk 导读 xff1a 本文将分享Temporal K
  • 肖仰华 | 基于知识图谱的问答系统

    本文转载自公众号知识工场 本文整理自复旦大学知识工场肖仰华教授在VLDB 2017 会议上的论文报告 xff0c 题目为 KBQA Learning Question Answering over QA Corpora and Knowle
  • 研讨会 | 知识图谱前沿技术课程暨学术研讨会(武汉大学站)

    知识图谱作为大数据时代重要的知识表示方式之一 xff0c 已经成为人工智能领域的一个重要支撑 4月 28日 xff0c 武汉大学信息集成与应用实验室 与 复旦大学知识工场实验室 联合举办 知识图谱前沿技术课程暨学术研讨会 xff0c 将结合
  • jdbc中Statement和PreparedStatement有什么区别?哪个性能更好?

    Statement和PreparedStatement的功能主要是对sql语句的执行 区别 xff08 1 xff09 Statement每执行一条sql语句就需要生成一条执行计划 xff0c 执行100条就需要100条执行计划Prepar
  • redis的特性

    redis的特性 承接上文redis入门篇 xff0c 本文具体介绍一下redis的特性 xff0c 以及与另外一个nosql数据库memcached的对比 一 redis的优点 根据上文 xff0c 我们知道redis的如下特性成为了他的
  • Ubuntu22.04安装windows字体

    找到C Windows目录 xff0c 将其中的Fonts文件夹拷贝至ubuntu中 将该文件夹放至ubuntu的 usr share fonts目录下面 xff0c 可用下列命令 span class token function sud
  • 阿里巴巴笔试题选解

    阿里巴巴笔试题选解 9月22日 xff0c 阿里巴巴北邮站 小题 xff1a 1 有三个结点 xff0c 可以构成多少种二叉树形结构 xff1f 2 一副牌52 张 去掉大小王 xff0c 从中抽取两张牌 xff0c 一红一黑的概率是多少
  • ActiveMQ与Logback日志组件SLF4J冲突导致日志不输出

    ActiveMQ与Logback中的SLF4J日志组件冲突导致日志不输出 xff0c 控制台提示 Class path contains multiple SLF4J bindings 的解决方案 近期码的时候发现logback的组件日志都
  • 腾讯2014软件开发笔试题目

    腾讯2014软件开发笔试题目 9月21日 xff0c 腾讯2014软件开发校招 简答题 广州 简答题 xff1a 1 请设计一个排队系统 xff0c 能够让每个进入队伍的用户都能看到自己在 中所处的位置和变化 队伍可能随时有人加入和退出 x
  • C++ 进程间通信详解

    一 xff0c C 43 43 常用进程间通信 管道 Pipe xff1a 管道可用于具有亲缘关系进程间的通信 xff0c 允许一个进程和另一个与它有共同祖先的进程之间进行通信 命名管道 named pipe xff1a 命名管道克服了管道
  • OTA系统包的制作和测试方法

    OTA有两种制作方案 整包升级 xff0c 以及差分包升级 整包升级 完整的升级文件aosp XXXX XXX ota 01 20 010 00 00 zip 差异包 将第一个ota整包升级包和第二个ota整包升级包 xff0c 执行 bu
  • JAVA日记之javaJDK原生简单爬虫

    java原生爬虫 指定一个种子url放入到队列中 从队列中获取某个URL 使用HTTP协议发起网络请求 在发起网络请求的过程中 xff0c 需要将域名转化成IP地址 xff0c 也就是域名解析 得到服务器的响应 xff0c 此时是二进制的输
  • idea中修改自定义maven打包参数,打成一个大的jar

    解决idea打包只会把项目本身打成一个jar的问题 加上这条指令 clean package spring boot repackage Dmaven test skip 61 true f pom xml 在maven命令单这个里会生成新
  • 虚拟机桥接后更换外部网络无法访问

    另外一个网卡也改成这样 然后关闭再打开即可

随机推荐