JMeter是一个纯Java项目,最早用于测试tomcat,版本2.13-SNAPSHO源码含1,083 个 Java文件,185行/文件,共计201,178行代码,其中core目录含61,972行Java代码,这是一个非常庞大而又历史悠久的项目。有时候,面对一个非常复杂的Java项目的源码时,我们可以先对里面涉及到23种涉及模式的类进行隔离分析,这样剩余代码也就不难理解了吧。
1 单例模式
JMeter采用了三种线程安全方式实现单例模式。包括在内部定义一个单例的静态私有Holder类、直接用synchronized修饰getInstance()和双重校验锁。
1.1 JMeterUtils -- 定义一个内部静态单例Holder类
用私有静态类LazyPatternCacheHolder作为单例的容器,保证在类加载时完成实例化
private
static
class
LazyPatternCacheHolder {
public
static
final
PatternCacheLRU INSTANCE =
new
PatternCacheLRU(
getPropDefault(
"oro.patterncache.size"
,
1000
),
// $NON-NLS-1$
new
Perl5Compiler());
}
|
// 获取单例
public
static
PatternCacheLRU getPatternCache() {
return
LazyPatternCacheHolder.INSTANCE;
}
|
1.2 SSLManager -- 用synchronized修饰getInstance()
/** Singleton instance of the manager */
//@GuardedBy("this")
private
static
SSLManager manager;
/**
* Resets the SSLManager so that we can create a new one with a new keystore
*/
public
static
synchronized
void
reset() {
SSLManager.manager =
null
;
}
/**
* Protected Constructor to remove the possibility of directly instantiating
* this object. Create the SSLContext, and wrap all the X509KeyManagers with
* our X509KeyManager so that we can choose our alias.
*/
protected
SSLManager() {
}
/**
* Static accessor for the SSLManager object. The SSLManager is a singleton.
*
* @return the singleton {@link SSLManager}
*/
public
static
final
synchronized
SSLManager getInstance() {
if
(
null
== SSLManager.manager) {
SSLManager.manager =
new
JsseSSLManager(
null
);
}
return
SSLManager.manager;
}
|
1.3 XPathUtil -- 用synchronized修饰getInstance()
//@GuardedBy("this")
private
static
DocumentBuilderFactory documentBuilderFactory;
/**
* Returns a suitable document builder factory.
* Caches the factory in case the next caller wants the same options.
*
* @param validate should the parser validate documents?
* @param whitespace should the parser eliminate whitespace in element content?
* @param namespace should the parser be namespace aware?
*
* @return javax.xml.parsers.DocumentBuilderFactory
*/
private
static
synchronized
DocumentBuilderFactory makeDocumentBuilderFactory(
boolean
validate,
boolean
whitespace,
boolean
namespace) {
if
(XPathUtil.documentBuilderFactory ==
null
|| documentBuilderFactory.isValidating() != validate
|| documentBuilderFactory.isNamespaceAware() != namespace
|| documentBuilderFactory.isIgnoringElementContentWhitespace() != whitespace) {
// configure the document builder factory
documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setValidating(validate);
documentBuilderFactory.setNamespaceAware(namespace);
documentBuilderFactory.setIgnoringElementContentWhitespace(whitespace);
}
return
XPathUtil.documentBuilderFactory;
}
|
1.4 ObjectFactory -- 定义一个内部静态单例Holder类
public
class
ObjectFactory {
private
static
class
ObjectFactoryHolder {
static
final
ObjectFactory FACTORY =
new
ObjectFactory();
}
|
1.5 ActionRouter -- 双重校验锁
private
static
final
Object LOCK =
new
Object();
private
static
volatile
ActionRouter router;
|
/**
* Gets the Instance attribute of the ActionRouter class
*
* @return The Instance value
*/
public
static
ActionRouter getInstance() {
if
(router ==
null
) {
synchronized
(LOCK) {
if
(router ==
null
) {
router =
new
ActionRouter();
router.populateCommandMap();
}
}
}
return
router;
}
|
2 工厂模式
2.1 ObjectFactory -- 简单工厂
public
class
ObjectFactory {
private
static
class
ObjectFactoryHolder {
static
final
ObjectFactory FACTORY =
new
ObjectFactory();
}
private
final
Parser PARSER;
protected
ObjectFactory() {
super
();
PARSER =
new
MonitorParser(
this
);
}
public
static
ObjectFactory getInstance() {
return
ObjectFactoryHolder.FACTORY;
}
public
Status parseBytes(
byte
[] bytes) {
return
PARSER.parseBytes(bytes);
}
public
Status parseString(String content) {
return
PARSER.parseString(content);
}
public
Status parseSampleResult(SampleResult result) {
return
PARSER.parseSampleResult(result);
}
public
Status createStatus() {
return
new
StatusImpl();
}
public
Connector createConnector() {
return
new
ConnectorImpl();
}
public
Jvm createJvm() {
return
new
JvmImpl();
}
public
Memory createMemory() {
return
new
MemoryImpl();
}
public
RequestInfo createRequestInfo() {
return
new
RequestInfoImpl();
}
public
ThreadInfo createThreadInfo() {
return
new
ThreadInfoImpl();
}
public
Worker createWorker() {
return
new
WorkerImpl();
}
public
Workers createWorkers() {
return
new
WorkersImpl();
}
protected
static
class
MonitorParser
extends
ParserImpl {
public
MonitorParser(ObjectFactory factory) {
super
(factory);
}
}
}
|
2.2 MenuFactory--工厂方法
......
public
static
JPopupMenu getDefaultControllerMenu() {
JPopupMenu pop =
new
JPopupMenu();
pop.add(MenuFactory.makeMenus(MENU_ADD_CONTROLLER,
JMeterUtils.getResString(
"add"
),
// $NON-NLS-1$
ActionNames.ADD));
pop.add(makeMenus(MENU_PARENT_CONTROLLER,
JMeterUtils.getResString(
"insert_parent"
),
// $NON-NLS-1$
ActionNames.ADD_PARENT));
pop.add(makeMenus(MENU_PARENT_CONTROLLER,
JMeterUtils.getResString(
"change_parent"
),
// $NON-NLS-1$
ActionNames.CHANGE_PARENT));
MenuFactory.addEditMenu(pop,
true
);
MenuFactory.addFileMenu(pop);
return
pop;
}
public
static
JPopupMenu getDefaultSamplerMenu() {
JPopupMenu pop =
new
JPopupMenu();
pop.add(MenuFactory.makeMenus(MENU_ADD_SAMPLER,
JMeterUtils.getResString(
"add"
),
// $NON-NLS-1$
ActionNames.ADD));
pop.add(makeMenus(MENU_PARENT_SAMPLER,
JMeterUtils.getResString(
"insert_parent"
),
// $NON-NLS-1$
ActionNames.ADD_PARENT));
MenuFactory.addEditMenu(pop,
true
);
MenuFactory.addFileMenu(pop);
return
pop;
}
public
static
JPopupMenu getDefaultConfigElementMenu() {
JPopupMenu pop =
new
JPopupMenu();
MenuFactory.addEditMenu(pop,
true
);
MenuFactory.addFileMenu(pop);
return
pop;
}
public
static
JPopupMenu getDefaultVisualizerMenu() {
JPopupMenu pop =
new
JPopupMenu();
MenuFactory.addEditMenu(pop,
true
);
MenuFactory.addFileMenu(pop);
return
pop;
}
public
static
JPopupMenu getDefaultTimerMenu() {
JPopupMenu pop =
new
JPopupMenu();
MenuFactory.addEditMenu(pop,
true
);
MenuFactory.addFileMenu(pop);
return
pop;
}
......
|
1.3 HTTPSamplerFactory -- 工厂方法
/**
* Create a new instance of the required sampler type
*
* @param alias HTTP_SAMPLER or HTTP_SAMPLER_APACHE or IMPL_HTTP_CLIENT3_1 or IMPL_HTTP_CLIENT4
* @return the appropriate sampler
* @throws UnsupportedOperationException if alias is not recognised
*/
public
static
HTTPSamplerBase newInstance(String alias) {
if
(alias ==
null
|| alias.length() ==
0
) {
return
new
HTTPSamplerProxy();
}
if
(alias.equals(HTTP_SAMPLER_JAVA) || alias.equals(IMPL_JAVA)) {
return
new
HTTPSamplerProxy(IMPL_JAVA);
}
if
(alias.equals(HTTP_SAMPLER_APACHE) || alias.equals(IMPL_HTTP_CLIENT3_1)) {
return
new
HTTPSamplerProxy(IMPL_HTTP_CLIENT3_1);
}
if
(alias.equals(IMPL_HTTP_CLIENT4)) {
return
new
HTTPSamplerProxy(IMPL_HTTP_CLIENT4);
}
throw
new
IllegalArgumentException(
"Unknown sampler type: '"
+ alias+
"'"
);
}
public
static
HTTPAbstractImpl getImplementation(String impl, HTTPSamplerBase base){
if
(HTTPSamplerBase.PROTOCOL_FILE.equals(base.getProtocol())) {
return
new
HTTPFileImpl(base);
}
if
(JOrphanUtils.isBlank(impl)){
impl = DEFAULT_CLASSNAME;
}
if
(IMPL_JAVA.equals(impl) || HTTP_SAMPLER_JAVA.equals(impl)) {
return
new
HTTPJavaImpl(base);
}
else
if
(IMPL_HTTP_CLIENT3_1.equals(impl) || HTTP_SAMPLER_APACHE.equals(impl)) {
return
new
HTTPHC3Impl(base);
}
else
if
(IMPL_HTTP_CLIENT4.equals(impl)) {
return
new
HTTPHC4Impl(base);
}
else
{
throw
new
IllegalArgumentException(
"Unknown implementation type: '"
+impl+
"'"
);
}
}
|
3 组合模式
定义:将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性。
3.1 TestElement -- 测试元件接口
public
interface
TestElement
extends
Cloneable
// 一般组合模式的接口都有实现对象克隆接口
|
测试计划、逻辑控制器、样本、线程组等等实现了测试元件接口。单个测试计划由若干测试元件对象组合而成。例如逻辑控制器对象可以相互嵌套,样本和逻辑控制器可以相互嵌套。这种设计模式,决定了测试计划内存数据结构是一颗树。
3.3 JMeterGUIComponent
JMeter的GUI组件显然也是一个组合模式。这些GUI组件主要是用于测试元件的配置,测试元件类一般与GUI组件类,一一对应。