Java : Parfait pour les applications monopages!
Java est toujours un leader sur le marché des langages de programmation. C’en est un fiable, robuste et typé et à peine 30% plus lent que le C dans la plupart des cas. C’est un heureux mélange entre la liberté et la rigidité, en plus d’être le langage de prédilection dans la conception d’applications mobiles.
Malheureusement (ou heureusement!), l’ère JSP/JSF tire à sa fin. Les développeurs Java traditionnels doivent donc s’adapter au marché des nouveaux frameworks comme React ou Angular afin de pouvoir remplir la demande de talents toujours grandissante.

Adaptation de MV* aux SPA
Si vous avez utilisé JSF (PrimeFaces, IceFaces, etc.), vous avez probablement toujours travaillé dans une architecture du type MV* (MVC, MVVM, MVP, etc.). Les éléments de la vue sont liés au back-end par des requêtes Ajax sans qu’on ait à faire quoique ce soit d’autre que :
1 |
<p:commandButton value="Do it" actionListener="#{buttonView.buttonAction}" /> |
Magiquement, lorsqu’on clique sur ce bouton, une action est exécutée dans le back-end en Java (la méthode buttonAction de buttonView ). On peut très bien s’imaginer le code JavaScript généré et tous les intermédiaires entre l’entrée de la requête dans le serveur et l’exécution de notre code. Tout ça, généré sur notre pauvre instance Tomcat et dégluti au navigateur.
Heureusement, on peut optimiser! Grâce à une application web monopage, on décharge le serveur applicatif de générer la vue, puis le navigateur va demander les données nécessaires à l’affichage et mettre à jour le DOM. Ces demandes sont faites via une API qu’on qualifie de RESTful :
- Sans état
- Cacheable
- Orienté client-serveur
- Avec une interface uniforme
- Avec un système de couche
- Un code à la demande (optionnel)
JSON sera utilisé comme format de données pour la communication.
Les dépendances
Mon choix de techno pour cet exemple est assez traditionnel. Nous allons utiliser Spring roulant sur Tomcat. La librairie utilisée pour le JSON est Jackson qui s’occupera du processus de sérialisation. Engraissons notre pom.xml :
1 2 3 4 5 6 7 8 9 10 11 12 |
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.11.RELEASE</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.1</version> </dependency> </dependencies> |
Configuration de Spring
À mes tous débuts avec Spring (2011), il y avait encore des applications configurées avec des centaines de fichiers XML. Ajourd’hui, je vous propose quelque chose de beaucoup plus léger pour être sur pieds en un éclair.
AppConfig
Cette classe est la base de la configuration de notre serveur. Nous utiliserons les annotations plutôt que les fichiers XML, ce qui est beaucoup facile à comprendre. Un langage, c’est déjà assez!
Créer une nouvelle classe appelée AppConfig. Cette classe doit hériter de WebMvcConfigurerAdapter.
1 2 3 4 5 6 7 8 9 10 |
@Configuration @EnableWebMvc @ComponentScan(basePackages = "com.ezoqc.blog.javarest") public class AppConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(final ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("/"); } } |
L’annotation @ComponentScan a une propriété basePackages qui prend une ou plusieurs chaînes de caractères qui correspondent aux packages ou se trouvent les composants Spring. C’est tout ce qu’il y a à voir ici.
AppInitializer
Ensuite, il faut initialiser l’application et Spring en lui disant simplement ou trouver la classe de configuration.
Créer une classe appelée AppInitializer qui hérite de AbstractAnnotationConfigDispatcherServletInitializer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class AppIntializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[] { AppConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return null; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } } |
Ici, la méthode getRootConfigClasses() doit retourner la classe créée à l’étape précédente et getServletMappings() est simplement configuré sur la racine.
Si vous démarrez un serveur Tomcat et y installez l’application, tout devrait rouler sans erreur.
1 |
INFO: Server startup in 4585 ms |
Les APIs
Pour démontrer comment s’y prendre pour créer les APIs, nous allons créer un point d’entrée : les salutations.
Dis-moi « Bonjour »!
Création du point d’entrée
Créer une classe GreetingsAPI dans le package déclaré dans l’annotation @ComponentScan de la classe AppConfig. Les méthodes de cette classes seront celles appelées lorsque le serveur recevra une requête. Pour ce faire, décorer la classe de @RestController afin qu’elle soit chargée en mémoire. Gardez en tête que cette classe n’est pas responsable de la logique d’affaires. Elle ne sert sert qu’à répondre à la requête, formater les paramètres pour communiquer avec une couche métier plus profonde, et formater la réponse en sortie.
Ensuite, créer une méthode sayHi() comme suit:
1 2 3 4 5 6 7 8 9 |
@RestController public class GreetingsAPI { public static final String ROOT = "/greetings"; @RequestMapping(path = ROOT + "/sayHi", method = RequestMethod.GET) public String sayHi() { return "hi!"; } } |
Si vous démarrez Tomcat et que vous accédez à http://localhost:8081/HelloAPI/greetings/sayHi, vous devriez voir « hi! » affiché!
Retour en JSON
Évidemment, ce n’est pas très utile comme service web. Du moins, il devrait retourner un objet sous le format JSON (et faire plus!). Pour se faire, nous allons utiliser un patron de conception très commun, l’objet de transfert de données ou DTO (Data Transfer Object).
Créer une classe APIResponseDTO comme suit:
1 2 3 4 5 6 7 8 |
public class APIResponseDTO { public Date time = Date.from(Instant.now()); public String message; public APIResponseDTO(String message) { this.message = message; } } |
PUBLIC?! Et l’encapsulation?!
Assurément. Je n’ai aucun n’intérêt à ajouter du code pour ajouter du code. L’encapsulation sert à s’assurer que l’objet qui représente une entité a 100% le contrôle sur son état, donc ses attributs. Ainsi, l’objet offre une API pour contrôler son état, mais ici on ne crée l’objet que pour avoir une structure de données à sérialiser en JSON.
Bref, il faut ensuite modifier le type de retour de la méthode sayHi() pour APIResponseDTO et retourner une nouvelle instance comme ceci :
1 2 3 4 5 6 7 8 9 |
@RestController public class GreetingsAPI { public static final String ROOT = "/greetings"; @RequestMapping(path = ROOT + "/sayHi", method = RequestMethod.GET) public APIResponseDTO sayHi() { return new APIResponseDTO("Hi!"); } } |
Si on test, on obtient :
1 2 3 4 |
{ responseDate: 1506461363664, message: "Hi!" } |
Injection d’un service
On peut s’imaginer que dans une application de production, ce soit bien séparé en couches. D’où son appellation d’architecture en « oignon ». Nous aurons donc besoin d’aller y greffer une couche de services d’affaires (lien en ang.). Ici, le service répondra simplement « Hi [nom]! »…
Créer une interface GreetingsServiceInterface pour découpler la couche d’API et la couche de services :
1 2 3 |
public interface GreetingsServiceInterface { String sayHi(String name); } |
Puis, il faut simplement implémenter et décorer des annotations de Spring pour que cette classe soit chargée par le framework :
1 2 3 4 5 6 7 8 9 |
@Service public class GreetingsService implements GreetingsServiceInterface { @Override public String sayHi(String name) { return new StringBuffer().append("Hi ").append(name).append("!").toString(); } } |
Paramètres d’entrée
Lorsqu’il est question d’un service web RESTful, les paramètres des requêtes GET sont passés dans l’URL. Par exemple:
http://site.com/api/user/10293810/photo
On peut comprendre que le service retourne une photo de l’utilisateur identifiable par 10293810. Donc, notre API doit pouvoir accepter un paramètre, soit le nom de la personne à saluer. Pour ce faire, il faut l’ajouter au point d’entrée défini dans l’annotation @RequestMapping de GreetingsAPI , puis ajouter un paramètre à la méthode sayHi() (le nom) et le décorer de l’annotation @PathVariable. Cette dernière annotation définit le nom du paramètre de l’URL, la liaison se fait seule. Puis, on injecte le service à l’aide de l’annotation @Autowired :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@RestController public class GreetingsAPI { public static final String ROOT = "/greetings"; @Autowired private GreetingsServiceInterface greetings; @RequestMapping(path = ROOT + "/sayHi/{name}", method = RequestMethod.GET) public APIResponseDTO sayHi(@PathVariable("name") String name) { String message = this.greetings.sayHi(name); return new APIResponseDTO(message); } } |
Dernier test!
http://localhost:8081/HelloAPI/greetings/sayHi/Donald
1 2 3 4 |
{ time: 1506463806224, message: "Hi Donald!" } |
Conclusion
Dans cet article, vous avez appris à créer une belle architecture prête pour accueillir n’importe quel type d’interface graphique. Vous pourriez aisément y greffer une application Angular, Cordova, iOS et même en console avec cURL!
Par contre, la sécurité est très importante pour éviter toute forme d’exploitation ou de piratage. Il existe plusieurs moyens de très simples à holy moly pour sécuriser les API, mais… c’est le sujet d’un prochain article 😉
N’hésitez surtout pas à partager car chaque vue nous encourage à continuer de produire du contenu gratuit! De plus, utilisez les commentaires ci-dessous pour me donner votre opinion! J’ai bien hâte d’engager la discussion avec vous!
Commentaires
Laisser un commentaire