Linux开机自动启动Tomcat应用

前言

刚过国庆长假,工作机房断电,重回工作岗位发现N多的应用需要手动启动。感觉烦燥?
so.. 配置linux开机自启动

配置

/etc/rc.local文件末尾增加以下内容

1
2
3
4
5
6
7
8
9
10
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

O_Path=`pwd`
cd /myapps/servers/apache-tomcat-6.0.43/bin/
sh startup.sh
cd $O_Path

需要注意以下几点:

  • 检查startup.sh是否已授执行权限,如果没有,执行chmod +x /myapps/servers/apache-tomcat-6.0.43/bin/startup.sh
  • 所需要执行的shell文件不能带参数。

经过以上配置则可以通过sudo reboot进行验证。当Linux重启成功后,可通过/var/log/boot.log来查看开机时的日志输出。

其它

who -r 查看开机级别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
who --help
用法:who [选项]... [ 文件 | 参数1 参数2 ]
显示当前已登录的用户信息。

-a, --all 等于-b -d --login -p -r -t -T -u 选项的组合
-b, --boot 上次系统启动时间
-d, --dead 显示已死的进程
-H, --heading 输出头部的标题列
-l,--login 显示系统登录进程
--lookup 尝试通过 DNS 查验主机名
-m 只面对和标准输入有直接交互的主机和用户
-p, --process 显示由 init 进程衍生的活动进程
-q, --count 列出所有已登录用户的登录名与用户数量
-r, --runlevel 显示当前的运行级别
-s, --short 只显示名称、线路和时间(默认)
-T, -w, --mesg 用+,- 或 ? 标注用户消息状态
-u, --users 列出已登录的用户
--message 等于-T
--writable 等于-T
--help 显示此帮助信息并退出
--version 显示版本信息并退出

观点仅代表自己,期待你的留言。

查看jvm堆内GC情况利器 - jstat

简介

jstat是JDK自带的一个轻量级小工具。全称“Java Virtual Machine statistics monitoring tool”,主要利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。
以命令行方式运行对系统资源占用很小,在大压力下很少影响性能。并且运行要求低,只要通过Telnet或SSH等方式远程登录到服务器所在机器,就可以进行监控。在与jstat、jstack等工具结合使用时非常方便。

命令安装

  1. Oracle JDK在安装完成后bin目录下就会发现有jstat命令。
  2. Open JDK在安装完成后需要安装openjdk-devel开发包才能得到jstat命令。
    安装方法如下(以CentOS为例):
    使用 yum whatprovides '*/jstat'查包含jstat命令开发包的名称。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@wujianjun-linux bin]# yum whatprovides '*/jstat'
    Loaded plugins: fastestmirror, security
    Loading mirror speeds from cached hostfile
    base/filelists_db | 6.4 MB 00:00
    extras/filelists_db | 38 kB 00:00
    updates/filelists_db | 1.5 MB 00:00
    1:java-1.7.0-openjdk-devel-1.7.0.99-2.6.5.1.el6.x86_64 : OpenJDK Development Environment
    Repo : base
    Matched from:
    Filename : /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.99.x86_64/bin/jstat
    ......
    .....
    ...

找到与jdk版本相对应的开发包进行安装

1
[root@wujianjun-linux bin]# yum install java-1.7.0-openjdk-devel

安装完成后查找命令所在目录

1
2
[root@wujianjun-linux bin]# whereis jstat
jstat: /usr/bin/jstat /usr/share/man/man1/jstat.1.gz

只要能显示相应目录,则表示安装成功。

jstat使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@order-zhiMaoQu-com ~]# jstat --help
invalid argument count
Usage: jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

Definitions:
<option> An option reported by the -options option
<vmid> Virtual Machine Identifier. A vmid takes the following form:
<lvmid>[@<hostname>[:<port>]]
Where <lvmid> is the local vm identifier for the target
Java virtual machine, typically a process id; <hostname> is
the name of the host running the target Java virtual machine;
and <port> is the port number for the rmiregistry on the
target host. See the jvmstat documentation for a more complete
description of the Virtual Machine Identifier.
<lines> Number of samples between header lines.
<interval> Sampling interval. The following forms are allowed:
<n>["ms"|"s"]
Where <n> is an integer and the suffix specifies the units as
milliseconds("ms") or seconds("s"). The default units are "ms".
<count> Number of samples to take before terminating.
-J<flag> Pass <flag> directly to the runtime system.

  • -gcutil: 查看gc情况
  • -class: 显示加载class的数量,及所占空间等信息
  • -compiler: 显示VM实时编译的数量等信息。
  • –gccapacity: 可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,
    如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。
    其他的可以根据这个类推,OC是old内纯的占用量。
  • -gcnew: new对象的信息
  • -gcnewcapacity: new对象的信息及其占用量
  • -gcold: old对象的信息。
  • -gcoldcapacity: old对象的信息及其占用量
  • -gcpermcapacity: perm对象的信息及其占用量
  • -printcompilation: 当前VM执行的信息

附GC显示术语解释:

  • S0C - 年轻代中第一个survivor(幸存区)的容量 (字节)
  • S1C - 年轻代中第二个survivor(幸存区)的容量 (字节)
  • S0U - 年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
  • S1U - 年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
  • EC - 年轻代中Eden的容量 (字节)
  • EU - 年轻代中Eden目前已使用空间 (字节)
  • OC - Old代的容量 (字节)
  • OU - Old代目前已使用空间 (字节)
  • PC - Perm(持久代)的容量 (字节)
  • PU - Perm(持久代)目前已使用空间 (字节)
  • YGC - 从应用程序启动到采样时年轻代中gc次数
  • YGCT - 从应用程序启动到采样时年轻代中gc所用时间(s)
  • FGC - 从应用程序启动到采样时old代(全gc)gc次数
  • FGCT - 从应用程序启动到采样时old代(全gc)gc所用时间(s)
  • GCT - 从应用程序启动到采样时gc用的总时间(s)
  • NGCMN - 年轻代(young)中初始化(最小)的大小 (字节)
  • NGCMX - 年轻代(young)的最大容量 (字节)
  • NGC - 年轻代(young)中当前的容量 (字节)
  • OGCMN - old代中初始化(最小)的大小 (字节)
  • OGCMX - old代的最大容量 (字节)
  • OGC - old代当前新生成的容量 (字节)
  • PGCMN - perm代中初始化(最小)的大小 (字节)
  • PGCMX - perm代的最大容量 (字节)
  • PGC - perm代当前新生成的容量 (字节)
  • S0 - 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
  • S1 - 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
  • E - 年轻代中Eden已使用的占当前容量百分比
  • O - old代已使用的占当前容量百分比
  • P - perm代已使用的占当前容量百分比
  • S0CMX - 年轻代中第一个survivor(幸存区)的最大容量 (字节)
  • S1CMX - 年轻代中第二个survivor(幸存区)的最大容量 (字节)
  • ECMX - 年轻代中Eden的最大容量 (字节)
  • DSS - 当前需要survivor(幸存区)的容量 (字节)(Eden区已满)
  • TT - 持有次数限制
  • MTT - 最大持有次数限制

观点仅代表自己,期待你的留言。

jvm查内存泄漏利器 - jmap

简介

Oracle将jmap描述为一种“输出进程、核心文件、远程调试服务器的共享对象内存映射和堆内存细节”的程序。
通过它可以查看内存中对象实例,从而解决程序出现不正常的高内存负载、频繁无响应或内存溢出等问题。

命令安装

  1. Oracle JDK在安装完成后bin目录下就会发现有jmap命令。
  2. Open JDK在安装完成后需要安装openjdk-devel开发包才能得到jmap命令。
    安装方法如下(以CentOS为例):
    使用 yum whatprovides '*/jmap'查包含jmap命令开发包的名称。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@wujianjun-linux bin]# yum whatprovides '*/jmap'
    Loaded plugins: fastestmirror, security
    Loading mirror speeds from cached hostfile
    base/filelists_db | 6.4 MB 00:00
    extras/filelists_db | 38 kB 00:00
    updates/filelists_db | 1.5 MB 00:00
    1:java-1.7.0-openjdk-devel-1.7.0.99-2.6.5.1.el6.x86_64 : OpenJDK Development Environment
    Repo : base
    Matched from:
    Filename : /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.99.x86_64/bin/jmap
    ......
    .....
    ...

找到与jdk版本相对应的开发包进行安装

1
[root@wujianjun-linux bin]# yum install java-1.7.0-openjdk-devel

安装完成后查找命令所在目录

1
2
[root@wujianjun-linux bin]# whereis jmap
jmap: /usr/bin/jmap /usr/share/man/man1/jmap.1.gz

只要能显示相应目录,则表示安装成功。

jmap使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
root@wujianjun-linux ~]# jmap --help
Usage:
jmap [option] <pid>
(to connect to running process)
jmap [option] <executable <core>
(to connect to a core file)
jmap [option] [server_id@]<remote server IP or hostname>
(to connect to remote debug server)

where <option> is one of:
<none> to print same info as Solaris pmap
-heap to print java heap summary
-histo[:live] to print histogram of java object heap; if the "live"
suboption is specified, only count live objects
-permstat to print permanent generation statistics
-finalizerinfo to print information on objects awaiting finalization
-dump:<dump-options> to dump java heap in hprof binary format
dump-options:
live dump only live objects; if not specified,
all objects in the heap are dumped.
format=b binary format
file=<file> dump heap to <file>
Example: jmap -dump:live,format=b,file=heap.bin <pid>
-F force. Use with -dump:<dump-options> <pid> or -histo
to force a heap dump or histogram when <pid> does not
respond. The "live" suboption is not supported
in this mode.
-h | -help to print this help message
-J<flag> to pass <flag> directly to the runtime system
  • jmap pid #打印内存使用的摘要信息
  • jmap –heap pid #java heap信息
  • jmap -histo:live pid #统计对象count ,live表示在使用
  • jmap -histo pid >mem.txt #打印比较简单的各个有多少个对象占了多少内存的信息,一般重定向的文件
  • jmap -dump:format=b,file=mem.dat pid #将内存使用的详细情况输出到mem.dat 文件
    通过jhat -port 7000 mem.dat可以将mem.dat的内容以web的方式暴露到网络,访问http://ip-server:7000查看。

针对内存泄漏这一问题,可以通过查看堆内存中存活对象实例及所占内存数查到对象是否一直被占用导致不能被GC回收。
如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[root@wujianjun-linux ~]# jps |grep -v Jps
9495 Bootstrap

[root@wujianjun-linux ~]# jmap -histo:live 9495
num #instances #bytes class name
----------------------------------------------
1: 294149 29662584 [C
2: 187061 27752880 <constMethodKlass>
3: 187061 23955120 <methodKlass>
4: 15897 19221104 <constantPoolKlass>
5: 61107 13783928 [B
6: 15897 11445056 <instanceKlassKlass>
7: 13934 11267712 <constantPoolCacheKlass>
8: 280651 6735624 java.lang.String
9: 102892 5761952 org.springframework.asm.Item
10: 234489 5627736 org.springframework.asm.Edge
11: 84750 5424000 org.springframework.asm.Label
12: 66211 5296880 java.lang.reflect.Method
13: 63388 4503312 [I
14: 75275 4215400 org.springframework.asm.Item
15: 62580 4005120 org.springframework.asm.Label
16: 132458 3178992 org.springframework.asm.Edge

[root@wujianjun-linux ~]# jmap -histo:live 9495|grep com.yqyw
num #instances #bytes class name
----------------------------------------------
2788: 1 48 com.yqyw.user.rpc.client.UserClient
3570: 1 32 com.yqyw.pnr.sdk.client.ShopClient
3731: 1 32 com.yqyw.pnr.sdk.client.ShopClient
3842: 1 24 com.yqyw.user.rpc.client.impl.FavoriteServiceImpl

从以上输出可以看出目前堆内存中自己程序中的对象实例以及占用bytes,从而确定程序中哪些位置对这些对象进行创建使用且未被释放(典型的情况是将对象存入List等集合类而未被remove)。
也通过多次执行jmap -histo:live pid >mem.txt将每次结果定向到不同的txt中,然后对象对比。发现对象实例的增长与占用字节的变化。

附 - jmap输出中class name非自定义类的说明:

BaseType Character Type Interpretation
B byte signed byte
C char Unicode character
D double double-precision floating-point value
F float single-precision floating-point value
I int integer
J long long integer
L; reference an instance of class
S short signed short
Z boolean true or false
[ reference one array dimension

观点仅代表自己,期待你的留言。

Spring ApplicationContext与常用接口笔记

一、在Web项目中的ApplicationContext

在web 项目中(spring mvc),系统会存在两个容器,一个是ApplicationContext,另一个就是我们自己的WebApplicationContext(作为ApplicationContext的子容器)。

  • ApplicationContext: 默认配置文件名applicationContext.xml,通过org.springframework.web.context.ContextLoaderListener进行加载并初始化。
  • WebApplicationContext: 默认配置文件名xxx-servlet.xml,xxx为DispatcherServlet配置的servlet-name, 通过org.springframework.web.servlet.DispatcherServlet进行加载并初始化。

WebApplicationContext通过getParent()获取到ApplicationContext,而ApplicationContext的getParent()将获取到null(ApplicationContext没有父容器)。

二、Spring常用接口

org.springframework.beans.factory.InitializingBean

InitializingBean表示为spring管理的初始类,当Bean初始时进行属性注入完成后调用afterPropertiesSet进行初始处理。( 趟坑笔记 add by 2017-07-18)

org.springframework.beans.factory.FactoryBean

FactoryBean 是创建 复杂的bean,一般的bean 直接用xml配置即可,如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,用xml配置比较困难,这时可以考虑用FactoryBean。

spring配置时会自动调用getObject来获取注入对象。

@see org.springframework.web.accept.ContentNegotiationManagerFactoryBean

org.springframework.context.ApplicationListener

ApplicationListener用于监听应用程序的事件。
所包含的事件详见ApplicationEvent子类。
当Application事件发生时会自动调用监听器的onApplicationEvent方法。

org.springframework.context.ApplicationContextAware

ApplicationContextAware用于用户保存ApplicationContext的引用,供客户端程序获取ApplicationContext时使用。

org.springframework.beans.factory.DisposableBean

DisposableBean用于标识可销毁的类,当类进行回收时调用destroy()


观点仅代表自己,期待你的留言。

工具_源代码生成器GGCode

简介

通过近期的努力,将编写的源代码生成器进行0.0.1版本的封版,代码生成器按数据表自动生成源代码包含简单业务的(增,删,改,查,分页等)。
部分集成的开源框架已通过配置文件进行开关配置,方便按业务进行开启和关闭。欢迎大家使用。
Github地址:https://github.com/stotem/GGCode

基础框架

源代码生成框架: rapid-framework

UI框架: sb-admin-2

代码框架: SpringMVC + mybatis + Velocity。

代码架构: 经典三层架构(MVC), 增加rpc模块做为调用三方api模块, 增加manager模块设置为缓存层与事务层。

主要转换规则:

  1. 数据表与数据列的注释做为UI展示title。
  2. 数据列的为空性、数据长度和是否可为空等属性转换为javax.validation的判断规则。
  3. 数据列的数据类型转换为实体对象属性的数据类型。

集成功能

  1. 增加shiro做为权限管理框架。
  2. 为文档数据存储添加MongoDB支持。
  3. 为缓存增加Redis支持, 且自动配置Spring Cache为Redis缓存。
  4. 以Velocity做为页面模板语言。
  5. 增加javax.validation为服务端验证框架。且Controller自动验证传入参数。
  6. 采用bonecp对数据库连接池化管理。
  7. spring的响应数据采用多视图配置。

如何使用?

在bin目录中通过配置generator.xml后直接java -jar GGCode-0.0.1.jar

generator.xml配置说明

详见generator.xml注释

数据库设计要求

为了规范生成的Code,针对数据表的设计,需要满足每张表包含以下字段,请使用者遵守:

1
2
3
4
data_id bigint PRIMARY KEY COMMENT '数据主键(与业务主键区分)', 
del_flag tinyint(1) DEFAULT 1 COMMENT '数据删除标识(1: 有效,2: 失效)',
create_time timestamp COMMENT '数据创建时间',
update_time timestamp COMMENT '数据最后update时间',

其它说明

  1. controller的访问URI不带后缀.
  2. 所有的页面文件后缀为view.

生成后程序截图


观点仅代表自己,期待你的留言。

Shiro与Cas集成后的登录流程时序图

框架介绍

Shiro是apache开源项目,主要用户客户端管理登录用户权限提示非常方式的认证接口及丰富的注解。
CAS ( Central Authentication Service )是Yale大学发起的一个企业级的、开源的项目,旨在为 Web 应用系统提供一种可靠的单点登录解决方案(属于Web SSO)。主要用于用户登录认证与登录有效期认证。

调用时序

集成cas与shiro后的用户登录时序图


观点仅代表自己,期待你的留言。

开源SSO项目cas认证返回更多的attribute信息

CAS项目介绍

介绍网址: https://github.com/apereo/cas
github地址:https://github.com/apereo/cas.git

默认情况下,当用户认证成功后,客户端代码中只能获取到用户的登录名称,不能获取到其它的信息(如:手机号,所属角色,已有授权等),好在提供了一个attribute的map可以进行其它信息的返回。

1
2
3
AttributePrincipal ap = AssertionHolder.getAssertion().getPrincipal();
String name = ap.getName();
Map<String,Object> attribute = ap.getAttributes();

增加attribute信息返回

修改ticket验证接口返回的xml信息

找到WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp,在节点之前添加以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
//...原有信息..//
<c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}">
<cas:attributes>
<c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}">
<cas:attribute>
<cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>
</cas:attribute>
</c:forEach>
</cas:attributes>
</c:if>
</cas:authenticationSuccess>
</cas:serviceResponse>

在用户登录验证时增加attribute关联

在deployerConfigContext.xml配置文件中,默认采用AcceptUsersAuthenticationHandler类的users属性(以key为登录名value为密码)进行系统用户的设定。
而通过PersonDirectoryPrincipalResolver的attributeRepository属性进行attribute映射和配置。默认由NamedStubPersonAttributeDao类的backingMap属性进行key-value配置,针对所有的用户都生效。
提示:IPersonAttributeDao有很多的子类实现,可以通过源码查看各自实现的方式,我这里主要介绍json,xml和数据库三种方式实现。

json配置的方式

而在实际业务系统设计中针对不同的用户需要返回不同的attribute这个需求似乎不太适用,经过查看可能配置JsonBackedComplexStubPersonAttributeDao来在json中针对单个用户attribute进行配置。指定init-method为init方法
配置如下:

1
2
3
<bean id="attributeRepository" class="org.jasig.services.persondir.support.JsonBackedComplexStubPersonAttributeDao" init-method="init">
<constructor-arg index="0" value="classpath:users.json" />
</bean>

users.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"user1":{
"id":["1001"],
"firstName":["View"],
"lastName":["Jack"],
"roles":["view"],
"permissions":["support:*:view"]
},
"user2":{
"id":["1002"],
"firstName":["Admin"],
"lastName":["One"],
"roles":["admin"],
"permissions":["support"]
}
}

xml配置的方式

配置attributeRepository为XmlPersonAttributeDao来在xml中针对单个用户的attribute进行配置。
配置如下:

1
2
3
<bean id="attributeRepository" class="org.jasig.services.persondir.support.xml.XmlPersonAttributeDao">
<property name="mappedXmlResource" value="classpath:users.xml" />
</bean>

database读取的方式

配置attributeRepository为NamedParameterJdbcPersonAttributeDao来在数据库按用户名进行attribute的配置加载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<bean id="attributeRepository" class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao">
<property name="dataSource" ref="datasource" />
<property name="sql" value="select email,name,username,password from cas_user where {0}" />
<!-- 组装sql用的查询条件属性 -->
<property name="queryAttributeMapping">
<map>
<!-- key必须是uername而且是小写否则会导致取不到用户的其它信息,value对应数据库用户名字段,系统会自己匹配 -->
<entry key="username" value="username" />
</map>
</property>
<property name="resultAttributeMapping">
<map>
<!-- key为对应的数据库字段名称,value为提供给客户端获取的属性名字,系统会自动填充值 -->
<entry key="username" value="username" />
<entry key="email" value="email" />
<entry key="name" value="name" />
<entry key="password" value="password" />
</map>
</property>
</bean>

密码加密方式配置

在实际开发中不能任由密码明文进行传输,一般来讲会进行加密。而cas可直接通过配置的方式进行加密方式的确定。
在配置的AcceptUsersAuthenticationHandler中继承了父类中的一个名为passwordEncoder的PasswordEncoder类型属性。通过给这个属性配置加密方式值就能实现。默认为PlainTextPasswordEncoder加密方式。
如配置md5的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
<bean id="primaryAuthenticationHandler" class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
<property name="passwordEncoder" ref="passwordEncoder" />
<property name="users">
<map>
<entry key="user1" value="123" />
<entry key="user2" value="123" />
</map>
</property>
</bean>
<bean id="passwordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">
<property name="characterEncoding" value="utf-8" />
<property name="encodingAlgorithm" value="MD5" />
</bean>

提示:配置了密码方式之后,所有配置文件或数据库中所存储的密码则应修改为密文。

重要发现

用户登录验证采用的AuthenticationHandler接口的子类实现,当我们自定义实现类完成验证时,不要妄想在通过HandlerResult的getPrincipal()获取到Principal然后给它设置attribute,因为cas在调用认证方式后会针对Principal的数据进行name的复制,所以即使你的认证方式进行了attribute的设置,在进行对象复制时也会丢掉。

详见:PolicyBasedAuthenticationManager的authenticateInternal方法中的逻辑。
PolicyBasedAuthenticationManager-authenticateInternal


观点仅代表自己,期待你的留言。

妙用Spring中的CompositeCacheManager

简介

Spring提供了@Cacheable,@CacheEvict,@CachePut等注解很方便有实现数据的缓存,而CompositeCacheManager主要用于集合多个CacheManager实例,在使用多种缓存容器时特别有用。

fallbackToNoOpCache属性的真实意义

经过查看Spring 4.1.9.RELEASE的源码,当CompositeCacheManager的fallbackToNoOpCache属性设置为true时,CompositeCacheManager会在已配置的cacheManagers末尾添加一个NoOpCacheManager。
当通过代码中指定的缓存容器(@Cacheable等注解设置的value)没有在cacheManagers中都找到时,则会进入到NoOpCacheManager中,此时就相当于禁用掉了缓存,而不抛出相应的异常。

网络上有朋友在说,当设置fallbackToNoOpCache属性设置为true时,则可以解决缓存容器没有准备好时自动禁用缓存的效果,经过查看Spring源码,并未实现。
不过,经测试以下方法可以实现通过配置禁用缓存。

通过配置禁用缓存

利用CompositeCacheManager + NoOpCacheManager还能解决当缓存容器没有准备好(缓存容器崩溃,网络不可用等)或者需要暂时去掉缓存的需求。

只需要将cacheManagers的list值的第一个元素设置为NoOpCacheManager就OK了。

spring.xml

1
2
3
4
5
6
7
8
9
<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
<property name="cacheManagers">
<list>
<bean class="org.springframework.cache.support.NoOpCacheManager" />
<ref bean="simpleCacheManager" />
</list>
</property>
<property name="fallbackToNoOpCache" value="true" />
</bean>


观点仅代表自己,期待你的留言。