جاوا و تکنولوژی های آن

java programming language

در این وبلاگ به بررسی نکات موجود در جاوا و تکنولوژی های آن می پردازیم

طبقه بندی موضوعی


در Spring توسط مکانیزم Data Binding میتوان داده ها را به نوع های ساده مثل int, String, boolean تبدیل کرد و انها را در قالب Object دریافت کرد.

ولی در عمل ما با دیتا های پیچیده تری روبرو خواهیم بود که مکانیزم تبدیل آنها را باید خودمان تعیین کنیم


ابتدا با یک مثال ساده شروع میکنیم

در Spring اینترفیس < Converter< S , T برای اینکار وجود دارد که S دیتایی که دریافت میکند و T دیتایی است که تبدیل کرده و برمی گرداند مانند :

@Component
public class StringToLocalDateTimeConverter
  implements Converter<String, LocalDateTime> {
 
    @Override
    public LocalDateTime convert(String source) {
        return LocalDateTime.parse(
          source, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
    }
}

حالا میتوانیم این Component را در Controller استفاده کنیم :

@GetMapping("/findbydate/{date}")
public GenericEntity findByDate(@PathVariable("date") LocalDateTime date) {
    return ...;
}


آیا میتوان در RequestParam@ مقادیر را به Enum هم تبدیل کنیم ؟ مثلا :

public enum Modes {
    ALPHA, BETA;
}

ابتدا برای آن یک تبدیل کننده مینویسیم :

public class StringToEnumConverter implements Converter<String, Modes> {
 
    @Override
    public Modes convert(String from) {
        return Modes.valueOf(from);
    }
}

حالا آنرا ثبت میکنیم :

@Configuration
public class WebConfig implements WebMvcConfigurer {
 
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToEnumConverter());
    }
}

و در Controller توسط RequestParam@ میتوان آنرا دریافت و تبدیل به نوع Enum ثبت شده کرد :

@GetMapping
public ResponseEntity<Object> getStringToMode(@RequestParam("mode") Modes mode) {
    // ...
}

و یا حتی بصورت PathVariable دریافت کرد :

@GetMapping("/entity/findbymode/{mode}")
public GenericEntity findByEnum(@PathVariable("mode") Modes mode) {
    // ...
}


حال کمی دیتا را پیچیده تر میکنیم و یک کلاس Abstract پدر را فرض کنید که تعدادی فرزند دارد و میخواهیم با استفاده همان پدر مشترک مابقی فرزندان توسط یک مبدل مشترک بصورت ابجکت تبدیل کنیم 


کلاس پدر :

public abstract class AbstractEntity {
    long id;
    public AbstractEntity(long id){
        this.id = id;
    }
}

که دو فرزند دارد :

public class Foo extends AbstractEntity {
    private String name;
     
    // standard constructors, getters, setters
}
public class Bar extends AbstractEntity {
    private int value;
     
    // standard constructors, getters, setters
}


برای اینکه تبدیل کننده آنر بنویسیم نیاز به <ConverterFactory<S, R داریم :


public class StringToAbstractEntityConverterFactory 
  implements ConverterFactory<String, AbstractEntity>{
 
    @Override
    public <T extends AbstractEntity> Converter<String, T> getConverter(Class<T> targetClass) {
        return new StringToAbstractEntityConverter<>(targetClass);
    }
 
    private static class StringToAbstractEntityConverter<T extends AbstractEntity>
      implements Converter<String, T> {
 
        private Class<T> targetClass;
 
        public StringToAbstractEntityConverter(Class<T> targetClass) {
            this.targetClass = targetClass;
        }
 
        @Override
        public T convert(String source) {
            long id = Long.parseLong(source);
            if(this.targetClass == Foo.class) {
                return (T) new Foo(id);
            }
            else if(this.targetClass == Bar.class) {
                return (T) new Bar(id);
            } else {
                return null;
            }
        }
    }
}


کلاس Converter ساخته شده را ثبت میکنیم :

@Configuration
public class WebConfig implements WebMvcConfigurer {
 
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(new StringToAbstractEntityConverterFactory());
    }
}


و در نهایت در Controller از آن استفاده میکنیم :

@RestController
@RequestMapping("/string-to-abstract")
public class AbstractEntityController {
 
    @GetMapping("/foo/{foo}")
    public ResponseEntity<Object> getStringToFoo(@PathVariable Foo foo) {
        return ResponseEntity.ok(foo);
    }
     
    @GetMapping("/bar/{bar}")
    public ResponseEntity<Object> getStringToBar(@PathVariable Bar bar) {
        return ResponseEntity.ok(bar);
    }
}



Bind کردن Domain Object :


گاهی دیتای ما که قرار است Bind شود بصورت مستقیم دریافت نمیشود مثلا از header, session, cookie قرار است آنرا دریافت کنیم و یا در یک data source ای ذخیره شده است در این دو حالت ما نیاز داریم روش دیگری را در پیش بگیریم 


برای همچین پارامتر هایی اول ما نیاز داریم یک annotation تعریف کنیم :

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Version {
}

حالا نیاز داریم که اینترفیس HandlerMethodArgumentResolver را پیاده سازی کنیم این اینترفیس یک استراتژی برای پیدا کردن مقادیر آرگومان های متد از دیتای ارسالی است 

public class HeaderVersionArgumentResolver
  implements HandlerMethodArgumentResolver {
 
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.getParameterAnnotation(Version.class) != null;
    }
 
    @Override
    public Object resolveArgument(
      MethodParameter methodParameter, 
      ModelAndViewContainer modelAndViewContainer, 
      NativeWebRequest nativeWebRequest, 
      WebDataBinderFactory webDataBinderFactory) throws Exception {
  
        HttpServletRequest request 
          = (HttpServletRequest) nativeWebRequest.getNativeRequest();
 
        return request.getHeader("Version");
    }
}


و در انتهای کار به Spring اجازه میدهیم نحوه پیدا کردن دیتا را بشناسد :

@Configuration
public class WebConfig implements WebMvcConfigurer {
 
    //...
 
    @Override
    public void addArgumentResolvers(
      List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new HeaderVersionArgumentResolver());
    }
}

حالا میتوانیم آنرا در Controller استفاده کنیم :

@GetMapping("/entity/{id}")
public ResponseEntity findByVersion(
  @PathVariable Long id, @Version String version) {
    return ...;
}


همانگونه که میبینیم مقدار برگشتی متد resolveArgument در کلاس پیاده سازی شده HandlerMethodArgumentResolver یک Object است و نه String




نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی