seckill項目創(chuàng)建流程---Service層

首先先贏明白service層的作用:Service層主要負責(zé)業(yè)務(wù)模塊的邏輯應(yīng)用設(shè)計秉颗,同樣是先設(shè)計接口卷要,再設(shè)計其實現(xiàn)的類辉懒,接著再是spring的配置其實現(xiàn)的關(guān)聯(lián)屈糊,這樣就可以在應(yīng)用中調(diào)用service接口來進行業(yè)務(wù)處理

1玩徊、service接口設(shè)計

~租悄、前臺頁面需要顯示參與秒殺商品的信息:getSeckillList(showPage):根據(jù)現(xiàn)實的頁碼進行列表查詢,查詢秒殺記錄
~恩袱、點擊相應(yīng)連接后前臺顯示具體商品信息:getById(sekillId):查詢單個秒殺記錄

package com.seckill.service;
public interface SeckillService {
    /**
     * 查詢所有秒殺記錄
     * @return
     */
    List<Seckill> getSeckillList(int showPage);

    /**
     * 在每頁顯示5條數(shù)據(jù)情況下
     * 獲得總頁數(shù)
     *
     * @return
     */
    int getPageCount();
    /**
     * 查詢單個秒殺記錄
     * @param seckillId
     * @return
     */
    Seckill getById(long seckillId);

    /**
     * 秒殺開啟時輸出秒殺接口地址
     * 否則輸出系統(tǒng)時間和秒殺時間
     * @param seckillId
     */
    Exposer exportSeckillUrl(long seckillId);

    /**
     * 執(zhí)行秒殺操作泣棋,有可能是失敗的,失敗的時候就拋出異常
     * @param seckillId 秒殺的商品Id
     * @param userPhone 手機號碼
     * @param md5 md5加密值
     */
    SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
            throws SeckillException,SeckillCloseException,RepeaKillException;

    /**
     * 執(zhí)行存儲過程操作
     * @param seckillId
     * @param userPhone
     * @param md5
     * @return
     */
    SeckillExecution executeSeckillProcedure(long seckillId, long userPhone,String md5);
}

當(dāng)開始編寫service接口時畔塔,需要思考一個問題潭辈,那就是后臺反饋給前臺的數(shù)據(jù)是一個怎樣的json數(shù)據(jù)?數(shù)字狀態(tài)碼澈吨,或者是文字把敢?
這里先解釋幾種對象:
~、PO: 也就是我們在為每一張數(shù)據(jù)庫表寫一個實體的類
~棚辽、VO, 對某個頁面或者展現(xiàn)層所需要的數(shù)據(jù),封裝成一個實體類
~技竟、BO, 就是業(yè)務(wù)對象,我也不是很了解
~、DTO, 跟VO的概念有點混淆,也是相當(dāng)于頁面需要的數(shù)據(jù)封裝成一個實體類
~屈藐、POJO, 簡單的無規(guī)則java對象
了解了這幾種對象榔组,便可以明白,我們需要向前臺返回的可以是DTO對象联逻,后臺對所需要返回的數(shù)據(jù)進行封裝搓扯。
因此對于service接口中的Esposer和SeckillExecution 對象便可以解釋,以及相應(yīng)的異常對象

com.seckill.dto.Exposer
com.seckill.dto.SeckillExecution
    /**
     * 秒殺開啟時輸出秒殺接口地址
     * 否則輸出系統(tǒng)時間和秒殺時間
     * @param seckillId
     */
    Exposer exportSeckillUrl(long seckillId);

    /**
     * 執(zhí)行秒殺操作,有可能是失敗的包归,失敗的時候就拋出異常
     * @param seckillId 秒殺的商品Id
     * @param userPhone 手機號碼
     * @param md5 md5加密值
     */
    SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
            throws SeckillException,SeckillCloseException,RepeaKillException;

Exposer如下:(編寫時也要想好Exposer包含的變量以及不同情況下對應(yīng)的構(gòu)造函數(shù))

package com.seckill.dto;

public class Exposer {

    @Override
    public String toString() {
        return "Exposer [exposed=" + exposed + ", md5=" + md5 + ", seckillId=" + seckillId + ", now=" + now + ", start="
                + start + ", end=" + end + "]";
    }
    //無參構(gòu)造函數(shù)
    public Exposer() {
        // Auto-generated constructor stub
    }
    //是否開啟秒殺
    private boolean exposed;
    // 一種加密措施
    private String md5;
    //秒殺商品id
    private long seckillId;
    // 系統(tǒng)當(dāng)前時間
    private long now;
    //開啟時間
    private long start;
    //結(jié)束時間
    private long end;

    /**
     * 當(dāng)秒殺時間不符合時锨推,返回的數(shù)據(jù)
     * @param exposed   false狀態(tài)
     * @param seckillId 商品ID
     * @param now   當(dāng)前系統(tǒng)時間(服務(wù)器時間)
     * @param start 秒殺開啟時間
     * @param end   秒殺結(jié)束時間
     */
    public Exposer(boolean exposed, Long seckillId,long now, long start, long end) {
        super();
        this.seckillId = seckillId;
        this.exposed = exposed;
        this.now = now;
        this.start = start;
        this.end = end;
    }

    /**
     * 該商品符合秒殺時返回的數(shù)據(jù)
     * @param exposed   狀態(tài)
     * @param md5   一種鹽值加密數(shù)據(jù)
     * @param seckillId 秒殺商品id
     */
    public Exposer(boolean exposed, String md5, long seckillId) {
        super();
        this.exposed = exposed;
        this.md5 = md5;
        this.seckillId = seckillId;
    }

    /**
     * 秒殺商品不存在時返回的數(shù)據(jù)
     * @param exposed
     * @param seckillId
     */
    public Exposer(boolean exposed, long seckillId) {
        super();
        this.exposed = exposed;
        this.seckillId = seckillId;
    }

    public boolean isExposed() {
        return exposed;
    }

    public void setExposed(boolean exposed) {
        this.exposed = exposed;
    }

    public String getMd5() {
        return md5;
    }

    public void setMd5(String md5) {
        this.md5 = md5;
    }

    public long getSeckillId() {
        return seckillId;
    }

    public void setSeckillId(long seckillId) {
        this.seckillId = seckillId;
    }

    public long getNow() {
        return now;
    }

    public void setNow(long now) {
        this.now = now;
    }

    public long getStart() {
        return start;
    }

    public void setStart(long start) {
        this.start = start;
    }

    public long getEnd() {
        return end;
    }

    public void setEnd(long end) {
        this.end = end;
    }
}

SeckillExecution:執(zhí)行秒殺后后臺返回給前臺的數(shù)據(jù)封裝對象

package com.seckill.dto;

import com.seckill.entity.SuccessKilled;
import com.seckill.enums.SeckillStateEnum;

public class SeckillExecution {
    public SeckillExecution() {
        //Auto-generated constructor stub
    }

    // 商品Id
    private long seckillId;

    // 秒殺結(jié)果的狀態(tài)
    private int state;

    /* 狀態(tài)的明文標(biāo)示 */
    private String stateInfo;

    /* 當(dāng)秒殺成功時,需要傳遞秒殺結(jié)果的對象回去 */
    private SuccessKilled successKilled;

    /* 秒殺成功返回的實體 */
    public SeckillExecution(long seckillId, SeckillStateEnum stateEnum, SuccessKilled successKilled) {
        super();
        this.seckillId = seckillId;
        this.state = stateEnum.getState();
        this.stateInfo = stateEnum.getStateInfo();
        this.successKilled = successKilled;
    }

    /* 秒殺失敗時返回的實體,沒有秒殺結(jié)果的對象 */
    public SeckillExecution(long seckillId, SeckillStateEnum stateEnum) {
        super();
        this.seckillId = seckillId;
        this.state = stateEnum.getState();
        this.stateInfo = stateEnum.getStateInfo();
    }

    @Override
    public String toString() {
        return "SeckillException [seckillId=" + seckillId + ", state=" + state + ", stateIofo=" + stateInfo
                + ", successKilled=" + successKilled + "]";
    }

    public long getSeckillId() {
        return seckillId;
    }

    public void setSeckillId(long seckillId) {
        this.seckillId = seckillId;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public String getStateInfo() {
        return stateInfo;
    }

    public void setStateInfo(String stateInfo) {
        this.stateInfo = stateInfo;
    }

    public SuccessKilled getSuccessKilled() {
        return successKilled;
    }

    public void setSuccessKilled(SuccessKilled successKilled) {
        this.successKilled = successKilled;
    }
}

定義秒殺過程中可能會出現(xiàn)的異常
~换可、定義一個基礎(chǔ)異常椎椰,所有的異常都繼承這個異常
SeckillException :

package com.seckill.exception;

public class SeckillException extends RuntimeException {

    public SeckillException() {

    }
    public SeckillException(String message) {
        super(message);
    }

    public SeckillException(String message, Throwable cause) {
        super(message, cause);
    }
}

~、定義重復(fù)秒殺異常:

package com.seckill.exception;

/**
 * 重復(fù)秒殺異常沾鳄,不需要我們手動去try catch
 * @author hyh47
 *
 */
public class RepeaKillException extends SeckillException{

    public RepeaKillException() {

    }
    public RepeaKillException(String message){
        super(message);
    }

    public RepeaKillException(String message,Throwable cause){
        super(message, cause);
    }

}

~慨飘、定義秒殺關(guān)閉異常

package com.seckill.exception;

/**
 * 秒殺已經(jīng)關(guān)閉異常,當(dāng)秒殺結(jié)束就會出現(xiàn)這個異常
 * @author hyh47
 *
 */
public class SeckillCloseException extends SeckillException{

    public SeckillCloseException() {

    }
    public SeckillCloseException(String message){
        super(message);
    }

    public SeckillCloseException(String message, Throwable cause){
        super(message, cause);
    }
}

2译荞、service接口實現(xiàn)

@Service
public class SeckillServiceImpl implements SeckillService {

    /*日志記錄*/
    private org.slf4j.Logger logger = LoggerFactory.getLogger(this.getClass());

    /*自定義用于md5加密的鹽值*/
    private final String salt = "ljflajvoia332131ADSFJJL(&(*(*#@";

    final int pageSize = 5;//單頁顯示的數(shù)量

    //注入service依賴
    @Autowired
    private SeckillDao seckillDao;

    @Autowired
    private SuccessKilledDao successKilledDao;

    @Autowired
    private RedisDao redisDao;

    public SeckillServiceImpl() {
        //  Auto-generated constructor stub
    }

    /**
     * 查詢?nèi)康拿霘⒂涗?     *
     * @return 數(shù)據(jù)庫中所有的秒殺記錄
     */
    /*
        pageSize(每個頁面所顯示的記錄數(shù))瓤的、
        pageCount(頁面的總數(shù))、
        showPage(目前顯示第幾頁)吞歼、
        recordCount(總的記錄數(shù))
     */
    @Override
    public List<Seckill> getSeckillList(int showPage) {
        // Auto-generated method stub
        return seckillDao.queryAll((showPage - 1) * pageSize, pageSize);
    }

    @Override
    public int getPageCount() {
        int recordCount = seckillDao.querySeckillNumber();//總數(shù)
        int pageCount = (recordCount % pageSize == 0) ? (recordCount / pageSize) : (recordCount / pageSize + 1);
        return pageCount;
    }

    /**
     * 查詢秒殺記錄圈膏,通過商品Id
     */
    @Override
    public Seckill getById(long seckillId) {
        //  Auto-generated method stub
        return seckillDao.queryById(seckillId);
    }

    @Override
    @Transactional
    /**
     * 使用注解控制事務(wù)方法的優(yōu)點:
     * 1:開發(fā)團隊打成一致約定,明確標(biāo)注事務(wù)方法的編程風(fēng)格
     * 2:保證事務(wù)方法的執(zhí)行時間盡可能短篙骡,不穿插其他網(wǎng)絡(luò)操作稽坤,RPC/HTTP請求或者剝離到事務(wù)方法外部
     * 3:不是所有的方法都需要事務(wù),比如只有一條修改操作医增,或者只讀操作不需要事務(wù)控制
     */
    public Exposer exportSeckillUrl(long seckillId) {
        //  Auto-generated method stub

        /*Seckill seckill = seckillDao.queryById(seckillId);
        if (seckill == null){
            return new Exposer(false,seckillId);
        }*/
        Seckill seckill = redisDao.getSeckill(seckillId);
        if (seckill == null) {
            //訪問數(shù)據(jù)庫讀取數(shù)據(jù)
            seckill = seckillDao.queryById(seckillId);
            if (seckill == null) {
                return new Exposer(false, seckillId);
            } else {
                //放入redis中
                redisDao.putSeckill(seckill);
            }
        }
        Date startTime = seckill.getStartTime();
        Date endTime = seckill.getEndTime();
        //當(dāng)前系統(tǒng)時間
        Date nowTime = new Date();

        if (nowTime.getTime() < startTime.getTime()
                || nowTime.getTime() > endTime.getTime()) {
            return new Exposer(false, seckillId, nowTime.getTime(),
                    startTime.getTime(), endTime.getTime());
        }
        String md5 = getMD5(seckillId);//

        return new Exposer(true, md5, seckillId);
    }

    private String getMD5(long seckillId) {
        String base = seckillId + "/" + salt;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;

    }

    @Override
    public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
            throws SeckillException, SeckillCloseException, RepeaKillException {

        //在這一系列操作中慎皱,對應(yīng)事務(wù),return是commit,拋異常是rollback
        try {
            if (md5 == null || !md5.equals(getMD5(seckillId))) {
                logger.error("秒殺數(shù)據(jù)被篡改");
                throw new SeckillException("seckill data rewrite");
            }

            //執(zhí)行秒殺邏輯: 減庫存+記錄購買行為
            Date nowTime = new Date();
            //記錄購買行為
            int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
            //唯一:seckillId,userPhone
            if (insertCount <= 0) {
                logger.warn("不允許重復(fù)秒殺");
                throw new RepeaKillException("repeaKill !!");
            } else {
                //減庫存,熱點商品競爭
                int updateCount = seckillDao.reduceNumber(seckillId, nowTime);
                if (updateCount <= 0) {
                    //沒有更新記錄叶骨,秒殺結(jié)束,可能時間結(jié)束也可能庫存為零茫多,rollback
                    logger.warn("沒有更新數(shù)據(jù)庫記錄,秒殺已經(jīng)結(jié)束了");
                    throw new SeckillCloseException("seckill is closed");
                } else {
                    //秒殺成功,commit
                    SuccessKilled successkilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
                    return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successkilled);
                }

            }

        } catch (SeckillCloseException | RepeaKillException e1) {

            throw e1;
        } catch (Exception e) {

            logger.error(e.getMessage(), e);
            throw new SeckillException("seckill inner error:" + e.getMessage());
        }

    }

    @Override
    public SeckillExecution executeSeckillProcedure(long seckillId, long userPhone, String md5) {

        if (md5 == null || !md5.equals(getMD5(seckillId))) {
            return new SeckillExecution(seckillId, SeckillStateEnum.DATE_PEWRITER);
        }

        Date killTime = new Date();
        Map<String, Object> map = new HashMap<>();
        map.put("seckillId", seckillId);
        map.put("phone", userPhone);
        map.put("killTime", killTime);
        map.put("result", null);
        //執(zhí)行存儲過程忽刽,result被復(fù)制

        try {
            seckillDao.killByProcedure(map);
            //獲取result
            int result = MapUtils.getInteger(map, "result", -2);
            if (result == 1) {
                SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
                return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled);
            } else {
                return new SeckillExecution(seckillId, SeckillStateEnum.stateOf(result));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR);
        }
    }
}

在這里我們捕獲了運行時異常,這樣做的原因就是Spring的事物默認就是發(fā)生了RuntimeException才會回滾,可以檢測出來的異常是不會導(dǎo)致事物的回滾的,這樣的目的就是你明知道這里會發(fā)生異常,所以你一定要進行處理.如果只是為了讓編譯通過的話,那捕獲異常也沒多意思,所以這里要注意事物的回滾.
然后我們還發(fā)現(xiàn)這里存在硬編碼的現(xiàn)象,就是返回各種字符常量,例如秒殺成功,秒殺失敗等等,這些字符串時可以被重復(fù)使用的,而且這樣維護起來也不方便,要到處去類里面尋找這樣的字符串,所有我們使用枚舉類來管理這樣狀態(tài),在com.seckill包下建立enum包,專門放置枚舉類,然后再建立SeckillStateEnum枚舉類:
此處關(guān)于秒殺結(jié)果的集中狀態(tài)天揖,采用枚舉類進行封裝

com.seckill.enums.SeckillStateEnum
package com.seckill.enums;

/**
 * 使用枚舉表述常量數(shù)據(jù)字段
 * @author hyh47
 *
 */
public enum SeckillStateEnum {
    SUCCESS(1,"秒殺成功"),
    END(0,"秒殺結(jié)束"),
    REPEAT_KILL(-1,"重復(fù)秒殺"),
    INNER_ERROR(-2,"系統(tǒng)異常"),
    DATE_PEWRITER(-3,"數(shù)據(jù)篡改");
    
    private int state;
    private String stateInfo;

    public int getState() {
        return state;
    }
    public String getStateInfo() {
        return stateInfo;
    }
    private SeckillStateEnum(int state, String stateInfo) {
        this.state = state;
        this.stateInfo = stateInfo;
    }

    public static SeckillStateEnum stateOf(int index){
        for (SeckillStateEnum state: values()){
            if (state.getState() == index){
                return state;
            }
        }
        return null;
    }
    
}

3、進行service層的spring配置跪帝,注入service

~今膊、掃描service包下的注解
~、配置事務(wù)伞剑,在事務(wù)中注入數(shù)據(jù)庫連接池
~斑唬、開啟基于注解的聲明式事務(wù)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd ">

    <!--掃描service包下所有使用注解的類型 -->
    <context:component-scan base-package="com.seckill.service"></context:component-scan>

    <!-- 配置事務(wù)管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入數(shù)據(jù)庫連接池 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 開啟基于注解的聲明式事務(wù) -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

在配置過程中應(yīng)該注意xml的頭信息

4、service層的測試

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring/spring-dao.xml", "classpath:spring/spring-service.xml"})
public class SeckillServiceTest {

    private org.slf4j.Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private SeckillService seckillService;

    @Test
    public void executeSeckillProcedure() throws Exception {

        long seckillId = 1001L;
        long userphone = 18126745520L;

        Exposer exposer = seckillService.exportSeckillUrl(seckillId);

        if (exposer.isExposed()) {
            String md5 = exposer.getMd5();
            SeckillExecution execution = seckillService.executeSeckillProcedure(seckillId, userphone, md5);
            logger.info(execution.getStateInfo());
        }
    }

    @Test
    public void getPageCount() throws Exception {
        System.out.println(seckillService.getPageCount());
    }

    @Test
    public void getSeckillList() throws Exception {

        List<Seckill> list = seckillService.getSeckillList(1);
        logger.info(list.toString());
        System.out.println(list.toString());
    }

    @Test
    public void getById() throws Exception {
        long seckillId = 1000L;
        Seckill seckill = seckillService.getById(seckillId);
        System.out.println(seckill.toString());
    }

    @Test
    public void exportSeckillUrl() throws Exception {
        long seckillId = 1000L;
        Exposer exposer = seckillService.exportSeckillUrl(seckillId);
        System.out.println(exposer.toString());
    }

    @Test
    public void executeSeckill() throws Exception {

        long seckillId = 1000L;
        long userPhone = 125906441181L;
        String md5 = "ab977232c7bfb60cf33df852e171edd9";
        try {
            SeckillExecution seckillExecution = seckillService.executeSeckill(seckillId, userPhone, md5);
            logger.info("result={}", seckillExecution);
        } catch (SeckillCloseException e) {

            logger.error(e.getMessage());
        } catch (RepeaKillException e) {

            logger.error(e.getMessage());
        } catch (SeckillException e) {

            logger.error(e.getMessage());
        }
    }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末黎泣,一起剝皮案震驚了整個濱河市恕刘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抒倚,老刑警劉巖褐着,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異托呕,居然都是意外死亡含蓉,警方通過查閱死者的電腦和手機频敛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來馅扣,“玉大人斟赚,你說我怎么就攤上這事∑裆ぃ” “怎么了汁展?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵鹊碍,是天一觀的道長厌殉。 經(jīng)常有香客問我,道長侈咕,這世上最難降的妖魔是什么公罕? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮耀销,結(jié)果婚禮上楼眷,老公的妹妹穿的比我還像新娘。我一直安慰自己熊尉,他們只是感情好罐柳,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狰住,像睡著了一般张吉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上催植,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天肮蛹,我揣著相機與錄音,去河邊找鬼创南。 笑死伦忠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的稿辙。 我是一名探鬼主播昆码,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼邻储!你這毒婦竟也來了赋咽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤芥备,失蹤者是張志新(化名)和其女友劉穎冬耿,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萌壳,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡亦镶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年日月,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缤骨。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡爱咬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绊起,到底是詐尸還是另有隱情精拟,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布虱歪,位于F島的核電站蜂绎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏笋鄙。R本人自食惡果不足惜师枣,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望萧落。 院中可真熱鬧践美,春花似錦、人聲如沸找岖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽许布。三九已至兴革,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間爹脾,已是汗流浹背帖旨。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留灵妨,地道東北人解阅。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像泌霍,于是被迫代替她去往敵國和親货抄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,283評論 25 707
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法朱转,類相關(guān)的語法蟹地,內(nèi)部類的語法,繼承相關(guān)的語法藤为,異常的語法怪与,線程的語...
    子非魚_t_閱讀 31,662評論 18 399
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 11,004評論 6 13
  • 我們每個人分别,都或多或少的在修行遍愿,修行學(xué)業(yè),身材耘斩,社交沼填,友情,愛情括授,親情坞笙,婚姻,工作業(yè)績…… 而修行荚虚,不一定會有陪伴...
    冰冰月月在何方閱讀 354評論 0 0
  • 組織文化 上一篇文章主要說了自己對敏捷測試的理解薛夜。其實很多事情都是說得容易做得難,從傳統(tǒng)過渡到敏捷曲管,我們會面臨到很...
    天秤座的橘子閱讀 459評論 0 7