simple spring memcached(ssm) 和spring的整合教程 附代碼


Memcached 是一個高性能的分布式內存對象緩存系統,在web應用中有很強大的應用場景。而spring作為一個流行的開源框架,在針對數據緩存中,也有着不少的需求。在眾多的第三方實現中,google的simple-spring-memcached是一種非常好的方式。下面就用一個簡單的訂單系統,描述了在spring-mvc中使用memcached的集成方法。 

場景描述:a、用戶在購買某件商品時,先進行商品的選擇,系統生成商品的訂單。訂單內容包括:商品名稱,商品id,訂單id,商品原金額。

       b、用戶進行訂單確定,可能會有一定的折扣(例如使用京東的優惠券等),此時需要更新訂單內容,訂單內容需要更新商品的金額。

       c、用戶需要選擇支付方式,此時需要更新訂單的支付渠道,同時還可能進行金額更新。(例如通過某寶搞活動等,呵呵)

     d、用戶完成支付。

一旦做完了d,訂單數據將進入交易數據庫表中。

如果未做完d,每一步在30分鍾之后,該筆訂單就失效,並在memcached中刪除訂單。

 基於這個特性,abc狀態的交易信息其實是交易信息的緩存,后面的增刪改查如果每次都需要去進行數據庫操作,是比較浪費的,因此考慮使用memcached進行訂單狀態的管理。

好了,需求大致落實好了,開干!

首先是在pom.xml完成與memcached相關的配置。(spring本身的pom配置就不在這里說了) 

        <!-- simple spring memcached -->
        <dependency>
            <groupId>com.google.code.simple-spring-memcached</groupId>
            <artifactId>spring-cache</artifactId>
            <version>3.6.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.simple-spring-memcached</groupId>
            <artifactId>xmemcached-provider</artifactId>
            <version>3.6.0</version>
        </dependency>

然后我們需要進行simple-spring-memcached的配置。

首先我們需要創建一個spring-memcached的基礎配置文件,主要用於聲明與spring相關的memcached配置。(例如命名空間等) 

<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:aop="http://www.springframework.org/schema/aop" 
    xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <import resource="simplesm-context.xml" />

    <aop:aspectj-autoproxy />
    <context:annotation-config />
    <import resource="xmemcached.xml"/>
</beans>

這里我們可以看到,我們使用了一個xmemcached.xml的配置文件對memcached具體的內容進行配置。xmemcached.xml內容如下

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

    <bean name="defaultMemcachedClient" class="com.google.code.ssm.CacheFactory">
        <!-- xmemcached配置方法 -->
        <property name="cacheClientFactory">
            <bean
                class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl" />
        </property>
        <!-- 定義了緩存節點的IP地址和端口號 -->
        <property name="addressProvider">
            <bean class="com.google.code.ssm.config.DefaultAddressProvider">
                <property name="address" value="localhost:11211" />
            </bean>
        </property>  
        
        <!-- 定義了緩存節點的查找方法 -->
        <property name="configuration">
            <bean class="com.google.code.ssm.providers.CacheConfiguration">
                <property name="consistentHashing" value="true" />
            </bean>
        </property>
    </bean>

</beans>

到此步為止,我們的與memcached配置相關的xml已經完了,接下來開始編寫我們的代碼了。

首先創建一個類,用於緩存我們的訂單信息,由於在memcached中是按照key-value形式進行數據存儲的,因此我們需要指定一個key,這個key可以用注解來實現。

public class OrderInfo implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = -608764679068384346L;
    
    private String orderId;
    private String productId;
    private String productName;
    private int productPrice;
    private int orderPrice;
    private String orderTime;
    private int paymentMethod;
    private int orderStatus;
    private String payTime;
    @CacheKeyMethod 
    public String getOrderId() {
        return orderId;
    }
    ...//get set method
}

 這里有兩個注意點,首先,我們這個作為緩存信息的類,需要實現Serializable接口,否在在memcached中是無法緩存對象的。其次我要要指定一個索引key,我們這里使用了orderId作為可以的索引。

然后是我們的dao層。

public interface OrderInfoDao {
    public OrderInfo getOrderInfo(String orderId); 
    public void orderInit(OrderInfo orderInfo);
    public void orderChecks(OrderInfo orderInfo);
    public void orderPayChecks(OrderInfo orderInfo);
    public void orderSuccess(String orderId);
}

dao層相對來說比較簡單,就不展開了。

最后是我們的關鍵了,dao層的實現,代碼如下:

 

@Repository("orderInfoDao")
@ImportResource("classpath:spring_memcached.xml")
public class OrderInfoDaoImpl implements OrderInfoDao{

    private static final String ORDER_INFO_SSM = "orderInfoSSM";
    
    @ReadThroughSingleCache(namespace = ORDER_INFO_SSM, expiration = 1800)
    public OrderInfo getOrderInfo(@ParameterValueKeyProvider String orderId) {
        System.out.println("no cached hitd from ssm");
        return null;
    }
    @UpdateSingleCache(namespace = ORDER_INFO_SSM, expiration = 1800)
    public void orderInit(
            @ParameterValueKeyProvider @ParameterDataUpdateContent OrderInfo orderInfo) {
        System.out.println("orderInit cached to ssm");
        System.out.println(orderInfo.toString());
    }
    @UpdateSingleCache(namespace = ORDER_INFO_SSM, expiration = 1800)
    public void orderChecks(@ParameterValueKeyProvider @ParameterDataUpdateContent OrderInfo orderInfo) {
        System.out.println("orderChecks cached to ssm");
        System.out.println(orderInfo.toString());
        
    }
    @UpdateSingleCache(namespace = ORDER_INFO_SSM, expiration = 1800)
    public void orderPayChecks(@ParameterValueKeyProvider @ParameterDataUpdateContent OrderInfo orderInfo) {
        System.out.println("orderPayChecks cached to ssm");
        System.out.println(orderInfo.toString());
    }
    
    @InvalidateSingleCache(namespace = ORDER_INFO_SSM)  
    public void orderSuccess(@ParameterValueKeyProvider String orderId) {
        System.out.println("orderSuccess delet cached from ssm");
        System.out.println(orderId);
        //TODO 進行數據持久化處理
    }
}

首先我們要進行memcached命名空間的指定,通常來說,每個dao的實現都應在memcached中擁有自己的命名空間。

接下來簡單描述下相關的注解:

@ReadThroughSingleCache 

1)、檢查緩存中是否存在,如果存在,返回緩存數據並退出。

2)、如果在緩存中沒有找到,將會繼續訪問下面的方法。

3)、如果方法返回了期望的對象,則memcached將會以注解ParameterValueKeyProvider修飾的對象作為key並把返回的對象記入緩存。

4)、如果方法沒有發揮期望的對象,則memcached將不會寫入該對象。

5)、expiration指定了失效時間,是以秒記的。

@UpdateSingleCache 

1)、用於更新緩存,包括兩個概念,一是寫入新的緩存,二是更新現有緩存。

2)、該注解中@ParameterDataUpdateContent代表更新之后進行對象刷新。

3)、由於我們之前在對象中使用了@CacheKeyMethod 的注解,因此我們這里可以直接使用該類進行寫入,key就是對象中寫的orderId。

@InvalidateSingleCache

這個注解就比較簡單了,就是使緩存失效。

 

好了,大功告成,我們編寫測試案例來試一下

@Test
    public void testSSM() throws InterruptedException {
        OrderInfo orderInfo = new OrderInfo();
        // step1 下訂單,使用當前時間作為orderId
        String orderId = String.valueOf(System.currentTimeMillis());
        orderInfo.setOrderId(orderId);
        orderInfo.setOrderTime("20151125115900");
        orderInfo.setProductId("10000000");
        orderInfo.setProductName("apple phone 6s");
        orderInfo.setProductPrice(528800);
        orderInfoDao.orderInit(orderInfo);

        // step2 進行確定金額
        // 先從緩存中獲取之前的數據,為了證明是從緩存中取出的,我們先初始化一下之前的對象
        orderInfo = null;
        orderInfo = orderInfoDao.getOrderInfo(orderId);
        System.out.println("get cached afterstep 1 orderInfo:" + orderInfo);
        orderInfo.setOrderStatus(2);
        // 更新金額,現在進行活動東,因此優惠288元
        orderInfo.setOrderPrice(500000);
        // 更新緩存
        orderInfoDao.orderChecks(orderInfo);

        // step3 進行支付方式選擇
        // 先從緩存中獲取之前的數據,為了證明是從緩存中取出的,我們先初始化一下之前的對象
        orderInfo = null;
        orderInfo = orderInfoDao.getOrderInfo(orderId);
        System.out.println("get cached afterstep 2 orderInfo:" + orderInfo);
        orderInfo.setOrderStatus(3);
        // 確定支付方式
        orderInfo.setPaymentMethod(1);
        // 支付方式繼續進行優惠
        orderInfo.setPayPrice(480000);
        orderInfo.setPayTime("20151125120000");
        // 更新緩存
        orderInfoDao.orderPayChecks(orderInfo);

        // step4 進行支付
        // 先從緩存中獲取之前的數據,為了證明是從緩存中取出的,我們先初始化一下之前的對象
        orderInfo = null;
        orderInfo = orderInfoDao.getOrderInfo(orderId);
        System.out.println("get cached afterstep 3 orderInfo:" + orderInfo);
        // TODO PAY action
        orderInfoDao.orderSuccess(orderInfo.getOrderId());

        // 確認緩存對象已經刪除
        orderInfo = null;
        orderInfo = orderInfoDao.getOrderInfo(orderId);
        System.out.println("get cached afterstep 4 orderInfo:" + orderInfo);
    }

 

 

然后我們用maven-test觀察下輸出

orderInit cached to ssm
OrderInfo [orderId=1448455664090, productId=10000000, productName=apple phone 6s, productPrice=528800, orderPrice=0, orderTime=20151125115900, paymentMethod=0, payPrice=0, orderStatus=0, payTime=null]
get cached afterstep 1 orderInfo:OrderInfo [orderId=1448455664090, productId=10000000, productName=apple phone 6s, productPrice=528800, orderPrice=0, orderTime=20151125115900, paymentMethod=0, payPrice=0, orderStatus=0, payTime=null]
orderChecks cached to ssm
OrderInfo [orderId=1448455664090, productId=10000000, productName=apple phone 6s, productPrice=528800, orderPrice=500000, orderTime=20151125115900, paymentMethod=0, payPrice=0, orderStatus=2, payTime=null]
get cached afterstep 2 orderInfo:OrderInfo [orderId=1448455664090, productId=10000000, productName=apple phone 6s, productPrice=528800, orderPrice=500000, orderTime=20151125115900, paymentMethod=0, payPrice=0, orderStatus=2, payTime=null]
orderPayChecks cached to ssm
OrderInfo [orderId=1448455664090, productId=10000000, productName=apple phone 6s, productPrice=528800, orderPrice=500000, orderTime=20151125115900, paymentMethod=1, payPrice=480000, orderStatus=3, payTime=20151125120000]
get cached afterstep 3 orderInfo:OrderInfo [orderId=1448455664090, productId=10000000, productName=apple phone 6s, productPrice=528800, orderPrice=500000, orderTime=20151125115900, paymentMethod=1, payPrice=480000, orderStatus=3, payTime=20151125120000]
orderSuccess delet cached from ssm
1448455664090
no cached hitd from ssm
get cached afterstep 4 orderInfo:null
是不是很easy。
當然,實際上memcached通常回合數據庫聯合起來使用,例如在@ReadThroughSingleCache中使用訪問數據庫生成緩存,這里咱們的目的是memcached,就不再展開了,嘿嘿。
示例代碼下載:
http://download.csdn.net/detail/highkgao1988/9300325





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM