spring cache 集成 cacheCloud redis

最近研究了一下cacheCloud,簡(jiǎn)單的環(huán)境搭建了一下,覺(jué)得非常厲害,這里就把一些集成客戶端的一些操作記錄下來(lái),方便以后查看

  1. pom.xml文件配置
<!-- sohu的相關(guān)jar包 -->
 <dependency>
          <groupId>com.sohu.tv</groupId>
            <artifactId>cachecloud-jedis</artifactId>
            <version>${cachecloud-jedis}</version>
  </dependency>

<dependency>
            <groupId>com.sohu.tv</groupId>
            <artifactId>cachecloud-open-client-redis</artifactId>
            <version>${cachecloud-open-client-basic}</version>
            <exclusions>
                <exclusion>
                    <artifactId>jedis</artifactId>
                    <groupId>redis.clients</groupId>
                </exclusion>
            </exclusions>
        </dependency>

<!-- spring相關(guān)的jar包  我這里的版本是 4.3.7.RELEASE -->
 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${springframework.version}</version>
        </dependency>
 <!-- 工具 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.16</version>
        </dependency>
  1. 客戶端代碼
    接口類(lèi) , 這里只定義了一些常用的操作,具體實(shí)現(xiàn)我就不貼了,這一塊可以根據(jù)你們自己的業(yè)務(wù)去做實(shí)現(xiàn)
import java.util.List;
import java.util.Map;

/**
 * 緩存接口定義
 *
 * @author Liukx
 * @create 2017-05-17 15:40
 * @email liukx@elab-plus.com
 **/
public interface ICacheClient {

    /**
     * 將一個(gè)數(shù)組key進(jìn)行原子性相減
     *
     * @param key 鍵
     * @param num 減去的值
     * @return
     */
    Long decrby(String key, long num);

    /**
     * 將一個(gè)原子性的數(shù)值即你想那個(gè)累加
     * @param key   鍵
     * @param num   累加的值
     * @return
     */
    Long incrby(String key, long num);

    /**
     * 判斷key是否存在
     *
     * @param key
     * @return
     */
    boolean exsit(String key);

    /**
     * 設(shè)置一個(gè)鍵的有效時(shí)長(zhǎng)
     *
     * @param key     鍵
     * @param seconds 有效時(shí)長(zhǎng) 單位:秒
     */
    boolean expire(String key, int seconds);

    /**
     * 獲取一個(gè)key的有效時(shí)長(zhǎng)
     *
     * @param key
     * @return
     */
    Long ttl(String key);

    /**
     * 添加一個(gè)普通值
     *
     * @param key   鍵
     * @param value 值
     */
    <T> void set(String key, T value);

    /**
     * 添加一個(gè)list的值
     *
     * @param key   鍵
     * @param list 值
     */
    <T> void setList(String key, List<T> list);

    /**
     * 添加一個(gè)map的值
     * @param key
     * @param map
     * @param <T>
     */
    <T> void setMap(String key, Map<String, T> map);

    /**
     * 添加一個(gè)值,并為它設(shè)置一個(gè)有效時(shí)間
     *
     * @param key       鍵
     * @param value     值
     * @param validTime 有效時(shí)間  1秒=1000   -1 永久有效
     */
    void set(String key, Object value, int validTime);

    /**
     * 根據(jù)鍵獲取值
     *
     * @param key 鍵 - 標(biāo)識(shí)
     * @return
     */
    <T>T get(String key, Class t);

    /**
     * 獲取list結(jié)果集
     * @param key
     * @param <T>
     * @return
     */
    <T> List<T> getList(String key, Class clazz);

    /**
     *  獲取map結(jié)果集
     * @param key
     * @param <T>
     * @return
     */
    <T> Map<String, T> getMap(String key, Class clazz);

    /**
     * 設(shè)置一個(gè)值到集合中,如果存在則返回key存在 則返回false
     *
     * @param key   鍵
     * @param value 值
     * @return
     */
    boolean setNX(String key, Object value);

    /**
     * 根據(jù)鍵刪除一個(gè)值
     *
     * @param key 鍵
     */
    void delete(String key);

    /**
     * 帶鎖的數(shù)據(jù)操作,例如當(dāng)設(shè)置一個(gè)setnx發(fā)現(xiàn)鍵已經(jīng)存在,則超時(shí)時(shí)間的范圍內(nèi)阻塞嘗試,直到鎖被釋放,如果過(guò)了超時(shí)時(shí)間則表示失敗
     *
     * @param key     key
     * @param value   值
     * @param timeout 超時(shí)時(shí)間 單位秒
     * @return
     */
    boolean tryLock(String key, Object value, int timeout);

    /**
     * 刪除帶鎖的數(shù)據(jù)
     *
     * @param key 帶鎖的key
     */
    void unLock(String key);

    /**
     * <p>通過(guò)key 和offset 從指定的位置開(kāi)始將原先value替換</p>
     * <p>下標(biāo)從0開(kāi)始,offset表示從offset下標(biāo)開(kāi)始替換</p>
     * <p>如果替換的字符串長(zhǎng)度過(guò)小則會(huì)這樣</p>
     * <p>example:</p>
     * <p>value : bigsea@zto.cn</p>
     * <p>str : abc </p>
     * <P>從下標(biāo)7開(kāi)始替換  則結(jié)果為</p>
     * <p>RES : bigsea.abc.cn</p>
     * @param key
     * @param value
     * @param offset 下標(biāo)位置
     * @return 返回替換后  value 的長(zhǎng)度
     */
    Long setRange(String key, String value, int offset);

    /**
     * <p>通過(guò)下標(biāo) 和key 獲取指定下標(biāo)位置的 value</p>
     * @param key
     * @param startOffset 開(kāi)始位置 從0 開(kāi)始 負(fù)數(shù)表示從右邊開(kāi)始截取
     * @param endOffset
     * @return 如果沒(méi)有返回null
     */
    String getRange(String key, int startOffset, int endOffset);

    /**
     * <p>通過(guò)批量的key獲取批量的value</p>
     * @param keys string數(shù)組 也可以是一個(gè)key
     * @return 成功返回value的集合, 失敗返回null的集合 ,異常返回空
     */
    List<String> mget(String... keys);

    /**
     * <p>批量的設(shè)置key:value,可以一個(gè)</p>
     * <p>example:</p>
     * <p>  obj.mset(new String[]{"key2","value1","key2","value2"})</p>
     * @param keysvalues
     * @return 成功返回OK 失敗 異常 返回 null
     *
     */
    String mset(String... keysvalues);


    /**
     * <p>批量的設(shè)置key:value,可以一個(gè),如果key已經(jīng)存在則會(huì)失敗,操作會(huì)回滾</p>
     * <p>example:</p>
     * <p>  obj.msetnx(new String[]{"key2","value1","key2","value2"})</p>
     * @param keysvalues
     * @return 成功返回1 失敗返回0
     */
    Long msetnx(String... keysvalues);

    /**
     * <p>通過(guò)key給Hash Field設(shè)置指定的值,如果key不存在,則先創(chuàng)建</p>
     * @param key
     * @param field 字段
     * @param value
     * @return 如果存在返回0 異常返回null
     */
    Long hset(String key, String field, String value);

    /**
     * <p>Sets field in the hash stored at key to value
     * 通過(guò)key給field設(shè)置指定的值,如果key不存在則先創(chuàng)建,如果field已經(jīng)存在,返回0</p>
     * @param key
     * @param field
     * @param value
     * @return
     */
    public Long hsetnx(String key, String field, String value);

    /**
     * <p>通過(guò)key同時(shí)設(shè)置 hash的多個(gè)field</p>
     * @param key
     * @param hash
     * @return 返回OK 異常返回null
     */
    public String hmset(String key, Map<String, String> hash);

    /**
     * <p>通過(guò)key 和 field 獲取指定的 value</p>
     * @param key
     * @param field
     * @return 沒(méi)有返回null
     */
    public String hget(String key, String field);

    /**
     * <p>通過(guò)key 和 fields 獲取指定的value 如果沒(méi)有對(duì)應(yīng)的value則返回null</p>
     * @param key
     * @param fields 可以是 一個(gè)String 也可以是 String數(shù)組
     * @return
     */
    public List<String> hmget(String key,String...fields);

    /**
     * <p>通過(guò)key向list頭部添加字符串</p>
     * @param key
     * @param strs 可以是一個(gè)string 也可以是string數(shù)組
     * @return 返回list的value個(gè)數(shù)
     */
    Long lpush(String key ,String...strs);

    /**
     * 獲取指定list
     * @param key 鍵
     * @param startIndex 開(kāi)始下標(biāo)
     * @param endIndex 結(jié)束下標(biāo)  -1 代表獲取所有
     * @return
     */
    List<String> lrange(String key, int startIndex, int endIndex);

    /**
     * 獲取所有l(wèi)ist
     * @param key 鍵
     * @return
     */
    List<String> lrange(String key);

    String get(String key);
}

然后就是實(shí)現(xiàn)Spring的cache接口的類(lèi),里面摻雜了一些我們業(yè)務(wù)相關(guān)的代碼,你可以不用關(guān)注,大概了解意思就行了


import com.elab.cache.ICacheClient;
import com.elab.core.bean.Info;
import com.elab.core.utils.ObjectUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;

/**

 這個(gè)類(lèi)是比較關(guān)鍵的,就是相當(dāng)于你實(shí)現(xiàn)了spring的接口,只要將你的緩存交給spring,讓他去做一系列的事情就行了

*/
public class SystemCacheManage implements Cache {

    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        return null;
    }

    /**
     * Redis
     */
    private ICacheClient cacheClient;

    /**
     * 緩存名稱(chēng)
     */
    private String name;

    /**
     * 超時(shí)時(shí)間
     */
    private int timeout;

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#getName()
     */
    @Override
    public String getName() {
        return this.name;
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#getNativeCache()
     */
    @Override
    public Object getNativeCache() {
        return this.cacheClient;
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#get(java.lang.Object)
     */
    @Override
    public ValueWrapper get(Object key) {
        if (ObjectUtils.isEmpty(key)) {
            return null;
        } else {
            final String finalKey;
            if (key instanceof String) {
                finalKey = (String) key;
            } else {
                finalKey = key.toString();
            }
            Object object = cacheClient.get(finalKey, Info.class);
            return (object != null ? new SimpleValueWrapper(object) : null);
        }
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#get(java.lang.Object, java.lang.Class)
     */
    @SuppressWarnings("unchecked")
    @Override
    public <T> T get(Object key, Class<T> type) {
        if (ObjectUtils.isEmpty(key) || null == type) {
            return null;
        } else {
            final String finalKey;
            final Class<T> finalType = type;
            if (key instanceof String) {
                finalKey = (String) key;
            } else {
                finalKey = key.toString();
            }
            final Object object = cacheClient.get(finalKey, type);
            if (finalType != null && finalType.isInstance(object) && null != object) {
                return (T) object;
            } else {
                return null;
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#put(java.lang.Object, java.lang.Object)
     */
    @Override
    public void put(final Object key, final Object value) {
        if (ObjectUtils.isEmpty(key) || ObjectUtils.isEmpty(value)) {
            return;
        } else {
            final String finalKey;
            if (key instanceof String) {
                finalKey = (String) key;
            } else {
                finalKey = key.toString();
            }
            if (!ObjectUtils.isEmpty(finalKey)) {
                cacheClient.set(finalKey, value, timeout);
            }
        }
    }

    /*
     * 根據(jù)Key 刪除緩存
     */
    @Override
    public void evict(Object key) {
        if (null != key) {
            final String finalKey;
            if (key instanceof String) {
                finalKey = (String) key;
            } else {
                finalKey = key.toString();
            }
            if (!com.elab.core.utils.ObjectUtils.isEmpty(finalKey)) {
                cacheClient.delete(finalKey);
            }
        }
    }

    /*
     * 清除系統(tǒng)緩存
     */
    @Override
    public void clear() {
        // TODO Auto-generated method stub
        // redisTemplate.execute(new RedisCallback<String>() {
        // public String doInRedis(RedisConnection connection) throws DataAccessException {
        // connection.flushDb();
        // return "ok";
        // }
        // });
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public ICacheClient getCacheClient() {
        return cacheClient;
    }

    public void setCacheClient(ICacheClient cacheClient) {
        this.cacheClient = cacheClient;
    }
}

動(dòng)態(tài)生成redis中的key生成策略,這個(gè)有需要就定義,沒(méi)有需要就不用,我這里是根據(jù)類(lèi)加方法名加參數(shù)構(gòu)成的一個(gè)key

import com.alibaba.fastjson.JSON;
import org.springframework.cache.interceptor.KeyGenerator;

import java.lang.reflect.Method;

/**
 * 默認(rèn)的緩存key生成策略
 *
 * @author Liukx
 * @create 2017-11-06 13:38
 * @email liukx@elab-plus.com
 **/
public class DefaultCacheKeyGenerator implements KeyGenerator {

    @Override
    public Object generate(Object target, Method method, Object... params) {
        String className = target.getClass().getSimpleName();
        String name = method.getName();
        String jsonParams = JSON.toJSONString(params);
        String cachekey = className + "_" + name + "_" + jsonParams;
        return cachekey;
    }
}

異常處理類(lèi),這里異常只記錄了日志沒(méi)有做什么特殊的實(shí)現(xiàn)

/**
 * 緩存異常處理
 *
 * @author Liukx
 * @create 2017-11-06 17:58
 * @email liukx@elab-plus.com
 **/
public class ErrorCacheHandle implements CacheErrorHandler {

    Logger logger = LoggerFactory.getLogger(ErrorCacheHandle.class);

    public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
        logger.error("‖‖‖‖‖‖‖‖‖緩存異常[handleCacheGetError]‖‖‖‖‖‖‖‖‖‖ cache : " + cache.getName() + " \t key : " + key.toString());
    }

    public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
        logger.error("‖‖‖‖‖‖‖‖‖緩存異常[handleCachePutError]‖‖‖‖‖‖‖‖‖‖ cache : " + cache.getName() + " \t key : " + key.toString() + "\t value : " + value);

    }

    public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
        logger.error("‖‖‖‖‖‖‖‖‖緩存異常[handleCacheEvictError]‖‖‖‖‖‖‖‖‖‖ cache : " + cache.getName() + " \t key : " + key.toString());
    }

    public void handleCacheClearError(RuntimeException exception, Cache cache) {
        logger.error("‖‖‖‖‖‖‖‖‖緩存異常[handleCacheClearError]‖‖‖‖‖‖‖‖‖‖ cache : " + cache.getName());
    }
}

接下來(lái)就是比較關(guān)鍵的配置文件定義了

<?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:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/cache
     http://www.springframework.org/schema/cache/spring-cache.xsd
    ">
    <!-- 默認(rèn)生成的緩存key的實(shí)現(xiàn)策略 -->
    <bean id="defaultCacheKeyGenerator" class="com.elab.cache.spring.generator.DefaultCacheKeyGenerator"/>
    <!-- 異常處理類(lèi) -->
    <bean id="errorCacheHandle" class="com.elab.cache.spring.handle.ErrorCacheHandle" />

    <!-- 開(kāi)啟緩存掃描注解包 -->
    <cache:annotation-driven  error-handler="errorCacheHandle" key-generator="defaultCacheKeyGenerator"/>
    
    <bean id="systemCacheManage" class="com.elab.cache.spring.manage.SystemCacheManage">
      <!-- 這里是cacheCloud的一些代碼定義,其實(shí)就是它的實(shí)現(xiàn)類(lèi) -->
        <property name="cacheClient" ref="redisStandaloneClient"/>
        <property name="timeout" value="300"/>
        <property name="name" value="redisClient"/>
    </bean>

   <!-- 實(shí)現(xiàn)簡(jiǎn)單的配置管理器 -->
    <bean id="simpleCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches" ref="systemCacheManage"/>
    </bean>

    <!-- 緩存管理器 -->
    <bean id="cacheManager"
          class="org.springframework.cache.support.CompositeCacheManager">
        <property name="cacheManagers">
            <list>
                <ref bean="simpleCacheManager"/>
            </list>
        </property>
        <!-- 當(dāng)value找不到時(shí),是否使用一個(gè)null[NoOpCacheManager]代替 -->
        <property name="fallbackToNoOpCache" value="true"/>
    </bean>
</beans>

如何測(cè)試?

在需要加入緩存的方法上面加入下面注解,需要注意的是

  • 第一個(gè)參數(shù)value 對(duì)應(yīng)的就是配置文件中的systemCacheManage的name給定的值
  • 第二個(gè)參數(shù)就是你自己默認(rèn)要指定的key,也就是redis中的key,可以是動(dòng)態(tài)的,不填的話,就會(huì)調(diào)用上面默認(rèn)的生成key的策略方法DefaultCacheKeyGenerator
  • 第三個(gè)參數(shù)就是當(dāng)前緩存的方法必須滿足condition 中的條件才能被緩存!
// 會(huì)從緩存中查詢key,如果存在則不會(huì)進(jìn)入該方法,不存在則進(jìn)入,執(zhí)行完之后進(jìn)行緩存
@Cacheable(value = "redisClient", key = "'lkx_test_'+#redisModel.id", condition = "#redisModel.id != null")


//這個(gè)注解適用于DML操作的方法,表示每次都會(huì)執(zhí)行該方法,并且緩存結(jié)果
    @CachePut(value = "redisClient", key = "'lkx_test_'+#redisModel.id")


    /**
    *  清除緩存的注解
     * allEntries : 是否清空所有緩存內(nèi)容,缺省為 false肩刃,如果指定為 true抒痒,則方法調(diào)用后將立即清空所有緩存
     * beforeInvocation : 是否在方法執(zhí)行前就清空括眠,缺省為 false盹牧,如果指定為 true,則在方法還沒(méi)有執(zhí)行的時(shí)候就清空緩存,缺省情況下开睡,如果方法執(zhí)行拋出異常害驹,則不會(huì)清空緩存
     *
     * @param redisModel
     * @return
     */
    @CacheEvict(value = "redisClient", key = "'lkx_test_'+#redisModel.id", allEntries = false, beforeInvocation = false)

另外這些注解都是需要被掃描到的,所以類(lèi)上面一定要有spring的掃描注解!!
比如@Service .... 寫(xiě)的不是特別詳細(xì),也是大概粗略的研究了下,不是特別深入,如果有不對(duì)的地方請(qǐng)指正,不懂的地方也可以提問(wèn)....

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鞭呕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子宛官,更是在濱河造成了極大的恐慌葫松,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件底洗,死亡現(xiàn)場(chǎng)離奇詭異腋么,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)亥揖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)珊擂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人费变,你說(shuō)我怎么就攤上這事摧扇。” “怎么了挚歧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵扛稽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我滑负,道長(zhǎng)在张,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任矮慕,我火速辦了婚禮瞧掺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凡傅。我一直安慰自己辟狈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布夏跷。 她就那樣靜靜地躺著哼转,像睡著了一般。 火紅的嫁衣襯著肌膚如雪槽华。 梳的紋絲不亂的頭發(fā)上壹蔓,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音猫态,去河邊找鬼佣蓉。 笑死披摄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的勇凭。 我是一名探鬼主播疚膊,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼虾标!你這毒婦竟也來(lái)了寓盗?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤璧函,失蹤者是張志新(化名)和其女友劉穎傀蚌,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蘸吓,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡善炫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了库继。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片销部。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖制跟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酱虎,我是刑警寧澤雨膨,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站读串,受9級(jí)特大地震影響聊记,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恢暖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一排监、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧杰捂,春花似錦舆床、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蒿往,卻和暖如春盛垦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓤漏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工腾夯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颊埃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓蝶俱,卻偏偏與公主長(zhǎng)得像班利,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子跷乐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理肥败,服務(wù)發(fā)現(xiàn),斷路器愕提,智...
    卡卡羅2017閱讀 134,638評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,778評(píng)論 6 342
  • application的配置屬性馒稍。 這些屬性是否生效取決于對(duì)應(yīng)的組件是否聲明為Spring應(yīng)用程序上下文里的Bea...
    新簽名閱讀 5,358評(píng)論 1 27
  • 有些人在團(tuán)隊(duì)中格格不入,會(huì)有一些很特別的原因浅侨,比如因?yàn)槟芰μ娑譀](méi)有給自己定位纽谒。 能力太全面,會(huì)讓團(tuán)隊(duì)的人不敢...
    吳少杰1988閱讀 158評(píng)論 0 0
  • 或許對(duì)于很多人來(lái)說(shuō)安工只是一個(gè)普通甚至比較差的二本院校,但是身為安工人我不得不說(shuō)它在我眼中比任何一所大學(xué)都要美麗不见、...
    有風(fēng)就好閱讀 807評(píng)論 0 4