java.util.ServiceLoader使用.doc_第1页
java.util.ServiceLoader使用.doc_第2页
java.util.ServiceLoader使用.doc_第3页
java.util.ServiceLoader使用.doc_第4页
java.util.ServiceLoader使用.doc_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

java.util.ServiceLoader使用今天在看Hadoop源代的时候发现,在FileSystem中用到了java.util.ServiceLoader这个类来从配置文件中加载子类或者接口的实现类。以前从来没有使用过这个类,进去大概看了一下具体的实现。主要是从META-INF/services这个目录下的配置文件加载给定接口或者基类的实现,ServiceLoader会根据给定的类的full name来在META-INF/services下面找对应的文件,在这个文件中定义了所有这个类的子类或者接口的实现类,返回一个实例。下面以一个具体的例子来说明一下ServiceLoader的具体使用,类似Hadoop FileSystem中的实现。首先定义一个接口,具体如下:public interface IService public String sayHello(); public String getScheme();该接口有两个子类,分别为HDFSService和LocalService:public class HDFSService implements IService Override public String sayHello() return Hello HDFS!; Override public String getScheme() return hdfs; public class LocalService implements IService Override public String sayHello() return Hello Local!; Override public String getScheme() return local; 需要在META-INF/services下以IService这个类的全名来新建立一个文件,文件中的内容为两个实现类的全名,如下:org.hadoop.java.HDFSServiceorg.hadoop.java.LocalService所有的实现和配置都已经完成,下面写一个测试类来看一下结果:Public class ServiceLoaderTest public static void main(String args) /need to define related class full name in /META-INF/services/. ServiceLoader serviceLoader = ServiceLoader .load(IService.class); for (IService service : serviceLoader) System.out.println(service.getScheme()+=+service.sayHello(); 具体的输出来如下:hdfs=Hello HDFS!local=Hello Local!可以看到ServiceLoader可以根据IService把定义的两个实现类找出来,返回一个ServiceLoader的实现,而ServiceLoader实现了Iterable接口,所以可以通过ServiceLoader来遍历所有在配置文件中定义的类的实例。HadoopFileSystem就是通过这个机制来根据不同文件的scheme来返回不同的FileSystem。FileSystem中的相关实例如下:private static void loadFileSystems() synchronized (FileSystem.class) if (!FILE_SYSTEMS_LOADED) ServiceLoader serviceLoader = ServiceLoader.load(FileSystem.class); for (FileSystem fs : serviceLoader) SERVICE_FILE_SYSTEMS.put(fs.getScheme(), fs.getClass(); FILE_SYSTEMS_LOADED = true; FileSystem对应的配置如下:org.apache.hadoop.fs.LocalFileSystemorg.apache.hadoop.fs.viewfs.ViewFileSystemorg.apache.hadoop.fs.s3.S3FileSystemorg.apache.hadoop.fs.s3native.NativeS3FileSystemorg.apache.hadoop.fs.kfs.KosmosFileSystemorg.apache.hadoop.fs.ftp.FTPFileSystemorg.apache.hadoop.fs.HarFileSystem通过之前的测试类输出对应的scheme和class如下:file=class org.apache.hadoop.fs.LocalFileSystemviewfs=class org.apache.hadoop.fs.viewfs.ViewFileSystems3=class org.apache.hadoop.fs.s3.S3FileSystems3n=class org.apache.hadoop.fs.s3native.NativeS3FileSystemkfs=class org.apache.hadoop.fs.kfs.KosmosFileSystemftp=class org.apache.hadoop.fs.ftp.FTPFileSystemhar=class org.apache.hadoop.fs.HarFileSystemhdfs=class org.apache.hadoop.hdfs.DistributedFileSystemhftp=class org.apache.hadoop.hdfs.HftpFileSystemhsftp=class org.apache.hadoop.hdfs.HsftpFileSystemwebhdfs=class org.apache.hadoop.hdfs.web.WebHdfsFileSystem可以看到FileSystem会把所有的FileSystem的实现都以scheme和class来cache,之后就从这个cache中取相应的值。因此,以后可以通过ServiceLoader来实现一些类似的功能。而不用依赖像Spring这样的第三方框架。消灭成堆的分支语句之类责任链模式摘要分支语句是所有编程语言的基本元素,比如Java语言中的if else和switch语句,它们提供一种能力允许程序根据一些条件动态地选择执行某些代码块。这种动态性给程序带来了很多的灵活性!正因为if else如此方便如此灵活,很多代码中它都会被滥用,就像下面这样让人崩溃的、嵌套的、成堆的分支语句:?12345678910111213141516171819202122232425262728293031if (context.equals(tutorial-room) if (pageNumber = 1) if (input.equals(2) / go next/ output step-2 prompt else / warning xxx/ output step-1 prompt else if (pageNumber = 2) if (state = State.QUITING) if (input.equals(y) / xxx else / restore else if (input.equals(22) / put chess else if (input.equals(q) / quiting? else / unknow instruct else . else if (context.equals(newbie-room) . else if (context.equals(easy-room) . else if (context.equals(normal-room) . else .本文会讨论一些程序设计的方法,把诸如上述的混乱代码重构成更清晰更优雅的代码。注:文中的代码皆为Java代码片段,仅使用标准JDK的类库。问题说上述代码结构让人崩溃,我们得有理有据。首先,它的可读性不好。这里说的可读性不好并非指变量名命名不规范、花括号风格不一致、对齐不统一等问题,而是指代码是否方便理解。比如:?12345if (cash = price)这一条件。在代码规模不是很大的时候,这样的隐含条件影响可能不大,但如果有很多个else条件并且里面同时还嵌套着很深的分支结构,当你看到最深层的代码时,你是否还确信自己能清楚地记得所有的前提条件?其次,它的维护性不好。比如在上面代码中加入会员机制,会员在购买商品时有积分,那相应的积分模块调用代码要同时出现在block B和block C中。如果之后会员又分了多个等级,那这段代码很快就成了庞然大物,任何的修改都会牵一发而动全身!查表法根据分支语句的特点,它可用于根据不同的输入返回特定的输出。比如如此理解面向对象一文中要根据系统名字,输出不同的提示语:?12345678910String osName = System.getProperty();if (osName.equals(SunOS) System.out.println(This is a UNIX box and therefore good.); else if (osName.equals(Linux) System.out.println(This is a Linux box and good as well.); else if (osName.equals(Windows NT) System.out.println(This is a Windows box and therefore bad.); else System.out.println(Unknow box.);我们暂且成这类分支为“数据型分支”。它犹如数学中的映射(Mapping),每一组特定的输入数据对应一组唯一的输出数据。因此,在输入数据比较简单时(比如第一个例子,输入数据只有系统名字一项),可以使用 java.util.Map 或 java.util.Properties 把映射关系持久化到配置文件中,程序启动时再加载到内存:?1234567891011121314151617import java.io.FileInputStream;import java.util.Properties;public class Main public static void main(String args) throws Exception Properties options = new Properties();options.load(new FileInputStream(perties);String osName = System.getProperty();String prompt = options.getProperty(osName);if (prompt = null) prompt = Unknow box.;System.out.println(prompt);其中配置文件信息如下:?123SunOS=This is a UNIX box and therefore good.Linux=This is a Linux box and good as well.Windows NT=This is a Windows box and therefore bad.使用这种方法,能很方便地支持新的系统或修改现有系统的提示语,且无须修改程序。不过开发中真实的输入项远不止一个字符串,正如jxqlove?同学之前在 /code/snippet_111708_17599 中提的:根据交易类型、支付方式等多个条件返回一个字符串。处理这种Key有多个元素构成的情况,解决方案的思想和单元素是一致的,只是把元数据移到了数据库中:?1234567891011create table metadata (trade_type varchar(16), - 交易类型,比如收入、支出等payment varchar(16), - 支付类型,比如现金、信用卡等code varchar(4) - 最终的返回值);insert into metadata values (income, cash, 001);insert into metadata values (income, credit card, 002);insert into metadata values (income, alipay, 003);insert into metadata values (expense, cash, 101);insert into metadata values (expense, credit card, 102);insert into metadata values (expense, alipay, 103);在应用程序这一端则需要动态地构造查询语句:?1234567891011public String queryStatement(Properties options) StringBuilder query = new StringBuilder(select code from metadata);Enumeration names = pertyNames();for (int i = 0; names.hasMoreElements(); i+) String key = names.nextElement().toString();String value = options.getProperty(key);query.append(i = 0? where : and );query.append(String.format(%s = %s, key, value);return query.toString();根据实际的情况,代码可能更复杂一些,比如value的内容需要转义等。这样设计的系统会非常灵活,比如输入端新增了一个选项,只需给metadata添加新的字段,并根据所有的合法值插入新的记录或更新现有记录,而代码无须修改。这种持久化到数据库的方法适用于一对一的无规律映射,即不存在或者只有少量的映射存在多组key对应同一个value的情况。它和数据的规模无关,比如一个字典程序的数据同样适用这种方式,数据量虽然很大但并不稀疏。与之相对的是稀疏的数据,比如有一项值域范围是1,100,其中1到50应对的值是无规律,从51到100的值全部是一个固定的常量(比如0)。这时候有一半的存储空间是浪费的,真心不如在代码里用 if (value 50) 来判断。下文会提供另一种方法处理这类问题。类责任链模式上面介绍的查表法把元数据从逻辑代码中剥离出来,避免因元数据(Metadata)变化导致修改程序。但从某种意义上来说,程序本该如此:程序本身只是逻辑的集合;元数据(辅助程序行为,诸如语言包文件)集中在配置文件里;待处理的数据来自外部输入(用户手工录入、本地文件、数据库等)。因此本节讨论分支语句更常用的方式:选择执行某段代码。?123456789if (optionA) if (optionB) doSomething1(); else doSomething2(); else doSomething3();类似上面的代码,根据不同的输入选项或命令行参数等调用不同的方法来完成某些操作,而不是单纯的返回数据。因此,这些选项是为了确定现在这个request是谁的职责,而这正是“责任链模式”要解决的问题!本节的标题为“类责任链模式”,表示我的解决方案是类似“责任链模式”,并不严格和它保持一致,但核心思想是一致的:使多个对象都有机会处理请求。因此,每个RequestHandler都需提供一个接口判断自己能否处理当前请求;如果能处理,则Client调用另一个执行的接口:?1234public interface Handler public boolean accept(Properties options);public void execute();于是,上面的分支结构对应三个独立的Handler类:?12345678910111213141516171819202122232425262728293031public class RequestHandler1 implements Handler public boolean accept(Properties options) return options.getProperty(A) != null& options.getProperty(B) != null;public void execute() doSomething1();public class RequestHandler2 implements Handler public boolean accept(Properties options) return options.getProperty(A) != null& options.getProperty(B) = null;public void execute() doSomething2();public class RequestHandler3 implements Handler public boolean accept(Properties options) return options.getProperty(A) = null;public void execute() doSomething3();接下来还需要一个额外的管理类负责这些类的实例化的请求的分发:?1234567891

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论