从一个输出日志的实例分析JAVA的代理机制
一、通用的日志输出方法 :需要在每个类里都增加对输出日志信息的代码
二、通过面向接口编程实现日志的输出(JAVA的静态代理):虽然实现了业务逻辑与输出日志信息的分离,但必须依赖固定的接口;
三、使用JAVA的代理机制进行日志输出(JAVA的动态代理):真正实现了对输入日志信息代码的重用,并且不依赖于固定的接口实现
通用的日志输出方法:
在每一个业务逻辑方法里编写记录日志的代码
1 //*****TimeBook.java******* 2 import org.apache.log4j.Level; 3 import org.apache.log4j.Logger; 4 public class TimeBook{ 5 private Logger logger = Logger.getLogger(this.getClass().getName()); 6 //审核数据的相关程序 7 public void doAuditing(String name){ 8 logger.log(Level.INFO,name+" 开始审核数据..."); 9 //审核数据的相关程序 10 ... 11 logger.log(Level.INFO,name+" 审核数据结束..."); 12 13 } 14 }
如上示例代码,日志信息添加到具体的业务逻辑中,如果程序中其他代码需要日志输出的功能,那么每个程序都要添加和上面类似的代码。
这样在程序中,就会存在很多类似的日志输出代码,造成很大的耦合。我们通过面向接口编程改进这个问题。
通过面向接口编程实现日志的输出:
1.首先把doAuditing()方法提取出来成为接口,然后通过一个实体类实现这个方法,在这个方法里编写具体的业务逻辑代码
2.通过一个代理类来进行日志输出
1 //******TimeBookProxy.java***** 2 package com.gc.action; 3 import org.apache.log4j.Level; 4 import org.apache.log4j.Logger; 5 6 import com.gc.impl.TimeBookInterface; 7 8 public class TimeBookProxy{ 9 private Logger logger = Logger.getLogger(this.getClass().getName); 10 private TimeBookInterface timeBookInterface; 11 //在该类中针对前面的接口编程,而不针对具体的类 12 public TimeBookProxy(TimeBookInterface timeBookInterface) 13 { 14 this.timeBookInterface = timeBookInterface; 15 } 16 //实际业务处理 17 public void doAuditing(String name){ 18 logger.log(Level.INFO,name+" 开始审核数据..."); 19 timeBookInterface.doAuditing(name); 20 logger.log(Level.INFO,name+" 审核数据结束..."); 21 } 22 }
3.编写测试程序
1 //****TestHelloWorld.java****** 2 package com.gc.test; 3 import com.gc.action.TimeBook; 4 import com.gc.action.TimeBookProxy; 5 public class TestHelloWorld{ 6 public static void main(String[] args){ 7 TimeBookProxy timeBookProxy = new TimeBookProxy(new TimeBook()); 8 timeBookProxy.doAuditing("victory"); 9 } 10 }
和前面一个日志做对比,可以看到在这个示例中,具体的业务逻辑代码和日志信息代码分离开了,只要实现了接口TimeBookInterface的类,都可以通过
代理类TimeBookProxy实现日志信息的输出,而不用再每个类里面都写日志信息输出的代码,从而实现了日志信息的代码重用。
使用JAVA的代理机制进行日志输出:
上面代码仍然有一些局限性,因为要使用代理类,就必须要实现固定的接口,有没有一种通用的机制,不管是不是实现这个接口,都可以实现日志信息的输出?
JAVA提供的InvocationHandler接口可以实现这种功能
1.编写一个日志信息的代理类,这个代理类实现接口InvocationHandler
1 //****LogProxy.java**** 2 package com.gc.action; 3 4 import java.lang.reflect.InovationHandler; 5 import java.lang.reflect.Method; 6 import java.lang.reflect.Proxy; 7 8 import org.apache.log4j.Level; 9 import org.apache.log4j.Logger; 10 //代理类实现了接口InvocationHandler 11 public class LogProxy implement InvocationHandler{ 12 private Logger logger = Logger.getLogger(this.getClass().getName()); 13 private Object obj; 14 //绑定代理对象 15 public Object bind(Object obj){ 16 this.obj = obj; 17 return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInstances(),this); 18 } 19 //针对接口编程 20 public Object invoke(Object proxy,Method method,Object[] args) throw Throwable{ 21 Object result = null; 22 try{ 23 //在方法调用前后进行日志输出 24 logger.log(Level.INFO,args[0]+"开始审核数据..."); 25 result = method.invoke(obj,args); 26 logger.log(Level.INFO,args[0]+"审核数据结束..."); 27 28 } 29 catch(Exception e){ 30 logger.log(Level.INFO,e,toString()); 31 } 32 return result; 33 } 34 }
2.编写一个接口,并实现这个接口,在实现类中编写具体的考勤审核代码
参看以上代码 接口和实现接口类
3.编写测试类,查看测试结果
1 //*******TestHelloWorld.java***** 2 3 package com.gc.test; 4 import com.gc.action.TimeBook; 5 import com.gc.action.TimeBookProxy; 6 import com.gc.impl.TimeBookInterface; 7 import com.gc.action.LogProxy; 8 public class TestHelloWorld{ 9 public static void mian(String[] args) 10 { 11 //实现了对日志类的重用 12 LogProxy logProxy = new LogProxy(); 13 TimeBookInterface timeBookProxy = (TimeBookInterface)logProxy.bind(new TimeBook()); 14 timeBookProxy.doAuditing("victory"); 15 } 16 }