`
hapinwater
  • 浏览: 70128 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java控制台的输出流重定向到多个输出流

阅读更多
使用System.setOut(java.io.PrintStream)和System.setErr(java.io.PrintStream)可以将控制台的输出流重定向到其它的单个输出流比如文件输出流中。



但若要将控制台的输出流重定向到多个输出流(包括控制台),这时,可以通过代理的方式来实现,在Java里一般有两种方式来实现代理,一是继承组合,一是动态代理




1.继承原先的PrintStream,并维持一个多个输出流的集合,当调用该类的任意方法时,调用输出流集合里输出流的方法,伪代码如下:

public ManyPrintStream extends PrintStream {

    private List<PrintStream> streamList = new ArrayList<PrintStream>();

    ................

    public void method(....) {

          for (int i = 0; i < streamList.size(); i++) {

                  PrintStream ps = streamList.get(i);

                  ps.method(....);

          }

   }

}


这种方法可行,但由于PrintStream的方法较多,实现比较麻烦。



2.一种更为简便的方法是动态代理,这种代理的原理在生成代理动态拦截对被代理类的方法的调用,我们可以使用java.lang.reflect.Proxy 类来生成动态代理(JDK动态代理),但是该代理只能基于接口(Interface)生成代理,而java.io.PrintStream是没有继承口的具体类,所以我们无法使用JDK动态代理来实现。但我们可以采用第三方提供的基于类生成代理的方法,CGLIB就是一种基于类(class)生成代理的开源库,用CGLIB生成代理要求被代理的类具有默认的无参构造函数(Constructor),而PrintStrem不符合些要求,但我们可能通过继承来达到些目的:

使用CGLIB需要的jar包:cglib-2.1_3.jar,asm-1.5.3.jar,asm-attrs-1.5.3.jar

                               或cglib-nodep-2.1_3.jar

                                二者选其一即可,两者都选会导致jar包冲突

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

//被代理的类

public class PseudoPrintStream extends PrintStream {
 
 public PseudoPrintStream() {
  super(new OutputStream() {//匿名OutputStream
   public void write(int b) throws IOException {
    //不用实现,只是为了比父类增加一个无参构造函数
   }
  });
 }
}

下面是代理的实现代码:

import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

//处理代理被调用的类

public class StreamHandler implements MethodInterceptor{

//输出流链表

private List<PrintStream> streamList = new ArrayList<PrintStream>();

//代理处理类生效,不允许再添加输出流标志  

private boolean validate = false;
 /**添加输出流*/

public synchronized void addStream(PrintStream ps) {
  if (validate) {
   throw new IllegalStateException();
  }
  streamList.add(ps);
 }
 /**代理生效*/
 public synchronized void validate() {
  validate = true;
 }
 /**查询代理是否生效*/
 public synchronized boolean getValidate() {
  return validate;
 }
 /**拦截被代理的方法并处理*/
 public Object intercept(Object object,
                   Method method,
                   Object[] args,
                   MethodProxy proxy)
 throws Throwable {
  //如果是PseudoPrintStream 的close方法被调用,则调用PseudoPrintStream 的close

 //方法关闭其匿名输出流
  if ("close".equals(method.getName())) {
   proxy.invokeSuper(object, args);
  }


   Object object;
   for (int i = 0; i < streamList.size(); i++) {
    PrintStream ps = streamList.get(i);
    object =  method.invoke(ps, args);
  }

  return obejct;//返回链表最后一个输出流调用的结果
 }
 

/**生成代理的方法*/
 public static PrintStream proxyFor(StreamHandler handler) {
  if (!handler.getValidate()) throw new RuntimeException("Handler not validate");
  Enhancer enhancer = new Enhancer();

 //被代理的ClassLoader
  enhancer.setClassLoader(PseudoPrintStream.class.getClassLoader());

 //被代理的Class
  enhancer.setSuperclass(PseudoPrintStream.class);

 //代理的方法被调用时,处理该调用的MethodInterceptor接口实现
  enhancer.setCallback(handler);

  //生成代理
  PrintStream proxy= (PseudoPrintStream) enhancer.create();

 //返回生成的代理
  return proxy;
 }
}



测试类

import java.io.FileOutputStream;
import java.io.PrintStream;

public class TestManyStream {

 public static void main(String[] args) throws Exception{
  //文件输出流1
  FileOutputStream fo1 = new FileOutputStream("E:\\file1.txt", true);

 //文件输出流2
  FileOutputStream fo2 = new FileOutputStream("E:\\file2.txt", true);

 //PrintStream1
  PrintStream ps1 = new PrintStream(fo1);

//PrintStream1
  PrintStream ps2 = new PrintStream(fo2);
  //代理被调用时的处理类
  StreamHandler sHandler = new StreamHandler();
  sHandler.addStream(ps1);
  sHandler.addStream(ps2);
  sHandler.addStream(System.out);
  sHandler.validate();
  PrintStream streamProxy = StreamHandler.proxyFor(sHandler);

 //系统输出流重定向到代理
  System.setOut(streamProxy);

  System.setErr(streamProxy);
  System.out.println("All stream print this out!");
  try {

      throw new Exception("An Exception Occured!");

  } catch (Exception e) {

     e.printStackTrace();

  }
  streamProxy.close();
  }
}

运行该测试类会发现控制台,E:\file1.txt,E:\file2.txt输出同样的内容

3.还可以通过继承OutputStream来实现,参照FilterOutputStream。
示例代码:不考虑一致性,错误处理策略等
public class MultiStream extends OutputStream {
	
	private List<OutputStream> streamList = new ArrayList<OutputStream>();
        //其他构造方法省略
	public MultiStream(OutputStream ...outputStreams) {
		streamList.addAll(Arrays.asList(outputStreams));
	}
	
	public void close() throws IOException {
		IOException e = null;
		//依次调用多个输出流的close方法
		for (OutputStream o:streamList) {
			try {
				o.close();
			} catch (IOException ioE) {
				e = ioE;
			}
		}
		
		if (e != null) {
			throw e;
		}
	}

	public void flush() throws IOException {
		...
	}


	public void write(byte[] b, int off, int len) throws IOException {
		IOException e = null;
		for (OutputStream o:streamList) {
			try {
				o.write(b, off, len);
			} catch (IOException ioE) {
				e = ioE;
			}
		}
		
		if (e != null) {
			throw e;
		}
	}

	public void write(byte[] b) throws IOException {
		...
	}

	public void write(int b) throws IOException {
		...
	}

}



测试类
import java.io.FileOutputStream;
import java.io.PrintStream;

public class TestMultiStream {
	
	public static void main(String[] args) throws Exception {
		
		//文件输出流1
		FileOutputStream fo1 = new FileOutputStream("E:\\file1.txt", true);
		//文件输出流2
		FileOutputStream fo2 = new FileOutputStream("E:\\file2.txt", true);
		//PrintStream
		MultiStream mStream = new MultiStream(fo1, fo2);
                //MultiStream mStream = new MultiStream(fo1, fo2,System.out);
		PrintStream ps = new PrintStream(mStream);
		//系统输出流重定向
		System.setOut(ps);  

		System.out.println("All stream print this out!");
		
		ps.close();
	}
} 

运行该测试类会发现控制台,E:\file1.txt,E:\file2.txt输出同样的内容
3
0
分享到:
评论

相关推荐

    Java经典编程300例(完整版+源码

    实例013 重定向输出流实现程序日志 实例014 自动类型转换与强制类型转换 实例015 加密可以这样简单(位运算) 实例016 用三元运算符判断奇数和偶数 .  实例017 不用乘法运算符实现2×16 实例018 实现两个变量的...

    Java经典编程源码基础例程300.zip

    实例013 重定向输出流实现程序日志 17 实例014 自动类型转换与强制类型转换 19 实例015 加密可以这样简单(位运算) 20 实例016 用三元运算符判断奇数和偶数 21 实例017 不用乘法运算符实现2×16 22 实例018 实现两...

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    实例174 捕获多个异常 222 第8章 枚举与泛型的应用 223 8.1 枚举使用的简介 224 实例175 查看枚举类型的定义 224 实例176 枚举类型的基本特性 225 实例177 增加枚举元素的信息 226 实例178 选择合适的枚举元素 227 ...

    h_JAVA 2应用编程150例.rar

    实例135 隐藏Java控制台 502 实例136 监控内存 505 实例137 获取本机的Mac地址 508 实例138 获取Java系统信息 509 实例139 控制Java与C++程序的通信 510 实例140 访问Windows注册表 514 第13章 Java配置与集成 523 ...

    java应用软件程序设计

    490 第12章 JNI编程 493 实例131 简单的JNI调用 494 实例132 调用Windows API 495 实例133 通过JNI运行OpenGL动画 496 实例134 JNI与COM之间的调用 500 实例135 隐藏Java控制台 502 实例136 ...

    Java开发技术大全 电子版

    14.4.9处理多个事件的例子463 14.5Swing组件的特性467 14.5.1Swing组件的优势467 14.5.2Swing组件的体系结构468 14.5.3使用Swing组件编写GUI的层次结构468 14.6顶层容器469 14.6.1框架类(JFrame)使用示例469...

    新版Android开发教程.rar

    � Google 提供了一套 Java 核心包 (J2SE 5,J2SE 6) 的有限子集,尚不承诺遵守 Java 任何 Java 规范 , 可能会造 成J ava 阵营的进一步分裂。 � 现有应用完善度不太够,需要的开发工作量较大。--------------------...

    深入java虚拟机光盘源码-Tools:Shell脚本等的分类集合,以帮助完成常见的IT任务

    这个工具可以节省很多时间,因为 1) 它可以只更新那些同时更改的文件的校验和(根据它们的“上次修改”时间戳),2)它可以恢复中断的校验和验证,而不是必须再次从第一个文件开始。 启动分离文件 启动与控制台分离...

    尚筹网项目中遇到的错误,问题.docx

    目录: 1.无法添加依赖 2.执行逆向生成操作的 ...41.Idea如何同时运行多个项目:点击edit configuration,然后如下图,报错即可,只要端口号不重复,就可以运行多个项目。 42. 重要错误parent的版本号对不上报错:Cann

    android logcat使用

    Android SDK下, 如何在程序中输出日志 以及如何查看日志. 闲话少说,直接进入正题 ...3.使用-f或者重定向(&gt;和&gt;&gt;)输出到文件 4.使用-s设置过滤器,得到想要的log。 当然,最重要的还是在程序中加入恰当的log

    网管教程 从入门到精通软件篇.txt

    网管教程 从入门到精通软件篇 ★一。★详细的xp修复控制台命令和用法!!! 放入xp(2000)的光盘,安装时候选R,修复! Windows XP(包括 Windows 2000)的控制台命令是在系统出现一些意外情况下的一种非常有效的...

    FireChat:与Firebase和Google翻译API集成的多语言聊天应用程序

    设置火力基地转到firebase.google.com,然后单击转到控制台 单击控制台中的添加项目并创建一个新项目 重定向后,从导航栏转到数据库,然后单击“实时数据库入门”。 在测试模式下启动 从导航栏转到项目概述,然后...

    eclipse 开发c/c++

    所有来自 make、编译器和链接程序的编译消息都被重定向到控制台窗口: 图 7. 带编译器输出的控制台窗口 编译成功之后, 您或许想要运行您的应用程序。所有用于运行和调试的选项都位于主 Eclipse 菜单的 Run 菜单...

    IIS6.0 IIS,互联网信息服务

    多个域名对应同个Web站点 你只需先将某个IP地址绑定到Web站点上,再在DNS服务器中,将所需域名全部映射向你的这个IP地址上,则你在浏览器中输入任何一个域名,都会直接得到所设置好的那个网站的内容。 搭建IIS服务器...

Global site tag (gtag.js) - Google Analytics