转载

springboot入门08 – 创建非web项目

概述

从开始使用SpringBoot到现在,一直都是在用SpringBoot开发web服务(API服务)。直到前段时间,需要帮其他组的同事写一个非web的简单服务时,才想到Springboot是不是也支持非web项目。

答案是肯定的:spring诞生之初就不是为web项目定制的,springBoot无非是在spring核心项目的基础上添加了一些方便开发者使用的组件,所以使用springboot开发非web项目也是可行的。

首先我们要弄明白常用的web项目和非web项目的区别在哪儿?私以为是服务启动和执行逻辑触发的方式:

  • web项目需要依赖web容器来启动,通过http请求来触发相关的服务;
  • 非web项目则不需要依赖web容器来启动,它可以是自启动的;
  • 非web项的服务通常是主动触发的或者通过非http的方式被动触发的。

接下来详细介绍下如何使用springboot构建非web项目。

依赖

创建web项目通常需要使用的依赖是 spring-boot-starter-web

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

这个依赖间接引用了tomcat,spring-webmvc,spring-context,json-starter等依赖。这些在非web项目里基本上都用不到,非web项目可以直接使用 spring-boot-starter

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

spring-boot-starter间接引用了spring-core、yaml、auto-configure等依赖,已经足够用来创建一个普通的spring项目了。

如果有特殊情况(说实话,我强烈怀疑)非要使用 spring-boot-starter-web 依赖来构建非web项目也不是不行,只需要加一些配置来避免启动web容器就行:

spring:
  main:
    web-application-type: none

或者在启动类中添加web配置并设置为 NONE

@SpringBootApplication
public class MyApplication {
 
    public static void main(String[] args) {
        new SpringApplicationBuilder()
                .web(NONE)
                .main(MyApplication.class)
                .build(args);
    }
 
}

启动

springboot非web项目的启动类定义和web项目并无不同。如下:

@SpringBootApplication
public class MyApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
 
}

但是执行启动类中的main方法以后呢?如果是web项目,在启动后等待HTTP请求调用就行了。不过这里是非web项目,我们得想办法执行我们定义好的业务逻辑。接下来按常见的业务逻辑特征分别介绍下。

1. 任务需要定时执行

这种情况需要配置定时任务。SpringBoot对定时任务的支持还算可以。前段时间我写过关于《 SpringBoot定时任务配置 》这样的文章,有需要可以参考下,这里就不重复介绍了。

2. 在某个类的实例注入后就立即执行

这种场景说实话不多见。通常是需要该类实现 InitializingBean 接口,并在 afterPropertiesSet 方法中实现相关的逻辑。如下:

@Service
public class MyService implements InitializingBean {
 
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("--------->>> start from my service");
    }
}

还有一种方式是使用java提供的 @ PostConstruct 注解:

@Service
public class MyService {
 
    @PostConstruct
    public void init() {
        System.out.println("--------->>> start from my service init method");
    }
 
}

建议最好使用前者。

3. 使用SpringBoot的提供的主动执行接口

springboot提供了两个接口 ApplicationRunner CommandLineRunner 来支持主动执行业务逻辑。

比如我们可以直接实现 ApplicationRunner 接口,并在 run 方法中添加业务逻辑:

@Component
public class MyRunner1 implements ApplicationRunner {
 
    @Autowired
    private MyService myService;
 
    @Override
    public void run(ApplicationArguments args) {
        System.out.println("--------->>> my runner1 has started");
        System.out.println(myService.getId());
    }
 
}

CommandLineRunner 接口和 ApplicationRunner 接口的差别不大。从执行时机还是调用过程上来着,这两者几乎都是一样的。这唯一的区别在于他们提供的 run 方法的args参数类型上:

  • ApplicationRunner 的args参数是ApplicationArguments类型,对原始参数做了一层封装;
  • CommandLineRunner 的args参数是字符串类型,取的是启动类收到的原始参数。

这种差别源于SpringBoot对二者的定位上:

ApplicationRunner 适用于启动即执行的场景,只需要读取一次参数信息即可。它的参数通常是“option=value”这种结构的,如:“–foo=bar –foo=baz” 。 ApplicationArguments 中封装了一些对这种参数进行处理的方法,以便开发使用。

CommandLineRunner 从名字上看就是用来做命令行交互用的,所以它这里直接取了原始参数,看一个使用示例:

@Component
public class MyRunner3 implements CommandLineRunner {
 
    @Override
    public void run(String... args) throws Exception {
        System.out.println("Please enter your name:");
        try (Scanner scanner = new Scanner(System.in)) {
            String name = scanner.nextLine();
            System.out.println("Hello " + name + "!");
            System.out.println("Bye!");
        }
    }
 
}

如果在执行命令行交互之前也需要读取解析传入的参数,那么这里的 MyRunner3 类完全也可以实现 ApplicationRunner 接口。二者的差别几乎可以忽略。

在一个应用里面可以有多个 ApplicationRunner CommandLineRunner 的实现。要调整两者的实现类之间的执行顺序可以使用 @ Order 注解。

就这样。这一节虽然介绍的是如何创建启动非web项目,但是在web项目中也会有主动触发一些执行逻辑的需要,上面介绍的这些方案也是完全可用的。

示例代码已上传到了 GitHub/Zhyea ,有需要可自行查阅。

End!

原文  https://www.zhyea.com/2019/12/11/springboot-base-08-create-non-web-app.html
正文到此结束
Loading...