Nous voici arrivé sur notre dernier article sur les évolutions de
Java. Nous avons vu les évolutions depuis la version 5 avec les articles
suivant:
Ces évolutions ont été variées, certaines syntaxiques (les
boucles, les lambda expressions par exemple) et d'autres conceptuelles
(le generiques, les streams, etc...). Certaines de ces évolutions ont
ete majeures, d'autres moins. Grâce a cela, en plus de ses atout
initiaux, Java s'est imposé avec toutes ces années comme un langage de
programmation incontournable au même titre qu'un C++.
Aujourd'hui
nous en sommes a la version 9 de java. Sortie en septembre 2017, celle
nouvelle version propose une évolution importante : les modules.
Avec
cette évolution, sur laquelle nous reviendrons par la suite, Java
marque le besoin de revenir a une dimension résonné du chargement en
mémoire at runtime. Ceci parlera forcement au adepte de la technologie
OSGI qui existe elle depuis déjà une bonne dizaines d'années (on peut
même se demander pourquoi une convergence des deux techno n'a pas être
proposé....)
Voyons donc les évolutions de cette version 9.
Des méthodes privées dans les interfaces
Une nouvelle
fonctionnalité de Java 9 est de permettre la
définition de méthode privée
dans les interfaces permettant de simplifier l’écriture et la
maintenabilité des méthodes par défaut des interfaces
L'API Collections et Streams
Les collections et les streams vont
benefier de
nouvelles methodes et entre autre la possibilité de
construire des listes immutables sans devoir les initialisés
laborieusement.
1
2
3
4
5
| List<String> maList = new ArrayList<>();
maList.add("Toto");
maList.add("Tata");
maList.add("Tutu");
maList = Collections.unmodifiableList(maList);
|
On ecrira:
1
| maList = List.of("Toto", "Tata", "Tutu");
|
Multi-Release JAR
Il
est desormais possible de packager dans un meme jar, differentes
version d'une meme classe afin de garantir la compatibilité descendante.
JShell
Java 9 propose maintenant une shell
permettant comme pour python ou groovy d’interpréter du code java en
suivant le modèle
REPL (Read, Evaluate, Print, Loop) afin de faciliter
l'apprentissage (accessoirement) et le prototypage.
L'API
reactive stream est issu de la collaboration d'un certain nombre
d'acteur tels que Kaazing, Netflix, Pivotal, Red Hat, Twitter, Typesafe.
Cette API se base sur les principes de la programmation réactive
decliné selon
4 axes principaux.
- Responsive : Le système doit répondre aussi vite que possible
- Resilient : Le système doit être capable d’être réactif même
face a des erreurs. On y répond par la réplication, l'isolation, la
délégation et la mise en conteneur (typiquement les technologies
microservice, docker, kubernetes)
- Elastic : Le système doit être capable de monter en charger
rapidement, de s'adapter aux nouvelles demandes. Cela passe par la
vitesse l'allocation des ressources, sa capacité à se répliquer pour
absorber de la charge
- Message-driven : Fonctionne sur un système de communication
interne de messages asynchrones pour le support des flux de données et
de contrôle (par exemple integration de Spring et apache Camel)
La programmation réactive est guidée par
l'utilisation des messages. Cela nécessite de traiter les erreurs comme
des messages. De même, les traitements doivent être gérer comme des
flux, par bloc et non dans une globalité de façon a ne pas avoir de
situation bloquante. ( On peut prendre facilement comme exemple un
système réactif comme nodeJS)
A coté de l'API ReactiveStream existe bien sur d'autres API pour l'utilisation des concepts décrits ci dessus.
On notera par exemple RxJava, Vert.x, RatPack, Akka Streams ou encore Reactor. Certains sont polyglottes et pourront faire l'affaire d'articles dédiés
Dans le concret, l'
API Reactive Stream (java.util.concurrent.Flow) est basé sur le pattern
observateur.
A ceci prêt que le Sujet est appelé Publisher, l'observateur,
Subscriber. Le pattern observateur n'interdit pas qu'un observateur soit
lui même observé, ici on ne préconise pas de le faire, au contraire et
on utilisera plutôt le Processor pour réaliser des relais entre
Publisher et Subscriber.
Exemple:
ConcretPublisher:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| public class ConcretPublisher implements Publisher<String>,Consumer<String>{
public Subscriber<String> arg0;
@Override
public void accept(String arg0) {
this.arg0.onNext(arg0);
}
@Override
public void subscribe(Subscriber<? super String> arg0) {
this.arg0=(Subscriber<String>) arg0;
}
}
|
ConcretProcessor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public class ConcretProcessor implements Processor<String,Integer>{
private Subscriber<? super Integer> arg0;
@Override
public void onNext(String arg0) {
System.out.println("On passe la main apres avoir string to int");
this.arg0.onNext(Integer.parseInt(arg0));
}
@Override
public void subscribe(Subscriber<? super Integer> arg0) {
this.arg0=arg0;
}
}
|
ConcretSubscriber
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| public class ConcretSubscriber implements Subscriber<Integer>{
@Override
public void onComplete() {
System.out.println("ConcretSubscriber Complete");
}
@Override
public void onError(Throwable arg0) {
arg0.printStackTrace();
}
@Override
public void onNext(Integer arg0) {
System.out.println("Vla ma valeur "+arg0);
}
}
|
Main
1
2
3
4
5
6
7
8
9
| ConcretPublisher concretPublisher= new ConcretPublisher();
ConcretSubscriber concretSubscriber=new ConcretSubscriber();
ConcretProcessor concretProcessor=new ConcretProcessor();
concretPublisher.subscribe(concretProcessor);
concretProcessor.subscribe(concretSubscriber);
List<String> items = List.of("1", "2", "3", "4", "5", "6");
items.forEach(concretPublisher);
|
Nous donne:
1
2
3
4
5
6
7
8
9
10
11
12
| On passe la main apres avoir string to int
Vla ma valeur 1
On passe la main apres avoir string to int
Vla ma valeur 2
On passe la main apres avoir string to int
Vla ma valeur 3
On passe la main apres avoir string to int
Vla ma valeur 4
On passe la main apres avoir string to int
Vla ma valeur 5
On passe la main apres avoir string to int
Vla ma valeur 6
|
Un concept supplémentaire est le
Subscription qui permet au Subscriber de gérer son abonnement auprès du Publisher. L'article
ci et
ci, illustre parfaitement les détails relevant de ce point de l'API.
Modularité
La modularité dans Java 9 a été introduite sous le nom de projet
Jigsaw.
L'idée du projet est d'ajouter la notion de module entre la notion de
jar et de package. Ceux qui sont familiarisé avec OSGI, devrait vite
comprendre l’intérêt de la chose: permettre d'exposer ou de limiter
l’accès a certains packages d'un Jar et de définir quels sont les
dépendances nécessaires au bon fonctionnement de ce même jar (dépendance
a résoudre au démarrage de la JVM).
Les objectifs de ce projet sont d’améliorer:
- les performance applicatives en permettant de limiter le chargement des classes et packages a sont plus stricts minimum.
- la sécurité (je vous renvoie a l'article en référence pour les détails)
- la
maintenabilité en fournissant un outil de gestion de la visibilité du
contenu des modules et donc la liste des packages accessibles.
In action, ça dit quoi?
Reprenons l'exemple
précédemment utilisé pour l'API ReactiveStreams. Avec la modularité,
nous allons maintenant trouver un nouveau type de fichier nommé:
module-info.java ainsi qu'un nouveau path: le module path (en
remplacement/complément du classpath, nous verrons pourquoi)
Plaçons
donc les classes métiers dans un projet séparé de la classe Main,
l'utilisant. Les deux projets définiront donc deux modules différents.
Nous aurons donc un module main déclarant l'utilisation d'un autre modules (la librairie):
1
2
3
4
| module proj9.main
{
requires proj9.react;
}
|
et le module lib déclarant les packages exposés par le module (par défaut ils sont privés) :
1
2
3
4
| module proj9.react
{
exports projet9.react;
}
|
Dans
la théorie ce n'est pas très compliqué. Sauf pour le cas de
l'utilisation de la réflexivité ou il faudra employé le mot clef
open (Je vous laisse consulter l'article pour plus d’informations).
A
noter que désormais, avec le fichier module, nous construirons donc des
fichiers jar "module" ou comme il serait possible d'en trouver dans le
répertoire
jmods du jdk, des fichiers
jmod.
Je ne rentre pas dans les détails de transitivité des dépendances que vous pourrez trouver dans cet
article.
Mais sachez qu'il est possible d’accéder a des packages par la simple
déclaration dans un module intermédiaire qui aurait utiliser le mot clef
public après le
require.
Attention,
d'autres subtilités existent. Je ne vais pas entrer dans leur détails
mais sachez qu'il faudra composer avec des notions de
module non-nommé (contenant des classes et packages accessible de façon classique par le classpath, en fait des modules sans
module-info.java) et enfin les
modules automatiques
qui sont la conversion d'un jar classique en module avec la capacité de
lire dans tout type de module (automatique et non-nommé) et d'exporter
tous leurs packages.
Enfin dernière particularité, afin de toujours pouvoir utiliser les
ServiceLoader (bien que par défaut, Java 9 va quand même réussir a les trouver), vous pouvez les déclarer dans le module a l'aide du mot clef
uses suivi du nom complet de la classe.
Je vous invite a lire cet
article très complet sur la modularité dans Java 9.
Autres
- Java
9 intègre également des amélioration sur l'interface avec les processus
de l'OS permettant par exemple d'en récupérer le PID.
- UN moteur de rendu 2D a été mis a jour vers un moteur plus
performant
Marlin. L'annotation @Deprecated a été améliorer en permettant de
préciser la version a partir de laquelle celle ci n'est plus conseillé
et également si l’élément est prévu d’être supprimé.
- Un vrai client http 2
- Une javadoc compatible HTML5
- Changement de fonctionnement du Garbage Collector (G1 a la place de Parrallel GC)
- De nouveaux utilitaires
- La simplification du debugage en production
- Une réorganisation profondes des répertoires du JDK/JRE
- Des améliorations en terme de performance sur la manipulation des chaines de caractères ne nécessitant plus l'utilisation de StringBuilder pour les concaténations de chaines.
Maintenant il va falloir se poser la
question avec ces modifications car la modularité fait peur comme le
laisse penser les commentaires de cet
article.
Alors comment allons nous
migrer vers Java 9 ? Comment va se mettre a jour tout l'eco-systeme gravitant autour de Java (outil, frameworks, etc...)?
Différents exemples existent comme pour :
Maintenant l'avenir nous dira si c'est aussi simple que cela.