Et tilpasset databinder i vår-MVC

1. Oversikt

Denne artikkelen vil vise hvordan vi kan bruke Spring's Data Binding-mekanisme for å gjøre koden vår mer tydelig og lesbar ved å bruke automatiske primitiver til objektkonvertering.

Som standard vet Spring bare hvordan man konverterer enkle typer. Med andre ord, når vi sender inn data til kontrolleren Int, String eller Boolsk type data, vil den automatisk være bundet til passende Java-typer.

Men i virkelige prosjekter vil det ikke være nok, som vi kan trenge å binde mer komplekse typer objekter.

2. Binde individuelle objekter til å be om parametere

La oss starte enkelt og først binde en enkel type; Vi må gi en tilpasset implementering av Konverter grensesnitt hvor S er typen vi konverterer fra, og T er typen vi konverterer til:

@Komponent offentlig klasse StringToLocalDateTimeConverter implementerer Converter {@Override public LocalDateTime convert (String source) {return LocalDateTime.parse (source, DateTimeFormatter.ISO_LOCAL_DATE_TIME); }}

Nå kan vi bruke følgende syntaks i kontrolleren vår:

@GetMapping ("/ findbydate / {date}") offentlig GenericEntity findByDate (@PathVariable ("date") LocalDateTime dato) {retur ...; }

2.1. Bruke Enums som forespørselsparametere

Deretter får vi se hvordan du bruker enum som en RequestParameter.

Her har vi en enkel enumModi:

public enum Modes {ALPHA, BETA; }

Vi bygger en String til enum Converter som følger:

offentlig klasse StringToEnumConverter implementerer Converter {@Override public Modes convert (String from) {return Modes.valueOf (from); }}

Deretter må vi registrere våre Konverter:

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

Nå kan vi bruke vår Enum som en RequestParameter:

@GetMapping offentlig ResponseEntity getStringToMode (@RequestParam ("modus") Modus-modus) {// ...}

Eller som en PathVariable:

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

3. Binding av et hierarki av objekter

Noen ganger trenger vi å konvertere hele treet i objekthierarkiet, og det er fornuftig å ha en mer sentralisert binding i stedet for et sett med individuelle omformere.

I dette eksemplet har vi AbstraktEnhet vår basisklasse:

offentlig abstrakt klasse AbstractEntity {lang id; offentlig AbstractEntity (lang id) {this.id = id; }}

Og underklassene Foo og Bar:

offentlig klasse Foo utvider AbstractEntity {privat strengnavn; // standard konstruktører, getters, setters}
public class Bar utvider AbstractEntity {private int-verdi; // standard konstruktører, getters, setters}

I dette tilfellet, vi kan implementere ConverterFactory hvor S vil være typen vi konverterer fra og R til å være basistypen definere rekkevidden av klasser vi kan konvertere til:

offentlig klasse StringToAbstractEntityConverterFactory implementerer ConverterFactory {@Override public Converter getConverter (Class targetClass) {returner ny StringToAbstractEntityConverter (targetClass); } privat statisk klasse StringToAbstractEntityConverter implementerer Converter {private Class targetClass; public StringToAbstractEntityConverter (Class targetClass) {this.targetClass = targetClass; } @ Override public T convert (Strengkilde) {long id = Long.parseLong (source); hvis (this.targetClass == Foo.class) {return (T) new Foo (id); } annet hvis (this.targetClass == Bar.class) {return (T) new Bar (id); } annet {return null; }}}}

Som vi kan se, er den eneste metoden som må implementeres getConverter () som returnerer omformer for nødvendig type. Konverteringsprosessen delegeres deretter til denne omformeren.

Deretter må vi registrere våre ConverterFactory:

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

Til slutt kan vi bruke det som vi vil i kontrolleren vår:

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

4. Bindende domeneobjekter

Det er tilfeller når vi ønsker å binde data til objekter, men de kommer enten på en ikke-direkte måte (for eksempel fra Økt, Overskrift eller Kjeks variabler) eller til og med lagret i en datakilde. I slike tilfeller må vi bruke en annen løsning.

4.1. Tilpasset argumentløsning

Først og fremst vil vi definere en kommentar for slike parametere:

@Retention (RetentionPolicy.RUNTIME) @Target (ElementType.PARAMETER) offentlig @interfaceversjon {}

Deretter vil vi implementere en tilpasset HandlerMethodArgumentResolver:

public class HeaderVersionArgumentResolver implementerer HandlerMethodArgumentResolver {@Override public boolean supportsParameter (MethodParameter methodParameter) {return methodParameter.getParameterAnnotation (Version.class)! = null; } @Override public Object resolutionArgument (MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) kaster unntak {HttpServletRequest request = (HttpServletRequRative) return request.getHeader ("Versjon"); }}

Det siste er å la Spring vite hvor de skal søke etter dem:

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

Det er det. Nå kan vi bruke den i en kontroller:

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

Som vi kan se, HandlerMethodArgumentResolver‘S løseArgument () metoden returnerer en Gjenstand. Med andre ord, vi kunne returnere ethvert objekt, ikke bare String.

5. Konklusjon

Som et resultat ble vi kvitt mange rutinemessige konverteringer og lot Spring gjøre det meste for oss. La oss avslutte på slutten:

  • For en enkelt enkelt type å motsette konverteringer, bør vi bruke Konverter gjennomføring
  • For innkapsling av konverteringslogikk for en rekke objekter kan vi prøve ConverterFactory gjennomføring
  • For data kommer indirekte, eller det kreves å bruke tilleggslogikk for å hente tilknyttede data, det er bedre å bruke HandlerMethodArgumentResolver

Som vanlig kan alle eksemplene alltid bli funnet på GitHub-depotet vårt.


$config[zx-auto] not found$config[zx-overlay] not found