>  기사  >  Java  >  SPI를 사용하여 Java에서 디커플링을 달성하는 방법

SPI를 사용하여 Java에서 디커플링을 달성하는 방법

王林
王林앞으로
2023-05-19 21:58:151352검색

개요

SPI의 전체 이름은 서비스 공급자 인터페이스로, 프레임워크의 확장 및 교체 구성 요소를 시작하는 데 사용할 수 있습니다.

핵심은 인터페이스 구현 + 전략 모드 + 구성 파일을 사용하여 구현 클래스의 동적 로딩을 달성하는 것입니다.

특정 용도에는 몇 가지 규칙이 있습니다.

(1) classPath의 META-INF/services/ 아래에 인터페이스의 전체 이름 파일을 생성하도록 규정되어 있습니다.

(2) 이 파일에 인터페이스를 작성합니다. 구현 클래스의 전체 이름(경로 + 파일 이름)입니다. 구현 클래스가 여러 개인 경우 별도의 줄에 작성합니다.

(3) 2를 사용하는 경우 java.util.ServiceLoader의 load(Interface.class)를 이용하여 구현 클래스를 얻어서 사용하시면 됩니다.

인터페이스 구현 클래스에는 매개변수 없는 생성자가 있어야 한다는 점은 주목할 가치가 있습니다.

구현 사례

이 애플리케이션에는 모듈 A와 모듈 B라는 두 개의 모듈이 있습니다. 이 두 모듈 중 모듈 A는 메인 모듈이고 B는 슬레이브 모듈이며 모듈 B는 모듈 A에 종속됩니다. 그러나 현재 모듈 B에 구현된 클래스가 있습니다. 모듈 A는 이 클래스의 함수를 호출해야 하며, 모듈은 더 이상 모듈 B에 의존할 수 없습니다. 이때 디커플링이 필요합니다. 이 구현에서는 SPI를 디커플링 구현에 사용합니다. 구체적인 구현 계획은 다음과 같습니다.

(1) 모듈 A: MyLogAppender에서 새 인터페이스를 만듭니다. 구체적인 구현은 다음과 같습니다.

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

public interface MyLogAppender {

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

이 인터페이스는 매우 간단하며 어펜더 개체만 반환합니다. 객체의 실제 동작을 위해서는 인터페이스 구현에서 동작이 수행된다.

(2) 모듈 B에 이 인터페이스의 구현을 추가합니다. 구체적인 작업은 다음과 같습니다.

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

이 인터페이스 선언과 인터페이스 구현에는 구현할 몇 가지 작은 트릭이 있습니다. 인터페이스에서는 클래스 획득만 선언되고 특정 메서드는 구현되지 않습니다. 구현 클래스에서는 이 클래스를 인스턴스화하고 새 클래스를 생성하여 반환합니다. 이때 사용자는 이 get 메서드에 따라 구현 클래스를 가져온 다음 구현 클래스에 대해 몇 가지 작업을 수행할 수 있습니다. 이러한 방식으로 작성하면 다음과 같은 두 가지 이점을 얻을 수 있습니다. i. 코드가 더 간결해지고, 인터페이스 코드가 간단하고 이해하기 쉽습니다. ii. 사용자가 이를 사용할 때 일부 매개변수를 삽입할 수 있습니다. get 메소드에 직접 주입할 수 있습니다.

(3) 구현 클래스가 있는 폴더, 즉 sandbox-app-epoch-starter에 구성 파일을 추가합니다. 구성 파일의 기본 경로는 resources/META-INF/services/입니다. 폴더 새 질문을 생성합니다. 파일 이름은 인터페이스의 경로이고 내용은 구현 클래스의 경로입니다. 이를 통해 인터페이스-->구현 클래스 매핑을 구현할 수 있습니다.

위 그림과 같이 파일 이름은 com.alibaba.halo.sandbox.app.util.MyLogAppender

파일 내용은 com.alibaba.lattice2.epoch.util.MeshLogAppender

The 원칙은 사용자가 인터페이스를 사용할 때 프로젝트 아래의 모든 파일을 검색하여 파일 이름 com.alibaba.halo.sandbox.app.util.MyLogAppender를 찾은 다음 해당 구현 클래스를 기반으로 검색한다는 것입니다. content

(4) A에서는 인터페이스를 직접 사용하여 호출할 수 있습니다. 구체적인 구현은 다음과 같습니다.

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

위에서 볼 수 있듯이 MyLogAppender 인터페이스를 직접 호출할 수 있으며, 얻은 Appender를 사용합니다. 이 인터페이스를 통해 값을 직접 할당합니다.

장점과 단점

장점: 코드의 분리가 가능

단점은 구현 클래스가 여러 개인 경우 특정 매개변수나 플래그를 통해서는 인스턴스를 얻을 수 없고 순회를 통해서만 인스턴스를 얻을 수 있으며, 구현할 수 없습니다. 지연 로딩

위 내용은 SPI를 사용하여 Java에서 디커플링을 달성하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제