
Spring Framework est l'un des cadres de compréhension et d'apprentissage les plus complexes. La plupart des développeurs l'apprennent lentement, grâce à des tâches pratiques et à Google. Cette approche n'est pas efficace, car elle ne donne pas une image complète et en même temps est coûteuse.
Je voudrais vous proposer une approche fondamentalement nouvelle de l'étude du printemps. Il consiste dans le fait qu'une personne passe par une série de tutoriels spécialement préparés et met en œuvre indépendamment la fonction du ressort. La particularité de cette approche est qu'en plus d'une compréhension à 100% des aspects étudiés de Spring, elle donne également une forte augmentation de Java Core (Annotations, Réflexion, Fichiers, Génériques).
L'article vous offrira une expérience inoubliable et vous fera vous sentir comme un développeur Pivotal. Étape par étape, vous ferez haricoter vos cours et organiserez leur cycle de vie (le même que dans un vrai printemps). Les classes que vous implémenterez sont
BeanFactory ,
Component ,
Service ,
BeanPostProcessor ,
BeanNameAware ,
BeanFactoryAware ,
InitializingBean ,
PostConstruct ,
PreDestroy ,
DisposableBean ,
ApplicationContext ,
ApplicationListener ,
ContextClosedEvent .
Un peu de moi
Je m'appelle Yaroslav et je suis développeur Java avec 4 ans d'expérience. En ce moment je travaille pour EPAM Systems (SPB), et je plonge profondément dans les technologies que nous utilisons. Très souvent, je dois faire face au printemps, et je vois en lui un terrain d'entente dans lequel vous pouvez grandir (Java tout le monde le sait si bien, et des outils et des technologies trop spécifiques peuvent aller et venir).
Il y a quelques mois, j'ai réussi la certification Spring Professional v5.0 (sans suivre de cours). Après cela, j'ai réfléchi à la façon d'enseigner à d'autres personnes qui sautaient. Malheureusement, il n'existe actuellement aucune méthodologie d'enseignement efficace. La plupart des développeurs ont une idée très superficielle du framework et de ses fonctionnalités. Le débogage des sources printanières est trop difficile et absolument inefficace du point de vue de la formation (j'aimais en quelque sorte cela). Faites 10 projets? Oui, quelque part, vous pouvez approfondir vos connaissances et acquérir beaucoup d'expérience pratique, mais une grande partie de ce qui est «sous le capot» ne s'ouvrira jamais devant vous. Lire Printemps en action? Cool, mais coûteux en efforts. Je l'ai travaillé à 40% (lors de la préparation à la certification), mais ce n'était pas facile.
La seule façon de comprendre quelque chose jusqu'au bout est de le développer vous-même. Récemment, j'ai eu l'idée que vous pouvez guider une personne à travers un tutoriel intéressant qui supervisera le développement de son cadre DI. Sa principale caractéristique sera que l'API coïncidera avec l'API étudiée. L'émerveillement de cette approche est qu'en plus d'une compréhension profonde (sans espaces) du printemps, une personne aura une énorme expérience dans Java Core. Franchement, j'ai moi-même appris beaucoup de nouvelles choses lors de la préparation de l'article, à la fois sur Spring et sur Java Core. Commençons à développer!
Projetez à partir de zéro
Donc, la première chose à faire est d'ouvrir votre IDE préféré et de créer un projet à partir de zéro. Nous ne connecterons aucun Maven ni aucune bibliothèque tierce. Nous ne connecterons même pas les dépendances Spring. Notre objectif est de développer une API qui ressemble le plus à l'API Spring et de l'implémenter nous-mêmes.
Dans un projet propre, créez 2 packages principaux. Le premier package est votre application (
com.kciray
) et la classe
Main.java
intérieur. Le deuxième package est org.springframework. Oui, nous dupliquerons la structure du package du ressort d'origine, le nom de ses classes et leurs méthodes. Il y a un effet tellement intéressant - lorsque vous créez quelque chose de vous-même, celui de vous-même commence à sembler simple et compréhensible. Ensuite, lorsque vous travaillez dans de grands projets, il vous semble que tout y est créé en fonction de votre pièce. Cette approche peut avoir un effet très positif sur la compréhension du système dans son ensemble, son amélioration, la correction de bogues, la résolution de problèmes, etc.
Si vous avez des problèmes, vous pouvez
prendre un projet de travail
ici .
Créer un conteneur
Pour commencer, définissez la tâche. Supposons que nous ayons 2 classes -
ProductFacade
et
PromotionService
. Imaginez maintenant que vous voulez connecter ces classes les unes aux autres, mais pour que les classes elles-mêmes ne se connaissent pas (Pattern DI). Nous avons besoin d'une classe distincte qui gérera toutes ces classes et déterminera les dépendances entre elles. Appelons cela un conteneur. Créons la classe
Container
... Bien que non, attendez! Spring n'a pas de classe de conteneur unique. Nous avons de nombreuses implémentations de conteneurs, et toutes ces implémentations peuvent être divisées en 2 types - les usines bin et les contextes. La fabrique de bacs crée des beans et les relie entre eux (injection de dépendances, DI), et le contexte fait à peu près la même chose, en plus d'ajouter des fonctionnalités supplémentaires (par exemple, internationaliser les messages). Mais nous n'avons pas besoin de ces fonctions supplémentaires maintenant, nous allons donc travailler avec l'usine bin.
Créez une nouvelle classe
BeanFactory
et placez-la dans le package
org.springframework.beans.factory
. Laissez les
Map<String, Object> singletons
stockés dans cette classe, dans laquelle l'
id
bin est mappé au bin lui-même. Ajoutez-y la
Object getBean(String beanName)
, qui extrait les beans par identifiant.
public class BeanFactory { private Map<String, Object> singletons = new HashMap(); public Object getBean(String beanName){ return singletons.get(beanName); } }
Veuillez noter que
BeanFactory
et
BeanFactory
sont deux choses différentes. La première est l'usine de bacs (conteneur) et la seconde est l'usine de bacs, qui se trouve à l'intérieur du récipient et produit également des bacs. Usine à l'intérieur de l'usine. Si vous êtes confus entre ces définitions, vous vous souvenez peut-être qu'en anglais, le deuxième nom est le premier et le premier est quelque chose comme un adjectif. Dans Bean
Factory, le mot principal est l'usine et dans Factory
Bean , le bean.
Maintenant, créez les classes
ProductService
et
PromotionsService
.
ProductService
renverra le produit de la base de données, mais avant cela, vous devez vérifier si des remises (promotions) s'appliquent à ce produit. Dans le commerce électronique, le travail à prix réduit est souvent attribué à une classe de service distincte (et parfois à un service Web tiers).
public class PromotionsService { } public class ProductService { private PromotionsService promotionsService; public PromotionsService getPromotionsService() { return promotionsService; } public void setPromotionsService(PromotionsService promotionsService) { this.promotionsService = promotionsService; } }
Nous devons maintenant faire en sorte que notre conteneur (
BeanFactory
) détecte nos classes, les crée pour nous et les injecte l'une dans l'autre. Les opérations telles que le
new ProductService()
doivent être situées à l'intérieur du conteneur et effectuées pour le développeur. Utilisons l'approche la plus moderne (analyse de classe et annotations). Pour ce faire, nous devons créer une annotation
@Component
avec les
@Component
(
org.springframework.beans.factory.stereotype
).
@Retention(RetentionPolicy.RUNTIME) public @interface Component { }
Par défaut, les annotations ne sont pas chargées en mémoire pendant l'exécution du programme (
RetentionPolicy.CLASS
). Nous avons modifié ce comportement via une nouvelle stratégie de rétention (
RetentionPolicy.RUNTIME
).
Ajoutez maintenant
@Component
avant les classes
ProductService
et avant
PromotionService
.
@Component public class ProductService {
Nous avons besoin de
BeanFactory
analyser notre package (
com.kciray
) et y trouver des classes qui sont annotées par
@Component
. Cette tâche est loin d'être anodine. Il n'y a
pas de solution toute faite dans Java Core, et nous devrons faire nous-mêmes une béquille. Des milliers d'applications à ressort utilisent le balayage des composants à travers cette béquille. Vous avez appris la terrible vérité. Vous devrez extraire les noms de
ClassLoader
de
ClassLoader
et vérifier
ClassLoader
se terminent par ".class" ou non, puis créer leur nom complet et en extraire les objets de classe!
Je veux vous avertir immédiatement qu'il y aura de nombreuses exceptions vérifiées, alors soyez prêt à les envelopper. Mais d'abord, décidons de ce que nous voulons. Nous voulons ajouter une méthode spéciale à
BeanFactory
et l'appeler dans
Main
:
Ensuite, nous devons obtenir
ClassLoader
. Il est responsable du chargement des classes, et il est extrait tout simplement:
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Vous avez probablement déjà remarqué que les packages sont séparés par un point et les fichiers par une barre oblique. Nous devons convertir le chemin du lot en chemin du dossier et obtenir quelque chose comme
List<URL>
(les chemins de votre système de fichiers où vous pouvez rechercher des fichiers de classe).
String path = basePackage.replace('.', '/');
Alors attendez un instant!
Enumeration<URL>
n'est pas une
List<URL>
. De quoi s'agit-il? Oh, horreur, c'est l'ancien ancêtre d'
Iterator
, disponible depuis Java 1.0. C'est l'héritage auquel nous devons faire face. S'il est possible de parcourir
Iterable
aide de for (toutes les collections l'implémentent), alors dans le cas de l'
Enumeration
vous devrez faire un bypass de poignée, à travers
while(resources.hasMoreElements())
et
nextElement()
. Et pourtant, il n'y a aucun moyen de supprimer des éléments de la collection. Seulement 1996, seulement hardcore. Oh oui, dans Java 9, ils ont ajouté la méthode
Enumeration.asIterator()
, afin que vous puissiez y travailler.
Allons plus loin. Nous devons extraire les dossiers et parcourir le contenu de chacun d'eux. Convertissez l'URL en fichier, puis obtenez son nom. Il convient de noter ici que nous n'analyserons pas les packages imbriqués afin de ne pas compliquer le code. Vous pouvez compliquer votre tâche et faire une récursion si vous le souhaitez.
while (resources.hasMoreElements()) { URL resource = resources.nextElement(); File file = new File(resource.toURI()); for(File classFile : file.listFiles()){ String fileName = classFile.getName();
Ensuite, nous devons obtenir le nom du fichier sans l'extension. Dans la cour en 2018, Java a développé des E / S de fichiers (NIO 2) pendant de nombreuses années, mais ne peut toujours pas séparer l'extension du nom de fichier. Je dois créer mon propre vélo, car nous avons décidé de ne pas utiliser de bibliothèques tierces comme Apache Commons. Utilisons l'ancienne méthode grand-père
lastIndexOf(".")
:
if(fileName.endsWith(".class")){ String className = fileName.substring(0, fileName.lastIndexOf(".")); }
Ensuite, nous pouvons obtenir l'objet classe en utilisant le nom complet de la classe (pour cela, nous appelons la classe de la classe
Class
):
Class classObject = Class.forName(basePackage + "." + className);
D'accord, maintenant nos cours sont entre nos mains. De plus, il ne reste plus qu'à mettre en évidence parmi eux ceux qui ont l'annotation
@Component
:
if(classObject.isAnnotationPresent(Component.class)){ System.out.println("Component: " + classObject); }
Exécutez et vérifiez. La console devrait ressembler à ceci:
Component: class com.kciray.ProductService Component: class com.kciray.PromotionsService
Maintenant, nous devons créer notre bean. Vous devez faire quelque chose comme
new ProductService()
, mais pour chaque bean, nous avons notre propre classe. La réflexion en Java nous fournit une solution universelle (le constructeur par défaut est appelé):
Object instance = classObject.newInstance();
Ensuite, nous devons mettre ce bean dans les
Map<String, Object> singletons
. Pour ce faire, sélectionnez le nom du bean (son id). En Java, nous appelons des variables comme des classes (seule la première lettre est en minuscule). Cette approche peut également être appliquée aux beans, car Spring est un framework Java! Convertissez le nom du bac de sorte que la première lettre soit petite et ajoutez-le à la carte:
String beanName = className.substring(0, 1).toLowerCase() + className.substring(1); singletons.put(beanName, instance);
Assurez-vous maintenant que tout fonctionne. Le conteneur doit créer des beans et ils doivent être récupérés par leur nom. Veuillez noter que le nom de votre méthode
classObject.newInstance();
et le nom de la méthode
classObject.newInstance();
ont une racine commune. De plus,
instantiate()
fait partie du cycle de vie du bean. En Java, tout est interconnecté!
Essayez également d'implémenter l'annotation
org.springframework.beans.factory.stereotype.Service
. Il remplit exactement la même fonction que
@Component
, mais il est appelé différemment. Tout est dans le nom - vous démontrez que la classe est un service, pas seulement un composant. C'est quelque chose comme la frappe conceptuelle. Lors de la certification du printemps, il y avait une question «Quelles annotations sont stéréotypées?» (de ceux énumérés). " Les annotations stéréotypées sont donc celles qui se trouvent dans le package de
stereotype
.
Remplissez les propriétés
Regardez le schéma ci-dessous, il montre le début du cycle de vie du bean. Ce que nous avons fait auparavant est Instantiate (création de beans via
newInstance()
). L'étape suivante est l'injection croisée de beans (injection de dépendance, c'est aussi l'inversion de contrôle (IoC)). Vous devez parcourir les propriétés des grains et comprendre quelles propriétés vous devez injecter. Si vous appelez
productService.getPromotionsService()
, vous obtiendrez
null
, car dépendance non encore ajoutée.

Tout d'abord, créez le package
org.springframework.beans.factory.annotation
et ajoutez-y l'annotation
@Autowired
. L'idée est de marquer les champs qui sont des dépendances avec cette annotation.
@Retention(RetentionPolicy.RUNTIME) public @interface Autowired { }
Ensuite, ajoutez-le à la propriété:
@Component public class ProductService { @Autowired PromotionsService promotionsService;
Maintenant, nous devons apprendre à notre
BeanFactory
trouver ces annotations et à leur injecter des dépendances. Ajoutez une méthode distincte pour cela et appelez-la depuis
Main
:
public class BeanFactory {
Ensuite, nous avons juste besoin de parcourir tous nos bacs dans la carte des
singletons
, et pour chaque bac de parcourir tous ses champs (
object.getClass().getDeclaredFields()
renvoie tous les champs, y compris les champs privés). Et vérifiez si le champ a une annotation
@Autowired
:
for (Object object : singletons.values()) { for (Field field : object.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { } } }
Ensuite, nous devons parcourir tous les bacs une fois de plus et voir leur type - soudain, c'est le type que notre bac veut prendre pour lui-même. Oui, nous obtenons un cycle en trois dimensions!
for (Object dependency : singletons.values()) { if (dependency.getClass().equals(field.getType())) { } }
De plus, lorsque nous avons découvert la dépendance, nous devons l'injecter. La première chose à laquelle vous pourriez penser est d'écrire le champ
promotionsService
utilisant directement la réflexion. Mais le printemps ne fonctionne pas comme ça. Après tout, si le champ a un modificateur
private
, nous devrons d'abord le définir comme
public
, puis écrire notre valeur, puis le redéfinir sur
private
(pour maintenir l'intégrité). Cela ressemble à une grosse béquille. Au lieu d'une grande béquille, faisons une petite béquille (nous formerons le nom du setter et l'appellerons):
String setterName = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
Maintenant, exécutez votre projet et assurez-vous qu'en appelant
productService.getPromotionsService()
au lieu de
null
, notre bean est retourné.
Ce que nous avons implémenté, c'est l'injection par type. Il existe également une injection par nom (annotation
javax.annotation.Resource
). Il diffère en ce qu'au lieu du type du champ, son nom sera extrait, et selon lui - la dépendance de la carte. Tout est similaire ici, même dans quelque chose de plus simple. Je vous recommande d'expérimenter et de créer votre propre bean, puis de l'injecter avec
@Resource
et d'étendre la méthode
populateProperties()
.
Nous soutenons les haricots qui connaissent leur nom

Il y a des moments où vous devez mettre son nom dans le bac. Un tel besoin ne se pose pas souvent, car les poubelles, par essence, ne devraient pas se connaître et qu'elles sont des poubelles. Dans les premières versions du printemps, on supposait que le bean était un POJO (Plain Old Java Objec, le bon vieil objet Java), et toute la configuration était rendue dans des fichiers XML et séparée de l'implémentation. Mais nous implémentons cette fonctionnalité, car l'injection de nom fait partie du cycle de vie du bac.
Comment savons-nous quel haricot veut savoir quel est son nom et ce qu'il ne veut pas? La première chose qui vous vient à l'esprit est de créer une nouvelle annotation de type
@InjectName
et de la sculpter en champs de type String. Mais cette solution sera trop générale et vous permet de vous tirer plusieurs fois dans le pied (placez cette annotation sur des champs de types inappropriés (pas String), ou essayez d'injecter un nom dans plusieurs champs de la même classe). Il existe une autre solution, plus précise - pour créer une interface spéciale avec une méthode de définition. Tous les bacs qui l'implémentent obtiennent leur nom. Créez la classe
BeanNameAware
dans le package
org.springframework.beans.factory
:
public interface BeanNameAware { void setBeanName(String name); }
Ensuite, laissez notre
PromotionsService
implémenter:
@Component public class PromotionsService implements BeanNameAware { private String beanName; @Override public void setBeanName(String name) { beanName = name; } public String getBeanName() { return beanName; } }
Et enfin, ajoutez une nouvelle méthode à l'usine de haricots. Tout est simple ici - nous passons par notre bin-singleton, vérifions si le bin implémente notre interface, et appelons le setter:
public void injectBeanNames(){ for (String name : singletons.keySet()) { Object bean = singletons.get(name); if(bean instanceof BeanNameAware){ ((BeanNameAware) bean).setBeanName(name); } } }
Exécutez et assurez-vous que tout fonctionne:
BeanFactory beanFactory = new BeanFactory(); beanFactory.instantiate("com.kciray"); beanFactory.populateProperties(); beanFactory.injectBeanNames();
Il convient de noter qu'au printemps, il existe d'autres interfaces similaires. Je vous recommande d'implémenter vous-
même l' interface
BeanFactoryAware , qui permet aux beans de recevoir un lien vers la fabrique de beans. Il est mis en œuvre de manière similaire.
Initialiser les haricots

Imaginez que vous ayez une situation où vous devez exécuter du code après que les dépendances ont été injectées (les propriétés du bac sont définies). En termes simples, nous devons donner au bac la possibilité de s'initialiser. Alternativement, nous pouvons créer une interface
InitializingBean
et y mettre la signature de la
void afterPropertiesSet()
. L'implémentation de ce mécanisme est exactement la même que celle présentée pour l'interface
BeanNameAware
, donc la solution est sous le spoiler. Pratiquez et faites-le vous-même en une minute:
Solution d'initialisation du bean Ajout de post-processeurs
Imaginez-vous à la place des premiers développeurs du printemps. Votre framework se développe et est très populaire auprès des développeurs, des lettres sont envoyées chaque jour par mail avec des demandes d'ajout de l'une ou l'autre fonctionnalité utile. Si pour chacune de ces fonctionnalités, vous ajoutez votre propre interface et la vérifiez dans le cycle de vie du bean, alors (le cycle de vie) sera obstrué par des informations inutiles. Au lieu de cela, nous pouvons créer une interface universelle qui vous permet d'ajouter de la logique (absolument aucune, qu'il s'agisse de vérifier l'annotation, de remplacer le bac par un autre bac, de définir des propriétés spéciales, etc.).
Réfléchissons à quoi sert cette interface. Il doit effectuer un post-traitement des beans, il peut donc être appelé BeanPostProcessor. Mais nous sommes confrontés à une question difficile - quand faut-il suivre la logique? Après tout, nous pouvons l'exécuter avant l'initialisation, mais nous pouvons l'exécuter après. Pour certaines tâches, la première option est meilleure, pour d'autres - la seconde ... Comment être?
Nous pouvons activer les deux options à la fois. Laissez un post-processeur transporter deux logiques, deux méthodes. L'un est exécuté avant l'initialisation (avant la méthode
afterPropertiesSet()
) et l'autre après. Réfléchissons maintenant aux méthodes elles-mêmes - quels paramètres devraient-elles avoir? De toute évidence, le
Object bean
lui-même (
Object bean
) doit être là. Pour plus de commodité, en plus du bac, vous pouvez transmettre le nom de ce bac. Vous vous souvenez que le bac lui-même ne connaît pas son nom. Et nous ne voulons pas forcer tous les beans à implémenter l'interface BeanNameAware. Mais, au niveau du post-processeur, le nom du bean peut être très utile. Par conséquent, nous l'ajoutons comme deuxième paramètre.
Et que doit renvoyer la méthode lors du post-traitement du bean? Faisons-le retourner le bac lui-même. Cela nous donne une super flexibilité, car au lieu d'un bac, vous pouvez glisser un objet proxy qui encapsule ses appels (et ajoute de la sécurité). Ou vous pouvez renvoyer complètement un autre objet en recréant le bac. Les développeurs bénéficient d'une très grande liberté d'action. Voici la version finale de l'interface conçue:
package org.springframework.beans.factory.config; public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName); Object postProcessAfterInitialization(Object bean, String beanName); }
Ensuite, nous devons ajouter une liste de processeurs simples à notre fabrique de haricots et la possibilité d'en ajouter de nouveaux. Oui, il s'agit d'une liste de tableaux régulière.
Modifiez maintenant la méthode
initializeBeans
pour qu'elle prenne en compte les post-processeurs:
public void initializeBeans() { for (String name : singletons.keySet()) { Object bean = singletons.get(name); for (BeanPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeforeInitialization(bean, name); } if (bean instanceof InitializingBean) { ((InitializingBean) bean).afterPropertiesSet(); } for (BeanPostProcessor postProcessor : postProcessors) { postProcessor.postProcessAfterInitialization(bean, name); } } }
Créons un petit post-processeur qui trace simplement les appels à la console et l'ajoutons à notre fabrique de bean:
public class CustomPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { System.out.println("---CustomPostProcessor Before " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { System.out.println("---CustomPostProcessor After " + beanName); return bean; } }
Maintenant, lancez-vous et assurez-vous que tout fonctionne. En tant que tâche de formation, créez un post-processeur qui fournira l'annotation
@PostConstruct (javax.annotation.PostConstruct)
. Il fournit un autre moyen d'initialiser (enraciné en Java, pas au printemps). Son essence est que vous placez l'annotation sur une méthode, et cette méthode sera appelée AVANT l'initialisation du ressort standard (InitializingBean).
Assurez-vous de créer toutes les annotations et les packages (même javax.annotation) manuellement, ne connectez pas les dépendances! Cela vous aidera à voir la différence entre le noyau du ressort et ses extensions (support javax), et à vous en souvenir. Cela gardera un style à l'avenir.
Vous serez intéressé par le fait que dans un vrai printemps l'annotation @PostConstruct
est implémentée de cette manière, via le post-processeur CommonAnnotationBeanPostProcessor. Mais n'y jetez pas un œil, écrivez votre implémentation.Enfin, je vous recommande d'ajouter une méthode void close()
à la classe BeanFactory
et d'élaborer deux autres mécanismes. La première est une annotation @PreDestroy (javax.annotation.PreDestroy)
, destinée aux méthodes qui doivent être appelées lorsque le conteneur est fermé. La seconde est l'interface org.springframework.beans.factory.DisposableBean
qui contient la méthode void destroy()
. Tous les bacs exécutant cette interface auront la capacité de se détruire (libérer des ressources, par exemple).@PreDestroy + DisposableBean Cycle de vie complet du bean
Nous avons donc mis en œuvre le cycle de vie complet du bac, dans sa forme moderne. J'espère que cette approche vous aidera à vous en souvenir.Notre contexte préféré
Les programmeurs utilisent très souvent le terme contexte, mais tout le monde ne comprend pas ce qu'il signifie vraiment. Maintenant, nous allons tout mettre en ordre. Comme je l'ai noté au début de l'article, le contexte est la mise en œuvre du conteneur, ainsi que BeanFactory
. Mais, en plus des fonctions de base (DI), il ajoute encore quelques fonctionnalités intéressantes. L'une de ces fonctionnalités est l'envoi et le traitement d'événements entre les bacs.L'article s'est avéré trop volumineux et le contenu a commencé à être coupé, j'ai donc mis les informations de contexte sous le spoiler.Nous réalisons le contexte.
org.springframework.context
,
ApplicationContext
.
BeanFactory
. ,
close()
.
public class ApplicationContext { private BeanFactory beanFactory = new BeanFactory(); public ApplicationContext(String basePackage) throws ReflectiveOperationException{ System.out.println("******Context is under construction******"); beanFactory.instantiate(basePackage); beanFactory.populateProperties(); beanFactory.injectBeanNames(); beanFactory.initializeBeans(); } public void close(){ beanFactory.close(); } }
Main
, , :
ApplicationContext applicationContext = new ApplicationContext("com.kciray"); applicationContext.close();
, .
close()
, « » - . , :
package org.springframework.context.event; public class ContextClosedEvent { }
ApplicationListener
, . , (
ApplicationListener<E>
). , Java-, . , , :
package org.springframework.context; public interface ApplicationListener<E>{ void onApplicationEvent(E event); }
ApplicationContext
.
close()
, , .
ApplicationListener<ContextClosedEvent>
,
onApplicationEvent(ContextClosedEvent)
. , ?
public void close(){ beanFactory.close(); for(Object bean : beanFactory.getSingletons().values()) { if (bean instanceof ApplicationListener) { } } }
. .
bean instanceof ApplicationListener<ContextClosedEvent>
. Java.
(type erasure) , <T> <Object>. , ? ,
ApplicationListener<ContextClosedEvent>
, ?
, , . , , , , :
for (Type type: bean.getClass().getGenericInterfaces()){ if(type instanceof ParameterizedType){ ParameterizedType parameterizedType = (ParameterizedType) type; } }
, , , — . , :
Type firstParameter = parameterizedType.getActualTypeArguments()[0]; if(firstParameter.equals(ContextClosedEvent.class)){ Method method = bean.getClass().getMethod("onApplicationEvent", ContextClosedEvent.class); method.invoke(bean, new ContextClosedEvent()); }
ApplicationListener:
@Service public class PromotionsService implements BeanNameAware, ApplicationListener<ContextClosedEvent> {
, Main , , :
Conclusion
Au départ, j'avais prévu cet article pour Baeldung en anglais, mais j'ai ensuite pensé que le public de l'Habré pouvait évaluer positivement cette approche de la formation. Si vous avez aimé mes idées, assurez-vous de soutenir l'article. Si elle obtient une note de plus de 30, je promets de continuer. Lors de la rédaction de l'article, j'ai essayé de montrer exactement les connaissances de Spring Core, qui est le plus souvent utilisé, et également basé sur le Guide d'étude de certification Core Spring 5.0 . À l'avenir, à l'aide de ces didacticiels, vous pourrez couvrir l'intégralité de la certification et rendre le printemps plus accessible aux développeurs Java.Mise à jour 05/10/2018
Des lettres me viennent constamment avec des questions "et quand la suite, on l'attend." Mais il n'y a pas de temps du tout, et d'autres projets personnels sont une priorité. Cependant, si l'un d'entre vous a vraiment aimé l'idée, vous pouvez étudier la section étroite du ressort et écrire un article de suite. Si vous n'avez pas de compte habr, je peux publier un article depuis mon compte ou vous aider à obtenir une invitation.Distribution des sujets:Spring Container - [nom d'utilisateur]Spring AOP - [nom d'utilisateur]Spring Web - [nom d'utilisateur]Spring Cloud - [nom d'utilisateur]