使用Spring的BeanPostProcessor優(yōu)雅的實(shí)現(xiàn)工廠模式

最近在學(xué)習(xí)狀態(tài)設(shè)計(jì)模式時(shí),發(fā)現(xiàn)一個(gè)實(shí)現(xiàn)工廠模式的優(yōu)雅方式叶沛,那就是借助注解和Spring的BeanPostProcessor拴测。使用處理器BeanPostProcessor可以在bean初始化的前后對(duì)bean進(jìn)行操作壹将,所以我們可以在bean初始化后對(duì)自己的目標(biāo)bean進(jìn)行緩存,進(jìn)而實(shí)現(xiàn)我們想要的處理邏輯腺怯。

BeanPostProcessor是Spring IOC容器給我們提供的一個(gè)擴(kuò)展接口袱饭,點(diǎn)擊源碼可以查看到BeanPostProcessor中有兩個(gè)方法:

  • (1)postProcessBeforeInitialization方法;
  • (2)postProcessAfterInitialization方法呛占;

兩個(gè)方面的運(yùn)行順序如下:

  • (1)Spring IOC容器實(shí)例化Bean
  • (2)調(diào)用BeanPostProcessor的postProcessBeforeInitialization方法
  • (3)調(diào)用bean實(shí)例的初始化方法
  • (4)調(diào)用BeanPostProcessor的postProcessAfterInitialization方法

注意:實(shí)測(cè)項(xiàng)目中只能有一個(gè)BeanPostProcessor的實(shí)現(xiàn)虑乖,當(dāng)出現(xiàn)多個(gè)時(shí),只是加載了一次晾虑。注意此點(diǎn)疹味,避免踩坑。

背景

當(dāng)我們需要根據(jù)不同的狀態(tài)調(diào)用接口不同的實(shí)現(xiàn)時(shí)帜篇,可以使用工廠模式實(shí)現(xiàn)糙捺。而之前最常用的方式就是通過多個(gè)if else的靜態(tài)工廠方式進(jìn)行處理。

進(jìn)入主題

通過BeanPostProcessor實(shí)現(xiàn)相同接口的不同實(shí)現(xiàn)bean的工廠笙隙。

1 定義一個(gè)注解继找,用于標(biāo)記不同的實(shí)現(xiàn)bean
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Component
public @interface OrderStatusProcessor {
    /**
     * 狀態(tài)枚舉
     */
    OrderStatusEnum status() ;

}

狀態(tài)枚舉定義:

@Getter
@AllArgsConstructor
public enum OrderStatusEnum {
    CREATE(1, "新建"),
    COMPLETE(2, "完成"),
    CANCLE(3, "取消");
    
    private final Integer statusCode;
    private final String statusDesc;

    public static OrderStatusEnum getEnumByCode(Integer statusCode) {
        if (statusCode == null) {
            return null;
        }
        return Arrays.stream(values()).filter(e -> e.getStatusCode().equals(statusCode)).findFirst().orElse(null);
    }
}
2 定義接口處理類
public interface StatusProcessor {
    public boolean action(OrderInfo orderInfo);
}

Model定義:

@Data
@Builder
public class OrderInfo {
    /**
     * 訂單編號(hào)
     */
    private Long orderId;

    /**
     * 訂單狀態(tài):1-新建;2-完成逃沿;3-取消婴渡;
     */
    private Integer orderStatus;
}
3 自定義處理器實(shí)現(xiàn),并用自定義的注解進(jìn)行標(biāo)識(shí)
@Slf4j
@OrderStatusProcessor(status = OrderStatusEnum.CREATE)
public class StatusCreateProcessor implements StatusProcessor {
    @Override
    public boolean action(OrderInfo orderInfo) {
        log.info("訂單==>編號(hào):({}),執(zhí)行({})操作凯亮。",orderInfo.getOrderId(),OrderStatusEnum.getEnumByCode(orderInfo.getOrderStatus()).getStatusDesc());
        return false;
    }
}
@Slf4j
@OrderStatusProcessor(status = OrderStatusEnum.COMPLETE)
public class StatusCompleteProcessor implements StatusProcessor {
    @Override
    public boolean action(OrderInfo orderInfo) {
        log.info("訂單==>編號(hào):({}),執(zhí)行({})操作边臼。",orderInfo.getOrderId(),OrderStatusEnum.getEnumByCode(orderInfo.getOrderStatus()).getStatusDesc());
        return false;
    }
}
@Slf4j
@OrderStatusProcessor(status = OrderStatusEnum.CANCLE)
public class StatusCancleProcessor implements StatusProcessor {
    @Override
    public boolean action(OrderInfo orderInfo) {
        log.info("訂單==>編號(hào):({}),執(zhí)行({})操作。",orderInfo.getOrderId(),OrderStatusEnum.getEnumByCode(orderInfo.getOrderStatus()).getStatusDesc());
        return false;
    }
}
4 通過BeanPostProcessor實(shí)現(xiàn)對(duì)bean后置處理假消,對(duì)注解的類型bean對(duì)象進(jìn)行緩存
@Slf4j
@Component
public class StatusProcessRegistry implements BeanPostProcessor {
    /**
     * 第一層key是訂單狀態(tài)柠并。
     * 第二層是對(duì)應(yīng)的處理器。
     */
    private static Map<Integer, StatusProcessor> statusProcessMap = new ConcurrentHashMap<>();

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("beanName =============>"+beanName);
        if (bean instanceof StatusProcessor && bean.getClass().isAnnotationPresent(OrderStatusProcessor.class)) {
            OrderStatusProcessor annotation = bean.getClass().getAnnotation(OrderStatusProcessor.class);
            OrderStatusEnum orderStatusEnum = annotation.status();
            Integer statusCode = orderStatusEnum.getStatusCode();
            if (!statusProcessMap.containsKey(statusCode)){
                statusProcessMap.put(statusCode,(StatusProcessor)bean);
            }
        }
        return bean;
    }

    public StatusProcessor acquireStatusProcess(Integer statusCode) {
        return statusProcessMap.get(statusCode);
    }

}
5 狀態(tài)機(jī)引擎處理類定義
@AllArgsConstructor
@Component
public class OrderOperatorEngine {

    private final StatusProcessRegistry statusProcessRegistry;

    public boolean operate(OrderInfo orderInfo) throws Exception{
        // 獲取當(dāng)前事件處理器
        StatusProcessor statusProcessor = statusProcessRegistry.acquireStatusProcess(orderInfo.getOrderStatus());
        if (statusProcessor == null) {
            throw new Exception("NOT_FOUND_PROCESSOR");
        }
        // 執(zhí)行處理邏輯
        return statusProcessor.action(orderInfo);
    }
}
6 Rest Controller定義
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/myorder")
public class OrderController {
    private final OrderOperatorEngine orderOperatorEngine;

    @GetMapping("/update/{orderId}/{orderStatus}")
    public void accept(@PathVariable(value = "orderId") Long orderId, @PathVariable(value = "orderStatus") Integer orderStatus) {
        OrderInfo orderInfo = OrderInfo.builder()
                .orderId(orderId)
                .orderStatus(orderStatus)
                .build();
        log.info("updateOrder info:{}", JSON.toJSONString(orderInfo));
        try {
            orderOperatorEngine.operate(orderInfo);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
}
7 演示日志
###|||2021-10-19 21:49:21.651|||INFO|||656c12ff21d64eec9226f68d9db5d999.201.16346513616080000|||-|||http-nio-8080-exec-3|||OrderController--->updateOrder info:{"orderId":10001,"orderStatus":2}
###|||2021-10-19 21:49:21.652|||INFO|||656c12ff21d64eec9226f68d9db5d999.201.16346513616080000|||-|||http-nio-8080-exec-3|||StatusCompleteProcessor--->訂單==>編號(hào):(10001),執(zhí)行(完成)操作富拗。
###|||2021-10-19 21:49:29.350|||INFO|||656c12ff21d64eec9226f68d9db5d999.204.16346513693480000|||-|||http-nio-8080-exec-6|||OrderController--->updateOrder info:{"orderId":10001,"orderStatus":1}
###|||2021-10-19 21:49:29.350|||INFO|||656c12ff21d64eec9226f68d9db5d999.204.16346513693480000|||-|||http-nio-8080-exec-6|||StatusCreateProcessor--->訂單==>編號(hào):(10001),執(zhí)行(新建)操作臼予。
###|||2021-10-19 21:49:38.690|||INFO|||656c12ff21d64eec9226f68d9db5d999.208.16346513786880000|||-|||http-nio-8080-exec-10|||OrderController--->updateOrder info:{"orderId":10001,"orderStatus":3}
###|||2021-10-19 21:49:38.690|||INFO|||656c12ff21d64eec9226f68d9db5d999.208.16346513786880000|||-|||http-nio-8080-exec-10|||StatusCancleProcessor--->訂單==>編號(hào):(10001),執(zhí)行(取消)操作。

到此這篇關(guān)于詳解使用Spring的BeanPostProcessor優(yōu)雅的實(shí)現(xiàn)工廠模式的文章就介紹到這了,希望能給你帶來一些幫助和啟發(fā)啃沪!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粘拾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子创千,更是在濱河造成了極大的恐慌缰雇,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件追驴,死亡現(xiàn)場(chǎng)離奇詭異械哟,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)殿雪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門暇咆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丙曙,你說我怎么就攤上這事爸业。” “怎么了河泳?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵沃呢,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我拆挥,道長(zhǎng)薄霜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任纸兔,我火速辦了婚禮惰瓜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘汉矿。我一直安慰自己崎坊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布洲拇。 她就那樣靜靜地躺著奈揍,像睡著了一般曲尸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上男翰,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天另患,我揣著相機(jī)與錄音,去河邊找鬼蛾绎。 笑死昆箕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的租冠。 我是一名探鬼主播鹏倘,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼顽爹!你這毒婦竟也來了纤泵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤话原,失蹤者是張志新(化名)和其女友劉穎夕吻,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體繁仁,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涉馅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了黄虱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稚矿。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖捻浦,靈堂內(nèi)的尸體忽然破棺而出晤揣,到底是詐尸還是另有隱情,我是刑警寧澤朱灿,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布昧识,位于F島的核電站,受9級(jí)特大地震影響盗扒,放射性物質(zhì)發(fā)生泄漏跪楞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一侣灶、第九天 我趴在偏房一處隱蔽的房頂上張望甸祭。 院中可真熱鬧,春花似錦褥影、人聲如沸池户。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)校焦。三九已至赊抖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寨典,已是汗流浹背熏迹。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凝赛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓坛缕,卻偏偏與公主長(zhǎng)得像墓猎,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赚楚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容