Maison  >  Article  >  Java  >  Comment utiliser SPI pour réaliser le découplage en Java

Comment utiliser SPI pour réaliser le découplage en Java

王林
王林avant
2023-05-19 21:58:151415parcourir

Présentation

Le nom complet de SPI est Service Provider Interface, qui peut être utilisé pour lancer des extensions et des composants de remplacement du framework.

L'essence est d'utiliser l'implémentation de l'interface + le mode stratégie + le fichier de configuration pour réaliser le chargement dynamique des classes d'implémentation.

Dans une utilisation spécifique, il existe quelques conventions :

(1) Il est stipulé que sous META-INF/services/ de classPath, créer le fichier nom complet de l'interface

(2) Dans ce fichier, écrire l'interface Le nom complet de la classe d'implémentation (chemin + nom du fichier). S'il existe plusieurs classes d'implémentation, écrivez-les sur des lignes séparées.

(3) Lorsque vous utilisez 2, utilisez load(Interface.class) de java.util.ServiceLoader pour obtenir la classe d'implémentation et vous pouvez l'utiliser.

Il convient de noter que la classe d'implémentation d'interface doit avoir un constructeur sans paramètres.

Cas d'implémentation

Dans cette application, il y a deux modules, à savoir le module A et le module B. Parmi ces deux modules, le module A est le module principal, B est le module esclave et le module B dépend du module A. Cependant, il existe actuellement une classe implémentée dans le module B. Le module A doit appeler les fonctions de cette classe et le module ne peut plus s'appuyer sur le module B. À ce stade, un découplage est requis. Dans cette implémentation, SPI est utilisé pour l’implémentation du découplage. Le plan d'implémentation spécifique est :

(1) Créez une nouvelle interface dans le module A : MyLogAppender. L'implémentation spécifique est :

/**
 * @author Huang gen(kenfeng)
 * @description 自定义的appender接口
 * @Since 2021/02/21
 **/

public interface MyLogAppender {

    /**
     * 获取实现的appender
     * @return  返回新建的appender对象
     * */
    Appender getAppender();
}

Cette interface est très simple et renvoie simplement un objet appender. Pour le fonctionnement proprement dit de l'objet, l'opération est effectuée dans l'implémentation de l'interface.

(2) Ajouter l'implémentation de cette interface dans le module B. L'opération spécifique est :

/**
 * @author Huang gen(kenfeng)
 * @description 自定义的appender
 * @Since 2021/02/21
 **/
@Component
public class MeshLogAppender extends UnsynchronizedAppenderBase<ILoggingEvent> implements MyLogAppender,ApplicationContextAware {

    private ApplicationContext applicationContext;

    public MeshLogAppender(){ }

    @Override
    public Appender getAppender() {
        MeshLogAppender meshLogAppender = new MeshLogAppender();
        return meshLogAppender;
    }

    @Override
    protected void append(ILoggingEvent iLoggingEvent) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String std = simpleDateFormat.format(new Date(Long.parseLong(String.valueOf(iLoggingEvent.getTimeStamp()))));
        String log = std + "\t" + iLoggingEvent.getLevel() +"\t"+"--- ["+ iLoggingEvent.getThreadName()+"]\t"+iLoggingEvent.getCallerData()[0]+":\t "+iLoggingEvent.getMessage();
        FlowMessage input = new FlowMessage();
        MeshFlowService meshFlowService = SandboxSystemServiceFactory.getService(MeshFlowService.class);
        Map<String, Object> body = new HashMap<>(2);
        body.put("log",log);
        input.setTenantCode(DefaultTenant.get());
        input.setAppCode("epoch");
        input.setFlowCode("log_broadcast");
        input.setBody(body);
        FlowMessage output = meshFlowService.process(input);
        if(!StringUtils.isEmpty(output.getErrorMessage())){
            throw new RuntimeException("发布日志时,广播失败");
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

Dans la déclaration de cette interface et l'implémentation de l'interface, il y a quelques petites astuces à implémenter. Dans l'interface, seule l'acquisition d'une classe est déclarée, et aucune méthode spécifique n'est implémentée. Dans la classe d'implémentation, instanciez cette classe, créez une nouvelle classe et renvoyez-la. À ce stade, l'utilisateur peut obtenir la classe d'implémentation selon cette méthode get, puis effectuer certaines opérations sur la classe d'implémentation. Écrire de cette manière peut apporter deux avantages : i. Le code est plus concis et le code de l'interface est simple et facile à comprendre. ii. Certains paramètres peuvent être injectés dans la méthode de construction de la classe d'implémentation. peut être injecté directement dans la méthode get.

(3) Ajoutez un fichier de configuration dans le dossier où se trouve la classe d'implémentation, c'est-à-dire sandbox-app-epoch-starter. Le chemin par défaut du fichier de configuration est : resources/META-INF/services/, dans celui-ci. dossier Créez une nouvelle question, le nom du fichier est le chemin de l'interface et le contenu est le chemin de la classe d'implémentation. À partir de là, le mappage de classe d'interface -> d'implémentation peut être implémenté.

Comme le montre l'image ci-dessus, le nom du fichier est : com.alibaba.halo.sandbox.app.util.MyLogAppender

Le contenu du fichier est : com.alibaba.lattice2.epoch.util.MeshLogAppender

Le Le principe est que lorsque l'utilisateur utilise l'interface, tous les fichiers du projet seront analysés pour trouver le nom de fichier com.alibaba.halo.sandbox.app.util.MyLogAppender, puis la classe d'implémentation appropriée sera trouvée en fonction de son contenu

(4) En A, vous pouvez utiliser directement l'interface pour passer l'appel. L'implémentation spécifique est la suivante :

				ServiceLoader<MyLogAppender> myLoaderInterfaceServiceLoader = ServiceLoader.load(MyLogAppender.class);
        Iterator<MyLogAppender> myLoaderInterfaceIterator = myLoaderInterfaceServiceLoader.iterator();
        while (myLoaderInterfaceIterator.hasNext()){
            MyLogAppender myLoaderInterface = myLoaderInterfaceIterator.next();

            Appender newAppender = myLoaderInterface.getAppender();
            newAppender.setName("application");
            newAppender.setContext(loggerContext);
            newAppender.start();
            rootLogger.addAppender(newAppender);
        }

Comme vous pouvez le voir ci-dessus, il peut appeler directement l'interface MyLogAppender, utilisez l'Appender obtenu. par cette interface, puis attribuez la valeur directement.

Avantages et inconvénients

Avantages : le découplage du code peut être obtenu

L'inconvénient est que s'il existe plusieurs classes d'implémentation, l'instance ne peut pas être obtenue via un certain paramètre ou indicateur, elle ne peut être obtenue que par traversée, et elle ne peut pas être implémenté Chargement paresseux

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer