欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Cloud Gateway 記錄請(qǐng)求應(yīng)答數(shù)據(jù)日志操作

 更新時(shí)間:2020年12月01日 10:15:03   作者:牛司隆  
這篇文章主要介紹了Spring Cloud Gateway 記錄請(qǐng)求應(yīng)答數(shù)據(jù)日志操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

我就廢話不多說(shuō)了,大家還是直接看代碼吧~

public class GatewayContext {
 public static final String CACHE_GATEWAY_CONTEXT = "cacheGatewayContext";
 /**
  * cache json body
  */
 private String cacheBody;
 /**
  * cache formdata
  */
 private MultiValueMap<String, String> formData;
 /**
  * cache reqeust path
  */
 private String path;
 public String getCacheBody() {
  return cacheBody;
 }
 public void setCacheBody(String cacheBody) {
  this.cacheBody = cacheBody;
 }
 public MultiValueMap<String, String> getFormData() {
  return formData;
 }
 public void setFormData(MultiValueMap<String, String> formData) {
  this.formData = formData;
 }
 public String getPath() {
  return path;
 }
 public void setPath(String path) {
  this.path = path;
 }
}
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import io.netty.buffer.ByteBufAllocator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
// https://segmentfault.com/a/1190000017898354
@Component
public class LogRequestGlobalFilter
  implements GlobalFilter {
 /**
  * default HttpMessageReader
  */
 private static final List<HttpMessageReader<?>> messageReaders =
   HandlerStrategies.withDefaults().messageReaders();
 private Logger log = LoggerFactory.getLogger(LogRequestGlobalFilter.class);
 @Override
 public Mono<Void> filter(
   ServerWebExchange exchange,
   GatewayFilterChain chain) {
  /**
   * save request path and serviceId into gateway context
   */
  ServerHttpRequest request = exchange.getRequest();
  String path = request.getPath().pathWithinApplication().value();
  GatewayContext gatewayContext = new GatewayContext();
  gatewayContext.setPath(path);
  /**
   * save gateway context into exchange
   */
  exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT,
    gatewayContext);
  HttpHeaders headers = request.getHeaders();
  MediaType contentType = headers.getContentType();
  log.info("start-------------------------------------------------");
  log.info("HttpMethod:{},Url:{}", request.getMethod(),
    request.getURI().getRawPath());
  log.info("Headers token: {}", headers.getFirst("token"));
  if (request.getMethod() == HttpMethod.GET) {
   log.info("end-------------------------------------------------");
  }
  if (request.getMethod() == HttpMethod.POST) {
   Mono<Void> voidMono = null;
   if (MediaType.APPLICATION_JSON.equals(contentType)
     || MediaType.APPLICATION_JSON_UTF8.equals(contentType)) {
    voidMono =
      readBody(exchange, chain, gatewayContext);
   }
   if (MediaType.APPLICATION_FORM_URLENCODED.equals(contentType)) {
    voidMono =
      readFormData(exchange, chain, gatewayContext);
   }
   return voidMono;
  }
  /* log.debug(
   "[GatewayContext]ContentType:{},Gateway context is set with {}",
   contentType, gatewayContext);*/
  return chain.filter(exchange);
 }
 /**
  * ReadFormData
  *
  * @param exchange
  * @param chain
  * @return
  */
 private Mono<Void> readFormData(
   ServerWebExchange exchange,
   GatewayFilterChain chain,
   GatewayContext gatewayContext) {
  final ServerHttpRequest request = exchange.getRequest();
  HttpHeaders headers = request.getHeaders();
  return exchange.getFormData()
    .doOnNext(multiValueMap -> {
     gatewayContext.setFormData(multiValueMap);
     log.info("Post x-www-form-urlencoded:{}",
       multiValueMap);
     log.info(
       "end-------------------------------------------------");
    })
    .then(Mono.defer(() -> {
     Charset charset = headers.getContentType().getCharset();
     charset = charset == null ? StandardCharsets.UTF_8 : charset;
     String charsetName = charset.name();
     MultiValueMap<String, String> formData =
       gatewayContext.getFormData();
     /**
      * formData is empty just return
      */
     if (null == formData || formData.isEmpty()) {
      return chain.filter(exchange);
     }
     StringBuilder formDataBodyBuilder = new StringBuilder();
     String entryKey;
     List<String> entryValue;
     try {
      /**
       * repackage form data
       */
      for (Map.Entry<String, List<String>> entry : formData
        .entrySet()) {
       entryKey = entry.getKey();
       entryValue = entry.getValue();
       if (entryValue.size() > 1) {
        for (String value : entryValue) {
         formDataBodyBuilder.append(entryKey).append("=")
           .append(
             URLEncoder.encode(value, charsetName))
           .append("&");
        }
       } else {
        formDataBodyBuilder
          .append(entryKey).append("=").append(URLEncoder
          .encode(entryValue.get(0), charsetName))
          .append("&");
       }
      }
     } catch (UnsupportedEncodingException e) {
      // ignore URLEncode Exception
     }
     /**
      * substring with the last char '&'
      */
     String formDataBodyString = "";
     if (formDataBodyBuilder.length() > 0) {
      formDataBodyString = formDataBodyBuilder.substring(0,
        formDataBodyBuilder.length() - 1);
     }
     /**
      * get data bytes
      */
     byte[] bodyBytes = formDataBodyString.getBytes(charset);
     int contentLength = bodyBytes.length;
     ServerHttpRequestDecorator decorator =
       new ServerHttpRequestDecorator(
         request) {
        /**
         * change content-length
         *
         * @return
         */
        @Override
        public HttpHeaders getHeaders() {
         HttpHeaders httpHeaders = new HttpHeaders();
         httpHeaders.putAll(super.getHeaders());
         if (contentLength > 0) {
          httpHeaders.setContentLength(contentLength);
         } else {
          httpHeaders.set(HttpHeaders.TRANSFER_ENCODING,
            "chunked");
         }
         return httpHeaders;
        }
        /**
         * read bytes to Flux<Databuffer>
         *
         * @return
         */
        @Override
        public Flux<DataBuffer> getBody() {
         return DataBufferUtils
           .read(new ByteArrayResource(bodyBytes),
             new NettyDataBufferFactory(
               ByteBufAllocator.DEFAULT),
             contentLength);
        }
       };
     ServerWebExchange mutateExchange =
       exchange.mutate().request(decorator).build();
    /* log.info("[GatewayContext]Rewrite Form Data :{}",
      formDataBodyString);*/
     return chain.filter(mutateExchange);
    }));
 }
 /**
  * ReadJsonBody
  *
  * @param exchange
  * @param chain
  * @return
  */
 private Mono<Void> readBody(
   ServerWebExchange exchange,
   GatewayFilterChain chain,
   GatewayContext gatewayContext) {
  /**
   * join the body
   */
  return DataBufferUtils.join(exchange.getRequest().getBody())
    .flatMap(dataBuffer -> {
     /*
      * read the body Flux<DataBuffer>, and release the buffer
      * //TODO when SpringCloudGateway Version Release To G.SR2,this can be update with the new version's feature
      * see PR https://github.com/spring-cloud/spring-cloud-gateway/pull/1095
      */
     byte[] bytes = new byte[dataBuffer.readableByteCount()];
     dataBuffer.read(bytes);
     DataBufferUtils.release(dataBuffer);
     Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
      DataBuffer buffer =
        exchange.getResponse().bufferFactory().wrap(bytes);
      DataBufferUtils.retain(buffer);
      return Mono.just(buffer);
     });
     /**
      * repackage ServerHttpRequest
      */
     ServerHttpRequest mutatedRequest =
       new ServerHttpRequestDecorator(exchange.getRequest()) {
        @Override
        public Flux<DataBuffer> getBody() {
         return cachedFlux;
        }
       };
     /**
      * mutate exchage with new ServerHttpRequest
      */
     ServerWebExchange mutatedExchange =
       exchange.mutate().request(mutatedRequest).build();
     /**
      * read body string with default messageReaders
      */
     return ServerRequest.create(mutatedExchange, messageReaders)
       .bodyToMono(String.class)
       .doOnNext(objectValue -> {
        log.info("PostBody:{}", objectValue);
        log.info(
          "end-------------------------------------------------");
        gatewayContext.setCacheBody(objectValue);
      /* log.debug("[GatewayContext]Read JsonBody:{}",
        objectValue);*/
       }).then(chain.filter(mutatedExchange));
    });
 }
}
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
@Component
@Slf4j
public class LogResponseGlobalFilter implements GlobalFilter, Ordered {
 private static final String REQUEST_PREFIX = "Request Info [ ";
 private static final String REQUEST_TAIL = " ]";
 private static final String RESPONSE_PREFIX = "Response Info [ ";
 private static final String RESPONSE_TAIL = " ]";
 private StringBuilder normalMsg = new StringBuilder();
 @Override
 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  ServerHttpRequest request = exchange.getRequest();
  ServerHttpResponse response = exchange.getResponse();
  DataBufferFactory bufferFactory = response.bufferFactory();
  normalMsg.append(RESPONSE_PREFIX);
  ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {
   @Override
   public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
    if (body instanceof Flux) {
     Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
     return super.writeWith(fluxBody.map(dataBuffer -> {
      // probably should reuse buffers
      byte[] content = new byte[dataBuffer.readableByteCount()];
      dataBuffer.read(content);
      String responseResult = new String(content, Charset.forName("UTF-8"));
      normalMsg.append("status=").append(this.getStatusCode());
      normalMsg.append(";header=").append(this.getHeaders());
      normalMsg.append(";responseResult=").append(responseResult);
      normalMsg.append(RESPONSE_TAIL);
      log.info(normalMsg.toString());
      return bufferFactory.wrap(content);
     }));
    }
    return super.writeWith(body); // if body is not a flux. never got there.
   }
  };
  return chain.filter(exchange.mutate().response(decoratedResponse).build());
 }
 @Override
 public int getOrder() {
  return -2;
 }
}

補(bǔ)充知識(shí):Spring Cloud Gateway 2.x 打印 Log

場(chǎng)景

在服務(wù)網(wǎng)關(guān)層面,需要打印出用戶每次的請(qǐng)求body和其他的參數(shù),gateway使用的是Reactor響應(yīng)式編程,和Zuul網(wǎng)關(guān)獲取流的寫法還有些不同,

不過(guò)基本的思路是一樣的,都是在filter中讀取body流,然后緩存回去,因?yàn)閎ody流,框架默認(rèn)只允許讀取一次。

思路

1. 添加一個(gè)filter做一次請(qǐng)求的攔截

GatewayConfig.java

添加一個(gè)配置類,配置一個(gè)高優(yōu)先級(jí)的filter,并且注入一個(gè)PayloadServerWebExchangeDecorator 對(duì)request和response做包裝的類。

package com.demo.gateway2x.config;
import com.demo.gateway2x.decorator.PayloadServerWebExchangeDecorator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.server.WebFilter;
@Configuration
public class GatewayConfig {
 @Bean
 @Order(Ordered.HIGHEST_PRECEDENCE) //過(guò)濾器順序
 public WebFilter webFilter() {
  return (exchange, chain) -> chain.filter(new PayloadServerWebExchangeDecorator(exchange));
 }
}

PayloadServerWebExchangeDecorator.java

這個(gè)類中,我們實(shí)現(xiàn)了框架的ServerWebExchangeDecorator類,同時(shí)注入了自定義的兩個(gè)類,PartnerServerHttpRequestDecorator 和 PartnerServerHttpResponseDecorator ,

這兩個(gè)類用于后面對(duì)請(qǐng)求與響應(yīng)的攔截。

package com.demo.gateway2x.decorator;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebExchangeDecorator;
public class PayloadServerWebExchangeDecorator extends ServerWebExchangeDecorator {
 private PartnerServerHttpRequestDecorator requestDecorator;
 private PartnerServerHttpResponseDecorator responseDecorator;
 public PayloadServerWebExchangeDecorator(ServerWebExchange delegate) {
  super(delegate);
  requestDecorator = new PartnerServerHttpRequestDecorator(delegate.getRequest());
  responseDecorator = new PartnerServerHttpResponseDecorator(delegate.getResponse());
 }
 @Override
 public ServerHttpRequest getRequest() {
  return requestDecorator;
 }
 @Override
 public ServerHttpResponse getResponse() {
  return responseDecorator;
 }
}

2. 在請(qǐng)求進(jìn)入時(shí),對(duì)request做一次攔截

PartnerServerHttpRequestDecorator.java

這個(gè)類實(shí)現(xiàn)了 ServerHttpRequestDecorator , 并在構(gòu)造函數(shù)中,使用響應(yīng)式編程,調(diào)用了打印log的方法,注意關(guān)注 Mono<DataBuffer> mono = DataBufferUtils.join(flux); ,

這里將Flux合并成了一個(gè)Mono,因?yàn)槿绻贿@么做,body內(nèi)容過(guò)多,將會(huì)被分段打印,這里是一個(gè)恒重要的點(diǎn),

在打印RequestParamsHandle.chain打印過(guò)日志后,我們又返回了一個(gè)dataBuffer,用作向下傳遞,否則dataBuffer被讀取過(guò)一次后就不能繼續(xù)使用了。

package com.demo.gateway2x.decorator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import static reactor.core.scheduler.Schedulers.single;
@Slf4j
public class PartnerServerHttpRequestDecorator extends ServerHttpRequestDecorator {
 private Flux<DataBuffer> body;
 public PartnerServerHttpRequestDecorator(ServerHttpRequest delegate) {
  super(delegate);
  Flux<DataBuffer> flux = super.getBody();
  if (ParamsUtils.CHAIN_MEDIA_TYPE.contains(delegate.getHeaders().getContentType())) {
   Mono<DataBuffer> mono = DataBufferUtils.join(flux);
   body = mono.publishOn(single()).map(dataBuffer -> RequestParamsHandle.chain(delegate, log, dataBuffer)).flux();
  } else {
   body = flux;
  }
 }
 @Override
 public Flux<DataBuffer> getBody() {
  return body;
 }
}

RequestParamsHandle.java

這個(gè)類主要用來(lái)讀取dataBuffer并做了日志打印處理,也可以做一些其他的例如參數(shù)校驗(yàn)等使用。

package com.demo.gateway2x.decorator;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
public class RequestParamsHandle {
 public static <T extends DataBuffer> T chain(ServerHttpRequest delegate, Logger log, T buffer) {
  ParamsUtils.BodyDecorator bodyDecorator = ParamsUtils.buildBodyDecorator(buffer);
  // 參數(shù)校驗(yàn) 和 參數(shù)打印
  log.info("Payload: {}", JSON.toJSONString(validParams(getParams(delegate, bodyDecorator.getBody()))));
  return (T) bodyDecorator.getDataBuffer();
 }
 public static Map<String,Object> getParams(ServerHttpRequest delegate, String body) {
  // 整理參數(shù)
  Map<String,Object> params = new HashMap<>();
  if (delegate.getQueryParams() != null) {
   params.putAll(delegate.getQueryParams());
  }
  if (!StringUtils.isEmpty(body)) {
   params.putAll(JSON.parseObject(body));
  }
  return params;
 }
 public static Map<String,Object> validParams(Map<String,Object> params) {
  // todo 參數(shù)校驗(yàn)
  return params;
 }
}

3. 在結(jié)果返回時(shí),對(duì)response做一次攔截

PartnerServerHttpResponseDecorator.java

這個(gè)類和上面的request的異曲同工,攔截響應(yīng)流,并做記錄入處理。

package com.demo.gateway2x.decorator;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import static reactor.core.scheduler.Schedulers.single;
@Slf4j
public class PartnerServerHttpResponseDecorator extends ServerHttpResponseDecorator {
 PartnerServerHttpResponseDecorator(ServerHttpResponse delegate) {
  super(delegate);
 }
 @Override
 public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
  return super.writeAndFlushWith(body);
 }
 @Override
 public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
  final MediaType contentType = super.getHeaders().getContentType();
  if (ParamsUtils.CHAIN_MEDIA_TYPE.contains(contentType)) {
   if (body instanceof Mono) {
    final Mono<DataBuffer> monoBody = (Mono<DataBuffer>) body;
    return super.writeWith(monoBody.publishOn(single()).map(dataBuffer -> ResponseParamsHandle.chain(log, dataBuffer)));
   } else if (body instanceof Flux) {
    Mono<DataBuffer> mono = DataBufferUtils.join(body);
    final Flux<DataBuffer> monoBody = mono.publishOn(single()).map(dataBuffer -> ResponseParamsHandle.chain(log, dataBuffer)).flux();
    return super.writeWith(monoBody);
   }
  }
  return super.writeWith(body);
 }
}

ResponseParamsHandle.java

響應(yīng)流的日志打印

package com.demo.gateway2x.decorator;
import org.slf4j.Logger;
import org.springframework.core.io.buffer.DataBuffer;
public class ResponseParamsHandle {
 public static <T extends DataBuffer> T chain(Logger log, T buffer) {
  ParamsUtils.BodyDecorator bodyDecorator = ParamsUtils.buildBodyDecorator(buffer);
  // 參數(shù)校驗(yàn) 和 參數(shù)打印
  log.info("Payload: {}", bodyDecorator.getBody());
  return (T) bodyDecorator.getDataBuffer();
 }
}

下面是實(shí)際操作,發(fā)送一次http請(qǐng)求:

控制臺(tái)log結(jié)果:

github源碼地址:https://github.com/qiaomengnan16/gateway-2x-log-demo

總結(jié)

gateway和zuul打印參數(shù)的方式思路是一致的,只是gateway采用的是reactor,寫法上與zuul的直接讀取流有些不同,這里需要知道的是Flux需要轉(zhuǎn)換為Mono這個(gè)地方,如果不轉(zhuǎn)換容易分多批打印。

以上這篇Spring Cloud Gateway 記錄請(qǐng)求應(yīng)答數(shù)據(jù)日志操作就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 深入理解Java設(shè)計(jì)模式之代理模式

    深入理解Java設(shè)計(jì)模式之代理模式

    這篇文章主要介紹了Java設(shè)計(jì)模式之代理模式的的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解下
    2021-11-11
  • Java泛型在集合使用與自定義及繼承上的體現(xiàn)和通配符的使用

    Java泛型在集合使用與自定義及繼承上的體現(xiàn)和通配符的使用

    泛型又稱參數(shù)化類型,是Jdk5.0 出現(xiàn)的新特性,解決數(shù)據(jù)類型的安全性問(wèn)題,在類聲明或?qū)嵗瘯r(shí)只要指定好需要的具體的類型即可。Java泛型可以保證如果程序在編譯時(shí)沒(méi)有發(fā)出警告,運(yùn)行時(shí)就不會(huì)產(chǎn)生ClassCastException異常。同時(shí),代碼更加簡(jiǎn)潔、健壯
    2021-09-09
  • SpringBoot Devtools實(shí)現(xiàn)項(xiàng)目熱部署的方法示例

    SpringBoot Devtools實(shí)現(xiàn)項(xiàng)目熱部署的方法示例

    這篇文章主要介紹了SpringBoot Devtools實(shí)現(xiàn)項(xiàng)目熱部署的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01
  • Java SiteMesh新手學(xué)習(xí)教程代碼案例

    Java SiteMesh新手學(xué)習(xí)教程代碼案例

    這篇文章主要介紹了Java SiteMesh新手學(xué)習(xí)教程代碼案例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Springboot實(shí)現(xiàn)圖片上傳功能的示例代碼

    Springboot實(shí)現(xiàn)圖片上傳功能的示例代碼

    本篇文章主要介紹了SpringBoot如何實(shí)現(xiàn)圖片上傳功能,文中通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2022-09-09
  • 詳解SpringMVC驗(yàn)證框架Validation特殊用法

    詳解SpringMVC驗(yàn)證框架Validation特殊用法

    本篇文章主要介紹了詳解SpringMVC驗(yàn)證框架Validation特殊用法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-02-02
  • java多態(tài)中的就近原則介紹

    java多態(tài)中的就近原則介紹

    大家好,本篇文章主要講的是java多態(tài)中的就近原則介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Spring如何基于注解配置使用ehcache

    Spring如何基于注解配置使用ehcache

    這篇文章主要介紹了Spring如何基于注解配置使用ehcache,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • IDEA下Maven的pom文件導(dǎo)入依賴出現(xiàn)Auto build completed with errors的問(wèn)題

    IDEA下Maven的pom文件導(dǎo)入依賴出現(xiàn)Auto build completed with errors的問(wèn)題

    這篇文章主要介紹了IDEA下Maven的pom文件導(dǎo)入依賴出現(xiàn)Auto build completed with errors,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • SpringBoot中注解@AliasFor的使用詳解

    SpringBoot中注解@AliasFor的使用詳解

    這篇文章主要為大家詳細(xì)介紹了SpringBoot中注解@AliasFor的用法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,需要的可以參考一下
    2022-05-05

最新評(píng)論