摘要:我們可不可以提供一個公共的入口進行統一的異常處理呢當然可以。一般我們可以在地址上帶上版本號,也可以在參數上帶上版本號,還可以再里帶上版本號,這里我們在地址上帶上版本號,大致的地址如,其中,即代表的是版本號。
上一篇帶領大家初步了解了如何使用 Spring Boot 搭建框架,通過 Spring Boot 和傳統的 SpringMVC 架構的對比,我們清晰地發現 Spring Boot 的好處,它使我們的代碼更加簡單,結構更加清晰。
從這一篇開始,我將帶領大家更加深入的認識 Spring Boot,將 Spring Boot 涉及到東西進行拆解,從而了解 Spring Boot 的方方面面。學完本文后,讀者可以基于 Spring Boot 搭建更加復雜的系統框架。
我們知道,Spring Boot 是一個大容器,它將很多第三方框架都進行了集成,我們在實際項目中用到哪個模塊,再引入哪個模塊。比如我們項目中的持久化框架用 MyBatis,則在 pom.xml 添加如下依賴:
org.mybatis.spring.boot mybatis-spring-boot-starter 1.1.1 mysql mysql-connector-java 5.1.40
yaml/properties 文件
我們知道整個 Spring Boot 項目只有一個配置文件,那就是 application.yml,Spring Boot 在啟動時,就會從 application.yml 中讀取配置信息,并加載到內存中。上一篇我們只是粗略的列舉了幾個配置項,其實 Spring Boot 的配置項是很多的,本文我們將學習在實際項目中常用的配置項(注:為了方便說明,配置項均以 properties 文件的格式寫出,后續的實際配置都會寫成 yaml 格式)。
下面是我參與的某個項目的 application.yml 配置文件內容:
server: port: 8080 context-path: /api tomcat: max-threads: 1000 min-spare-threads: 50 connection-timeout: 5000 spring: profiles: active: dev http: multipart: maxFileSize: -1 datasource: url: jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=UTF-8&useSSL=true username: root password: root driverClassName: com.mysql.jdbc.Driver jpa: database: MYSQL showSql: true hibernate: namingStrategy: org.hibernate.cfg.ImprovedNamingStrategy properties: hibernate: dialect: org.hibernate.dialect.MySQL5Dialect mybatis: configuration: #配置項:開啟下劃線到駝峰的自動轉換. 作用:將數據庫字段根據駝峰規則自動注入到對象屬性。 map-underscore-to-camel-case: true
以上列舉了常用的配置項,所有配置項信息都可以在官網中找到,本課程就不一一列舉了。
多環境配置
在一個企業級系統中,我們可能會遇到這樣一個問題:開發時使用開發環境,測試時使用測試環境,上線時使用生產環境。每個環境的配置都可能不一樣,比如開發環境的數據庫是本地地址,而測試環境的數據庫是測試地址。那我們在打包的時候如何生成不同環境的包呢?
這里的解決方案有很多:
1.每次編譯之前手動把所有配置信息修改成當前運行的環境信息。這種方式導致每次都需要修改,相當麻煩,也容易出錯。
2.利用 Maven,在 pom.xml 里配置多個環境,每次編譯之前將 settings.xml 里面修改成當前要編譯的環境 ID。這種方式會事先設置好所有環境,缺點就是每次也需要手動指定環境,如果環境指定錯誤,發布時是不知道的。
3.第三種方案就是本文重點介紹的,也是作者強烈推薦的方式。
首先,創建 application.yml 文件,在里面添加如下內容:
spring: profiles: active: dev
含義是指定當前項目的默認環境為 dev,即項目啟動時如果不指定任何環境,Spring Boot 會自動從 dev 環境文件中讀取配置信息。我們可以將不同環境都共同的配置信息寫到這個文件中。
然后創建多環境配置文件,文件名的格式為:application-{profile}.yml,其中,{profile} 替換為環境名字,如 application-dev.yml,我們可以在其中添加當前環境的配置信息,如添加數據源:
spring: datasource: url: jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=UTF-8&useSSL=true username: root password: root driverClassName: com.mysql.jdbc.Driver
這樣,我們就實現了多環境的配置,每次編譯打包我們無需修改任何東西,編譯為 jar 文件后,運行命令:
java -jar api.jar --spring.profiles.active=dev
其中 --spring.profiles.active 就是我們要指定的環境。
常用注解
我們知道,Spring Boot 主要采用注解的方式,在上一篇的入門實例中,我們也用到了一些注解。
本文,我將詳細介紹在實際項目中常用的注解。
@SpringBootApplication
我們可以注意到 Spring Boot 支持 main 方法啟動,在我們需要啟動的主類中加入此注解,告訴 Spring Boot,這個類是程序的入口。如:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
如果不加這個注解,程序是無法啟動的。
我們查看下 SpringBootApplication 的源碼,源碼如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude") Class>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName") String[] excludeName() default {}; /** * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses} * for a type-safe alternative to String-based package names. * @return base packages to scan * @since 1.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; /** * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to * scan for annotated components. The package of each class specified will be scanned. ** Consider creating a special no-op marker class or interface in each package that * serves no purpose other than being referenced by this attribute. * @return base packages to scan * @since 1.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class>[] scanBasePackageClasses() default {}; }
在這個注解類上有3個注解,如下:
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
因此,我們可以用這三個注解代替 SpringBootApplication,如:
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
其中,SpringBootConfiguration 表示 Spring Boot 的配置注解,EnableAutoConfiguration 表示自動配置,ComponentScan 表示 Spring Boot 掃描 Bean 的規則,比如掃描哪些包。
@Configuration
加入了這個注解的類被認為是 Spring Boot 的配置類,我們知道可以在 application.yml 設置一些配置,也可以通過代碼設置配置。
如果我們要通過代碼設置配置,就必須在這個類上標注 Configuration 注解。如下代碼:
@Configuration public class WebConfig extends WebMvcConfigurationSupport{ @Override protected void addInterceptors(InterceptorRegistry registry) { super.addInterceptors(registry); registry.addInterceptor(new ApiInterceptor()); } }
不過 Spring Boot 官方推薦 Spring Boot 項目用 SpringBootConfiguration 來代替 Configuration。
@Bean
這個注解是方法級別上的注解,主要添加在 @Configuration 或 @SpringBootConfiguration 注解的類,有時也可以添加在 @Component 注解的類。它的作用是定義一個Bean。
請看下面代碼:
@Bean public ApiInterceptor interceptor(){ return new ApiInterceptor(); }
那么,我們可以在 ApiInterceptor 里面注入其他 Bean,也可以在其他 Bean 注入這個類。
@Value
通常情況下,我們需要定義一些全局變量,都會想到的方法是定義一個 public static 變量,在需要時調用,是否有其他更好的方案呢?答案是肯定的。下面請看代碼:
@Value("${server.port}") String port; @RequestMapping("/hello") public String home(String name) { return "hi "+name+",i am from port:" +port; }
其中,server.port 就是我們在 application.yml 里面定義的屬性,我們可以自定義任意屬性名,通過 @Value 注解就可以將其取出來。
它的好處不言而喻:
1.定義在配置文件里,變量發生變化,無需修改代碼。
2.變量交給Spring來管理,性能更好。
注: 本課程默認針對于對 SpringMVC 有所了解的讀者,Spring Boot 本身基于 Spring 開發的,因此,本文不再講解其他 Spring 的注解。
注入任何類
本節通過一個實際的例子來講解如何注入一個普通類,并且說明這樣做的好處。
假設一個需求是這樣的:項目要求使用阿里云的 OSS 進行文件上傳。
我們知道,一個項目一般會分為開發環境、測試環境和生產環境。OSS 文件上傳一般有如下幾個參數:appKey、appSecret、bucket、endpoint 等。不同環境的參數都可能不一樣,這樣便于區分。按照傳統的做法,我們在代碼里設置這些參數,這樣做的話,每次發布不同的環境包都需要手動修改代碼。
這個時候,我們就可以考慮將這些參數定義到配置文件里面,通過前面提到的 @Value 注解取出來,再通過 @Bean 將其定義為一個 Bean,這時我們只需要在需要使用的地方注入該 Bean 即可。
首先在 application.yml 加入如下內容:
appKey: 1 appSecret: 1 bucket: lynn endPoint: https://www.aliyun.com
其次創建一個普通類:
public class Aliyun { private String appKey; private String appSecret; private String bucket; private String endPoint; public static class Builder{ private String appKey; private String appSecret; private String bucket; private String endPoint; public Builder setAppKey(String appKey){ this.appKey = appKey; return this; } public Builder setAppSecret(String appSecret){ this.appSecret = appSecret; return this; } public Builder setBucket(String bucket){ this.bucket = bucket; return this; } public Builder setEndPoint(String endPoint){ this.endPoint = endPoint; return this; } public Aliyun build(){ return new Aliyun(this); } } public static Builder options(){ return new Aliyun.Builder(); } private Aliyun(Builder builder){ this.appKey = builder.appKey; this.appSecret = builder.appSecret; this.bucket = builder.bucket; this.endPoint = builder.endPoint; } public String getAppKey() { return appKey; } public String getAppSecret() { return appSecret; } public String getBucket() { return bucket; } public String getEndPoint() { return endPoint; } }
然后在 @SpringBootConfiguration 注解的類添加如下代碼:
@Value("${appKey}") private String appKey; @Value("${appSecret}") private String appSecret; @Value("${bucket}") private String bucket; @Value("${endPoint}") private String endPoint; @Bean public Aliyun aliyun(){ return Aliyun.options() .setAppKey(appKey) .setAppSecret(appSecret) .setBucket(bucket) .setEndPoint(endPoint) .build(); }
最后在需要的地方注入這個 Bean 即可:
@Autowired private Aliyun aliyun;
攔截器
我們在提供 API 的時候,經常需要對 API 進行統一的攔截,比如進行接口的安全性校驗。
本節,我會講解 Spring Boot 是如何進行攔截器設置的,請看接下來的代碼。
創建一個攔截器類:ApiInterceptor,并實現 HandlerInterceptor 接口:
public class ApiInterceptor implements HandlerInterceptor { //請求之前 @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { System.out.println("進入攔截器"); return true; } //請求時 @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } //請求完成 @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
@SpringBootConfiguration 注解的類繼承 WebMvcConfigurationSupport 類,并重寫 addInterceptors 方法,將 ApiInterceptor 攔截器類添加進去,代碼如下:
@SpringBootConfiguration public class WebConfig extends WebMvcConfigurationSupport{ @Override protected void addInterceptors(InterceptorRegistry registry) { super.addInterceptors(registry); registry.addInterceptor(new ApiInterceptor()); } }
異常處理
我們在 Controller 里提供接口,通常需要捕捉異常,并進行友好提示,否則一旦出錯,界面上就會顯示報錯信息,給用戶一種不好的體驗。最簡單的做法就是每個方法都使用 try catch 進行捕捉,報錯后,則在 catch 里面設置友好的報錯提示。如果方法很多,每個都需要 try catch,代碼會顯得臃腫,寫起來也比較麻煩。
我們可不可以提供一個公共的入口進行統一的異常處理呢?當然可以。方法很多,這里我們通過 Spring 的 AOP 特性就可以很方便的實現異常的統一處理。
@Aspect @Component public class WebExceptionAspect { private static final Logger logger = LoggerFactory.getLogger(WebExceptionAspect.class); //凡是注解了RequestMapping的方法都被攔截 @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)") private void webPointcut() { } /** * 攔截web層異常,記錄異常日志,并返回友好信息到前端 目前只攔截Exception,是否要攔截Error需再做考慮 * * @param e * 異常對象 */ @AfterThrowing(pointcut = "webPointcut()", throwing = "e") public void handleThrowing(Exception e) { e.printStackTrace(); logger.error("發現異常!" + e.getMessage()); logger.error(JSON.toJSONString(e.getStackTrace())); //這里輸入友好性信息 writeContent("出現異常"); } /** * 將內容輸出到瀏覽器 * * @param content * 輸出內容 */ private void writeContent(String content) { HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) .getResponse(); response.reset(); response.setCharacterEncoding("UTF-8"); response.setHeader("Content-Type", "text/plain;charset=UTF-8"); response.setHeader("icop-content-type", "exception"); PrintWriter writer = null; try { writer = response.getWriter(); } catch (IOException e) { e.printStackTrace(); } writer.print(content); writer.flush(); writer.close(); } }
這樣,我們無需每個方法都添加 try catch,一旦報錯,則會執行 handleThrowing 方法。
優雅的輸入合法性校驗
為了接口的健壯性,我們通常除了客戶端進行輸入合法性校驗外,在 Controller 的方法里,我們也需要對參數進行合法性校驗,傳統的做法是每個方法的參數都做一遍判斷,這種方式和上一節講的異常處理一個道理,不太優雅,也不易維護。
其實,SpringMVC 提供了驗證接口,下面請看代碼:
@GetMapping("authorize")//GetMapping是RequestMapping(method=method.GET)的組合 public void authorize(@Valid AuthorizeIn authorize, BindingResult ret){ if(result.hasFieldErrors()){ ListerrorList = result.getFieldErrors(); //通過斷言拋出參數不合法的異常 errorList.stream().forEach(item -> Assert.isTrue(false,item.getDefaultMessage())); } } public class AuthorizeIn extends BaseModel{ @NotBlank(message = "缺少response_type參數") private String responseType; @NotBlank(message = "缺少client_id參數") private String ClientId; private String state; @NotBlank(message = "缺少redirect_uri參數") private String redirectUri; public String getResponseType() { return responseType; } public void setResponseType(String responseType) { this.responseType = responseType; } public String getClientId() { return ClientId; } public void setClientId(String clientId) { ClientId = clientId; } public String getState() { return state; } public void setState(String state) { this.state = state; } public String getRedirectUri() { return redirectUri; } public void setRedirectUri(String redirectUri) { this.redirectUri = redirectUri; } }
在 controller 的方法需要校驗的參數后面必須跟 BindingResult,否則無法進行校驗。但是這樣會拋出異常,對用戶而言不太友好!
那怎么辦呢?
很簡單,我們可以利用上一節講的異常處理,對報錯進行攔截:
@Component @Aspect public class WebExceptionAspect implements ThrowsAdvice{ public static final Logger logger = LoggerFactory.getLogger(WebExceptionAspect.class); //攔截被GetMapping注解的方法 @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)") private void webPointcut() { } @AfterThrowing(pointcut = "webPointcut()",throwing = "e") public void afterThrowing(Exception e) throws Throwable { logger.debug("exception 來了!"); if(StringUtils.isNotBlank(e.getMessage())){ writeContent(e.getMessage()); }else{ writeContent("參數錯誤!"); } } /** * 將內容輸出到瀏覽器 * * @param content * 輸出內容 */ private void writeContent(String content) { HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) .getResponse(); response.reset(); response.setCharacterEncoding("UTF-8"); response.setHeader("Content-Type", "text/plain;charset=UTF-8"); response.setHeader("icop-content-type", "exception"); PrintWriter writer = null; try { writer = response.getWriter(); writer.print((content == null) ? "" : content); writer.flush(); writer.close(); } catch (IOException e) { e.printStackTrace(); } } }
這樣當我們傳入不合法的參數時就會進入 WebExceptionAspect 類,從而輸出友好參數。
我們再把驗證的代碼多帶帶封裝成方法:
protected void validate(BindingResult result){ if(result.hasFieldErrors()){ ListerrorList = result.getFieldErrors(); errorList.stream().forEach(item -> Assert.isTrue(false,item.getDefaultMessage())); } }
這樣每次參數校驗只需要調用 validate 方法就行了,我們可以看到代碼的可讀性也大大的提高了。
接口版本控制
一個系統上線后會不斷迭代更新,需求也會不斷變化,有可能接口的參數也會發生變化,如果在原有的參數上直接修改,可能會影響線上系統的正常運行,這時我們就需要設置不同的版本,這樣即使參數發生變化,由于老版本沒有變化,因此不會影響上線系統的運行。
一般我們可以在地址上帶上版本號,也可以在參數上帶上版本號,還可以再 header 里帶上版本號,這里我們在地址上帶上版本號,大致的地址如:http://api.example.com/v1/test,其中,v1 即代表的是版本號。具體做法請看代碼:
@Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface ApiVersion { /** * 標識版本號 * @return */ int value(); } public class ApiVersionCondition implements RequestCondition{ // 路徑中版本的前綴, 這里用 /v[1-9]/的形式 private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(d+)/"); private int apiVersion; public ApiVersionCondition(int apiVersion){ this.apiVersion = apiVersion; } @Override public ApiVersionCondition combine(ApiVersionCondition other) { // 采用最后定義優先原則,則方法上的定義覆蓋類上面的定義 return new ApiVersionCondition(other.getApiVersion()); } @Override public ApiVersionCondition getMatchingCondition(HttpServletRequest request) { Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI()); if(m.find()){ Integer version = Integer.valueOf(m.group(1)); if(version >= this.apiVersion) { return this; } } return null; } @Override public int compareTo(ApiVersionCondition other, HttpServletRequest request) { // 優先匹配最新的版本號 return other.getApiVersion() - this.apiVersion; } public int getApiVersion() { return apiVersion; } } public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping { @Override protected RequestCondition getCustomTypeCondition(Class> handlerType) { ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class); return createCondition(apiVersion); } @Override protected RequestCondition getCustomMethodCondition(Method method) { ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class); return createCondition(apiVersion); } private RequestCondition createCondition(ApiVersion apiVersion) { return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value()); } } @SpringBootConfiguration public class WebConfig extends WebMvcConfigurationSupport { @Bean public AuthInterceptor interceptor(){ return new AuthInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()); } @Override @Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping(); handlerMapping.setOrder(0); handlerMapping.setInterceptors(getInterceptors()); return handlerMapping; } }
Controller 類的接口定義如下:
@ApiVersion(1) @RequestMapping("{version}/dd") public class HelloController{}
這樣我們就實現了版本控制,如果增加了一個版本,則創建一個新的 Controller,方法名一致,ApiVersion 設置為2,則地址中 v1 會找到 ApiVersion 為1的方法,v2 會找到 ApiVersion 為2的方法。
自定義 JSON 解析
Spring Boot 中 RestController 返回的字符串默認使用 Jackson 引擎,它也提供了工廠類,我們可以自定義 JSON 引擎,本節實例我們將 JSON 引擎替換為 fastJSON,首先需要引入 fastJSON:
com.alibaba fastjson ${fastjson.version}
其次,在 WebConfig 類重寫 configureMessageConverters 方法:
@Override public void configureMessageConverters(List> converters) { super.configureMessageConverters(converters); /* 1.需要先定義一個convert轉換消息的對象; 2.添加fastjson的配置信息,比如是否要格式化返回的json數據 3.在convert中添加配置信息 4.將convert添加到converters中 */ //1.定義一個convert轉換消息對象 FastJsonHttpMessageConverter fastConverter=new FastJsonHttpMessageConverter(); //2.添加fastjson的配置信息,比如:是否要格式化返回json數據 FastJsonConfig fastJsonConfig=new FastJsonConfig(); fastJsonConfig.setSerializerFeatures( SerializerFeature.PrettyFormat ); fastConverter.setFastJsonConfig(fastJsonConfig); converters.add(fastConverter); }
單元測試
Spring Boot 的單元測試很簡單,直接看代碼:
@SpringBootTest(classes = Application.class) @RunWith(SpringJUnit4ClassRunner.class) public class TestDB { @Test public void test(){ } }
模板引擎
在傳統的 SpringMVC 架構中,我們一般將 JSP、HTML 頁面放到 webapps 目錄下面,但是 Spring Boot 沒有 webapps,更沒有 web.xml,如果我們要寫界面的話,該如何做呢?
Spring Boot 官方提供了幾種模板引擎:FreeMarker、Velocity、Thymeleaf、Groovy、mustache、JSP。
這里以 FreeMarker 為例講解 Spring Boot 的使用。
首先引入 FreeMarker 依賴:
org.springframework.boot spring-boot-starter-freemarker
在 resources 下面建立兩個目錄:static 和 templates,如圖所示:
其中 static 目錄用于存放靜態資源,譬如:CSS、JS、HTML 等,templates 目錄存放模板引擎文件,我們可以在 templates 下面創建一個文件:index.ftl(freemarker 默認后綴為 .ftl),并添加內容:
Hello World!
然后創建 PageController 并添加內容:
@Controller public class PageController { @RequestMapping("index.html") public String index(){ return "index"; } }
啟動 Application.java,訪問:http://localhost:8080/index.html,就可以看到如圖所示:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76778.html
摘要:結合我自己的經驗,我整理了一份全棧工程師進階路線圖,給大家參考。乾坤大挪移第一層第一層心法,主要都是基本語法,程序設計入門,悟性高者十天半月可成,差一點的到個月也說不準。 技術更新日新月異,對于初入職場的同學來說,經常會困惑該往那個方向發展,這一點松哥是深有體會的。 我剛開始學習 Java 那會,最大的問題就是不知道該學什么,以及學習的順序,我相信這也是很多初學者經常面臨的問題。?我...
摘要:而的日志文件在由指定。創建啟動類控制臺打印開源項目本地日志打印效果這里因為配置中將不同級別的日志設置了在不同文件中打印,這樣很大程度上方便項目出問題查找問題。 你是否因為項目出現問題,查找日志文件定位錯誤花費N多時間,是否為此苦不堪言,沒關系!現在通過這篇文章,將徹底解決你的煩惱,這篇文篇介紹,如何通過logback配置文件將日志進行分級打印,一個配置文件徹底搞定日志查找得煩惱。 準備...
摘要:你是否為一個功能多個和多個文件區分不同運行環境配置,經常為這些配置文件的管理而頭疼,現在通過這篇文章,將徹底解決你的煩惱,這篇文篇介紹,怎么通過文件構建多文檔塊,區分不同環境配置,自由切換不同環境啟動項目,一個配置文件搞定。 你是否為SpringBoot一個功能多個yml和多個properties文件區分不同運行環境配置,經常為這些配置文件的管理而頭疼,現在通過這篇文章,將徹底解決你的...
閱讀 891·2021-11-22 12:04
閱讀 2092·2021-11-02 14:46
閱讀 618·2021-08-30 09:44
閱讀 2101·2019-08-30 15:54
閱讀 719·2019-08-29 13:48
閱讀 1592·2019-08-29 12:56
閱讀 3444·2019-08-28 17:51
閱讀 3283·2019-08-26 13:44