爲基於spring-boot的應用添加根據運行時操做系統環境來提示用戶選擇active profile的功能

spring-boot有一個根據JVM變量-Dspring.profiles.active來設置運行時的active profile的功能,可是有些時候咱們也許會不當心忘記設置這個變量,這樣在生產環境中會帶來必定的困擾,因此我想了一個辦法,來給忘記設置-Dspring.profiles.active的程序員一次「secend chance」。java

先來說一下思路:程序員

  • step0 約定好profiles的命名,「development」表明開發環境(也能夠將默認的profile設爲開發環境),「production」表明生產環境
  • step1 判斷是否設置了-Dspring.profiles.active,若是已經設置,直接跳轉step3
  • step2 判斷當前操做系統環境,若是不是Linux環境則認定爲開發環境,自動倒計時激活開發的profile;若是是Linux環境則認定爲生產環境,輸出選擇profile的控制檯信息,並等待用戶控制檯輸入進行選擇,並依據用戶選擇來激活profile
  • step3 SpringApplication.run()

代碼以下:spring

spring-boot配置文件(使用了默認profile做爲開發環境):apache

spring:
  application:
    name: comchangyoueurekaserver #注意命名要符合RFC 2396,不然會影響服務發現 詳見https://stackoverflow.com/questions/37062828/spring-cloud-brixton-rc2-eureka-feign-or-rest-template-configuration-not-wor

server:
  port: 8001

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}

---
spring:
  profiles: production
  application:
    name: comchangyoueurekaserver

server:
  port: 8001

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}
複製代碼

BootStarter封裝了step1-step3的邏輯:tomcat

import org.apache.commons.lang3.StringUtils;

import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Pattern;

public class BootStarter {

    //用於後續Spring Boot操做的回調
    public interface Callback {
        void bootRun();
    }

    private boolean enableAutomaticallyStart = true;
    private int automaticallyStartDelay = 10;

    public boolean isEnableAutomaticallyStart() {
        return enableAutomaticallyStart;
    }

    public void setEnableAutomaticallyStart(boolean enableAutomaticallyStart) {
        this.enableAutomaticallyStart = enableAutomaticallyStart;
    }

    public int getAutomaticallyStartDelay() {
        return automaticallyStartDelay;
    }

    public void setAutomaticallyStartDelay(int automaticallyStartDelay) {
        this.automaticallyStartDelay = automaticallyStartDelay;
    }

    public void startup(boolean enableAutomaticallyStart, int automaticallyStartDelay, Callback callback) {
        if (StringUtils.isBlank(System.getProperty("spring.profiles.active"))) { //若是沒有經過參數spring.profiles.active設置active profile則讓用戶在控制檯本身選擇
            System.out.println("***Please choose active profile:***\n\tp: production\n\td: development");

            final boolean[] started = {false};
            Timer timer = new Timer();
            if (enableAutomaticallyStart && System.getProperty("os.name").lastIndexOf("Linux") == -1) { //若是當前操做系統環境爲非Linux環境(通常爲開發環境)則automaticallyStartDelay秒後自動設置爲開發環境
                System.out.printf("\nSystem will automatically select 'd' in %d seconds.\n", automaticallyStartDelay);
                final int[] count = {automaticallyStartDelay};
                timer.scheduleAtFixedRate(new TimerTask() {
                    @Override
                    public void run() {
                        if (count[0]-- == 0) {
                            timer.cancel();
                            started[0] = true;

                            System.setProperty("spring.profiles.active", "development");
                            callback.bootRun();
                        }
                    }
                }, 0, 1000);
            }

            Scanner scanner = new Scanner(System.in);
            Pattern pattern = Pattern.compile("^p|d$");
            //若是是Linux系統(通常爲生產環境)則強制等待用戶輸入(通常是忘記設置spring.profiles.active了,這等於給了設置active profile的"second chance")
            while (scanner.hasNextLine()) {
                if (started[0]) {
                    break;
                }
                String line = scanner.nextLine();
                if (!pattern.matcher(line).find()) {
                    System.out.println("INVALID INPUT!");
                } else {
                    timer.cancel();
                    System.setProperty("spring.profiles.active", line.equals("d") ? "development" : "production");
                    callback.bootRun();
                    break;
                }
            }
        } else { //若是已經經過參數spring.profiles.active設置了active profile直接啓動
            callback.bootRun();
        }
    }

    public void startup(Callback callback) {
        startup(this.enableAutomaticallyStart, this.automaticallyStartDelay, callback);
    }
}

複製代碼

main():安全

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.context.ApplicationContext;

@EnableEurekaServer
@SpringBootApplication
public class App {

    private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

    public static void main(String[] args) {
        new BootStarter().startup(() -> {
            ApplicationContext applicationContext = SpringApplication.run(App.class, args);
            for (String activeProfile : applicationContext.getEnvironment().getActiveProfiles()) {
                LOGGER.warn("***Running with profile: {}***", activeProfile);
            }
        });
    }
}
複製代碼

運行效果(開發環境Mac OS): bash

擴展: 其實在這裏咱們還能夠發散一下思惟,基於spring-boot的應用比起傳統spring應用的一大優點是本身能夠掌控main()方法,有了這一點,咱們是能玩出不少花樣來的,思路不要被侷限在tomcat時代了。app

main法在手,天下我有。ide


2017-1-22更新:增長了線程安全的處理spring-boot

import org.apache.commons.lang3.StringUtils;

import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Pattern;

public class BootStarter {

    private volatile boolean started;

    //用於後續Spring Boot操做的回調
    public interface Callback {
        void bootRun();
    }

    private boolean enableAutomaticallyStart = true;
    private int automaticallyStartDelay = 3;

    public boolean isEnableAutomaticallyStart() {
        return enableAutomaticallyStart;
    }

    public void setEnableAutomaticallyStart(boolean enableAutomaticallyStart) {
        this.enableAutomaticallyStart = enableAutomaticallyStart;
    }

    public int getAutomaticallyStartDelay() {
        return automaticallyStartDelay;
    }

    public void setAutomaticallyStartDelay(int automaticallyStartDelay) {
        this.automaticallyStartDelay = automaticallyStartDelay;
    }

    public void startup(boolean enableAutomaticallyStart, int automaticallyStartDelay, Callback callback) {
        if (StringUtils.isBlank(System.getProperty("spring.profiles.active"))) { //若是沒有經過參數spring.profiles.active設置active profile則讓用戶在控制檯本身選擇
            System.out.println("***Please choose active profile:***\n\tp: production\n\td: development");

            Timer timer = new Timer();
            if (enableAutomaticallyStart && System.getProperty("os.name").lastIndexOf("Linux") == -1) { //若是當前操做系統環境爲非Linux環境(通常爲開發環境)則automaticallyStartDelay秒後自動設置爲開發環境
                System.out.printf("\nSystem will automatically select 'd' in %d seconds.\n", automaticallyStartDelay);
                timer.scheduleAtFixedRate(new TimerTask() {

                    private ThreadLocal<Integer> countDown = ThreadLocal.withInitial(() -> automaticallyStartDelay);

                    @Override
                    public void run() {
                        if (countDown.get() == 0) {
                            timer.cancel();
                            started = true;

                            System.setProperty("spring.profiles.active", "development");
                            callback.bootRun();
                        }
                        countDown.set(countDown.get() - 1);
                    }
                }, 0, 1000);
            }

            Scanner scanner = new Scanner(System.in);
            Pattern pattern = Pattern.compile("^p|d$");
            //若是是Linux系統(通常爲生產環境)則強制等待用戶輸入(通常是忘記設置spring.profiles.active了,這等於給了設置active profile的"second chance")
            while (scanner.hasNextLine()) {
                if (started) {
                    break;
                }
                String line = scanner.nextLine();
                if (!pattern.matcher(line).find()) {
                    System.out.println("INVALID INPUT!");
                } else {
                    timer.cancel();
                    System.setProperty("spring.profiles.active", line.equals("d") ? "development" : "production");
                    callback.bootRun();
                    break;
                }
            }
        } else { //若是已經經過參數spring.profiles.active設置了active profile直接啓動
            callback.bootRun();
        }
    }

    public void startup(Callback callback) {
        startup(this.enableAutomaticallyStart, this.automaticallyStartDelay, callback);
    }
}

複製代碼

2017-01-25更新:

補上了 try-with-resource

import org.apache.commons.lang3.StringUtils;

import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Pattern;

public class BootStarter {

    private volatile boolean started;

    //用於後續Spring Boot操做的回調
    public interface Callback {
        void bootRun();
    }

    private boolean enableAutomaticallyStart = true;
    private int automaticallyStartDelay = 3;

    public boolean isEnableAutomaticallyStart() {
        return enableAutomaticallyStart;
    }

    public void setEnableAutomaticallyStart(boolean enableAutomaticallyStart) {
        this.enableAutomaticallyStart = enableAutomaticallyStart;
    }

    public int getAutomaticallyStartDelay() {
        return automaticallyStartDelay;
    }

    public void setAutomaticallyStartDelay(int automaticallyStartDelay) {
        this.automaticallyStartDelay = automaticallyStartDelay;
    }

    public void startup(boolean enableAutomaticallyStart, int automaticallyStartDelay, Callback callback) {
        if (StringUtils.isBlank(System.getProperty("spring.profiles.active"))) { //若是沒有經過參數spring.profiles.active設置active profile則讓用戶在控制檯本身選擇
            System.out.println("***Please choose active profile:***\n\tp: production\n\td: development");

            Timer timer = new Timer();
            if (enableAutomaticallyStart && System.getProperty("os.name").lastIndexOf("Linux") == -1) { //若是當前操做系統環境爲非Linux環境(通常爲開發環境)則automaticallyStartDelay秒後自動設置爲開發環境
                System.out.printf("\nSystem will automatically select 'd' in %d seconds.\n", automaticallyStartDelay);
                timer.scheduleAtFixedRate(new TimerTask() {

                    private ThreadLocal<Integer> countDown = ThreadLocal.withInitial(() -> automaticallyStartDelay);

                    @Override
                    public void run() {
                        if (countDown.get() == 0) {
                            timer.cancel();
                            started = true;

                            System.setProperty("spring.profiles.active", "development");
                            callback.bootRun();
                        }
                        countDown.set(countDown.get() - 1);
                    }
                }, 0, 1000);
            }

            try (Scanner scanner = new Scanner(System.in)) {
                Pattern pattern = Pattern.compile("^p|d$");
                //若是是Linux系統(通常爲生產環境)則強制等待用戶輸入(通常是忘記設置spring.profiles.active了,這等於給了設置active profile的"second chance")
                while (scanner.hasNextLine()) {
                    if (started) {
                        break;
                    }
                    String line = scanner.nextLine();
                    if (!pattern.matcher(line).find()) {
                        System.out.println("INVALID INPUT!");
                    } else {
                        timer.cancel();
                        System.setProperty("spring.profiles.active", line.equals("d") ? "development" : "production");
                        callback.bootRun();
                        break;
                    }
                }
            }
        } else { //若是已經經過參數spring.profiles.active設置了active profile直接啓動
            callback.bootRun();
        }
    }

    public void startup(Callback callback) {
        startup(this.enableAutomaticallyStart, this.automaticallyStartDelay, callback);
    }
}

複製代碼
相關文章
相關標籤/搜索