通过mycat来学习java了^^。
接前一篇:http://blog.csdn.net/john_chang11/article/details/78679867
前一篇文章介绍了单例模式创建了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);
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里的某个目录找。