1. 程式人生 > >Spring cache資料(二 ,詳解)

Spring cache資料(二 ,詳解)

Spring Cache 介紹

2015-01-05 01:45 by Rollen Holt, 7781 閱讀, 8 評論, 收藏編輯

Spring Cache

快取是實際工作中非常常用的一種提高效能的方法, 我們會在許多場景下來使用快取。

本文通過一個簡單的例子進行展開,通過對比我們原來的自定義快取和 spring 的基於註釋的 cache 配置方法,展現了 spring cache 的強大之處,然後介紹了其基本的原理,擴充套件點和使用場景的限制。通過閱讀本文,你應該可以短時間內掌握 spring 帶來的強大快取技術,在很少的配置下即可給既有程式碼提供快取能力。

概述

Spring 3.1 引入了激動人心的基於註釋(annotation)的快取(cache)技術,它本質上不是一個具體的快取實現方案(例如EHCache 或者 OSCache),而是一個對快取使用的抽象,通過在既有程式碼中新增少量它定義的各種 annotation,即能夠達到快取方法的返回物件的效果。

Spring 的快取技術還具備相當的靈活性,不僅能夠使用 SpEL(Spring Expression Language)來定義快取的 key 和各種 condition,還提供開箱即用的快取臨時儲存方案,也支援和主流的專業快取例如 EHCache 整合。

其特點總結如下:

  • 通過少量的配置 annotation 註釋即可使得既有程式碼支援快取
  • 支援開箱即用 Out-Of-The-Box,即不用安裝和部署額外第三方元件即可使用快取
  • 支援 Spring Express Language,能使用物件的任何屬性或者方法來定義快取的 key 和 condition
  • 支援 AspectJ,並通過其實現任何方法的快取支援
  • 支援自定義 key 和自定義快取管理者,具有相當的靈活性和擴充套件性

本文將針對上述特點對 Spring cache 進行詳細的介紹,主要通過一個簡單的例子和原理介紹展開,然後我們將一起看一個比較實際的快取例子,最後會介紹 spring cache 的使用限制和注意事項。好吧,讓我們開始吧

我們以前如何自己實現快取的呢

這裡先展示一個完全自定義的快取實現,即不用任何第三方的元件來實現某種物件的記憶體快取。

場景如下:

對一個賬號查詢方法做快取,以賬號名稱為 key,賬號物件為 value,當以相同的賬號名稱查詢賬號的時候,直接從快取中返回結果,否則更新快取。賬號查詢服務還支援 reload 快取(即清空快取)

首先定義一個實體類:賬號類,具備基本的 id 和 name 屬性,且具備 getter 和 setter 方法

public class Account {

    private int id;
    private String name;

    public Account(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

然後定義一個快取管理器,這個管理器負責實現快取邏輯,支援物件的增加、修改和刪除,支援值物件的泛型。如下:

import com.google.common.collect.Maps;

import java.util.Map;

/**
 * @author wenchao.ren
 *         2015/1/5.
 */
public class CacheContext<T> {

    private Map<String, T> cache = Maps.newConcurrentMap();

    public T get(String key){
        return  cache.get(key);
    }

    public void addOrUpdateCache(String key,T value) {
        cache.put(key, value);
    }

    // 根據 key 來刪除快取中的一條記錄
    public void evictCache(String key) {
        if(cache.containsKey(key)) {
            cache.remove(key);
        }
    }

    // 清空快取中的所有記錄
    public void evictCache() {
        cache.clear();
    }

}

好,現在我們有了實體類和一個快取管理器,還需要一個提供賬號查詢的服務類,此服務類使用快取管理器來支援賬號查詢快取,如下:

import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author wenchao.ren
 *         2015/1/5.
 */
@Service
public class AccountService1 {

    private final Logger logger = LoggerFactory.getLogger(AccountService1.class);

    @Resource
    private CacheContext<Account> accountCacheContext;

    public Account getAccountByName(String accountName) {
        Account result = accountCacheContext.get(accountName);
        if (result != null) {
            logger.info("get from cache... {}", accountName);
            return result;
        }

        Optional<Account> accountOptional = getFromDB(accountName);
        if (!accountOptional.isPresent()) {
            throw new IllegalStateException(String.format("can not find account by account name : [%s]", accountName));
        }

        Account account = accountOptional.get();
        accountCacheContext.addOrUpdateCache(accountName, account);
        return account;
    }

    public void reload() {
        accountCacheContext.evictCache();
    }

    private Optional<Account> getFromDB(String accountName) {
        logger.info("real querying db... {}", accountName);
        //Todo query data from database
        return Optional.fromNullable(new Account(accountName));
    }

}

現在我們開始寫一個測試類,用於測試剛才的快取是否有效

import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import static org.junit.Assert.*;

public class AccountService1Test {

    private AccountService1 accountService1;

    private final Logger logger = LoggerFactory.getLogger(AccountService1Test.class);

    @Before
    public void setUp() throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
        accountService1 = context.getBean("accountService1", AccountService1.class);
    }

    @Test
    public void testInject(){
        assertNotNull(accountService1);
    }

    @Test
    public void testGetAccountByName() throws Exception {
        accountService1.getAccountByName("accountName");
        accountService1.getAccountByName("accountName");

        accountService1.reload();
        logger.info("after reload ....");

        accountService1.getAccountByName("accountName");
        accountService1.getAccountByName("accountName");
    }
}

按照分析,執行結果應該是:首先從資料庫查詢,然後直接返回快取中的結果,重置快取後,應該先從資料庫查詢,然後返回快取中的結果. 檢視程式執行的日誌如下:

00:53:17.166 [main] INFO  c.r.s.cache.example1.AccountService - real querying db... accountName
00:53:17.168 [main] INFO  c.r.s.cache.example1.AccountService - get from cache... accountName
00:53:17.168 [main] INFO  c.r.s.c.example1.AccountServiceTest - after reload ....
00:53:17.168 [main] INFO  c.r.s.cache.example1.AccountService - real querying db... accountName
00:53:17.169 [main] INFO  c.r.s.cache.example1.AccountService - get from cache... accountName

可以看出我們的快取起效了,但是這種自定義的快取方案有如下劣勢:

  • 快取程式碼和業務程式碼耦合度太高,如上面的例子,AccountService 中的 getAccountByName()方法中有了太多快取的邏輯,不便於維護和變更
  • 不靈活,這種快取方案不支援按照某種條件的快取,比如只有某種型別的賬號才需要快取,這種需求會導致程式碼的變更
  • 快取的儲存這塊寫的比較死,不能靈活的切換為使用第三方的快取模組

如果你的程式碼中有上述程式碼的影子,那麼你可以考慮按照下面的介紹來優化一下你的程式碼結構了,也可以說是簡化,你會發現,你的程式碼會變得優雅的多!

Spring cache是如何做的呢

我們對AccountService1 進行修改,建立AccountService2:

import com.google.common.base.Optional;
import com.rollenholt.spring.cache.example1.Account;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @author wenchao.ren
 *         2015/1/5.
 */
@Service
public class AccountService2 {

    private final Logger logger = LoggerFactory.getLogger(AccountService2.class);

    // 使用了一個快取名叫 accountCache
    @Cacheable(value="accountCache")
    public Account getAccountByName(String accountName) {

        // 方法內部實現不考慮快取邏輯,直接實現業務
        logger.info("real querying account... {}", accountName);
        Optional<Account> accountOptional = getFromDB(accountName);
        if (!accountOptional.isPresent()) {
            throw new IllegalStateException(String.format("can not find account by account name : [%s]", accountName));
        }

        return accountOptional.get();
    }

    private Optional<Account> getFromDB(String accountName) {
        logger.info("real querying db... {}", accountName);
        //Todo query data from database
        return Optional.fromNullable(new Account(accountName));
    }

}

我們注意到在上面的程式碼中有一行:

     @Cacheable(value="accountCache")
     

這個註釋的意思是,當呼叫這個方法的時候,會從一個名叫 accountCache 的快取中查詢,如果沒有,則執行實際的方法(即查詢資料庫),並將執行的結果存入快取中,否則返回快取中的物件。這裡的快取中的 key 就是引數 accountName,value 就是 Account 物件。“accountCache”快取是在 spring*.xml 中定義的名稱。我們還需要一個 spring 的配置檔案來支援基於註釋的快取

<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.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">

    <context:component-scan base-package="com.rollenholt.spring.cache"/>

    <context:annotation-config/>

    <cache:annotation-driven/>

    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
                    <property name="name" value="default"/>
                </bean>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
                    <property name="name" value="accountCache"/>
                </bean>
            </set>
        </property>
    </bean>

</beans>

注意這個 spring 配置檔案有一個關鍵的支援快取的配置項:

<cache:annotation-driven />

這個配置項預設使用了一個名字叫 cacheManager 的快取管理器,這個快取管理器有一個 spring 的預設實現,即org.springframework.cache.support.SimpleCacheManager,這個快取管理器實現了我們剛剛自定義的快取管理器的邏輯,它需要配置一個屬性 caches,即此快取管理器管理的快取集合,除了預設的名字叫 default 的快取,我們還自定義了一個名字叫 accountCache 的快取,使用了預設的記憶體儲存方案 ConcurrentMapCacheFactoryBean,它是基於 java.util.concurrent.ConcurrentHashMap 的一個記憶體快取實現方案。

然後我們編寫測試程式:

 import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import static org.junit.Assert.*;

public class AccountService2Test {

    private AccountService2 accountService2;

    private final Logger logger = LoggerFactory.getLogger(AccountService2Test.class);

    @Before
    public void setUp() throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
        accountService2 = context.getBean("accountService2", AccountService2.class);
    }

    @Test
    public void testInject(){
        assertNotNull(accountService2);
    }

    @Test
    public void testGetAccountByName() throws Exception {
        logger.info("first query...");
        accountService2.getAccountByName("accountName");

        logger.info("second query...");
        accountService2.getAccountByName("accountName");
    }
}

上面的測試程式碼主要進行了兩次查詢,第一次應該會查詢資料庫,第二次應該返回快取,不再查資料庫,我們執行一下,看看結果

01:10:32.435 [main] INFO  c.r.s.c.example2.AccountService2Test - first query...
01:10:32.456 [main] INFO  c.r.s.cache.example2.AccountService2 - real querying account... accountName
01:10:32.457 [main] INFO  c.r.s.cache.example2.AccountService2 - real querying db... accountName
01:10:32.458 [main] INFO  c.r.s.c.example2.AccountService2Test - second query...

可以看出我們設定的基於註釋的快取起作用了,而在 AccountService.java 的程式碼中,我們沒有看到任何的快取邏輯程式碼,只有一行註釋:@Cacheable(value="accountCache"),就實現了基本的快取方案,是不是很強大?

如何清空快取

好,到目前為止,我們的 spring cache 快取程式已經執行成功了,但是還不完美,因為還缺少一個重要的快取管理邏輯:清空快取.

當賬號資料發生變更,那麼必須要清空某個快取,另外還需要定期的清空所有快取,以保證快取資料的可靠性。

為了加入清空快取的邏輯,我們只要對 AccountService2.java 進行修改,從業務邏輯的角度上看,它有兩個需要清空快取的地方

  • 當外部呼叫更新了賬號,則我們需要更新此賬號對應的快取
  • 當外部呼叫說明重新載入,則我們需要清空所有快取

我們在AccountService2的基礎上進行修改,修改為AccountService3,程式碼如下:

import com.google.common.base.Optional;
import com.rollenholt.spring.cache.example1.Account;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @author wenchao.ren
 *         2015/1/5.
 */
@Service
public class AccountService3 {

    private final Logger logger = LoggerFactory.getLogger(AccountService3.class);

    // 使用了一個快取名叫 accountCache
    @Cacheable(value="accountCache")
    public Account getAccountByName(String accountName) {

        // 方法內部實現不考慮快取邏輯,直接實現業務
        logger.info("real querying account... {}", accountName);
        Optional<Account> accountOptional = getFromDB(accountName);
        if (!accountOptional.isPresent()) {
            throw new IllegalStateException(String.format("can not find account by account name : [%s]", accountName));
        }

        return accountOptional.get();
    }

    @CacheEvict(value="accountCache",key="#account.getName()")
    public void updateAccount(Account account) {
        updateDB(account);
    }

    @CacheEvict(value="accountCache",allEntries=true)
    public void reload() {
    }

    private void updateDB(Account account) {
        logger.info("real update db...{}", account.getName());
    }

    private Optional<Account> getFromDB(String accountName) {
        logger.info("real querying db... {}", accountName);
        //Todo query data from database
        return Optional.fromNullable(new Account(accountName));
    }
}

我們的測試程式碼如下:

import com.rollenholt.spring.cache.example1.Account;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AccountService3Test {


    private AccountService3 accountService3;

    private final Logger logger = LoggerFactory.getLogger(AccountService3Test.class);

    @Before
    public void setUp() throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
        accountService3 = context.getBean("accountService3", AccountService3.class);
    }

    @Test
    public void testGetAccountByName() throws Exception {

        logger.info("first query.....");
        accountService3.getAccountByName("accountName");

        logger.info("second query....");
        accountService3.getAccountByName("accountName");

    }

    @Test
    public void testUpdateAccount() throws Exception {
        Account account1 = accountService3.getAccountByName("accountName1");
        logger.info(account1.toString());
        Account account2 = accountService3.getAccountByName("accountName2");
        logger.info(account2.toString());

        account2.setId(121212);
        accountService3.updateAccount(account2);

        // account1會走快取
        account1 = accountService3.getAccountByName("accountName1");
        logger.info(account1.toString());
        // account2會查詢db
        account2 = accountService3.getAccountByName("accountName2");
        logger.info(account2.toString());

    }

    @Test
    public void testReload() throws Exception {
        accountService3.reload();
        // 這2行查詢資料庫
        accountService3.getAccountByName("somebody1");
        accountService3.getAccountByName("somebody2");

        // 這兩行走快取
        accountService3.getAccountByName("somebody1");
        accountService3.getAccountByName("somebody2");
    }
}

在這個測試程式碼中我們重點關注testUpdateAccount()方法,在測試程式碼中我們已經註釋了在update完account2以後,再次查詢的時候,account1會走快取,而account2不會走快取,而去查詢db,觀察程式執行日誌,執行日誌為:

01:37:34.549 [main] INFO  c.r.s.cache.example3.AccountService3 - real querying account... accountName1
01:37:34.551 [main] INFO  c.r.s.cache.example3.AccountService3 - real querying db... accountName1
01:37:34.552 [main] INFO  c.r.s.c.example3.AccountService3Test - Account{id=0, name='accountName1'}
01:37:34.553 [main] INFO  c.r.s.cache.example3.AccountService3 - real querying account... accountName2
01:37:34.553 [main] INFO  c.r.s.cache.example3.AccountService3 - real querying db... accountName2
01:37:34.555 [main] INFO  c.r.s.c.example3.AccountService3Test - Account{id=0, name='accountName2'}
01:37:34.555 [main] INFO  c.r.s.cache.example3.AccountService3 - real update db...accountName2
01:37:34.595 [main] INFO  c.r.s.c.example3.AccountService3Test - Account{id=0, name='accountName1'}
01:37:34.596 [main] INFO  c.r.s.cache.example3.AccountService3 - real querying account... accountName2
01:37:34.596 [main] INFO  c.r.s.cache.example3.AccountService3 - real querying db... accountName2
01:37:34.596 [main] INFO  c.r.s.c.example3.AccountService3Test - Account{id=0, name='accountName2'}

我們會發現實際執行情況和我們預估的結果是一致的。

如何按照條件操作快取

前面介紹的快取方法,沒有任何條件,即所有對 accountService 物件的 getAccountByName 方法的呼叫都會起動快取效果,不管引數是什麼值。

如果有一個需求,就是隻有賬號名稱的長度小於等於 4 的情況下,才做快取,大於 4 的不使用快取

雖然這個需求比較坑爹,但是拋開需求的合理性,我們怎麼實現這個功能呢?

通過檢視CacheEvict註解的定義,我們會發現:

/**
 * Annotation indicating that a method (or all methods on a class) trigger(s)
 * a cache invalidate operation.
 *
 * @author Costin Leau
 * @author Stephane Nicoll
 * @since 3.1
 * @see CacheConfig
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {

	/**
	 * Qualifier value for the specified cached operation.
	 * <p>May be used to determine the target cache (or caches), matching the qualifier
	 * value (or the bean name(s)) of (a) specific bean definition.
	 */
	String[] value() default {};

	/**
	 * Spring Expression Language (SpEL) attribute for computing the key dynamically.
	 * <p>Default is "", meaning all method parameters are considered as a key, unless
	 * a custom {@link #keyGenerator()} has been set.
	 */
	String key() default "";

	/**
	 * The bean name of the custom {@link org.springframework.cache.interceptor.KeyGenerator} to use.
	 * <p>Mutually exclusive with the {@link #key()} attribute.
	 */
	String keyGenerator() default "";

	/**
	 * The bean name of the custom {@link org.springframework.cache.CacheManager} to use to
	 * create a default {@link org.springframework.cache.interceptor.CacheResolver} if none
	 * is set already.
	 * <p>Mutually exclusive with the {@link #cacheResolver()}  attribute.
	 * @see org.springframework.cache.interceptor.SimpleCacheResolver
	 */
	String cacheManager() default "";

	/**
	 * The bean name of the custom {@link org.springframework.cache.interceptor.CacheResolver} to use.
	 */
	String cacheResolver() default "";

	/**
	 * Spring Expression Language (SpEL) attribute used for conditioning the method caching.
	 * <p>Default is "", meaning the method is always cached.
	 */
	String condition() default "";

	/**
	 * Whether or not all the entries inside the cache(s) are removed or not. By
	 * default, only the value under the associated key is removed.
	 * <p>Note that setting this parameter to {@code true} and specifying a {@link #key()}
	 * is not allowed.
	 */
	boolean allEntries() default false;

	/**
	 * Whether the eviction should occur after the method is successfully invoked (default)
	 * or before. The latter causes the eviction to occur irrespective of the method outcome (whether
	 * it threw an exception or not) while the former does not.
	 */
	boolean beforeInvocation() default false;
}

定義中有一個

相關推薦

Spring cache資料( )

Spring Cache 介紹 2015-01-05 01:45 by Rollen Holt, 7781 閱讀, 8 評論, 收藏, 編輯 Spring Cache 快取是實際工作中非常常用的一種提高

減少網站跳轉時間增強網站資料安全——HSTS

近年來隨著 Google、Apple、百度等公司不斷推動 HTTPS 普及,全網 HTTPS 已是大勢所趨。目前多數網站都已經支援 HTTPS 訪問,但是在由 HTTP 轉向 HTTPS 路程中,不少網站依然會面臨很多問題。 通常使用者準備訪問某個網站時,不會在輸入的域名前面加上 http:// 或者 ht

spring的事務註解@Transactional

事務管理是應用系統開發中必不可少的一部分。Spring 為事務管理提供了豐富的功能支援。 Spring 事務管理分為程式設計式和宣告式的兩種方式。 程式設計式事務指的是通過編碼方式實現事務,程式設計式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransaction

互斥量、條件變數與pthread_cond_wait()函式的使用()

1.Linux“執行緒”      程序與執行緒之間是有區別的,不過linux核心只提供了輕量程序的支援,未實現執行緒模型。Linux是一種“多程序單執行緒”的作業系統。Linux本身只有程序的概念,而其所謂的“執行緒”本質上在核心裡仍然是程序。      大家知道,

spring boot介紹及使用帶你走進spring

Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。通過這種方式,Boot致力於在蓬勃發展的快速應用開發領域(rapid appli

資料分析的資料架構知識

我們在前面的文章中提到了BI系統,從文章中我們不難發現BI系統處理資料的時候都是很有效的,但是當資料量過大的時候,我們系統的效能就會弱了很多。當然了,如果我們處理的資料在TB或者TB以上的資料量的時候,這個系統根本就不能夠正常執行,所以,我們就需要解決這個問題。 大家都知道資料庫的規則是有很多的,資料庫

buffer cache 和shared pool (之三shared pool原理)

【深入解析--eygle】 學習筆記 1.2 shared pool原理 Shared Pool是Oracle SGA設定中最複雜也是最重要的一部分內容,Oracle通過Shared Pool來實現SQL共享、減少程式碼硬解析等,從而提高資料庫的效能。在某些版本中,如果設

(轉)Spring boot——logback.xml 配置

回到頂部1 根節點<configuration>包含的屬性scan:當此屬性設定為true時,配置檔案如果發生改變,將會被重新載入,預設值為true。scanPeriod:設定監測配置檔案是否有修改的時間間隔,如果沒有給出時間單位,預設單位是毫秒。當scan為true時,此屬性生效。預設的時間間隔

資料學習路線零基礎學大資料學習路線

在大資料蓬勃發展的現今,大家都看到了大資料未來不可限量的發展前景,跟著科技發展的潮流,不斷學習新的技術知識,科多大資料相信,成為人生贏家不在話下。 大資料的三個發展方向,平臺搭建/優化/運維/監控、大資料開發/設計/架構、資料分析/挖掘。 推薦下小編的大資料學習群;251956502,不管你

buffer cache 和shared pool(之五問題診斷總結)

【深入解析--eygle】 學習筆記 1.2.7 診斷和解決ORA-04031 錯誤 Shared  Pool的主要問題在根本上只有一個,就是碎片過多帶來的效能影響。 1.2.7.1 什麼是ORA-04031錯誤 當嘗試在共享池分配大塊的連續記憶體失敗(很

Spring boot——logback.xml 配置

原文地址:https://www.cnblogs.com/lixuwu/p/5810912.html                   https://aub.iteye.com/blog/1101260 閱

thinkPHP3.1.2多表事務批量插入資料 and例項

往list 表裡新增一行記錄,然後更新一下user表裡的status欄位 先用 M 函式例項化一個空物件,使用 table 方法進行多個表的操作,如果操作成功則提交,失敗則回滾 public func

mybatis與Spring三種開發方式&三)Mapper動態代理開發&Mapper動態代理掃描包形式開發

mybatis與Spring三種開發方式詳解(二)Mapper動態代理開發 之前我們說到傳統的dao層開發,今天我們來談談第二種mybatis與Spring的開發方式,Mapper動態代理開發。 首先這裡上一波@test測試程式碼,大家先看一下mybat

spring學習筆記(17)資料庫配置[1]spring資料連線池

資料連線池 在spring中,常使用資料庫連線池來完成對資料庫的連線配置,類似於執行緒池的定義,資料庫連線池就是維護有一定數量資料庫連線的一個緩衝池,一方面,能夠即取即用,免去初始化的時間,另一方面,用完的資料連線會歸還到連線池中,這樣就免去了不必要的連線建立

android 摘要----資料儲存全方案持久化技術

資料持久化:         只將那些記憶體中的瞬時資料儲存到儲存裝置中,保證即使在手機或電腦關機的情況下,這些資料也不會丟失。 1,檔案儲存:             是android中最基本的一種資料儲存方式,不對儲存內容進行任何格式化處理,所有詩句原封不動的儲存到檔案

myeclipse+maven實現多模組專案struts+spring+mybatis

本文中建立maven專案,不使用命令,完全用myeclipse實現,個人感覺這樣新手更容易接受! 開發環境:myeclipse 8.5+maven(非myeclipse自帶外掛) 目標:期望有兩個web專案A和B,訪問同一個資料庫,即依賴同一個java專案C 這裡借用

Spring Cloud Eureka 常用配置建議收藏!

new enable seconds 指定 頻率 集群 系列 name tps 前幾天,棧長分享了 《Spring Cloud Eureka 註冊中心集群搭建,Greenwich 最新版!》,今天來分享下 Spring Cloud Eureka 常用的一些參數配置及說明。

SpringSpring MVC原理及配置

進行 return sub sca scrip uil 線程安全 松耦合 必須 1.Spring MVC概述: Spring MVC是Spring提供的一個強大而靈活的web框架。借助於註解,Spring MVC提供了幾乎是POJO的開發模式,使得控制器的開發和測試更加簡

轉:Android命令Monkey壓力測試

語句 shel gre href 輸入 white option blacklist 文件 停止Monkey命令: 1. ps命令 查找uiautomator的進程 打開cmd命令行窗口 輸入: adb shell ps | grep monkey 返回來的第一個數字,即

Spring Boot 配置文件:Properties和YAML

列表 config 其他 操作系統 des num mat 變量 onf 一.配置文件的生效順序,會對值進行覆蓋: 1. @TestPropertySource 註解 2. 命令行參數 3. Java系統屬性(System.getProperties