0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Java中保持擴展性的實現(xiàn)方法

OSC開源社區(qū) ? 來源: 阿里云開發(fā)者 ? 2023-12-01 10:01 ? 次閱讀

阿里妹導讀

在日常的開發(fā)中,作者總結(jié)了平常用到的一些低成本、保持擴展性的套路,分享出來,歡迎大家討論。

前言


SOLID(單一、開閉、里式替換、接口隔離、依賴倒置)五大原則和23種設(shè)計模式(常見的單例、構(gòu)建者、裝飾、適配、代理、組合、模板等等),小伙伴們對這些肯定都很熟悉。這些原則和設(shè)計模式能夠輔助我們,讓我們在設(shè)計的時候有所抉擇,從而達到高內(nèi)聚、低耦合的目的。

那說到設(shè)計,肯定會提到架構(gòu)兩個字,常見的架構(gòu)名詞:分層架構(gòu)、六邊形架構(gòu)、SOA架構(gòu)、CQRS架構(gòu)、EDA架構(gòu)等等。我個人對架構(gòu)的理解是邊界的確認以及邊界內(nèi)部元素的組合,其實只要是程序員,他就一定是架構(gòu)師,只不過是好的架構(gòu)師還是不那么好的架構(gòu)師;人人都是架構(gòu)師,我認為這句話是沒有問題的,區(qū)別在于他的認知決定了他所能確認的邊界、如何更高效的組合內(nèi)部元素;技術(shù)的架構(gòu)師肯定側(cè)重于技術(shù),業(yè)務的架構(gòu)師肯定側(cè)重于業(yè)務,商品的架構(gòu)師所能看到的邊界大概率還是局限在商品域,ICBU架構(gòu)組的架構(gòu)師更多考慮是橫向的業(yè)務支撐。以上是我個人對架構(gòu)兩個字的理解,今天我們不討論具體架構(gòu),我們討論一些套路,在日常的開發(fā)中,我總結(jié)了些我平常用到的一些低成本、保持擴展性的套路,分享出來,歡迎大家討論。


基于管道(pipeline)的套路

關(guān)鍵點

56f4d74a-8f6d-11ee-939d-92fbcf53809c.jpg

管道(Pipeline)----用于串聯(lián)閥門的管道通路

閥門(PipelineValue)----用于每一個節(jié)點處理實際業(yè)務訴求

管道上下文(PipelineContext)----用于管道上下文中數(shù)據(jù)的扭轉(zhuǎn)

適用場景

當你的數(shù)據(jù)流需要經(jīng)過很多同等邏輯處理時,可以考慮使用此套路,便于后續(xù)擴展

實現(xiàn)代碼

Pipeline/StandardPipeline

package com.example.ownertest.dm.pipelline;


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


    /**
     * 執(zhí)行
     *
     * @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 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";


    /**
     * 設(shè)置
     *
     * @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 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 {


    /**
     * 節(jié)點執(zhí)行
     *
     * @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);


        // 調(diào)用管道
        pipeline.invoke(pipelineContext);


    }
}

常見框架中的應用

網(wǎng)絡層的扛把子netty框架中,例如ChannelPipeline、ChannelHandler、ChannelHandlerContext,分別用于處理tcp拆包、加解碼等等之類。

57037a20-8f6d-11ee-939d-92fbcf53809c.png

基于責任鏈(filterchain)的套路

關(guān)鍵點

570cd584-8f6d-11ee-939d-92fbcf53809c.jpg

571c7fde-8f6d-11ee-939d-92fbcf53809c.jpg

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

過濾器(Filter)----實際處理業(yè)務的節(jié)點

過濾鏈(FilterChain)----串聯(lián)過濾器的鏈條

適用場景

例如常見的web請求場景

實現(xiàn)代碼

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 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----入口測試

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

常見框架中的應用

hsf的filter機制,服務端擴展的ServerFilter和客戶端擴展的ClientFilter;

572ba6d0-8f6d-11ee-939d-92fbcf53809c.png

??開發(fā)過java web的小伙伴都知道的servlet,servlet的入口即是FilterChain、Filter;

573bcd62-8f6d-11ee-939d-92fbcf53809c.png

基于組合/模板的套路

關(guān)鍵點

5741d824-8f6d-11ee-939d-92fbcf53809c.jpg

處理器注冊器----用于存儲處理器的集合

處理器工廠----用于創(chuàng)建處理器

處理器----實際的處理器以及擴展的實現(xiàn)

處理器上下文----處理器上下文,用于參數(shù)的傳遞

適用場景

適合于有共性、后續(xù)持續(xù)擴展的場景

實現(xiàn)代碼

PiiHandlerRegistry----處理器注冊器

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 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  int handlerRead(T domain, Field domainField, PiiContent piiContent) {
        int num = 0;
        for (Map.Entry piiDomainFieldHandlerEntry :
            piiDomainFieldHandlerMap.entrySet()) {
            if (piiDomainFieldHandlerEntry.getValue().isSupport(domain, domainField)) {
                piiDomainFieldHandlerEntry.getValue().handlerRead(domain, domainField, piiContent);
            }
        }
        return num;
    }


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


    public static Map getPiiDomainFieldHandlerMap() {
        return piiDomainFieldHandlerMap;
    }


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


            for (PiiDomainFieldHandler piiDomainFieldHandler :
                piiDomainFieldHandlerList) {
                putHandler(piiDomainFieldHandler.getPiiDomainMeta(), piiDomainFieldHandler);
            }
        }
    }
}

PiiDomainFieldHandlerFactory----處理器工廠

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 {


    /**
     * 創(chuàng)建領(lǐng)域處理器
     *
     * @return
     */
    public static List createPiiDomainFieldHandler() {
        List 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獲取數(shù)據(jù)回填domain
     *
     * @param domain
     * @param domainField
     * @param piiContent
     * @param 
     * @return
     */
     boolean handlerRead(T domain, Field domainField, PiiContent piiContent);


    /**
     * 處理實際操作
     * 寫----將domain中需要寫入pii的字段數(shù)據(jù)寫入PiiContent
     *
     * @param domain
     * @param domainField
     * @param piiContent
     * @param 
     * @return
     */
     boolean handlerWrite(T domain, Field domainField, PiiContent piiContent);


    /**
     * 當前處理器是否支持該領(lǐng)域?qū)ο?     *
     * @param domain
     * @param domainField
     * @param 
     * @return
     */
     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  boolean handlerRead(T domain, Field domainField, PiiContent piiContent) {
        // to do business read


        return true;
    }


    @Override
    public  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  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  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----上下文

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 piiDataMap = Maps.newHashMap();


    private Map 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----入口的測試類

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 entryHandler :
            PiiHandlerRegistry.getPiiDomainFieldHandlerMap().entrySet()) {
            System.out.println(entryHandler.getKey() + "	" + 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");


    }
}

常見框架中的應用

這個就太多了,例如spring最核心的BeanPostProcessor機制,通過org.springframework.beans.factory.support.AbstractBeanFactory#beanPostProcessors管理一些列的beanPostProcessors,在spring上下文org.springframework.context.support.AbstractApplicationContext#refresh的時候,進行bean的init(InitDestroyAnnotationBeanPostProcessor)、解析注解(ScheduledAnnotationBeanPostProcessor、AutowiredAnnotationBeanPostProcessor)、解析aop(AnnotationAwareAspectJAutoProxyCreator)等等。

基于注解的套路

關(guān)鍵點

57507b90-8f6d-11ee-939d-92fbcf53809c.jpg

??注解元定義----用來定義通用的元信息;

注解解析器----解析類上是否有指定的注解,進而進行對應的擴展操作;

spring的BeanPostProcessor----這里是借用spring的BeanPostProcessor機制,在spring容器初始化的時候,進行回調(diào),完成預期的擴展行為;

適用場景

簡化內(nèi)部使用

實現(xiàn)代碼

ForTestAnnotation----注解元定義

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----注解解析器

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----測試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---測試入口

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

常見框架中的應用

例如集團內(nèi)部的spring-boot-alibaba-diamond-autoconfigure

575e07b0-8f6d-11ee-939d-92fbcf53809c.png

基于事件分發(fā)的套路

關(guān)鍵點??

5771aa18-8f6d-11ee-939d-92fbcf53809c.jpg

事件源--事件觸發(fā)者

事件--標識產(chǎn)生的來源

事件監(jiān)聽器--事件的關(guān)注者,即處理者

事件分發(fā)器--用于將事件源的事件轉(zhuǎn)發(fā)給事件監(jiān)聽器

實現(xiàn)代碼

EventSource/EventSourceForTest/EventSourceForTest2

package com.example.ownertest.dm.event;


/**
 * 發(fā)出事件
 * @Author: linear.zw
 * @Date: 2023/11/1 14:12
 */
public interface EventSource {


    /**
     * 發(fā)出事件
     *
     * @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() + " 	 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() + " 	 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() + "	 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() {


    }


    /**
     * 分發(fā)事件
     *
     * @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 eventListenerList = Lists.newArrayList();


    /**
     * 添加事件監(jiān)聽器
     *
     * @param eventListener
     * @return
     */
    public static boolean addEventListener(EventListener eventListener) {
        if (!eventListenerList.contains(eventListener)) {
            return eventListenerList.add(eventListener);
        }


        return true;
    }


    /**
     * 移除事件監(jiān)聽器
     *
     * @param eventListener
     * @return
     */
    public static boolean removeEventListener(EventListener eventListener) {
        if (eventListenerList.contains(eventListener)) {
            return eventListenerList.remove(eventListener);
        }


        return true;
    }


    public static List 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) {


        // 創(chuàng)建事件源
        EventSource eventSourceForTest = new EventSourceForTest();
        EventSource eventSourceForTest2 = new EventSourceForTest2();


        // 創(chuàng)建事件監(jiān)聽器
        EventListener eventListener = new EventListenerForTest();
        EventListenerManager.addEventListener(eventListener);


        // 發(fā)布事件
        EventDispatcher.dispatchEvent(eventSourceForTest.fireEvent());
        EventDispatcher.dispatchEvent(eventSourceForTest2.fireEvent());


    }
}

基于SPI機制的套路

關(guān)鍵點

577c3e2e-8f6d-11ee-939d-92fbcf53809c.jpg

5795afa8-8f6d-11ee-939d-92fbcf53809c.png

服務調(diào)用方

服務實現(xiàn)方----以接口名稱為文件名稱,放在META-INF/services,值為該接口的實現(xiàn)

57ec380a-8f6d-11ee-939d-92fbcf53809c.png

標準服務接口

適用場景??

57fb480e-8f6d-11ee-939d-92fbcf53809c.png

實現(xiàn)代碼

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為空 提前進行判斷 以免業(yè)務執(zhí)行出現(xiàn)問題
        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");
        }
        // 添加進統(tǒng)一的依賴管理
        DependServiceRegistryHelper.registry(ProductPackageRemoteServiceInterface.class, serviceInterface);
        return serviceInterface;
    }


    /**
     * 以spi的方式加載實現(xiàn)類
     *
     * @param cls
     * @param 

* @return */ private static

P loadSpiImpl(Class

cls) { ServiceLoader

spiLoader = ServiceLoader.load(cls); Iterator

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 {


    /**
     * 存儲策略依賴的服務,統(tǒng)一管理
     */
    private static Map dependManagerMap = Maps.newHashMap();


    public static boolean registryMap(Map dependManagerMap) {
        for (Map.Entry 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();


    }
}

常見框架中的應用

目前大多數(shù)中臺的策略包,都是基于spi方式的,動態(tài)加載業(yè)務的實現(xiàn),進而達到擴展的目的;

例如google開源的auto-service,通過注解的方式,自動生成spi的實現(xiàn)目錄;

58066108-8f6d-11ee-939d-92fbcf53809c.png

最后

程序員大多數(shù)都是實干派,所以,你的套路有哪些,評論區(qū)有你的位置,Show me the code。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2946

    瀏覽量

    104369
  • eda
    eda
    +關(guān)注

    關(guān)注

    71

    文章

    2672

    瀏覽量

    172562
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4697

    瀏覽量

    68084

原文標題:Java中保持擴展性的幾種套路和實現(xiàn)

文章出處:【微信號:OSC開源社區(qū),微信公眾號:OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Facebook的擴展性挑戰(zhàn)討論

    今天我們一起來了解Facebook背后的軟件,看看作為當今世界上訪問量最大的網(wǎng)站之一,F(xiàn)acebook是如何保證5億用戶的系統(tǒng)一直穩(wěn)定可靠的運行。 Facebook的擴展性挑戰(zhàn)
    發(fā)表于 07-17 07:22

    請問處理器擴展性有什么重要之處?

    處理器擴展性有什么重要之處?
    發(fā)表于 06-17 09:51

    多機飛行仿真系統(tǒng)擴展性研究

    針對以往多機飛行仿真系統(tǒng)中存在擴展性差的問題,在利用新一代分布式仿真技術(shù)框架結(jié)構(gòu)開發(fā)該系統(tǒng)的基礎(chǔ)上,采用時間同步和數(shù)據(jù)過濾的方式來提高系統(tǒng)的擴展性。從基于HLA 協(xié)議開
    發(fā)表于 05-12 17:06 ?22次下載
    多機飛行仿真系統(tǒng)<b class='flag-5'>擴展性</b>研究

    OpenStack 企業(yè)私有云需大規(guī)模擴展性支持

    擴展性(Scalability)是云的基本要素之一,因此對 OpenStack 云也不例外。 一方面,和已經(jīng)非常成熟的公有云和私有云方案相比,目前的 OpenStack 在擴展性方面還有很多的不足
    發(fā)表于 10-11 10:25 ?0次下載
    OpenStack 企業(yè)私有云需大規(guī)模<b class='flag-5'>擴展性</b>支持

    基于軟件定義網(wǎng)絡控制可擴展性研究

    軟件定義網(wǎng)絡(software-defined networking,簡稱SDN)遵循控制轉(zhuǎn)發(fā)分離的設(shè)計原則,其控制平面采用集中的控制邏輯,在提供靈活高效的網(wǎng)絡控制的同時,也面臨著嚴重的可擴展性
    發(fā)表于 12-19 18:07 ?0次下載
    基于軟件定義網(wǎng)絡控制可<b class='flag-5'>擴展性</b>研究

    為什么區(qū)塊鏈擴展性如此困難

    擴展性的爭論遍及整個數(shù)字貨幣社區(qū)。隨著一些重大事件的發(fā)生,在短短幾天時間內(nèi),就會導致整個以太網(wǎng)絡的崩潰,比如CryptoKitties。正如大家所知道的,當前狀態(tài)下最大的公共區(qū)塊鏈不能處理過多的事務,因此需要擴展
    發(fā)表于 12-13 14:51 ?1220次閱讀

    如何使用BPL和DLL進行程序的擴展性資料說明

    本文檔的主要內(nèi)容詳細介紹的是如何使用BPL和DLL進行程序的擴展性資料說明。
    發(fā)表于 05-17 18:05 ?0次下載
    如何使用BPL和DLL進行程序的<b class='flag-5'>擴展性</b>資料說明

    區(qū)塊鏈可擴展性的要點分別是什么

    大多數(shù)關(guān)于可擴展性的討論都圍繞著各種平臺每秒可以處理的交易數(shù)量。
    發(fā)表于 10-31 09:31 ?2447次閱讀

    千兆位串行鏈路實現(xiàn)無限可擴展性應用

    多內(nèi)核處理器可為越來越多的高性能、數(shù)據(jù)密集型應用帶來優(yōu)勢,如無線基站與高性能計算平臺等,因此系統(tǒng)可擴展性只能通過大容量嵌入式互連實現(xiàn)。千兆位串行鏈路不但可降低系統(tǒng)成本,減少面積占用與引腳數(shù),同時還可提高并行性,改進性能與容量,從而有助于
    發(fā)表于 01-19 10:15 ?1296次閱讀

    區(qū)塊鏈可擴展性有怎樣的要點

    很難說誰的可擴展性方法最終會更可行。然而,如果每個參與者都認識到存在的選擇比表面上的要多,那就更好了。
    發(fā)表于 03-07 14:40 ?782次閱讀

    影響軟件高可擴展性的六大因素

    軟件可擴展性是一個有趣的話題。實現(xiàn)軟件可擴展性涉及很多因素,我們在本文將討論一些與開發(fā)和運維方面相關(guān)的因素。
    的頭像 發(fā)表于 02-17 16:13 ?8650次閱讀
    影響軟件高可<b class='flag-5'>擴展性</b>的六大因素

    以小尺寸設(shè)計實現(xiàn)擴展性和遷移

      可擴展性建立在芯片支持的基礎(chǔ)上,利用板級一致性和便利性,例如驅(qū)動程序的現(xiàn)成可用性。設(shè)計可以遵循相同的引腳分配和連接器方案,同時通過每一代新的 COM 保持平穩(wěn)的路徑來提高性能。
    的頭像 發(fā)表于 06-14 14:45 ?1030次閱讀

    什么是可擴展性,為什么它很重要

    擴展性是按需輕松擴展或升級的能力。它是產(chǎn)品、系統(tǒng)、團隊或公司提供滿足不斷增長的需求的服務的能力。提供足夠的基礎(chǔ)設(shè)施來滿足更苛刻的IT要求,例如增加存儲和安全性,同時保持低成本,是數(shù)據(jù)中心運營商的日常斗爭。
    的頭像 發(fā)表于 04-21 10:36 ?4780次閱讀
    什么是可<b class='flag-5'>擴展性</b>,為什么它很重要

    SD-WAN組網(wǎng)的可擴展性怎么樣?

    SD-WAN組網(wǎng)具有很好的可擴展性,能夠輕松滿足企業(yè)網(wǎng)絡不斷擴張和增長的需求,同時保持網(wǎng)絡的高效和可管理性,這使得SD-WAN組網(wǎng)能夠隨著企業(yè)的快速發(fā)展而快速調(diào)整規(guī)模,變更拓撲,采取不同的接入方式等
    的頭像 發(fā)表于 08-18 11:29 ?473次閱讀

    擴展性對物聯(lián)網(wǎng)管理系統(tǒng)有哪些影響?

    擴展性對于物聯(lián)網(wǎng)管理系統(tǒng)的設(shè)計和開發(fā)非常重要,它直接影響著系統(tǒng)的性能、可靠性和能耗等方面,是評估一個系統(tǒng)優(yōu)劣的重要因素之一???b class='flag-5'>擴展性對物聯(lián)網(wǎng)管理系統(tǒng)的影響主要體現(xiàn)在以下幾個方面:
    的頭像 發(fā)表于 10-11 15:15 ?437次閱讀