最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Feign?請求動態(tài)URL方式

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

Feign 請求動態(tài)URL

注意事項

  • FeignClient 中不要寫url, 使用 @RequestLine修飾方法
  • 調(diào)用地方必須引入 FeignClientConfiguration, 必須有Decoder, Encoder
  • 調(diào)用類必須以構(gòu)建函數(shù)(Constructor) 的方式注入 FeignClient 類
  • 傳入URL作為參數(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

背景

由于項目采用的是 消費層 + API層(FeignClient) +服務(wù)層 的方式。 

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

場景

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

效果展示

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

public interface UserDemoApi {
? ? /**
? ? ?* 新增用戶
? ? ?* @param userDemo 用戶實體
? ? ?* @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ù)端層:主要做的事情就是注冊RequestMapping.由于我們的api上是沒有寫任何注解的,所以我們需要在項目啟動的時候把a(bǔ)pi上的方法都注冊上去。另外一個要做的就是,由于我們不寫RequestBody,所以要做參數(shù)解析配置
  • 客戶端層:主要做的事情是,項目啟動的時候,feign會掃描api上的方法,在它掃描的同時,直接把url定下來存入RequestTemplate中。這樣即使后續(xù)feign在執(zhí)行apply沒有路徑時也不影響feign的正常請求。

實現(xiàn)

Feign的服務(wù)端層

1. 繼承RequestMappingHandlerMapping,重寫getMappingForMethod

  • METHOD_MAPPING 為自定義的路徑(即RequestMapping的value值)
  • 在服務(wù)層的實現(xiàn)類中,打上自定義注解@CustomerAnnotation,只有有該注解的實現(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

  • 實現(xiàn)api接口(即Feign層的接口)
  • 打上自定義的標(biāo)識注解@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 對象中獲取到RequestTemplate,后續(xù)的所有操作都是針對于這個
  • 獲取到類名以及方法名,作為RequestTemplate對象中url的值,此處應(yīng)與服務(wù)層的配置的URL路徑一致
  • 默認(rèn)請求方式都為POST

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

  • 判斷當(dāng)前方法的參數(shù)數(shù)量,如果不超過2個不做任何處理
  • 對于超過2個參數(shù)的方法,需要對其做限制。首先,方法必須滿足命名規(guī)范,即類似findByUserIdAndUsername。以By為起始,And作為連接。
  • 截取并獲取參數(shù)名稱
  • 將名稱按順序存入RequestTemplate對象中的querie屬性中。同時,要記得MethodMetadata 對象中的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 請求模板
? ? ?*/
? ? 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ù)校驗
? ? ?* @param configKey 類名#方法名
? ? ?* @return 參數(shù)是否為1個以上
? ? ?*/
? ? private boolean manyParameters(String configKey){
? ? ? ? Method method = processedMethods.get(configKey);
? ? ? ? return method.getParameterTypes().length > 1;
? ? }

最后還有一處修改

由于我們在方法上沒有寫上RequestBody注解,所以此處需要進(jìn)行額外的處理

  • 只針對于帶有FeignClient的實現(xiàn)類才做特殊處理
  • 如果入?yún)榉亲远x對象,即為基本數(shù)據(jù)類型,則直接返回即可
  • 自定義對象,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,并且是自定義類型對象 也按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)容,整體代碼量很少。

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

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

相關(guān)文章

  • springboot配置flyway(入門級別教程)

    springboot配置flyway(入門級別教程)

    本文介紹了springboot配置flyway,主要介紹基于SpringBoot集成flyway來管理數(shù)據(jù)庫的變更,具有一定的參考價值,感興趣的可以了解一下
    2023-09-09
  • SpringBoot中的@CrossOrigin注解詳解

    SpringBoot中的@CrossOrigin注解詳解

    這篇文章主要介紹了SpringBoot中的@CrossOrigin注解詳解,跨源資源共享(CORS)是由大多數(shù)瀏覽器實現(xiàn)的W3C規(guī)范,允許您靈活地指定什么樣的跨域請求被授權(quán),而不是使用一些不太安全和不太強(qiáng)大的策略,需要的朋友可以參考下
    2023-11-11
  • Spring覆蓋容器中Bean的注解如何實現(xiàn)@OverrideBean

    Spring覆蓋容器中Bean的注解如何實現(xiàn)@OverrideBean

    文章介紹了在項目開發(fā)中如何通過偷梁換柱的方式重寫Spring容器中的內(nèi)置Bean,并指出了需要注意的兩點:1. 對應(yīng)的Bean應(yīng)基于接口注入;2. 如果不是基于接口注入,可以使用同包名同類名的方式重寫(可能存在潛在問題,不推薦),文章還強(qiáng)調(diào)了“基于接口編程”的好處
    2025-01-01
  • WxJava微信公眾號開發(fā)入門實戰(zhàn)

    WxJava微信公眾號開發(fā)入門實戰(zhàn)

    本文主要介紹了WxJava微信公眾號開發(fā)入門實戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Springboot+ElementUi實現(xiàn)評論、回復(fù)、點贊功能

    Springboot+ElementUi實現(xiàn)評論、回復(fù)、點贊功能

    這篇文章主要介紹了通過Springboot ElementUi實現(xiàn)評論、回復(fù)、點贊功能。如果是自己評論的還可以刪除,刪除的規(guī)則是如果該評論下還有回復(fù),也一并刪除。需要的可以參考一下
    2022-01-01
  • Java如何限制IP訪問頁面

    Java如何限制IP訪問頁面

    這篇文章主要介紹了Java如何限制IP訪問頁面,幫助大家完成需求,實現(xiàn)白名單,感興趣的朋友可以了解下
    2020-09-09
  • java-thymeleaf的使用方式

    java-thymeleaf的使用方式

    這篇文章主要介紹了java-thymeleaf的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • SpringBoot項目部署到服務(wù)器上的方法(Jar包)

    SpringBoot項目部署到服務(wù)器上的方法(Jar包)

    這篇文章主要介紹了SpringBoot項目部署到服務(wù)器上的方法(Jar包),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • spring中@Configuration和@Bean注解的用法

    spring中@Configuration和@Bean注解的用法

    這篇文章主要介紹了spring中@Configuration和@Bean注解的用法,@Configuration用于定義配置類,可替換xml配置文件,被注解的類內(nèi)部包含有一個或多個被@Bean注解的方法,需要的朋友可以參考下
    2023-05-05
  • java循環(huán)結(jié)構(gòu)、數(shù)組的使用小結(jié)

    java循環(huán)結(jié)構(gòu)、數(shù)組的使用小結(jié)

    這篇文章主要介紹了java循環(huán)結(jié)構(gòu)、數(shù)組的使用小結(jié),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09

最新評論

图们市| 余姚市| 长沙县| 墨玉县| 姜堰市| 沧州市| 卓资县| 黄大仙区| 永昌县| 慈利县| 莆田市| 行唐县| 达州市| 苍南县| 德令哈市| 米易县| 武威市| 威信县| 巢湖市| 千阳县| 蚌埠市| 尼木县| 陈巴尔虎旗| 昌图县| 高要市| 广丰县| 银川市| 托克托县| 北海市| 宜阳县| 海晏县| 铜川市| 尚志市| 景德镇市| 沧源| 玉山县| 墨竹工卡县| 长乐市| 泽普县| 宁晋县| 商都县|