NO END FOR LEARNING

Writing blog if you feel tired | 学海无涯 苦写博客

了解Spring Cache

| Comments

自从Spring 3.1,Spring框架提供了透明式给Spring应用添加缓存。类似于对事物的支持,抽象缓存机制允许一致的方式来实现不同的缓存解决方案,以满足对代码的最小影响。

Spring 4.1后,抽象缓存机制有了重大的改进,包含支持JSR-107注解和更多的定制选项。

Cache vs Buffer

术语“buffer”和“cache”倾向于交换的使用。然而,它们代表着不同的东西。buffer常常被作为快慢实体之间(比如:发送方很快,接收方较慢)的中间临时存储。乙方必须等待另一方,因为执行效率(速率)的不同,buffer通过允许数据在移动时,以一整块数据为单位而不是小块形式来调解这种速率不一致的问题。数据在buffer中只会读写一次。更进一步,至少对于一方来说buffer是可见的。

另一方面,Cache就定义上而言,对于任何一方都是隐藏。它也会提升性能,但是仅限于同样地数据以快速的方式读取多次。

Cache的核心是将抽象层应用到Java方法上,减少方法执行的次数,因为信息在Cache中可以获取到。那就是,每次目标方法被触发时,抽象层就会执行一个cache行为,来检测方法是否已经针对指定的参数执行过一次。如果是,那么就会cache结果,而不会执行实际方法;如果没有,那么就执行相应的方法,方法执行的结果会被cache,下次同样的方法和参数执行的时候,就会返回cache的结果。这样,对于消耗更大的方法(无论是CPU还是IO)都只会针对给定的参数执行一次。Cache的逻辑完全是透明的,不会对触发调用产生任何干扰。

该抽象层也提供了其他Cache相关的操作的,允许更新Cache的内容或者删除一部分内容(条目)。这对于需要在应用程序运行阶段改变Cache的情况非常有用。

就像其他Spring框架的其他服务,Cache服务只是一个抽象层,需要实际的存储方式来存储数据。抽象层的关键类是org.springframework.cache.Cache和org.springframework.cache.CacheManager接口。

有几种开箱即用的实现:JDK java.util.concurrent.ConcurrentMap based caches, EhCache, Gemfire cache, Guava caches, JSR-107 compliant caches。

要使用cache抽象机制,需要负责两个方面: cache声明 - 指定要被cache的方法以及策略 cache配置 - 背后的cache存储在哪里,从哪里读取

关键注解

1
2
3
4
5
@Cacheable triggers cache population
@CacheEvict triggers cache eviction
@CachePut updates the cache without interfering with the method execution
@Caching regroups multiple cache operations to be applied on a method
@CacheConfig shares some common cache-related settings at class-level

@Cacheable用来声明方法可以被缓存(cache)

1
2
@Cacheable("books")
public Book findBook(ISBN isbn) {...}

其中,books是该cache的名字。
在大部分情况下,只有一个cache被声明,注解允许多个名字被指定,所以就可以有多个cache被使用。在这种情况下,每个cache都会在方法执行之前被检测,如果至少有一个cache命中,那么相关的值就会被返回:

1
2
@Cacheable({"books", "isbns"})
public Book findBook(ISBN isbn) {...}

cache本质上是键值对存储,每一个cache的方法在被触发时,都会转换成一个合适的key来访问cache。开箱即用的是,有一个简单地KeyGenerator基于下面这个简单算法:

如果没有参数,就返回SimpleKey.EMPTY
如果有一个参数,就返回该实例
如果有多个参数,就返回一个SimpleKey包含所有的参数

只要参数包含自然键值,并且实现了合法的hashCode和equals方法,这种算法就适用于所有场景。

默认情况下,Cache抽象层会使用一个简单地CacheResolver来获取cache。

有条件的cache

有些时候,一个方法并不适合在任何时候都cache,cache注解支持通过SpEL来指定条件,只有满足条件的参数来会被cache。

1
2
@Cacheable(cacheNames="book", condition="#name.length < 32")
public Book findBook(String name)

cache抽象不仅允许存储缓存,也允许收回(eviction)缓存。这个操作对于删除陈旧无用的缓存非常有用。和注解@Cacheable相反,@CacheEvict指定方法执行cache收回,也就是方法执行时会从cache中删除数据。和@Cacheable需要指定一个或者多个cache的名字。还提供一个额外allEntries属性,用来指定是否需要一个更广范围的cache需要收回(所有条目(无参数的,一个参数的,多个参数的))。

1
2
@CacheEvict(cacheNames="books", allEntries=true)
public void loadBooks(InputStream batch)

CacheConfig

如果一个类中的所有方法操作都需要Cache,那么就可以使用@CacheConfig注解。

1
2
3
4
5
@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {
    @Cacheable
    public Book findBook(ISBN isbn) {...}
}

EnableCaching

仅仅声明上面你需要cache的方法或者类是不足够的,和Spring的其他服务一样,该特性需要被启动(Enable)。

1
2
3
4
@Configuration
@EnableCaching
public class AppConfig {
}

Cache正如它在对于Buffer时所解释的那样,用来解决对重复获取的数据,减少对数据库的重复操作,从而提高数据获取速度。

参考资料:

1.http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html

JavaScript渐入佳境 - This指针

| Comments

在JavaScript中,随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用该函数的那个对象。this关键字在Javascript中和执行环境,而非声明环境有关。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
var someone = {
    name: "Bob",
    showName: function(){
        alert(this.name);
    }
};

var other = {
    name: "Tom",
    showName: someone.showName
}

other.showName();  //Tom

这里其实应该好理解,other的showName属性指向了someone的showName所声明的函数,他等价于:

1
2
3
4
5
6
7
8
var other = {
    name: "Tom",
    showName: function(){
        alert(this.name);
    }
}

other.showName();  //Tom

所以this指针理所当然的指向other自己。

1
2
3
4
5
6
7
8
9
10
11
var name = "Tom";

var Bob = {
    name: "Bob",
    show: function(){
        alert(this.name);
    }
}

var show = Bob.show;
show();  //Tom

上面这个show为什么显示Tom,它的执行对象是什么?Window对象。

Window对象是所有客户端JavaScript特性和API的主要接入点。它表示Web浏览器的一个窗口,并且可以用标示符window(小写)来引用。

1
2
3
4
window
$ Window {external: Object, chrome: Object, document: document, \_ASYNC_START: 1451649155228, \_chrome_37_fix: undefined}
Window
$ function Window() { [native code] }

Window对象定义了一些属性,比如,指代Location对象(location)的location属性。

1
2
window.location === location
$ true

Window对象还定义了一些方法,比如alert()和setTimeout(),使用时,我们都没有显示的使用window属性。Window对象是全局对象,处于作用域链的顶部,它的属性和方法实际上全是全局变量和全局函数。 通过window变量可以引用到Window对象本身,但是如果要使用全局窗口对象的属性,并不需要使用window。

我们常说,JavaScript很容易就创建一个全局变量或者全局函数。比如,直接var a_global_variable = ‘a global variable’。它不仅使全局变量,它还是window的一个属性。如下:

1
2
3
4
5
6
7
var a_global_variable = 'a global variable'
a_global_variable
$ "a global variable"
window.goodtest
$ "a global variable"
window.a_global_variable === a_global_variable
$ true

为什么在Window下?head/全局对象/顶层对象

JavaScript代码本身必须包含在对象内部。在Web浏览器环境中编写JavaScript代码时,JavaScript被包含在Window对象内,并在其内部执行。这个Window对象被认为是“head对象”。JavaScript的所有实现都需要使用一种head对象。

head对象是由JavaScript在幕后创建,用于封装用户用自定义代码,并容纳JavaScript预定义的原生代码。JavaScript将用户自定义代码放入head对象中来执行。在编写JavaScript代码时,它将被编写在head对象的上下文中。

换一种解释:

JavaScript的所有对象都存在于一个运行环境之中,这个运行环境本身也是对象,称为“顶层对象”。这就是说,JavaScript的所有对象,都是“顶层对象”的下属。不同的运行环境有不同的“顶层对象”,在浏览器环境中,这个顶层对象就是Window对象。

所有浏览器环境的全局变量,都是Window对象的属性。可以把Window理解成JavaScript Context 上下文环境。

理解了setTimeout是Window的属性之后,理解下面这段代码应该比较容易了:

1
2
3
4
5
6
7
8
9
10
11
var name = "Bob";
var nameObj ={
    name : "Tom",
    showName : function(){
        alert(this.name);//此时this指向的是window  
    },
    waitShowName : function(){
        setTimeout(this.showName, 1000);//这里的this指向的是nameObj.showName
    }
};
nameObj.waitShowName();// Bob

和下面这段代码相似

1
2
3
4
var name = "Bob";
setTimeout(function(){
    alert(this.name);
}, 1000);

new关键字

new关键字指向创建的对象,在上一篇文章中已经介绍的很清楚了。 http://benweizhu.github.io/blog/2015/12/31/javascript-contructor-new-prototype/

1
2
3
4
5
6
7
8
function Person(name){
    this.name = name; //这个this指向用该构造函数构造的新对象,这个例子是Bob对象
}
Person.prototype.show = function(){
    alert(this.name);
}
var Bob = new Person("Bob");
Bob.show();        //Bob

apply和call改变this指向的对象

apply和call能够强制改变函数执行时的当前对象,让this指向其他对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var name = "window";

var someone = {
    name: "Bob",
    showName: function(){
        alert(this.name);
    }
};

var other = {
    name: "Tom"
};

someone.showName();   //Bob
someone.showName.apply();    //window
someone.showName.apply(other);    //Tom

参考资料:
1.http://www.cnblogs.com/justany/archive/2012/11/01/the_keyword_this_in_javascript.html
2.JavaScript启示录
3.http://blog.csdn.net/zoutongyuan/article/details/29355021