这个包的类主要用于spring框架的异常处理和一些核心的助手类(与框架具体部分无关的)。
这个包中主要应用到了简单工厂模式,用于判断jdk版本,根据jdk版本不同提供不同的集合类、当前方法栈信息等。我们来看看是如何判断当前用户的jdk版本的:
package
org.springframework.core;
public
class
JdkVersion {
public
static
final
int
JAVA_13
=
0
;
public
static
final
int
JAVA_14
=
1
;
public
static
final
int
JAVA_15
=
2
;
private
static
String javaVersion;
private
static
int
majorJavaVersion
=
JAVA_13;
static
{ javaVersion
=
System.getProperty(
"
java.version
"
);
//
should look like "1.4.1_02"
if
(javaVersion.indexOf(
"
1.4.
"
)
!=
-
1
) { majorJavaVersion
=
JAVA_14; }
else
if
(javaVersion.indexOf(
"
1.5.
"
)
!=
-
1
) { majorJavaVersion
=
JAVA_15; }
//
else leave as 1.3 default
}
/**
* Return the full Java version string, as returned by * <code>System.getProperty("java.version")</code>.
*/
public
static
String getJavaVersion() {
return
javaVersion; }
/**
* Get the major version code. This means we can do things like * <code>if (getMajorJavaVersion() < JAVA_14)</code>. *
@return
a code comparable to the JAVA_XX codes in this class *
@see
#JAVA_13 *
@see
#JAVA_14 *
@see
#JAVA_15
*/
public
static
int
getMajorJavaVersion() {
return
majorJavaVersion; } }
直接获取系统的java.version属性来进行jdk版本的判断。而CollectionFactory依据这个类来创建不同的集合类型,如果是jdk1.4就优先使用jdk1.4的集合框架,再次选择Commons Collections,最后才不得已就使用jdk1.3的集合框架,这里比较有趣的是判断Commons Collections的方法就是尝试Class.forName一个Commons集合框架中的对象,如果成功,当然证明classpath有commons-collections.jar包:
static
{
//
Check whether JDK 1.4+ collections and/or
//
Commons Collections 3.x are available.
if
(JdkVersion.getMajorJavaVersion()
>=
JdkVersion.JAVA_14) { logger.info(
"
JDK 1.4+ collections available
"
); }
try
{ Class.forName(COMMONS_COLLECTIONS_CLASS_NAME); commonsCollections3xAvailable
=
true
; logger.info(
"
Commons Collections 3.x available
"
); }
catch
(ClassNotFoundException ex) { commonsCollections3xAvailable
=
false
; } }
然后就是一系列的getXXXIfPossible()方法用以获取最优版本的集合类型,比如getLinkedHashMapIfPossible():
public
static
Map createLinkedMapIfPossible(
int
initialCapacity) {
if
(JdkVersion.getMajorJavaVersion()
>=
JdkVersion.JAVA_14) { logger.debug(
"
Creating [java.util.LinkedHashMap]
"
);
return
Jdk14CollectionFactory.createLinkedHashMap(initialCapacity); }
else
if
(commonsCollections3xAvailable) { logger.debug(
"
Creating [org.apache.commons.collections.map.LinkedMap]
"
);
return
CommonsCollectionFactory.createLinkedMap(initialCapacity); }
else
{ logger.debug(
"
Falling back to [java.util.HashMap] for linked map
"
);
return
new
HashMap(initialCapacity); } }
其中的Jdk14CollectionFactory 和CommonsCollectionFactory 也都是工厂类。可以看到,一个优秀的通用框架对于版本的兼容性非常重视。
这个包中另外一个需要注意的就是用于spring AOP功能实现的辅助类——ControlFlow。ControlFlow按照rod johnson的说法就是用于获取当前调用的方法栈的具体信息。ControlFlow是一个接口,拥有3个方法用于判断当前方法栈的位置:
public
interface
ControlFlow {
/**
查找当前方法调用是否则在某类中 *
@param
clazz the clazz to look for
*/
boolean
under(Class clazz);
/**
* 查找当前方法调用是否则在某类的某个方法中 * according to the current stack trace. *
@param
clazz the clazz to look for *
@param
methodName the name of the method to look for
*/
boolean
under(Class clazz, String methodName);
/**
* 当前栈帧是否包含传入的记号 *
@param
token the token to look for
*/
boolean
underToken(String token); }
然后根据jdk版本的不同采用不同的方式实现这个接口:Jdk14ControlFlow和Jdk13ControlFlow。这是典型的
策略模式的应用。需要注意的是,这两个具体类的是放在工厂类ControlFlowFactory中作为内部类实现的:
public
abstract
class
ControlFlowFactory {
static
class
Jdk13ControlFlow
implements
ControlFlow {
static
class
Jdk14ControlFlow
implements
ControlFlow { }
在这里,我们可以学到的东西就如何去判断当前方法栈的信息?jdk1.4之前只能通过对StackTrace的字符串进行分析,而jdk1.4引入了java.lang.StackTraceElement用于获取当前方法调用所处的栈帧的信息,看看spring的使用方法,相当简单:
static
class
Jdk14ControlFlow
implements
ControlFlow {
private
StackTraceElement[] stack;
public
Jdk14ControlFlow() {
this
.stack
=
new
Throwable().getStackTrace(); }
/**
* Searches for class name match in a StackTraceElement.
*/
public
boolean
under(Class clazz) { Assert.notNull(clazz,
"
Class must not be null
"
); String className
=
clazz.getName();
for
(
int
i
=
0
; i
<
stack.length; i
++
) {
if
(
this
.stack[i].getClassName().equals(className)) {
return
true
; } }
return
false
; }
/**
* Searches for class name match plus method name match * in a StackTraceElement.
*/
public
boolean
under(Class clazz, String methodName) { Assert.notNull(clazz,
"
Class must not be null
"
); Assert.notNull(methodName,
"
Method name must not be null
"
); String className
=
clazz.getName();
for
(
int
i
=
0
; i
<
this
.stack.length; i
++
) {
if
(
this
.stack[i].getClassName().equals(className)
&&
this
.stack[i].getMethodName().equals(methodName)) {
return
true
; } }
return
false
; }
/**
* Leave it up to the caller to decide what matches. * Caller must understand stack trace format, so there's less abstraction.
*/
public
boolean
underToken(String token) {
if
(token
==
null
) {
return
false
; } StringWriter sw
=
new
StringWriter();
new
Throwable().printStackTrace(
new
PrintWriter(sw)); String stackTrace
=
sw.toString();
return
stackTrace.indexOf(token)
!=
-
1
; } }
获取当前栈帧的信息,对于一般的java开发者没有什么意义,对于AOP的实现和框架开发者可能有比较重要的作用,我还未研读spring的aop部分,不敢妄言,留待以后解答,如果您已经研读过这部分代码,不吝赐教。 这个包另外的一个特点就是将java的反射API演示了一遍,特别是Constant.java(用于提取某个类public static final定义的常量)和ReflectiveVisitorHelper (反射助手类),对于学习java反射技术也有不小的帮助。
文章转自庄周梦蝶 ,原文发布时间5.17