JAVA 与 MyCat(3) 关于getResourceAsStream(fileName)的文件位置

9 篇文章 0 订阅

通过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);


看看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不是以'/'开头的,则是指类所在目录。

例如有如下目录结构:


假如classpath为/u01/mycat/lib/,那么:

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里的某个目录找。




















































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值