Home  >  Article  >  Java  >  How to use SPI to achieve decoupling in Java

How to use SPI to achieve decoupling in Java

王林
王林forward
2023-05-19 21:58:151400browse

Overview

The full name of SPI is Service Provider Interface, which can be used to initiate extensions and replacement components of the framework.

The essence is to use the interface implementation strategy mode configuration file to dynamically load the implementation class.

In specific use, there are some conventions:

(1) It is stipulated that the full name file of the interface be created under META-INF/services/ of classPath

(2) In this file, write the full name (path file name) of the interface implementation class. If there are multiple implementation classes, write them in separate lines.

(3) When using 2, use load(Interface.class) of java.util.ServiceLoader to get the implementation class and you can use it.

It is worth noting that the interface implementation class must have a constructor without parameters.

Implementation Case

In this application, there are two modules, namely A module and B module. Among these two modules, A module is the main module, B is the slave module, and B The module depends on module A. But currently there is a class that is implemented in module B. Module A needs to call the functions of this class, and the module can no longer rely on module B. At this time, decoupling is required. In this implementation, SPI is used for decoupling implementation. The specific implementation plan is:

(1) Create a new interface in module A: MyLogAppender. The specific implementation is:

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

public interface MyLogAppender {

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

This interface is very simple and just returns an appender object. For the actual operation of the object, the operation is performed in the implementation of the interface.

(2) Add the implementation of this interface in module B. The specific operation is:

/**
 * @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;
    }
}

In the declaration of this interface and the implementation of the interface, there are some small tricks to implement. In the interface, only the acquisition of a class is declared, and no specific method is implemented. In the implementation class, instantiate this class, create a new class and return it. At this time, the user can get the implementation class according to this get method, and then perform some operations on the implementation class. Writing this way can bring two benefits: i. The code is more concise, and the interface code is simple and easy to understand. ii. Some parameters can be injected into the construction method of the implementation class. When the user uses it, they can be injected directly into the get method.

(3) Add a configuration file in the folder where the implementation class is located, that is, sandbox-app-epoch-starter. The default path of the configuration file is: resources/META-INF/services/, in Create a new question in this folder. The file name is the path to the interface, and the content is the path to the implementation class. From this, the interface-->implementation class mapping can be implemented.

As shown in the picture above, the file name is: com.alibaba.halo.sandbox.app.util.MyLogAppender

The content in the file is: com.alibaba.lattice2.epoch.util. MeshLogAppender

The principle is that when the user uses the interface, all files under the project will be scanned, the file name is com.alibaba.halo.sandbox.app.util.MyLogAppender, and then the file is found based on its content. Relevant implementation class

(4) In A, you can directly use the interface to make calls. The specific implementation is as follows:

				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);
        }

As you can see from the above, it can directly call the MyLogAppender interface, using this The Appender obtained through the interface can be assigned directly afterwards.

Advantages and Disadvantages

Advantages: Decoupling of code can be achieved

The disadvantage is that if there are multiple implementation classes, you cannot pass a parameter or flag to Obtaining instances can only be obtained through traversal, and lazy loading cannot be implemented

The above is the detailed content of How to use SPI to achieve decoupling in Java. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete