1. dec. 2009 | Øvrige artikler
Java EE 6 står i tilgængelighedens tegn
I denne artikel fortæller Kasper Sørensen om et par af de helt nye ting fra Java EE 6: Bean Validation og Contexts and Dependency Injection. Disse udvidelser til Java EE platformen har stor indflydelse på hele arkitekturen. Kort sagt vil det få stor betydning for måden man komponerer moduler og styrer disses livscyklus - begge dele på tværs af alle tiers i Java EE platformen.
Den 10. december udkom Java Enterprise Edition (Java EE) 6 specifikationen, samt Sun's reference implementation, Glassfish. Der går typisk altid lidt tid før de andre store Java EE produkter kommer med på nyeste version, men i mellemtiden kan vi med rimelighed begynde at gøre os nogle forventninger. Jeg vil i denne artikel gennemgå et par ting, som jeg tror bliver helt centrale for Java EE 6.
Java EE 6 bliver nok ikke en lige så omfattende omvæltning som Java EE 5 var ifht. J2EE 1.4. Dermed ikke sagt at der ikke er store ændringer i udsigt - for det er der i høj grad! Der er tale om en videreudvikling af Enterprise Edition, som får komponenterne i arkitekturen til at hænge meget bedre sammen og får forenet de ting, som tidligere har båret præg af lidt for mange uafhængige forsøg på at løse de samme problemer. Ændringerne er altså ikke så omvæltende, men det er mit bud, at Java EE 6 kommer til at tilføje EE-platformen mere værdi end tidligere releases har gjort – netop fordi der fokuseres på at få delene til at spille bedre sammen.
Bean Validation
Et eksempel på denne forenede tilgang til problemerne er Bean Validation. Bean Validation er en nyhed i Java EE 6, som tilbyder data-validering på en ensartet måde i hele arkitekturen (eksempelvis ved input fra brugeren, som en del af batch-job eller før indsættelse i databasen). Det er nok ingen overraskelse, at Bean Validation har haft indflydelse på mange områder: Servlets, EJB, JavaServer Faces, JAX-WS mfl. Hvis man vil benytte Bean Validation allerede nu, er det faktisk muligt - der eksisterer en reference implementation, nemlig Hibernate Validator 4.x.Dependency Injection
En anden meget spændende ting i Java EE 6 er henholdsvis Contexts and Dependency Injection for the Java EE Platform (tidl. kendt som “WebBeans”) og Dependency Injection for Java. Begge dele inkluderes i Java EE 6 og handler om, hvordan man tilgår komponenter i Java EE - eller rettere sagt, hvordan de gøres tilgængelige. Som mange nok ved, er Dependency Injection et mønster for, hvordan man “injecter” afhængigheder istedet for at en afhængighed skal “slås op” - det sparer os for en masse kode og det gør administrationen af afhængigheder imellem komponenter meget lettere at håndtere. I Java EE 6 bliver Dependency Injection en helt central mekanisme i platformen. En af de grundlæggende principper bag Dependency Injection i EE 6 er “løs kobling med typestærk kvalificering”. Løs kobling i denne forbindelse henviser til et velkendt princip for Dependency Injection: Hvis modul A skal bruge en instans af B, bør A ikke nødvendigvis kende hvilken konkret B der er tale om. Den bør heller ikke kende til B's livscyklus. Dette princip opfordrer bl.a. til at man koder mod interfaces, så afhængigheder let kan udskiftes. Typestærk kvalificering henviser til behovet for at kunne foretage wiring af afhængigheder på en typestærk måde. Mange vil være bekendt med Spring Frameworks XML-baserede wiring, hvor man navngiver sine beans og benytter disse, når man injecter. Selvom denne XML-løsning giver os Dependency Injection, hvilket klart er et fremskridt, så er der mange problemer forbundet med den: Løsningen er ikke typestærk og kan derfor ikke compile-time checkes, hvorfor den er svær at vedligeholde/refaktorere.Java EE 6's løsning
I Java EE 6 er der derfor brugt såkaldte Qualifier-annotationer til at kvalificere, hvad der skal injectes. Hvis der kun findes én konkret implementation af typen vi vil injecte, behøves qualifiers ikke. Men antag at vi vil injecte noget (fx en service til registrering af ordrer) som der findes flere af (fx en synkron og en asynkron variant) - så skal vi bruge noget til at kvalificere dem, så vi kan skelne den ene fra den anden. Vi definerer derfor vores egne qualifier annotationer (fx. @Asynchronous og @Synchronous):public interface OrderService { ... }
@Asynchronous
public class AsyncOrderService implements OrderService { ... }
@Synchronous
public class SynchronousOrderService implements OrderService {...}
Som sagt er de to annotationer i eksemplet ovenfor nogle vi selv har defineret. Disse annotationer skal yderligere annoteres med @Qualifier. Eksempelvis:
@Qualifier
@Retention(RUNTIME)
public @interface Asynchronous {
}
Når vi skal injecte en OrderService, skal vi kvalificere hvilken type vi er interesseret i. Vi benytter qualifier annotationerne til at foretage en løst koblet kvalificering, da vi ikke ønsker at referere stærkt til den ene eller anden implementation (de skal jo kunne udskiftes). Når vi injecter benyttes altid @Inject annotationen også:
public class MyBean {
@Inject @Asynchronous
OrderService orderService;
public void myMethod() {
// her kan jeg benytte "orderService"
}
}
Hvis dette virker kompliceret, så husk: Vi skal kun benytte qualifier annotationer i de tilfælde hvor vi ØNSKER den løse kobling kombineret med udskifteligheden. Vi kan undlade dem fuldstændigt ved fx at injecte noget mere konkret (“mindre løst” koblet):
@Inject AsyncOrderService asyncOrderService;Noget der er exceptionelt lækkert ved EE 6 ifht. tidligere versioner er, at hvis man har brug for eksempelvis ServletContexten, en BeanValidator, EntityManager'en eller lign., så skal man “bare” injecte den. Det fører til, at man i høj grad kan vælge hvor tier-opdelt man ønsker sin applikation skal være. Det betyder også, at alting pludseligt er tilgængeligt. Hvor ofte har man ikke siddet og bandet over at man fx. ikke kunne trække et objekt ud af sessionen fordi HttpSession-objektet ikke var med i parameter-listen på den metode man sad og udviklede i? De dage er forbi