[Spring cloud 一步步實現廣告系統] 13. 索引服務編碼實現

上一節咱們分析了廣告索引的維護有2種,全量索引加載增量索引維護。由於廣告檢索是廣告系統中最爲重要的環節,你們必定要認真理解咱們索引設計的思路,接下來咱們來編碼實現索引維護功能。html

咱們來定義一個接口,來接收全部index的增刪改查操做,接口定義一個範型,來接收2個參數,K表明咱們索引的健值,V表明返回值。java

/** * IIndexAware for 實現廣告索引的增刪改查 * * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> */
public interface IIndexAware<K, V> {

    /** * 經過key 獲取索引 */
    V get(K key);
    /** * 添加索引 * @param key * @param value */
    void add(K key, V value);
    /** * 更新索引 */
    void update(K key, V value);
    /** * 刪除索引 */
    void delete(K key, V value);
}
複製代碼

咱們必定要知道,並非全部的數據庫表都須要建立索引,好比User表咱們在數據檢索的時候實際上是不須要的,固然也就不必建立索引,而且,也不是表中的全部字段都須要索引,這個也是根據具體的業務來肯定字段信息,好比咱們接下來要編寫的推廣計劃索引中,推廣計劃名稱就能夠不須要。下面,咱們來實現咱們的第一個正向索引git

  • 首先建立操做推廣計劃的實體對象
/** * AdPlanIndexObject for 推廣計劃索引對象 * 這個索引對象咱們沒有添加 推廣計劃名稱 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdPlanIndexObject {

    private Long planId;
    private Long userId;
    private Integer planStatus;
    private Date startDate;
    private Date endDate;

    /** * 根據實際字段來更新索引 */
    public void update(AdPlanIndexObject newObject) {

        if (null != newObject.getPlanId()) {
            this.planId = newObject.getPlanId();
        }
        if (null != newObject.getUserId()) {
            this.userId = newObject.getUserId();
        }
        if (null != newObject.getPlanStatus()) {
            this.planStatus = newObject.getPlanStatus();
        }
        if (null != newObject.getStartDate()) {
            this.startDate = newObject.getStartDate();
        }
        if (null != newObject.getEndDate()) {
            this.endDate = newObject.getEndDate();
        }
    }
}
複製代碼
  • 而後建立推廣計劃索引實現類,並實現IIndexAware接口。
/** * AdPlanIndexAwareImpl for 推廣計劃索引實現類 * * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> */
@Slf4j
@Component
public class AdPlanIndexAwareImpl implements IIndexAware<Long, AdPlanIndexObject> {

    private static Map<Long, AdPlanIndexObject> planIndexObjectMap;

    /** * 由於操做索引的過程當中有可能對索引進行更新,爲了防止多線程形成的線程不安全問題,咱們不能使用hashmap,須要實現ConcurrentHashMap */
    static {
        planIndexObjectMap = new ConcurrentHashMap<>();
    }

    @Override
    public AdPlanIndexObject get(Long key) {
        return planIndexObjectMap.get(key);
    }

    @Override
    public void add(Long key, AdPlanIndexObject value) {

        log.info("AdPlanIndexAwareImpl before add::{}", planIndexObjectMap);
        planIndexObjectMap.put(key, value);
        log.info("AdPlanIndexAwareImpl after add::{}", planIndexObjectMap);
    }

    @Override
    public void update(Long key, AdPlanIndexObject value) {

        log.info("AdPlanIndexAwareImpl before update::{}", planIndexObjectMap);
				//查詢當前的索引信息,若是不存在,直接新增索引信息
        AdPlanIndexObject oldObj = planIndexObjectMap.get(key);
        if (null == oldObj) {
            planIndexObjectMap.put(key, value);
        } else {
            oldObj.update(value);
        }

        log.info("AdPlanIndexAwareImpl after update::{}", planIndexObjectMap);
    }

    @Override
    public void delete(Long key, AdPlanIndexObject value) {

        log.info("AdPlanIndexAwareImpl before delete::{}", planIndexObjectMap);
        planIndexObjectMap.remove(key);
        log.info("AdPlanIndexAwareImpl after delete::{}", planIndexObjectMap);
    }
}
複製代碼

至此,咱們已經完成了推廣計劃的索引對象和索引操做的代碼編寫,你們能夠參考上面的示例,依次完成推廣單元推廣創意地域興趣關鍵詞以及推廣創意和推廣單元的關聯索引,或者可直接從 Github傳送門 / Gitee傳送門 下載源碼。程序員

按照上述代碼展現,咱們已經實現了全部的索引操做的定義,可是實際狀況中,咱們須要使用這些服務的時候,須要在每個Service中@Autowired注入,咱們那麼多的索引操做類,還不包含後續還有可能須要新增的索引維度,工做量實在是太大,並且不方便維護,做爲一個合格的程序員來講,這是很是不友好的,也許會讓後續的開發人員罵娘。github

爲了防止後續被罵,咱們來編寫一個索引緩存工具類com.sxzhongf.ad.index.IndexDataTableUtils,經過這個索引緩存工具類來實現一次注入,解決後顧之憂。要實現這個工具類,咱們須要實現2個接口:org.springframework.context.ApplicationContextAwareorg.springframework.core.PriorityOrderedspring

  • org.springframework.context.ApplicationContextAware, 統一經過實現該接口的類,來操做Spring容器以及其中的Bean實例。 在Spring中,以Aware爲後綴結束的類,你們能夠簡單的理解爲應用程序想要XXX,好比ApplicationContextAware表明應用程序想要ApplicationContext,BeanFactoryAware 表示應用程序想要BeanFactory...等等
  • org.springframework.core.PriorityOrdered組件加載順序,也能夠使用org.springframework.core.Ordered 如下代碼爲咱們的工具類:
package com.sxzhongf.ad.index;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/** * IndexDataTableUtils for 全部索引服務須要緩存的Java Bean * * 使用方式: * 獲取{@link com.sxzhongf.ad.index.creative.CreativeIndexAwareImpl}索引服務類 * 以下: * {@code * IndexDataTableUtils.of(CreativeIndexAwareImpl.class) * } * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> */
@Component
public class IndexDataTableUtils implements ApplicationContextAware, PriorityOrdered {

    //注入ApplicationContext
    private static ApplicationContext applicationContext;

    /** * 定義用於保存全部Index的Map * Class標示咱們的索引類 */
    private static final Map<Class, Object> dataTableMap = new ConcurrentHashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        IndexDataTableUtils.applicationContext = applicationContext;
    }

    /** * 獲取索引服務緩存 */
    public static <T> T of(Class<T> klass) {
        T instance = (T) dataTableMap.get(klass);
        //若是獲取到索引bean,直接返回當前bean
        if (null != instance) {
            return instance;
        }
        //首次獲取索引bean爲空,寫入Map
        dataTableMap.put(klass, bean(klass));
        return (T) dataTableMap.get(klass);
    }

    /** * 獲取Spring 容器中的Bean對象 */
    private static <T> T bean(String beanName) {
        return (T) applicationContext.getBean(beanName);
    }

    /** * 獲取Spring 容器中的Bean對象 */
    private static <T> T bean(Class klass) {
        return (T) applicationContext.getBean(klass);
    }

    @Override
    public int getOrder() {
        return PriorityOrdered.HIGHEST_PRECEDENCE;
    }
}
複製代碼
相關文章
相關標籤/搜索