Several routines and implementations to maintain scalability in Java

In daily development, the author summarizes some commonly used low-cost and scalable routines and shares them. Everyone is welcome to discuss them.

Preface

The five principles of SOLID (single, open and closed, inner replacement, interface isolation, dependency inversion) and 23 design patterns (common singletons, builders, decorations, adaptations, agents, combinations, templates, etc.), friends You must be familiar with these. These principles and design patterns can help us make decisions when designing to achieve high cohesion and low coupling.

When it comes to design, the word architecture will definitely be mentioned. Common architecture terms: layered architecture, hexagonal architecture, SOA architecture, CQRS architecture, EDA architecture, etc. My personal understanding of architecture is the confirmation of boundaries and the combination of elements within the boundaries. In fact, as long as a programmer is a programmer, he must be an architect, but whether he is a good architect or a not-so-good architect; everyone is an architect Architect, I think there is no problem with this sentence. The difference is that his cognition determines the boundaries he can confirm and how to combine internal elements more efficiently; technical architects must focus on technology, and business architects must focus on The boundaries that business and product architects can see are most likely limited to the product domain. The architects of the ICBU architecture team consider more about horizontal business support. The above is my personal understanding of the word architecture. Today we will not discuss the specific architecture. We will discuss some routines. In daily development, I have summarized some low-cost and scalable routines that I usually use to share. Come out and everyone is welcome to discuss.

Pipeline-based routines

key point

  • Pipeline----pipeline passage for series valves
  • Valve (PipelineValue)----used for each node to handle actual business requirements
  • Pipeline Context (PipelineContext)----used for the rotation of data in the pipeline context

Applicable scene

  • When your data flow needs to go through a lot of equal logical processing, you can consider using this routine to facilitate subsequent expansion.

Implement code

  • Pipeline/StandardPipeline
package com.example.ownertest.dm.pipelline;

/**
 * @Author: linear.zw
 * @Date: 2023/10/25 19:46
 */
public interface Pipeline {

    /**
     * 执行
     *
     * @return
     */
    boolean invoke(PipelineContext pipelineContext);

    /**
     * 添加值
     *
     * @param pipelineValue
     * @return
     */
    boolean addValue(PipelineValue pipelineValue);

    /**
     * 移除值
     *
     * @param pipelineValue
     * @return
     */
    boolean removeValue(PipelineValue pipelineValue);
}




package com.example.ownertest.dm.pipelline;

import java.util.List;

import com.google.common.collect.Lists;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * @Author: linear.zw
 * @Date: 2023/10/25 19:46
 */
@Data
@Slf4j
public class StandardPipeline implements Pipeline {

    private List<PipelineValue> pipelineValueList = Lists.newArrayList();

    @Override
    public boolean invoke(PipelineContext pipelineContext) {
        boolean isResult = true;
        for (PipelineValue pipelineValue :
            pipelineValueList) {
            try {
                isResult = pipelineValue.execute(pipelineContext);
                if (!isResult) {
                    log.error("{},exec is wrong", pipelineValue.getClass().getSimpleName());
                }

            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }

        return isResult;
    }

    @Override
    public boolean addValue(PipelineValue pipelineValue) {
        if (pipelineValueList.contains(pipelineValue)) {
            return true;
        }

        return pipelineValueList.add(pipelineValue);
    }

    @Override
    public boolean removeValue(PipelineValue pipelineValue) {
        return pipelineValueList.remove(pipelineValue);
    }
}
  • PipelineContext/StandardPipelineContext
package com.example.ownertest.dm.pipelline;

/**
 * @Author: linear.zw
 * @Date: 2023/10/25 19:47
 */
public interface PipelineContext {

    String FOR_TEST = "forTest";

    /**
     * 设置
     *
     * @param contextKey
     * @param contextValue
     */
    void set(String contextKey, Object contextValue);

    /**
     * 获取值
     *
     * @param contextKey
     * @return
     */
    Object get(String contextKey);
}




package com.example.ownertest.dm.pipelline;

import java.util.Map;

import com.google.common.collect.Maps;

/**
 * @Author: linear.zw
 * @Date: 2023/10/25 19:47
 */
public class StandardPipelineContext implements PipelineContext {

    private Map<String, Object> contentMap = Maps.newConcurrentMap();

    @Override
    public void set(String contextKey, Object contextValue) {
        contentMap.put(contextKey, contextValue);
    }

    @Override
    public Object get(String contextKey) {
        return contentMap.get(contextKey);
    }
}
  • PipelineValue/AbstractPipelineValue/GraySwitchValue/ForTestValue
package com.example.ownertest.dm.pipelline;

/**
 * @Author: linear.zw
 * @Date: 2023/10/25 19:47
 */
public interface PipelineValue {

    /**
     * 节点执行
     *
     * @param pipelineContext
     * @return
     */
    boolean execute(PipelineContext pipelineContext);

}



package com.example.ownertest.dm.pipelline;

/**
 * @Author: linear.zw
 * @Date: 2023/10/25 19:48
 */
public abstract class AbstractPipelineValue implements PipelineValue {

    @Override
    public boolean execute(PipelineContext pipelineContext) {

        System.out.println(this.getClass().getSimpleName() + " start ");

        boolean result = doExec(pipelineContext);

        System.out.println(this.getClass().getSimpleName() + " end ");

        return result;
    }

    protected abstract boolean doExec(PipelineContext pipelineContext);
}


package com.example.ownertest.dm.pipelline;

/**
 * @Author: linear.zw
 * @Date: 2023/10/25 19:48
 */
public class GraySwitchValue extends AbstractPipelineValue {
    @Override
    public boolean doExec(PipelineContext pipelineContext) {

        pipelineContext.set(PipelineContext.FOR_TEST, true);

        return true;
    }
}



package com.example.ownertest.dm.pipelline;

/**
 * @Author: linear.zw
 * @Date: 2023/10/25 19:48
 */
public class ForTestValue extends AbstractPipelineValue {
    @Override
    public boolean doExec(PipelineContext pipelineContext) {

        return true;
    }
}
  • PipelineClient
package com.example.ownertest.dm.pipelline;

/**
 * 入口类
 *
 * @Author: linear.zw
 * @Date: 2023/10/25 19:48
 */
public class PipelineClient {

    public static void main(String[] args) {

        // 管道初始化
        Pipeline pipeline = new StandardPipeline();

        // value扩展
        PipelineValue pipelineValue = new GraySwitchValue();
        PipelineValue pipelineValue2 = new ForTestValue();

        // 上下文
        PipelineContext pipelineContext = new StandardPipelineContext();

        pipeline.addValue(pipelineValue);
        pipeline.addValue(pipelineValue2);

        // 调用管道
        pipeline.invoke(pipelineContext);

    }
}

Applications in common frameworks

  • In the netty framework, the main components of the network layer, such as ChannelPipeline, ChannelHandler, and ChannelHandlerContext, are used to handle tcp unpacking, decoding, etc. respectively.

Routine based on filter chain

key point

来源--https://www.oracle.com/java/technologies/intercepting-filter.html

  • Filter - the node that actually processes business
  • FilterChain----a chain of filters in series

Applicable scene

  • For example, common web request scenarios

Implement code

  • Filter/ForTest1Filter/ForTest2Filter
package com.example.ownertest.dm.filter;

/**
 * @Author: linear.zw
 * @Date: 2023/10/26 19:22
 */
public interface Filter {

    void doFilter(HttpRequest httpRequest,FilterChain filterChain);
}



package com.example.ownertest.dm.filter;

/**
 * @Author: linear.zw
 * @Date: 2023/10/26 19:22
 */
public class ForTest1Filter implements Filter {
    @Override
    public void doFilter(HttpRequest httpRequest, FilterChain filterChain) {
        // do

        System.out.println(this.getClass().getSimpleName() + " before " + System.currentTimeMillis());

        filterChain.doFilter(httpRequest);

        // after

        System.out.println(this.getClass().getSimpleName() + " end " + System.currentTimeMillis());

    }
}





package com.example.ownertest.dm.filter;

/**
 * @Author: linear.zw
 * @Date: 2023/10/26 19:22
 */
public class ForTest2Filter implements Filter {
    @Override
    public void doFilter(HttpRequest httpRequest, FilterChain filterChain) {
        // do

        System.out.println(this.getClass().getSimpleName() + " before " + System.currentTimeMillis());

        filterChain.doFilter(httpRequest);

        // after

        System.out.println(this.getClass().getSimpleName() + " end " + System.currentTimeMillis());
    }
}
  • FilterChain/StandardFilterChain
package com.example.ownertest.dm.filter;

/**
 * @Author: linear.zw
 * @Date: 2023/10/26 19:23
 */
public interface FilterChain {

    void doFilter(HttpRequest httpRequest);

    void addFilter(Filter filter);
}




package com.example.ownertest.dm.filter;

import java.util.List;

import com.google.common.collect.Lists;

/**
 * @Author: linear.zw
 * @Date: 2023/10/26 19:24
 */
public class StandardFilterChain implements FilterChain {

    private List<Filter> filterList = Lists.newArrayList();

    private int currentIndex = 0;

    @Override
    public void doFilter(HttpRequest httpRequest) {
        if (currentIndex == filterList.size()) { return; }

        Filter filter = filterList.get(currentIndex);

        currentIndex = currentIndex + 1;

        filter.doFilter(httpRequest, this);
    }

    @Override
    public void addFilter(Filter filter) {
        if (filterList.contains(filter)) {
            return;
        }

        filterList.add(filter);
    }

}
  • HttpRequest/StandardHttpRequest
package com.example.ownertest.dm.filter;

/**
 * @Author: linear.zw
 * @Date: 2023/10/26 19:24
 */
public interface HttpRequest {
}




package com.example.ownertest.dm.filter;

/**
 * @Author: linear.zw
 * @Date: 2023/10/26 19:24
 */
public class StandardHttpRequest implements HttpRequest {
}
  • FilterClient----entry test
package com.example.ownertest.dm.filter;

/**
 * @Author: linear.zw
 * @Date: 2023/10/26 19:25
 */
public class FilterClient {

    public static void main(String[] args) {
        FilterChain filterChain = new StandardFilterChain();

        filterChain.addFilter(new ForTest1Filter());
        filterChain.addFilter(new ForTest2Filter());

        filterChain.doFilter(new StandardHttpRequest());
    }
}

Applications in common frameworks

  • hsf's filter mechanism, server-side extended ServerFilter and client-side extended ClientFilter;

  • Friends who have developed Java web know about servlet. The entrance of servlet is FilterChain and Filter;

Combination/template based routines

key point

  • Processor register----used to store a collection of processors
  • Processor factory----used to create processors
  • Processor - the actual processor and the implementation of the extension
  • Processor context----Processor context, used for passing parameters

Applicable scene

  • Suitable for scenarios that have common characteristics and will continue to expand in the future

Implement code

  • PiiHandlerRegistry----processor register
package com.example.ownertest.dm.comp;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * @Author: linear.zw
 * @Date: 2023/10/31 20:45
 */
@Slf4j
public class PiiHandlerRegistry {

    private static Map<String, PiiDomainFieldHandler> piiDomainFieldHandlerMap = Maps.newHashMap();

    public static void putHandler(String piiDomainFieldName, PiiDomainFieldHandler piiDomainFieldHandler) {
        if (StringUtils.isEmpty(piiDomainFieldName)) {
            log.warn(" piiDomainFieldName is null,continue");
            return;
        }

        if (piiDomainFieldHandler == null) {
            log.warn(piiDomainFieldName + " piiDomainFieldHandler is null,continue");
            return;
        }

        if (!piiDomainFieldHandlerMap.containsKey(piiDomainFieldName)) {
            piiDomainFieldHandlerMap.put(piiDomainFieldName, piiDomainFieldHandler);
        }
    }

    public static <T extends Object> int handlerRead(T domain, Field domainField, PiiContent piiContent) {
        int num = 0;
        for (Map.Entry<String, PiiDomainFieldHandler> piiDomainFieldHandlerEntry :
            piiDomainFieldHandlerMap.entrySet()) {
            if (piiDomainFieldHandlerEntry.getValue().isSupport(domain, domainField)) {
                piiDomainFieldHandlerEntry.getValue().handlerRead(domain, domainField, piiContent);
            }
        }
        return num;
    }

    public static <T extends Object> int handlerWrite(T domain, Field domainField, PiiContent piiContent) {
        int num = 0;
        for (Map.Entry<String, PiiDomainFieldHandler> piiDomainFieldHandlerEntry :
            piiDomainFieldHandlerMap.entrySet()) {
            if (piiDomainFieldHandlerEntry.getValue().isSupport(domain, domainField)) {
                piiDomainFieldHandlerEntry.getValue().handlerWrite(domain, domainField, piiContent);
            }
        }
        return num;
    }

    public static Map<String, PiiDomainFieldHandler> getPiiDomainFieldHandlerMap() {
        return piiDomainFieldHandlerMap;
    }

    public static void init() {
        List<PiiDomainFieldHandler> piiDomainFieldHandlerList = PiiDomainFieldHandlerFactory
            .createPiiDomainFieldHandler();
        if (CollectionUtils.isNotEmpty(piiDomainFieldHandlerList)) {

            for (PiiDomainFieldHandler piiDomainFieldHandler :
                piiDomainFieldHandlerList) {
                putHandler(piiDomainFieldHandler.getPiiDomainMeta(), piiDomainFieldHandler);
            }
        }
    }
}
  • PiiDomainFieldHandlerFactory----processor factory
package com.example.ownertest.dm.comp;

import java.util.List;

import com.google.common.collect.Lists;

/**
 * @Author: linear.zw
 * @Date: 2023/10/31 20:46
 */
public class PiiDomainFieldHandlerFactory {

    /**
     * 创建领域处理器
     *
     * @return
     */
    public static List<PiiDomainFieldHandler> createPiiDomainFieldHandler() {
        List<PiiDomainFieldHandler> piiDomainFieldHandlerList = Lists.newArrayList();

        //
        piiDomainFieldHandlerList.add(new ForTestSupportFieldHandler());
        piiDomainFieldHandlerList.add(new ForTestNotSupportFieldHandler());

        return piiDomainFieldHandlerList;
    }
}
  • PiiDomainFieldHandler/PiiDomainFieldHandlerBase/ForTestNotSupportFieldHandler/ForTestSupportFieldHandler----处理器
package com.example.ownertest.dm.comp;

import java.lang.reflect.Field;

/**
 * @Author: linear.zw
 * @Date: 2023/10/31 20:46
 */
public interface PiiDomainFieldHandler {

    /**
     * 处理实际操作
     * 读----从PiiContent获取数据回填domain
     *
     * @param domain
     * @param domainField
     * @param piiContent
     * @param <T>
     * @return
     */
    <T extends Object> boolean handlerRead(T domain, Field domainField, PiiContent piiContent);

    /**
     * 处理实际操作
     * 写----将domain中需要写入pii的字段数据写入PiiContent
     *
     * @param domain
     * @param domainField
     * @param piiContent
     * @param <T>
     * @return
     */
    <T extends Object> boolean handlerWrite(T domain, Field domainField, PiiContent piiContent);

    /**
     * 当前处理器是否支持该领域对象
     *
     * @param domain
     * @param domainField
     * @param <T>
     * @return
     */
    <T extends Object> boolean isSupport(T domain, Field domainField);

    /**
     * 获取处理器对应的元信息
     *
     * @return
     */
    String getPiiDomainMeta();
}




package com.example.ownertest.dm.comp;

import java.lang.reflect.Field;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author: linear.zw
 * @Date: 2023/10/31 20:47
 */
@Slf4j
public abstract class PiiDomainFieldHandlerBase implements PiiDomainFieldHandler {

    @Override
    public <T extends Object> boolean handlerRead(T domain, Field domainField, PiiContent piiContent) {
        // to do business read

        return true;
    }

    @Override
    public <T extends Object> boolean handlerWrite(T domain, Field domainField, PiiContent piiContent) {

        // to do business write

        return true;
    }
}




package com.example.ownertest.dm.comp;

import java.lang.reflect.Field;

/**
 * @Author: linear.zw
 * @Date: 2023/10/31 20:47
 */
public class ForTestSupportFieldHandler extends PiiDomainFieldHandlerBase {
    @Override
    public <T> boolean isSupport(T domain, Field domainField) {

        if (this.getClass().getSimpleName().equalsIgnoreCase(domain.getClass().getSimpleName())) {

            // to do business

            System.out.println(this.getClass().getSimpleName() + " is support, to do some business");

            return true;
        }

        return false;
    }

    @Override
    public String getPiiDomainMeta() {
        return this.getClass().getSimpleName();
    }
}



package com.example.ownertest.dm.comp;

import java.lang.reflect.Field;

/**
 * @Author: linear.zw
 * @Date: 2023/10/31 20:48
 */
public class ForTestNotSupportFieldHandler extends PiiDomainFieldHandlerBase {
    @Override
    public <T> boolean isSupport(T domain, Field domainField) {

        if (this.getClass().getSimpleName().equalsIgnoreCase(domain.getClass().getSimpleName())) {

            // to do business

            System.out.println(this.getClass().getSimpleName() + " is support, to do some business");

            return true;
        }

        return false;
    }

    @Override
    public String getPiiDomainMeta() {
        return this.getClass().getSimpleName();
    }
}
  • PiiContent----Context
package com.example.ownertest.dm.comp;

import java.util.Map;

import com.google.common.collect.Maps;
import lombok.Data;

/**
 * @Author: linear.zw
 * @Date: 2023/10/31 20:48
 */
@Data
public class PiiContent {

    public static String FORTEST="fortest";

    private Map<String, Object> piiDataMap = Maps.newHashMap();

    private Map<String, Object> piiContextMap = Maps.newHashMap();

    public void putPiiData(String domainFieldName, Object domainFieldValue) {
        piiDataMap.put(domainFieldName, domainFieldValue);
    }

    public Object getPiiData(String domainFieldName) {
        return piiDataMap.get(domainFieldName);
    }

    public void putPiiContext(String contextName, Object contextNameValue) {
        piiContextMap.put(contextName, contextNameValue);
    }

    public Object getPiiContext(String contextName) {
        return piiContextMap.get(contextName);
    }
}
  • PiiClient----entry test class
package com.example.ownertest.dm.comp;

import java.util.Map;

/**
 * @Author: linear.zw
 * @Date: 2023/10/31 20:48
 */
public class PiiClient {

    public static void main(String[] args) {
        PiiHandlerRegistry.init();

        // 遍历处理器
        for (Map.Entry<String, PiiDomainFieldHandler> entryHandler :
            PiiHandlerRegistry.getPiiDomainFieldHandlerMap().entrySet()) {
            System.out.println(entryHandler.getKey() + "\t" + entryHandler.getValue().getPiiDomainMeta());
        }

        //
        PiiContent piiContent = new PiiContent();
        piiContent.putPiiContext(PiiContent.FORTEST, PiiContent.FORTEST);

        // 请求处理
        System.out.println("ForTestSupportFieldHandler start");
        PiiHandlerRegistry.handlerRead(new ForTestSupportFieldHandler(), null, piiContent);
        System.out.println("ForTestSupportFieldHandler end");

        // 请求处理
        System.out.println("ForTestNotSupportFieldHandler start");
        PiiHandlerRegistry.handlerRead(new ForTestNotSupportFieldHandler(), null, piiContent);
        System.out.println("ForTestNotSupportFieldHandler end");

    }
}

Applications in common frameworks

  • There are too many. For example, the core BeanPostProcessor mechanism of spring manages a series of beanPostProcessors through org.springframework.beans.factory.support.AbstractBeanFactory#beanPostProcessors. When the spring context org.springframework.context.support.AbstractApplicationContext#refresh , perform bean init (InitDestroyAnnotationBeanPostProcessor), parse annotations (ScheduledAnnotationBeanPostProcessor, AutowiredAnnotationBeanPostProcessor), parse aop (AnnotationAwareAspectJAutoProxyCreator), etc.

Annotation-based routines

key point

  • Annotation meta definition----used to define general meta information;
  • Annotation parser - parses whether there are specified annotations on the class, and then performs corresponding extension operations;
  • Spring's BeanPostProcessor----Here we borrow spring's BeanPostProcessor mechanism. When the spring container is initialized, a callback is performed to complete the expected expansion behavior;

Applicable scene

  • Simplify internal use

Implement code

  • ForTestAnnotation----annotation meta-definition
package com.example.ownertest.dm.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.stereotype.Component;

/**
 * 用于测试的标识注解
 *
 * @Author: linear.zw
 * @Date: 2023/11/1 10:21
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ForTestAnnotation {
}
  • ForTestAnnotationProcessor----annotation parser
package com.example.ownertest.dm.annotation;

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

/**
 * 注解解析器
 * @Author: linear.zw
 * @Date: 2023/11/1 10:25
 */
@Component
public class ForTestAnnotationProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        // 获取目标类是否有ForTestAnnotation注解
        ForTestAnnotation annotation = AnnotationUtils.findAnnotation(AopUtils.getTargetClass(bean),
            ForTestAnnotation.class);

        if (annotation == null) {
            return bean;
        }

        // 处理想要的扩展
        System.out.println(beanName + " has ForTestAnnotation");

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
  • ForTestBean----test bean
package com.example.ownertest.dm.annotation;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 10:26
 */
@ForTestAnnotation
public class ForTestBean {

    public ForTestBean() {
        System.out.println(ForTestBean.class.getSimpleName() + " init");
    }
}
  • ForTestClient---Test entrance
package com.example.ownertest.dm.annotation;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 10:26
 */
public class ForTestClient {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(
            "com.example.ownertest.dm.annotation");

        System.out.println(ForTestClient.class.getSimpleName());
    }
}

Applications in common frameworks

  • For example, spring-boot-alibaba-diamond-autoconfigure within the group

Routines based on event distribution

key point

  • Event source--event triggerer
  • Event - the source of the identification
  • Event listener - the follower of the event, that is, the handler
  • Event dispatcher--used to forward events from event sources to event listeners

Implement code

  • EventSource/EventSourceForTest/EventSourceForTest2
package com.example.ownertest.dm.event;

/**
 * 发出事件
 * @Author: linear.zw
 * @Date: 2023/11/1 14:12
 */
public interface EventSource {

    /**
     * 发出事件
     *
     * @return
     */
    Event fireEvent();
}





package com.example.ownertest.dm.event;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 14:14
 */
public class EventSourceForTest implements EventSource {
    @Override
    public Event fireEvent() {

        Event event = new EventForTest();
        System.out.println(getClass().getSimpleName() + " \t fireEvent " + event.getName());

        return event;
    }
}





package com.example.ownertest.dm.event;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 14:15
 */
public class EventSourceForTest2 implements EventSource {
    @Override
    public Event fireEvent() {

        Event event = new EventForTest2();
        System.out.println(getClass().getSimpleName() + " \t fireEvent " + event.getName());

        return event;
    }
}
  • Event/EventForTest/EventForTest2
package com.example.ownertest.dm.event;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 14:15
 */
public interface Event {

    /**
     * 事件名称
     *
     * @return
     */
    String getName();
}




package com.example.ownertest.dm.event;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 14:17
 */
public class EventForTest implements Event {
    @Override
    public String getName() {
        return getClass().getSimpleName();
    }
}




package com.example.ownertest.dm.event;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 14:17
 */
public class EventForTest2 implements Event {
    @Override
    public String getName() {
        return getClass().getSimpleName();
    }
}
  • EventListener/EventListenerForTest
package com.example.ownertest.dm.event;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 14:17
 */
public interface EventListener {

    /**
     * 是否支持此事件
     *
     * @param event
     * @return
     */
    boolean supportEvent(Event event);

    /**
     * 处理事件
     *
     * @return
     */
    boolean handlerEvent(Event event);
}





package com.example.ownertest.dm.event;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 14:18
 */
public class EventListenerForTest implements EventListener {
    @Override
    public boolean supportEvent(Event event) {

        return event.getName().contains("Test");
    }

    @Override
    public boolean handlerEvent(Event event) {

        System.out.println(this.getClass().getSimpleName() + "\t handler " + event.getName());

        return true;
    }
}
  • EventDispatcher/EventListenerManager
package com.example.ownertest.dm.event;

import org.apache.commons.collections4.CollectionUtils;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 14:18
 */
public class EventDispatcher {

    /**
     * 单例模式
     */
    private static EventDispatcher eventDispatcher = new EventDispatcher();

    private EventDispatcher() {

    }

    /**
     * 分发事件
     *
     * @param event
     * @return
     */
    public static boolean dispatchEvent(Event event) {
        if (CollectionUtils.isNotEmpty(EventListenerManager.getEventListenerList())) {
            for (EventListener eventListener :
                EventListenerManager.getEventListenerList()) {
                if (eventListener.supportEvent(event)) {
                    eventListener.handlerEvent(event);
                }
            }
        }
        return true;
    }
}



package com.example.ownertest.dm.event;

import java.util.List;

import com.google.common.collect.Lists;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 14:18
 */
public class EventListenerManager {

    private static List<EventListener> eventListenerList = Lists.newArrayList();

    /**
     * 添加事件监听器
     *
     * @param eventListener
     * @return
     */
    public static boolean addEventListener(EventListener eventListener) {
        if (!eventListenerList.contains(eventListener)) {
            return eventListenerList.add(eventListener);
        }

        return true;
    }

    /**
     * 移除事件监听器
     *
     * @param eventListener
     * @return
     */
    public static boolean removeEventListener(EventListener eventListener) {
        if (eventListenerList.contains(eventListener)) {
            return eventListenerList.remove(eventListener);
        }

        return true;
    }

    public static List<EventListener> getEventListenerList() {
        return eventListenerList;
    }
}
  • EventClient
package com.example.ownertest.dm.event;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 14:19
 */
public class EventClient {

    public static void main(String[] args) {

        // 创建事件源
        EventSource eventSourceForTest = new EventSourceForTest();
        EventSource eventSourceForTest2 = new EventSourceForTest2();

        // 创建事件监听器
        EventListener eventListener = new EventListenerForTest();
        EventListenerManager.addEventListener(eventListener);

        // 发布事件
        EventDispatcher.dispatchEvent(eventSourceForTest.fireEvent());
        EventDispatcher.dispatchEvent(eventSourceForTest2.fireEvent());

    }
}

Routine based on SPI mechanism

key point

 
 
  • service caller
  • Service implementer----use the interface name as the file name, put it in META-INF/services, and the value is the implementation of the interface

  • Standard service interface

Applicable scene

Implement code

  • SpiServiceLoaderHelper
package com.example.ownertest.dm.spi;

import java.util.Iterator;
import java.util.Objects;
import java.util.ServiceLoader;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 15:32
 */
public class SpiServiceLoaderHelper {

    public static ProductPackageRemoteServiceInterface getProductPackageRemoteServiceInterface() {
        // 先从缓存中加载
        Object serviceCache = DependServiceRegistryHelper.getDependObject(ProductPackageRemoteServiceInterface.class);
        if (serviceCache != null) {
            return (ProductPackageRemoteServiceInterface) serviceCache;
        }
        // spi 方式加载
        ProductPackageRemoteServiceInterface serviceInterface = loadSpiImpl(ProductPackageRemoteServiceInterface.class);
        // 防止注入的bean为空 提前进行判断 以免业务执行出现问题
        boolean isExist = true;
        if (Objects.isNull(serviceInterface)) {
            isExist = false;
        } else if (Objects.isNull(serviceInterface.getProductPackageRemoteService())) {
            isExist = false;
        }
        if (!isExist) {
            throw new RuntimeException("getProductPackageRemoteService load impl failed,please check spi service");
        }
        // 添加进统一的依赖管理
        DependServiceRegistryHelper.registry(ProductPackageRemoteServiceInterface.class, serviceInterface);
        return serviceInterface;
    }

    /**
     * 以spi的方式加载实现类
     *
     * @param cls
     * @param <P>
     * @return
     */
    private static <P> P loadSpiImpl(Class<P> cls) {
        ServiceLoader<P> spiLoader = ServiceLoader.load(cls);
        Iterator<P> iaIterator = spiLoader.iterator();
        if (iaIterator.hasNext()) {
            return iaIterator.next();
        }
        return null;
    }
}
  • DependServiceRegistryHelper
package com.example.ownertest.dm.spi;

import java.util.Map;

import com.google.common.collect.Maps;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 15:35
 */
public class DependServiceRegistryHelper {

    /**
     * 存储策略依赖的服务,统一管理
     */
    private static Map<String, Object> dependManagerMap = Maps.newHashMap();

    public static boolean registryMap(Map<Class, Object> dependManagerMap) {
        for (Map.Entry<Class, Object> dependEntry :
            dependManagerMap.entrySet()) {
            registry(dependEntry.getKey(), dependEntry.getValue());
        }
        return true;
    }

    public static boolean registry(Class cls, Object dependObject) {
        dependManagerMap.put(cls.getCanonicalName(), dependObject);
        return true;
    }

    public static Object getDependObject(Class cls) {

        return dependManagerMap.get(cls.getCanonicalName());
    }
}
  • SpiServiceLoaderClientTest
package com.example.ownertest.dm.spi;

/**
 * @Author: linear.zw
 * @Date: 2023/11/1 15:37
 */
public class SpiServiceLoaderClientTest {

    public static void main(String[] args) {
        ProductPackageRemoteServiceInterface productPackageRemoteServiceInterface
            = SpiServiceLoaderHelper.getProductPackageRemoteServiceInterface();

    }
}

Applications in common frameworks

  • At present, most of the strategy packages in the middle office are based on the SPI method and dynamically load the business implementation to achieve the purpose of expansion;
  • For example, Google's open source auto-service automatically generates the SPI implementation directory through annotations;

at last

Most programmers are practical people, so what are your routines? Your place is in the comment area, Show me the code.

Author|Ghat

Original link

This article is original content from Alibaba Cloud and may not be reproduced without permission.

Spring Boot 3.2.0 is officially released. The most serious service failure in Didi’s history. Is the culprit the underlying software or “reducing costs and increasing laughter”? Programmers tampered with ETC balances and embezzled more than 2.6 million yuan a year. Google employees criticized the big boss after leaving their jobs. They were deeply involved in the Flutter project and formulated HTML-related standards. Microsoft Copilot Web AI will be officially launched on December 1, supporting Chinese PHP 8.3 GA Firefox in 2023 Rust Web framework Rocket has become faster and released v0.5: supports asynchronous, SSE, WebSockets, etc. Loongson 3A6000 desktop processor is officially released, the light of domestic production! Broadcom announces successful acquisition of VMware
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/yunqi/blog/10314488