Springboot2.x yaml文件整合logback+druid+mysql及多個環境管理

  1. 新建項目,首先配置pom.xml,有其他需要可以自行加入,這些都是我寫項目必備的
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.2.RELEASE</version>
            <relativePath/>
        </parent>
        
        <!-- 通過maven的package插件打包成的文件名爲name.version.packaging -->
        <groupId>com</groupId>
        <artifactId>test</artifactId>
        <version>1.0.0</version>
        <packaging>war</packaging>
        <name>test</name>
        
        <!-- Java版本 -->
        <properties>
            <java.version>13</java.version>
        </properties>
    
        <dependencies>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.1</version>
            </dependency>
    
            <!-- mysql配置 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.11</version>
            </dependency>
    
            <!-- 使用lombok配置 -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <!-- 內置tomcat配置 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <scope>provided</scope>
            </dependency>
    
            <!-- 使用fastjson解析配置 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.62</version>
            </dependency>
    
            <!-- druid配置,打包會提示失效,但功能不影響,目前沒找到原因 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.21</version>
            </dependency>
    
            <!-- 自定義配置文件,並通過@Value("${attributeName}")註解獲取值配置 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
    
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>

     

  2. 在src/main/resource下創建application.yaml文件,並配置

    spring:
      # 環境配置,會在加載完當前yaml文件後,根據輸入的名稱加載application-環境名稱.yaml文件,現在會加載application-dev.yaml文件
      profiles:
        active: dev
      # 數據源配置,使用當前版本druid不需要註明Driver,url在環境配置中區分配置
      datasource:
        username: root
        password: 123456
        # 聲明使用druid作爲連接池
        type: com.alibaba.druid.pool.DruidDataSource
        # druid配置
        druid:
          # 初始化時建立物理連接的個數。初始化發生在顯示調用 init 方法,或者第一次 getConnection 時
          initialSize: 5
          # 最小連接池數量
          minIdle: 5
          # 最大連接池數量
          maxActive: 10
          # 獲取連接時最大等待時間,單位毫秒,配置之後,缺省啓用公平鎖,併發效率會有所下降,如果需要可以通過配置useUnfairLock屬性爲true使用非公平鎖
          maxWait: 60000
          # 線程檢測連接的間隔時間,如果連接空閒時間超出則關閉物理連接
          timeBetweenEvictionRunsMillis: 60000
          # 生成sql日誌間隔時間
          timeBetweenLogStatsMillis: 300000
          # 連接保持空閒而不被驅逐的最小時間
          minEvictableIdleTimeMillis: 300000
          # 檢測連接是否有效,oracle寫成 SELECT 1 FROM DUAL,我用的mysql
          validationQuery: SELECT 1
          # 申請連接的時候檢測,如果空閒時間大於線程檢測連接的間隔時間,檢測連接是否有效,不影響性能,並且保證安全性
          testWhileIdle: true
          # 申請連接時檢測連接是否有效,做了這個配置會降低性能
          testOnBorrow: false
          # 歸還連接時檢測連接是否有效,做了這個配置會降低性能
          testOnReturn: false
          # 是否自動回收超時連接
          removeAbandoned: true
          # 超時時間
          remove-abandoned-timeout: 180
          # 監控配置,訪問地址http://localhost:8080/druid/login.html
          web-stat-filter:
            # 是否開啓WebStatFilter
            enabled: true
            # 需要攔截的url
            url-pattern: /*
            # 排除靜態資源的請求
            exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
          # 展示統計信息。
          stat-view-servlet:
            #是否啓用StatViewServlet
            enabled: true
            # 需要攔截的 url
            url-pattern: /druid/*
            # 是否允許清空統計數據
            reset-enable: true
            login-username: druid
            login-password: druid
            # 白名單,只能寫ip
            allow: 127.0.0.1
            # 黑名單,優先級高於白名單
            # deny: xxx.xxx.xxx.xxx
    # mybatis配置
    mybatis:
      # mapper存放路徑
      type-aliases-package: com.demo.bean.mapper
      configuration:
        # 將下劃線命名轉化爲駝峯命名
        map-underscore-to-camel-case: true
    # 自定義的配置文件,會報黃線不知道怎麼去除,但不影響使用
    my-properties:
      # logback相關配置
      logback:
        # 項目名稱,用於log文件的命名
        project-name: test
        # 文件空間
        file-size: 100MB
        # 最大存儲歷史文件數量
        max-history: 30
        # 最大佔用空間
        total-size-cap: 1GB
        # 日誌文件的格式化方式,最後顯示的格式如下
        # [2020-01-07 11:05:55.679]-[INFO]-[main]-[92]-[o.s.b.w.e.tomcat.TomcatWebServer]-[Tomcat initialized with port(s): 8080 (http)]
        file-log-pattern: "[%d{yyyy-MM-dd HH:mm:ss.SSS}]-[%level]-[%thread]-[%L]-[%logger{36}]-[%msg]%n"

     

  3. 分別創建所需環境文件並配置,我用application-dev.yaml來舉例
    # 生產模式
    spring:
      datasource:
        <!-- 生產模式使用的mysql路徑,分別配置第一因爲數據庫測試時與生產時使用的不同,第二因爲生產模式訪問數據庫用內網地址,可以修改mysql的用戶權限限制其他ip訪問,速度快更安全-->
        url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=CTT
    # 區分環境的自定義配置文件
    my-properties:
      # 生產環境下的文件路徑
      file-path: /home/user/document/document-dev
      logback:
        # xml文件不能直接用&符號,需要轉義,所以這裏只給出url,參數在logback.xml中進行拼接
        mysql-url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/test

     

  4. 在src/main/resource下創建logback-spring.xml文件,並配置,如果創建成別的名稱,則不能使用spring配置文件中的屬性,因爲會先於spring的配置文件加載
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- debug="true"可以將log內容輸出在catalina.out中 -->
    <configuration debug="true">
        <!-- 引入控制檯輸出顏色插件,在開發時候看起來比較舒服 -->
        <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
        <!-- 引用spring配置文件的屬性 -->
        <springProperty scope="context" name="MYSQL_URL" source="my-properties.logback.mysql-url"/>
        <springProperty scope="context" name="MYSQL_USERNAME" source="spring.datasource.username"/>
        <springProperty scope="context" name="MYSQL_PASSWORD" source="spring.datasource.password"/>
        <springProperty scope="context" name="PROJECT" source="my-properties.logback.project-name"/>
        <springProperty scope="context" name="FILE_SIZE" source="my-properties.logback.file-size"/>
        <springProperty scope="context" name="MAX_HISTORY" source="my-properties.logback.max-history"/>
        <springProperty scope="context" name="TOTAL_SIZE_CAP" source="my-properties.logback.total-size-cap"/>
        <property name="ROOT" value="logs/${PROJECT}/"/>
        <springProperty scope="context" name="FILE_LOG_PATTERN" source="my-properties.logback.file-log-pattern"/>
        <!-- 控制檯輸出內容格式化,INFO綠色,WAR黃色,ERROR紅色 -->
        <property name="CONSOLE_LOG_PATTERN"
                  value="%clr([%d{yyyy-MM-dd HH:mm:ss.SSS}])-%clr([%level])-%clr([%thread])-%clr([%L])-%clr([%logger{36}])-%clr([%msg]) %n"/>
        <!-- 輸出到控制檯 -->
        <!-- 控制檯輸出配置 -->
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            </encoder>
        </appender>
        <!-- 輸出到文件配置,文件生成在logs/test/test.log-->
        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${ROOT}/${PROJECT}.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <fileNamePattern>${ROOT}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <maxFileSize>${FILE_SIZE}</maxFileSize>
                <maxHistory>${MAX_HISTORY}</maxHistory>
                <totalSizeCap>${TOTAL_SIZE_CAP}}</totalSizeCap>
            </rollingPolicy>
            <encoder charset="utf-8">
                <pattern>${FILE_LOG_PATTERN}</pattern>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>INFO</level>
            </filter>
        </appender>
        <!-- ERROR級別輸出到文件配置,文件生成在logs/test/test-error.log-->
        <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${ROOT}/${PROJECT}-error.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <fileNamePattern>${ROOT}/%d{yyyy-MM-dd}.%i-error.log</fileNamePattern>
                <maxFileSize>${FILE_SIZE}</maxFileSize>
                <maxHistory>${MAX_HISTORY}</maxHistory>
                <totalSizeCap>${TOTAL_SIZE_CAP}}</totalSizeCap>
            </rollingPolicy>
            <encoder charset="utf-8">
                <pattern>${FILE_LOG_PATTERN}</pattern>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
        <!-- 輸出到數據庫配置 -->
        <appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
            <connectionSource
                    class="ch.qos.logback.core.db.DriverManagerConnectionSource">
                    <driverClass>com.mysql.cj.jdbc.Driver</driverClass>
                    <url>
                        ${MYSQL_URL}?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false&amp;autoReconnect=true&amp;failOverReadOnly=false&amp;serverTimezone=CTT
                    </url>
                    <user>${MYSQL_USERNAME}</user>
                    <password>${MYSQL_PASSWORD}</password>
            </connectionSource>
            <!-- WARN級別以上的保存至數據庫 -->
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>WARN</level>
            </filter>
        </appender>
        <root level="INFO">
            <appender-ref ref="STDOUT"/>
            <appender-ref ref="FILE"/>
            <appender-ref ref="ERROR_FILE"/>
            <appender-ref ref="DB"/>
        </root>
    </configuration>

     

  5. 如果需要輸出到數據庫,需要在數據庫創建3張表,創建語句如下,我用的是utf8mb4編碼格式,可以支持emoji,比較方便
    CREATE TABLE `logging_event` (
      `timestmp` bigint(20) NOT NULL,
      `formatted_message` text COLLATE utf8mb4_bin NOT NULL,
      `logger_name` varchar(254) COLLATE utf8mb4_bin NOT NULL,
      `level_string` varchar(254) COLLATE utf8mb4_bin NOT NULL,
      `thread_name` varchar(254) COLLATE utf8mb4_bin DEFAULT NULL,
      `reference_flag` smallint(6) DEFAULT NULL,
      `arg0` varchar(254) COLLATE utf8mb4_bin DEFAULT NULL,
      `arg1` varchar(254) COLLATE utf8mb4_bin DEFAULT NULL,
      `arg2` varchar(254) COLLATE utf8mb4_bin DEFAULT NULL,
      `arg3` varchar(254) COLLATE utf8mb4_bin DEFAULT NULL,
      `caller_filename` varchar(254) COLLATE utf8mb4_bin NOT NULL,
      `caller_class` varchar(254) COLLATE utf8mb4_bin NOT NULL,
      `caller_method` varchar(254) COLLATE utf8mb4_bin NOT NULL,
      `caller_line` char(4) COLLATE utf8mb4_bin NOT NULL,
      `event_id` bigint(20) NOT NULL AUTO_INCREMENT,
      PRIMARY KEY (`event_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=460 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
    
    
    CREATE TABLE `logging_event_property` (
      `event_id` bigint(20) NOT NULL,
      `mapped_key` varchar(100) COLLATE utf8mb4_bin NOT NULL,
      `mapped_value` text COLLATE utf8mb4_bin,
      PRIMARY KEY (`event_id`,`mapped_key`),
      CONSTRAINT `logging_event_property_ibfk_1` FOREIGN KEY (`event_id`) REFERENCES `logging_event` (`event_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
    
    
    CREATE TABLE `logging_event_exception` (
      `event_id` bigint(20) NOT NULL,
      `i` smallint(6) NOT NULL,
      `trace_line` varchar(254) COLLATE utf8mb4_bin NOT NULL,
      PRIMARY KEY (`event_id`,`i`),
      CONSTRAINT `logging_event_exception_ibfk_1` FOREIGN KEY (`event_id`) REFERENCES `logging_event` (`event_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

     

  6. 需要的話,在src/main/com/test/controller下創建DruidStatController用以使用請求方式獲取健康檢查信息,訪問路徑爲http://localhost:8080/stat,返回的是json數據
    @RestController
    @Slf4j
    public class DruidStatController {
    	
    	@RequestMapping("stat")
    	private Object method() {
    		log.info("德魯伊健康檢查");
    		return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();
    	}
    }

     

  7. 在項目中引用配置文件的屬性
    @Value("${my-properties.file-path}")
    private String filePath;

     

  8. 更換環境只需要更改application.yaml中的spring-profiles-active的值
  9. 自定義異常枚舉類
    public enum BaseEnum {
        UNKNOWN_ERROR(501,"未知錯誤");
        @Getter
        private Integer statusCode;
        @Getter
        private String errMsg;
        BaseEnum(Integer statusCode, String errMsg) {
            this.statusCode = statusCode;
            this.errMsg = errMsg;
        }
    }

     

  10. 自定義響應格式類
    public class BaseResponse {
        @Getter
        @Setter
        private Integer statusCode;
        @Getter
        @Setter
        private Object responseData;
        @Getter
        @Setter
        private String errMsg;
        public BaseResponse(){
            suc("");
        }
        public BaseResponse(BaseEnum baseEnum){
            fail(baseEnum.getStatusCode(),baseEnum.getErrMsg());
        }
        public BaseResponse(Object responseData){
            suc(responseData);
        }
        public BaseResponse(Integer statusCode,String errMsg){
            fail(statusCode,errMsg);
        }
        private void suc(Object responseData){
            this.statusCode = 200;
            this.responseData = responseData;
            this.errMsg = "";
        }
        private void fail(Integer statusCode,String errMsg){
            this.statusCode = statusCode;
            this.errMsg = errMsg;
            this.responseData = "";
        }
    }

     

  11. 自定義線程池進行異步處理
    public class VisibleAsync extends ThreadPoolTaskExecutor {
        private void showThreadPoolInfo(String prefix){
            final ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
        }
    }
    
    
    @Component
    public class BaseAsync {
        ThreadPoolTaskExecutor executor;
        public ThreadPoolTaskExecutor async() {
            if(executor==null){
                executor = new VisibleAsync();
                //配置核心線程數
                executor.setCorePoolSize(5);
                //配置最大線程數
                executor.setMaxPoolSize(100);
                //配置隊列大小
                executor.setQueueCapacity(99999);
                //配置線程池中的線程的名稱前綴
                executor.setThreadNamePrefix("線程池");
                // rejection-policy:當pool已經達到max size的時候,如何處理新任務
                // CALLER_RUNS:不在新線程中執行任務,而是有調用者所在的線程來執行
                executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
                //執行初始化
                executor.initialize();
            }
            return executor;
        }
    }

     

  12. 自定義異常類
    public class BaseException extends RuntimeException{
        @Setter
        @Getter
        private Integer statusCode;
        @Setter
        @Getter
        private String errMsg;
        @Setter
        @Getter
        private String className;
        @Setter
        @Getter
        private String methodName;
        public BaseException(BaseEnum baseEnum,String className,String methodName){
            this(baseEnum.getStatusCode(),baseEnum.getErrMsg(),className,methodName);
        }
        public BaseException(Integer statusCode,String errMsg,String className,String methodName){
            this.statusCode = statusCode;
            this.errMsg = errMsg;
            this.className = className;
            this.methodName = methodName;
        }
    
        @Override
        public String toString() {
            return "[" + statusCode + "][" + errMsg +"][" + className + "][" + methodName + "]";
        }
    }

     

  13. 全局異常處理程序,並在異常發生時輸出日誌
    @Slf4j
    @ControllerAdvice
    public class MyExceptionHandler {
    
        // 處理綁定異常
        @ResponseBody
        @ExceptionHandler(value= BindException.class)
        public Object MethodArgumentNotValidHandler(BindException ex) {
            log.error(ex.getMessage());
            return new BaseResponse(500,ex.getMessage());
        }
    
        // 處理自定義異常
        @ResponseBody
        @ExceptionHandler(value = BaseException.class)
        public BaseResponse myExceptionHandler(BaseException ex){
            log.warn(String.valueOf(ex));
            return new BaseResponse(ex.getStatusCode(),ex.getErrMsg());
        }
    
        // 處理其他異常
        @ResponseBody
        @ExceptionHandler(value = Exception.class)
        public BaseResponse exceptionHandler(Exception ex){
            log.error(ex.getMessage());
            return new BaseResponse(500,ex.getMessage());
        }
    
    }
  14. 跨域配置
    @Configuration
    public class CorsConfig {
        private CorsConfiguration buildConfig() {
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            corsConfiguration.addAllowedOrigin("*");
            corsConfiguration.addAllowedHeader("*");
            corsConfiguration.addAllowedMethod("*");
            return corsConfiguration;
        }
    
        @Bean
        public CorsFilter corsFilter() {
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", buildConfig()); // 4
            return new CorsFilter(source);
        }
    }

     

  15. 主動拋出自定義異常代碼,基本全局都不用try-catch
    throw new BaseException(BaseEnum.SHARE_ERROR,"className","methodName");

     

  16. 自定義基礎控制器類,統一異步處理請求,並輸出日誌
    @Slf4j
    @Component
    public class BaseController {
    	private final BaseAsync baseAsync;
    	
    	public BaseController(BaseAsync baseAsync) {
    		this.baseAsync = baseAsync;
    	}
    	
    	public BaseResponse getResponse(BaseResponse callback,Object param,String info) throws ExecutionException, InterruptedException {
    		log.info("請求]-["+info+"]-[{}",param);
    		return baseAsync.async().submit(()->callback).get();
    	}
    }

    其他控制器代碼

    @RestController
    @Slf4j
    public class TestController {
        private final BaseController baseController;
        
        public TestController(BaseController baseController) {
            this.baseController = baseController;
        }
        
        @RequestMapping("test")
        private BaseResponse testParameter(TestRequestVo testRequestVo) throws ExecutionException, InterruptedException {
            return baseController.getResponse(new BaseResponse(testRequestVo),testRequestVo,"測試接口");
        }
    }

     

  17. 網絡請求工具類
    public class HttpUtil {
        public static JSONObject get(String domain) throws IOException {
            StringBuilder stringBuilder = new StringBuilder();
            URL url = new URL(domain);
            URLConnection urlConnection = url.openConnection();
            urlConnection.connect();
            @Cleanup BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            String line;
            while ((line = bufferedReader.readLine()) != null)
                stringBuilder.append(line);
            return JSON.parseObject(stringBuilder.toString());
        }
    
        public static JSONObject post(String domain, JSONObject jsonParam) throws IOException {
            StringBuilder stringBuilder = new StringBuilder();
            URL url = new URL(domain);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setDoInput(true);
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setRequestMethod("POST");
            httpURLConnection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
            @Cleanup DataOutputStream dataOutputStream = new DataOutputStream(httpURLConnection.getOutputStream());
            dataOutputStream.write(jsonParam.toString().getBytes());
            dataOutputStream.flush();
            if (HttpURLConnection.HTTP_OK == httpURLConnection.getResponseCode()) {
                @Cleanup BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), StandardCharsets.UTF_8));
                String line;
                while ((line = bufferedReader.readLine()) != null)
                    stringBuilder.append(line);
                return JSONObject.parseObject(stringBuilder.toString());
            } else {
                throw new BaseException(httpURLConnection.getResponseCode(),httpURLConnection.getResponseMessage(),"HttpUtils","post");
            }
        }
    }

     

  18. 代碼要儘可能的考慮周全,錯誤碼細分,儘量排除邏輯錯誤,有任何問題歡迎指正
發佈了23 篇原創文章 · 獲贊 0 · 訪問量 2613
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章