JAVA 與 MyCat(3) 關於getResourceAsStream(fileName)的檔案位置
通過mycat來學習java了^^。
前一篇文章介紹了單例模式建立了MycatServer單例項,下面一起看看MycatServer初始化方法的內容:
還是挑重點,一行行看看:private MycatServer() { // 讀取檔案配置 this.config = new MycatConfig(); // 定時執行緒池,單執行緒執行緒池 scheduler = Executors.newSingleThreadScheduledExecutor(); // SQL記錄器 this.sqlRecorder = new SQLRecorder(config.getSystem().getSqlRecordCount()); /** * 是否線上,MyCat manager中有命令控制 | offline | Change MyCat status to OFF | | * online | Change MyCat status to ON | */ this.isOnline = new AtomicBoolean(true); // 快取服務初始化 cacheService = new CacheService(); // 路由計算初始化 routerService = new RouteService(cacheService); // load datanode active index from properties dnIndexProperties = loadDnIndexProps(); try { // SQL解析器 sqlInterceptor = (SQLInterceptor) Class.forName(config.getSystem().getSqlInterceptor()).newInstance(); } catch (Exception e) { throw new RuntimeException(e); } // catlet載入器 catletClassLoader = new DynaClassLoader(SystemConfig.getHomePath() + File.separator + "catlet", config.getSystem().getCatletClassCheckSeconds()); // 記錄啟動時間 this.startupTime = TimeUtil.currentTimeMillis(); if (isUseZkSwitch()) { String path = ZKUtils.getZKBasePath() + "lock/dnindex.lock"; dnindexLock = new InterProcessMutex(ZKUtils.getConnection(), path); } }
this.config = new MycatConfig();
初始化了MycatConfig的一個例項,Ctrl+左擊看看初始化都執行了什麼:
看看第一行:public MycatConfig() { //讀取schema.xml,rule.xml和server.xml ConfigInitializer confInit = new ConfigInitializer(true); this.system = confInit.getSystem(); this.users = confInit.getUsers(); this.schemas = confInit.getSchemas(); this.dataHosts = confInit.getDataHosts(); this.dataNodes = confInit.getDataNodes(); for (PhysicalDBPool dbPool : dataHosts.values()) { dbPool.setSchemas(getDataNodeSchemasOfDataHost(dbPool.getHostName())); } this.firewall = confInit.getFirewall(); this.cluster = confInit.getCluster(); //初始化重載入配置時間 this.reloadTime = TimeUtil.currentTimeMillis(); this.rollbackTime = -1L; this.status = RELOAD; //配置載入鎖 this.lock = new ReentrantLock(); }
ConfigInitializer confInit = new ConfigInitializer(true);
看看ConfigInitializer的初始化方法都有什麼:
看看第一行:public ConfigInitializer(boolean loadDataHost) { // 讀取rule.xml和schema.xml SchemaLoader schemaLoader = new XMLSchemaLoader(); // 讀取server.xml XMLConfigLoader configLoader = new XMLConfigLoader(schemaLoader); schemaLoader = null; // 載入配置 this.system = configLoader.getSystemConfig(); this.users = configLoader.getUserConfigs(); this.schemas = configLoader.getSchemaConfigs(); // 是否重新載入DataHost和對應的DataNode if (loadDataHost) { this.dataHosts = initDataHosts(configLoader); this.dataNodes = initDataNodes(configLoader); } // 許可權管理 this.firewall = configLoader.getFirewallConfig(); this.cluster = initCobarCluster(configLoader); // 不同型別的全域性序列處理器的配置載入 if (system.getSequnceHandlerType() == SystemConfig.SEQUENCEHANDLER_MYSQLDB) { IncrSequenceMySQLHandler.getInstance().load(); } if (system.getSequnceHandlerType() == SystemConfig.SEQUENCEHANDLER_LOCAL_TIME) { IncrSequenceTimeHandler.getInstance().load(); } if (system.getSequnceHandlerType() == SystemConfig.SEQUENCEHANDLER_ZK_DISTRIBUTED) { DistributedSequenceHandler.getInstance(system).load(); } if (system.getSequnceHandlerType() == SystemConfig.SEQUENCEHANDLER_ZK_GLOBAL_INCREMENT) { IncrSequenceZKHandler.getInstance().load(); } /** * 配置檔案初始化, 自檢 */ // 檢查user與schema配置對應以及schema配置不為空 this.selfChecking0(); }
SchemaLoader schemaLoader = new XMLSchemaLoader();
看看XMLSchemaLoader的初始化方法有什麼:
public XMLSchemaLoader() {
this(null, null);
}
public XMLSchemaLoader(String schemaFile, String ruleFile) {
// 先讀取rule.xml
XMLRuleLoader ruleLoader = new XMLRuleLoader(ruleFile);
// 將tableRules拿出,用於這裡載入Schema做rule有效判斷,以及之後的分片路由計算
this.tableRules = ruleLoader.getTableRules();
// 釋放ruleLoader
ruleLoader = null;
this.dataHosts = new HashMap<String, DataHostConfig>();
this.dataNodes = new HashMap<String, DataNodeConfig>();
this.schemas = new HashMap<String, SchemaConfig>();
// 讀取載入schema配置
this.load(DEFAULT_DTD, schemaFile == null ? DEFAULT_XML : schemaFile);
}
看看第一行:
XMLRuleLoader ruleLoader = new XMLRuleLoader(ruleFile);
看看XMLRuleLoader(ruleFile)方法裡有什麼,這裡ruleFile為null:
public XMLRuleLoader(String ruleFile) {
// this.rules = new HashSet<RuleConfig>();
// rule名 -> rule
this.tableRules = new HashMap<String, TableRuleConfig>();
// function名 -> 具體分片演算法
this.functions = new HashMap<String, AbstractPartitionAlgorithm>();
// 預設為:/rule.dtd和/rule.xml
load(DEFAULT_DTD, ruleFile == null ? DEFAULT_XML : ruleFile);
}
第一行初始化了一個HashMap型別全域性變數,存放分片規則;第二行也初始化了一個HashMap型別全域性變數,存放分片函式;看看第三行:
load(DEFAULT_DTD, ruleFile == null ? DEFAULT_XML : ruleFile);
該方法的第一個引數值為:/rule.dtd,第二個引數值為:/rule.xml,看看該方法的內容:
private void load(String dtdFile, String xmlFile) {
InputStream dtd = null;
InputStream xml = null;
try {
dtd = XMLRuleLoader.class.getResourceAsStream(dtdFile);
xml = XMLRuleLoader.class.getResourceAsStream(xmlFile);
// 讀取出語意樹
Element root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();
// 載入Function
loadFunctions(root);
// 載入TableRule
loadTableRules(root);
} catch (ConfigException e) {
throw e;
} catch (Exception e) {
throw new ConfigException(e);
} finally {
if (dtd != null) {
try {
dtd.close();
} catch (IOException e) {
}
}
if (xml != null) {
try {
xml.close();
} catch (IOException e) {
}
}
}
}
這裡有一個java知識點:
XMLRuleLoader.class.getResourceAsStream(dtdFile);
這裡的dtdFile值為/rule.dtd,那麼這個檔案在OS的檔案系統哪呢?
關於Class.getResourceAsStream(fileName)的檔案位置
1.首先fileName都是相對於classpath來說的,也就是fileName一定在classpath目錄下的某個地方。
2.如果fileName是以'/'開頭的,就是指classpath目錄。有的文章說是類的根目錄,對但不全對,因為可以是classpath中任一目錄。
3.如果fileName不是以'/'開頭的,則是指類所在目錄。
例如有如下目錄結構:
Test.class.getResourceAsStream('conf.xml');就是指/u01/mycat/lib/io/mycat/conf.xml檔案。
Test.class.getResourceAsStream('/io/mycat/conf.xml');也是指/u01/mycat/lib/io/mycat/conf.xml檔案。
Test.class.getResourceAsStream('/conf.xml');就是指/u01/mycat/lib/conf.xml檔案。
一般我們都將配置檔案放在conf目錄下,而不放在lib目錄下,那麼只要在classpath裡再多指定一個conf目錄即可,JVM會搜尋所有classpath指定的目錄。
我們知道,classpath可以是目錄,也可以是jar包,JVM會自動將jar包解壓成目錄,所以conf.xml是在目錄裡,還是在jar包裡都可以。
說個題外話,classpath指定jar包時,需要一個一個指定,即使所有的jar包都在同一個目錄,也不能使用萬用字元,如果有一大堆jar包需要指定比較煩,其實有個引數可以使用:-Djava.ext.dirs,例如所有jar包在/u01/mycat/lib/目錄下,可以如下指定:java -Djava.ext.dirs=/u01/mycat/lib MycatStartup,而不用再一個個地指定jar包。
除了class.getResourceAsStream(fileName)外,還可以使用ClassLoader.getResourceAsStream(fileName)方法來獲取資源。實際上Class.getResourceAsStream(fileName)方法就是委託給ClassLoader.getResourceAsStream(fileName)方法來執行的,兩者基本上沒有區別,只是在路徑指定上稍有區別。ClassLoader.getResourceAsStream(fileName)中的fileName不能以'/'開頭,檔案相對路徑永遠從classpath開始,例如:
ClassLoader.getResourceAsStream('conf.xml');就是指/u01/mycat/lib/conf.xml檔案。
ClassLoader.getResourceAsStream('io/mycat/conf.xml');就是指/u01/mycat/lib/io/mycat/conf.xml檔案。
獲取資源流的方法還有許多,總結如下:this.getClass().getResourceAsStream("fileName");
this.getClass().getClassLoader().getResourceAsStream("fileName");
ClassLoader.getSystemResourceAsStream("fileName");
Thread.currentThread().getContextClassLoader().getResourceAsStream("fileName");
為什麼為這麼多方法呢,其實是跟類載入有關。ClassLoader是個抽象類,比如mycat裡的MyDynaClassLoader類就是ClassLoader的子類,這些子類可能會重寫與資源載入有關的方法,所以各子類的getResourceAsStream("fileName")方法獲取資源的方式可能會有所不同。不同的場合使用不同的資源獲取方法,例如上面的四個方法:
this.getClass().getResourceAsStream("fileName"); 與this.getClass().getClassLoader().getResourceAsStream("fileName"); 兩種方式都是使用本類的載入器獲取資源的,可以認為是同一種方式;
ClassLoader.getSystemResourceAsStream("fileName");這種是系統預設的類載入器,最好別用。例項上上面的兩種在本類沒有特殊指定載入器時,使用的就是這個系統預設載入器,所以使用上面各種方式就行了,不要使用這種方式。
Thread.currentThread().getContextClassLoader().getResourceAsStream("fileName");使用當前執行緒的類載入器,這種方式多用在伺服器框架上,如Tomcat、WebSphere等。程式在Tomcat裡執行時,要獲取資源,肯定是從Tomcat某個目錄找的,而不是從執行Tomcat的java程序的classpath裡的某個目錄找。