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

Feign?請(qǐng)求動(dòng)態(tài)URL方式

 更新時(shí)間:2022年07月04日 08:37:30   作者:行云之流水  
這篇文章主要介紹了Feign?請(qǐng)求動(dòng)態(tài)URL方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Feign 請(qǐng)求動(dòng)態(tài)URL

注意事項(xiàng)

  • FeignClient 中不要寫url, 使用 @RequestLine修飾方法
  • 調(diào)用地方必須引入 FeignClientConfiguration, 必須有Decoder, Encoder
  • 調(diào)用類必須以構(gòu)建函數(shù)(Constructor) 的方式注入 FeignClient 類
  • 傳入U(xiǎn)RL作為參數(shù);

代碼如下:

FeignClient類:

@CompileStatic
@FeignClient(name = "xxxxClient")
public interface XxxFeignClient {
? ? @RequestLine("POST")
? ? ResponseDto notifySomething(URI baseUri, ApproveNotifyDto notifyDto);
? ? /**
? ? ?*?
? ? ?* @param uri
? ? ?* @param queryMap: {userId: userId}
? ? ?* @return
? ? ?*/
? ? @RequestLine("GET")
? ? ResponseDto getSomething(URI baseUri, @QueryMap Map<String, String> queryMap)
??
}

ClientCaller類:

@CompileStatic
@Slf4j
@Component
@Import(FeignClientsConfiguration.class)
public class CallerService {
? ? private XxxFeignClient xxxFeignClient
? ? @Autowired
? ? public CallerService(Decoder decoder, Encoder encoder) {
? ? ? ? xxxFeignClient = Feign.builder()
? ? ? ? ? ? ? ? //.client(client)
? ? ? ? ? ? ? ? .encoder(encoder)
? ? ? ? ? ? ? ? .decoder(decoder)
? ? ? ? ? ? ? ? .target(Target.EmptyTarget.create(XxxFeignClient.class))
? ? }
? ? public ResponseDto notifySomething(String url, XxxxDto dto) {
? ? ? ? return xxxFeignClient.notifySomething(URI.create(url), dto)
? ? }
? ? /**
? ? ?* @param url: http://localhost:9104/
? ? ?* @param userId?
? ? ?*/
? ? public String getSomething(String url, String userId) {
? ? ? ? return xxxFeignClient.getSomething(URI.create(url), ["userId": userId])
? ? }
}

Feign重寫URL以及RequestMapping

背景

由于項(xiàng)目采用的是 消費(fèi)層 + API層(FeignClient) +服務(wù)層 的方式。 

導(dǎo)致原項(xiàng)目有太多代碼冗余復(fù)制的地方。例如FeignClient上要寫@RequestMapping,同時(shí)要寫請(qǐng)求路徑,消費(fèi)層還要寫上RequestBody等等,比較麻煩。遂改進(jìn)了一下方案,簡化日常代碼開發(fā)的工作量

場(chǎng)景

項(xiàng)目的架構(gòu)是微服務(wù)架構(gòu),SpringBoot與SpringCloud均為原生版本。

效果展示

feign層無需寫RequestMapping以及RequestBody或者RequestParam等

public interface UserDemoApi {
? ? /**
? ? ?* 新增用戶
? ? ?* @param userDemo 用戶實(shí)體
? ? ?* @return 用戶信息
? ? ?*/
? ? ApiResponse<UserDemo> add(UserDemo userDemo);
? ? /**
? ? ?* 獲取用戶信息
? ? ?* @param userId 用戶id
? ? ?* @param username 用戶名
? ? ?* @return 用戶信息
? ? ?*/
? ? ApiResponse<UserDemo> findByUserIdAndUsername(String userId,String username);
? ? /**
? ? ?* 用戶列表
? ? ?* @param reqVo 條件查詢
? ? ?* @return 用戶列表
? ? ?*/
? ? ApiResponse<PageResult<UserDemo>> findByCondition(UserDemoReqVo reqVo);
}
@FeignClient(value = "${api.feign.method.value}",fallback = UserDemoFeignApiFallbackImpl.class)
public interface UserDemoFeignApi extends UserDemoApi {
}

整體思路

  • 首先要拆成兩部分處理,一部分是服務(wù)端層,另一部分是客戶端層
  • 服務(wù)端層:主要做的事情就是注冊(cè)RequestMapping.由于我們的api上是沒有寫任何注解的,所以我們需要在項(xiàng)目啟動(dòng)的時(shí)候把a(bǔ)pi上的方法都注冊(cè)上去。另外一個(gè)要做的就是,由于我們不寫RequestBody,所以要做參數(shù)解析配置
  • 客戶端層:主要做的事情是,項(xiàng)目啟動(dòng)的時(shí)候,feign會(huì)掃描api上的方法,在它掃描的同時(shí),直接把url定下來存入RequestTemplate中。這樣即使后續(xù)feign在執(zhí)行apply沒有路徑時(shí)也不影響feign的正常請(qǐng)求。

實(shí)現(xiàn)

Feign的服務(wù)端層

1. 繼承RequestMappingHandlerMapping,重寫getMappingForMethod

  • METHOD_MAPPING 為自定義的路徑(即RequestMapping的value值)
  • 在服務(wù)層的實(shí)現(xiàn)類中,打上自定義注解@CustomerAnnotation,只有有該注解的實(shí)現(xiàn)類才重寫RequestMapping。
  • 自定義的路徑采用的是 類名+方法名

重寫的內(nèi)容

public class CustomerRequestMapping extends RequestMappingHandlerMapping {
? ? private final static String METHOD_MAPPING = "/remote/%s/%s";
? ? @Override
? ? protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
? ? ? ? RequestMappingInfo requestMappingInfo = super.getMappingForMethod(method,handlerType);
? ? ? ? if(requestMappingInfo!=null){
? ? ? ? ? ? if(handlerType.isAnnotationPresent(CustomerAnnotation.class)){
? ? ? ? ? ? ? ? if(requestMappingInfo.getPatternsCondition().getPatterns().isEmpty()||
? ? ? ? ? ? ? ? ? ? ? ? requestMappingInfo.getPatternsCondition().getPatterns().contains("")){
? ? ? ? ? ? ? ? ? ? String[] path = getMethodPath(method, handlerType);
? ? ? ? ? ? ? ? ? ? requestMappingInfo = RequestMappingInfo.paths(path).build().combine(requestMappingInfo);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }else{
? ? ? ? ? ? if(handlerType.isAnnotationPresent(CustomerAnnotation.class)){
? ? ? ? ? ? ? ? String[] path = getMethodPath(method, handlerType);
? ? ? ? ? ? ? ? requestMappingInfo = RequestMappingInfo.paths(path).methods(RequestMethod.POST).build();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return requestMappingInfo;
? ? }
? ? private String[] getMethodPath(Method method, Class<?> handlerType) {
? ? ? ? Class<?>[] interfaces = handlerType.getInterfaces();
? ? ? ? String methodClassName = interfaces[0].getSimpleName();
? ? ? ? methodClassName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, methodClassName);
? ? ? ? String[] path = {String.format(METHOD_MAPPING,methodClassName, method.getName())};
? ? ? ? return path;
? ? }

覆蓋RequestMappingHandlerMapping

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class VersionControlWebMvcConfiguration implements WebMvcRegistrations {
? ? @Override
? ? public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
? ? ? ? return new CustomerRequestMapping();
? ? }
}

2. 服務(wù)層Controller

  • 實(shí)現(xiàn)api接口(即Feign層的接口)
  • 打上自定義的標(biāo)識(shí)注解@CustomerAnnotation
@RestController
@CustomerAnnotation
@RequestMapping
public class UserDemoController implements UserDemoFeignApi {
? ? private final static Logger logger = LoggerFactory.getLogger(UserDemoController.class);
? ? @Resource
? ? private UserDemoService userDemoService;
? ? @Override
? ? public ApiResponse<UserDemo> add(UserDemo userDemo) {
? ? ? ? logger.info("request data:<{}>",userDemo);
? ? ? ? return userDemoService.add(userDemo);
? ? }
? ? @Override
? ? public ApiResponse<UserDemo> findByUserIdAndUsername(String userId,String username) {
? ? ? ? logger.info("request data:<{}>",userId+":"+username);
? ? ? ? return ApiResponse.success(new UserDemo());
? ? }
? ? @Override
? ? public ApiResponse<PageResult<UserDemo>> findByCondition(UserDemoReqVo reqVo) {
? ? ? ? logger.info("request data:<{}>",reqVo);
? ? ? ? return userDemoService.findByCondition(reqVo);
? ? }
}

自此,F(xiàn)iegn的服務(wù)端的配置已經(jīng)配置完畢?,F(xiàn)在我們來配置Feign的客戶端

Feign的客戶端配置

重寫Feign的上下文SpringMvcContract

核心代碼為重寫processAnnotationOnClass的內(nèi)容。

  • 從MethodMetadata 對(duì)象中獲取到RequestTemplate,后續(xù)的所有操作都是針對(duì)于這個(gè)
  • 獲取到類名以及方法名,作為RequestTemplate對(duì)象中url的值,此處應(yīng)與服務(wù)層的配置的URL路徑一致
  • 默認(rèn)請(qǐng)求方式都為POST

特殊情況處理:在正常情況下,多參數(shù)且沒有@RequestParams參數(shù)注解的情況下,F(xiàn)eign會(huì)直接拋異常且終止啟動(dòng)。所以需要對(duì)多參數(shù)做額外處理

  • 判斷當(dāng)前方法的參數(shù)數(shù)量,如果不超過2個(gè)不做任何處理
  • 對(duì)于超過2個(gè)參數(shù)的方法,需要對(duì)其做限制。首先,方法必須滿足命名規(guī)范,即類似findByUserIdAndUsername。以By為起始,And作為連接。
  • 截取并獲取參數(shù)名稱
  • 將名稱按順序存入RequestTemplate對(duì)象中的querie屬性中。同時(shí),要記得MethodMetadata 對(duì)象中的indexToName也需要存入信息。Map<Integer,Collection> map,key為參數(shù)的位置(從0開始),value為參數(shù)的名稱
@Component
public class CustomerContact extends SpringMvcContract {
? ? private final static Logger logger = LoggerFactory.getLogger(CustomerContact.class);
? ? private final static String METHOD_PATTERN_BY = "By";
? ? private final static String METHOD_PATTERN_AND = "And";
? ? private final Map<String, Method> processedMethods = new HashMap<>();
? ? private final static String METHOD_MAPPING = "/remote/%s/%s";
? ? private Map<String, Integer> parameterIndexMap = new ConcurrentHashMap<>(100);
? ? public CustomerContact() {
? ? ? ? this(Collections.emptyList());
? ? }
? ? public CustomerContact(List<AnnotatedParameterProcessor> annotatedParameterProcessors) {
? ? ? ? this(annotatedParameterProcessors,new DefaultConversionService());
? ? }
? ? public CustomerContact(List<AnnotatedParameterProcessor> annotatedParameterProcessors, ConversionService conversionService) {
? ? ? ? super(annotatedParameterProcessors, conversionService);
? ? }
? ? /**
? ? ?* 重寫URL
? ? ?* @param data 類名以及方法名信息
? ? ?* @param clz api類
? ? ?*/
? ? @Override
? ? protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
? ? ? ? RequestTemplate template = data.template();
? ? ? ? String configKey = data.configKey();
? ? ? ? if(StringUtils.isBlank(template.url())){
? ? ? ? ? ? template.append(getTemplatePath(configKey));
? ? ? ? ? ? template.method(RequestMethod.POST.name());
? ? ? ? }
? ? ? ? // 構(gòu)造查詢條件
? ? ? ? templateQuery(template,data);
? ? ? ? super.processAnnotationOnClass(data, clz);
? ? }
? ? /**
? ? ?* @param template 請(qǐng)求模板
? ? ?*/
? ? private void templateQuery(RequestTemplate template,MethodMetadata data){
? ? ? ? try{
? ? ? ? ? ? String configKey = data.configKey();
? ? ? ? ? ? if(manyParameters(data.configKey())){
? ? ? ? ? ? ? ? Method method = processedMethods.get(configKey);
? ? ? ? ? ? ? ? String methodName = method.getName();
? ? ? ? ? ? ? ? String key = getTemplatePath(configKey);
? ? ? ? ? ? ? ? Integer parameterIndex = 0;
? ? ? ? ? ? ? ? if(parameterIndexMap.containsKey(key)){
? ? ? ? ? ? ? ? ? ? parameterIndexMap.put(key,parameterIndex++);
? ? ? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? ? ? parameterIndexMap.put(key,parameterIndex);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? int index = methodName.indexOf(METHOD_PATTERN_BY);
? ? ? ? ? ? ? ? if(index>=0){
? ? ? ? ? ? ? ? ? ? String[] parametersName = methodName.substring(index+METHOD_PATTERN_BY.length()).split(METHOD_PATTERN_AND);
? ? ? ? ? ? ? ? ? ? String parameterName = parametersName[parameterIndex];
? ? ? ? ? ? ? ? ? ? String caseName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, parameterName);
? ? ? ? ? ? ? ? ? ? Collection<String> param = addTemplatedParam(template.queries().get(parameterName), caseName);
? ? ? ? ? ? ? ? ? ? template.query(caseName,param);
? ? ? ? ? ? ? ? ? ? setNameParam(data,caseName,parameterIndex);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }catch (Exception e){
? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? logger.error("template construct query failed:<{}>",e.getMessage());
? ? ? ? }
? ? }
? ? /**
? ? ?* 構(gòu)造url路徑
? ? ?* @param configKey 類名#方法名信息
? ? ?* @return URL路徑
? ? ?*/
? ? private String getTemplatePath(String configKey) {
? ? ? ? Method method = processedMethods.get(configKey);
? ? ? ? int first = configKey.indexOf("#");
? ? ? ? String apiName = configKey.substring(0,first);
? ? ? ? String methodClassName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, apiName);
? ? ? ? return String.format(METHOD_MAPPING,methodClassName,method.getName());
? ? }
? ? @Override
? ? public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
? ? ? ? this.processedMethods.put(Feign.configKey(targetType, method), method);
? ? ? ? return super.parseAndValidateMetadata(targetType,method);
? ? }
? ? @Override
? ? protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
? ? ? ? if(manyParameters(data.configKey())){
? ? ? ? ? ? return true;
? ? ? ? }
? ? ? ? return super.processAnnotationsOnParameter(data,annotations,paramIndex);
? ? }
? ? private void setNameParam(MethodMetadata data, String name, int i) {
? ? ? ? Collection<String>
? ? ? ? ? ? ? ? names =
? ? ? ? ? ? ? ? data.indexToName().containsKey(i) ? data.indexToName().get(i) : new ArrayList<String>();
? ? ? ? names.add(name);
? ? ? ? data.indexToName().put(i, names);
? ? }
? ? /**
? ? ?*
? ? ?* 多參數(shù)校驗(yàn)
? ? ?* @param configKey 類名#方法名
? ? ?* @return 參數(shù)是否為1個(gè)以上
? ? ?*/
? ? private boolean manyParameters(String configKey){
? ? ? ? Method method = processedMethods.get(configKey);
? ? ? ? return method.getParameterTypes().length > 1;
? ? }

最后還有一處修改

由于我們?cè)诜椒ㄉ蠜]有寫上RequestBody注解,所以此處需要進(jìn)行額外的處理

  • 只針對(duì)于帶有FeignClient的實(shí)現(xiàn)類才做特殊處理
  • 如果入?yún)榉亲远x對(duì)象,即為基本數(shù)據(jù)類型,則直接返回即可
  • 自定義對(duì)象,json轉(zhuǎn)換后再返回
@Configuration
public class CustomerArgumentResolvers implements HandlerMethodArgumentResolver {
? ? // 基本數(shù)據(jù)類型
? ? private static final Class[] BASE_TYPE = new Class[]{String.class,int.class,Integer.class,boolean.class,Boolean.class, MultipartFile.class};
? ? @Override
? ? public boolean supportsParameter(MethodParameter parameter) {
? ? ? ? //springcloud的接口入?yún)]有寫@RequestBody,并且是自定義類型對(duì)象 也按JSON解析
? ? ? ? if (AnnotatedElementUtils.hasAnnotation(parameter.getContainingClass(), FeignClient.class)) {
? ? ? ? ? ? if(parameter.getExecutable().getParameters().length<=1){
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return false;
? ? }
? ? @Override
? ? public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
? ? ? ? final Type type = parameter.getGenericParameterType();
? ? ? ? String parameters = getParameters(nativeWebRequest);
? ? ? ? if(applyType(type)){
? ? ? ? ? ? return parameters;
? ? ? ? }else {
? ? ? ? ? ? return JSON.parseObject(parameters,type);
? ? ? ? }
? ? }
? ? private String getParameters(NativeWebRequest nativeWebRequest) throws Exception{
? ? ? ? HttpServletRequest servletRequest = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
? ? ? ? String jsonBody = "";
? ? ? ? if(servletRequest!=null){
? ? ? ? ? ? ServletInputStream inputStream = servletRequest.getInputStream();
? ? ? ? ? ? jsonBody = ?IOUtils.toString(inputStream);
? ? ? ? }
? ? ? ? return jsonBody;
? ? }
? ? private boolean applyType(Type type){
? ? ? ? for (Class classType : BASE_TYPE) {
? ? ? ? ? ? if(type.equals(classType)){
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return false;
? ? }
}
@Configuration
public class CustomerConfigAdapter implements WebMvcConfigurer {
? ? @Resource
? ? private CustomerArgumentResolvers customerArgumentResolvers;
? ? @Override
? ? public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
? ? ? ? resolvers.add(customerArgumentResolvers);
? ? }
}

以上就是配置的所有內(nèi)容,整體代碼量很少。

但可能需要讀下源碼才能理解 

這些僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java使用CountDownLatch實(shí)現(xiàn)網(wǎng)絡(luò)同步請(qǐng)求的示例代碼

    Java使用CountDownLatch實(shí)現(xiàn)網(wǎng)絡(luò)同步請(qǐng)求的示例代碼

    CountDownLatch 是一個(gè)同步工具類,用來協(xié)調(diào)多個(gè)線程之間的同步,它能夠使一個(gè)線程在等待另外一些線程完成各自工作之后,再繼續(xù)執(zhí)行。被將利用CountDownLatch實(shí)現(xiàn)網(wǎng)絡(luò)同步請(qǐng)求,異步同時(shí)獲取商品信息組裝,感興趣的可以了解一下
    2023-01-01
  • Maven在Windows中的配置以及IDE中的項(xiàng)目創(chuàng)建實(shí)例

    Maven在Windows中的配置以及IDE中的項(xiàng)目創(chuàng)建實(shí)例

    下面小編就為大家?guī)硪黄狹aven在Windows中的配置以及IDE中的項(xiàng)目創(chuàng)建實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • Java中的分割字符串?split(“.”)無效問題

    Java中的分割字符串?split(“.”)無效問題

    這篇文章主要介紹了Java中的分割字符串?split(“.”)無效問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • 深入理解java線程通信

    深入理解java線程通信

    開發(fā)中不免會(huì)遇到需要所有子線程執(zhí)行完畢通知主線程處理某些邏輯的場(chǎng)景?;蛘呤蔷€程 A 在執(zhí)行到某個(gè)條件通知線程 B 執(zhí)行某個(gè)操作。下面我們來一起學(xué)習(xí)如何解決吧
    2019-05-05
  • Java實(shí)現(xiàn)DES加密與解密,md5加密以及Java實(shí)現(xiàn)MD5加密解密類

    Java實(shí)現(xiàn)DES加密與解密,md5加密以及Java實(shí)現(xiàn)MD5加密解密類

    這篇文章主要介紹了Java實(shí)現(xiàn)DES加密與解密,md5加密以及Java實(shí)現(xiàn)MD5加密解密類 ,需要的朋友可以參考下
    2015-11-11
  • 史上最佳springboot Locale 國際化方案

    史上最佳springboot Locale 國際化方案

    今天給大家分享史上最佳springboot Locale 國際化方案,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2021-08-08
  • java 按行讀取文件并輸出到控制臺(tái)的方法

    java 按行讀取文件并輸出到控制臺(tái)的方法

    今天小編就為大家分享一篇java 按行讀取文件并輸出到控制臺(tái)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • Java SpringBoot自動(dòng)裝配原理詳解及源碼注釋

    Java SpringBoot自動(dòng)裝配原理詳解及源碼注釋

    SpringBoot的自動(dòng)裝配是拆箱即用的基礎(chǔ),也是微服務(wù)化的前提。其實(shí)它并不那么神秘,我在這之前已經(jīng)寫過最基本的實(shí)現(xiàn)了,大家可以參考這篇文章,來看看它是怎么樣實(shí)現(xiàn)的,我們透過源代碼來把握自動(dòng)裝配的來龍去脈
    2021-10-10
  • 詳解Spring Security 中的四種權(quán)限控制方式

    詳解Spring Security 中的四種權(quán)限控制方式

    這篇文章主要介紹了詳解Spring Security 中的四種權(quán)限控制方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Oracle+Mybatis的foreach insert批量插入報(bào)錯(cuò)的快速解決辦法

    Oracle+Mybatis的foreach insert批量插入報(bào)錯(cuò)的快速解決辦法

    本文給大家介紹Oracle+Mybatis的foreach insert批量插入報(bào)錯(cuò)的快速解決辦法,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友參考下吧
    2016-08-08

最新評(píng)論