2017年8月10日---階段性工作總結(事件驅動)

在實際生產開發中,我們經常會遇到業務邏輯的嵌套,比如說在支付場景中:

1.用戶在我們商城購買商品以後,進行支付操作

2.用戶支付完以後,我給他發一條短信告訴買家他支付成功了

3.我告訴賣家讓他發貨,因爲買家支付成功了


在這個場景中,我可以這樣寫代碼,通過最初級的方式:

@Service
@Transactional
public class PayService {

    @Resource
    private Msg msgService;
    @Resource
    private Send sendService;
    @Resource
    private TFlightMapper tFlightMapper;

    public void pay(int user, String fcode) {
        TFlight flight = new TFlight();
        flight.setFcode(fcode);
        flight.setOrdertime(new Date());
        flight.setUser(user);
        int insert = tFlightMapper.insert(flight);
        if (insert > 0) {
            System.out.println("用戶支付成功了~");
            TMsg msg = new TMsg();
            msg.setContent("訂單號爲:" + fcode);
            msg.setCreatetime(new Date());
            msg.setPhone("15577882500");
            msg.setUser(user);
            msgService.msg(msg);
            Email email = new Email("系統", String.valueOf(user), "訂單支付已成功", "請您儘快發貨:" + fcode);
            sendService.sendMail(email);
        }
    }
}

@Service
@Transactional
public class Msg {

    @Resource
    private TMsgMapper tMsgMapper;

    public void msg(TMsg msg) {

        int insert = tMsgMapper.insert(msg);
        if (insert > 0) {
            System.out.println("給買家發一條短信提醒!!!");
        }
    }
}

@Service
@Transactional
public class Send {

    public void sendMail(Email email) {
        try {
            FileCopyUtils.copy(SerializationUtils.serialize(email), new File("e:/temp/" + email.getTitle() + ".txt"));
        } catch (IOException e) {
            throw new RuntimeException("io異常,異常信息爲:" + e.getMessage());
        }
    }
}

通知賣家發貨我是以郵件的方式,因爲我電腦上並沒有e盤
所以肯定拋出異常,我對異常進行轉換。然後輸出如下:


數據庫中並沒有插入一條數據,有些人肯定認爲這不挺好嘛,我沒通知成功賣家發貨,
我數據庫裏也沒存進去數據,我短信庫裏也沒插入短信發送記錄。
但是實際生產中不是這樣的,用戶是在銀行的系統完成支付的,這相當於錢從用戶賬戶到你賬戶了,你在本地表裏沒給用戶插入記錄。
而且沒插入短信發送記錄不代表用戶沒收到短信。。。這樣一搞由於耦合的存在,你這就都亂了

於是我們想到了mq,想到了redis的list隊列,想到了分佈式,想到了一大堆高大尚的技術。
但是今天我們在這裏都不用,我一直認爲技術不是用來炫耀的。用最簡單的技術實現最複雜的業務場景是我一直追求的目標。
我們只用spring的事件驅動去完成一個異步的處理,當然他處理能力是有限的,比前面的高大尚的技術還是有很大差距的。
於是我對類進行改造
@Service
@Transactional
public class PayService implements ApplicationContextAware {
    //ApplicationContextAware這個接口很特殊,實現這個接口就一定要重寫
    //setApplicationContext這個方法,然後spring就會自動的把applicationContext
    //這個對象注入到這個方法的入參,別告訴我你不知道applicationContext是啥
    private ApplicationContext applicationContext;

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


    @Resource
    private TFlightMapper tFlightMapper;

    public void pay(int user, String fcode) {
        System.out.println("用戶支付開始~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

        TFlight flight = new TFlight();
        flight.setFcode(fcode);
        flight.setOrdertime(new Date());
        flight.setUser(user);
        int insert = tFlightMapper.insert(flight);

        applicationContext.publishEvent(new PayEvent(this, fcode, user));

        System.out.println("用戶支付結束~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

    }
}
加入了事件源和監聽器
public class PayEvent extends ApplicationEvent {
    private String fcode;

    private int user;

    public String getFcode() {
        return fcode;
    }

/*
    public void setFcode(String fcode) {
        this.fcode = fcode;
    }
*/

    public int getUser() {
        return user;
    }

/*    public void setUser(int user) {
        this.user = user;
    }*/

    public PayEvent(Object source, String fcode, int user) {
        super(source);
        this.fcode = fcode;
        this.user = user;
    }
}

@Service
public class MsgHandler implements ApplicationListener {
    //PayEvent是這個監聽器監聽的事件

    @Resource
    private Msg msgService;

    @Override
    public void onApplicationEvent(PayEvent payEvent) {
        //監聽到事件後做如下操作
        System.out.println("開始發消息~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        TMsg msg = new TMsg();
        msg.setContent("訂單號爲:" + payEvent.getFcode());
        msg.setCreatetime(new Date());
        msg.setPhone("12222220000");
        msg.setUser(payEvent.getUser());
        msgService.msg(msg);
        System.out.println("消息發送完成~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

    }
}

@Service
public class MailHandler implements ApplicationListener {
    @Resource
    private Send sendService;

    @Override
    public void onApplicationEvent(PayEvent payEvent) {
        System.out.println("開始發郵件~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        Email email = new Email("系統", String.valueOf(payEvent.getUser()), "訂單支付已成功", "請您儘快發貨:" + payEvent.getFcode());
        sendService.sendMail(email);
        System.out.println("郵件發送成功~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    }
}


注意我下面這個啊,用的是一個spring自帶的工具類,把對象轉換成byte數組的,帶緩存,對象不大的時候用這個工具類轉就可以
對象大的話轉json或者怎麼着的再根據情況





然後我就很happy了,事件驅動了,解耦了,通過事件去通知監聽者,由監聽者去執行發短信和髮油價操作了。
太開心了,於是我運行了一下代碼,按着想法應該是支付數據插入成功,短信數據插入成功,然後郵件發送失敗
但是現實卻還是跟剛纔一樣,爲什麼呢?看一下控制檯輸出


開始了,開始了,拋異常了,沒結束。
開始了,開始了,拋異常了,沒結束。
開始了,開始了,拋異常了,沒結束。
開始了,開始了,拋異常了,沒結束。
開始了,開始了,拋異常了,沒結束。
開始了,開始了,拋異常了,沒結束。
反覆執行多次都是這樣,說明,這個事件的機制根本就是在一個線程裏面。。。。

    
    

感覺這個類,爲每一個監聽器開了一個線程應該是。
SimpleApplicationEventMulticaster就這個類。

加上以後再運行看到了想要的效果,好了不早了,先說到這裏吧
最後溫馨提示一下,測試的時候,讓主線程sleep一會兒,別測試線程沒結束呢主線程先完了。












開始了,開始了,拋異常了,沒結束。
開始了,開始了,拋異常了,沒結束。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章