三、使用Java的代理机制进行日志输出
前面的代码虽然有了一些改进,但是仍然有一定局限性,因为要使用代理类,就必须要实现固定的接口,有没有一种通用的机制,不管是不是实现这个接口,都可以实现日志信息的输出呢?
Java提供的InvocationHandler接口可以实现这种功能,首先编写一个日志信息的代理类,这个代理类实现了接口InvocationHandler,然后和前面一个实例类似,编写一个接口,并实现这个接口,在实现类中编写具体的考勤审核代码,最后针对接口编写测试类,查看测试结果。具体步骤如下:
(1)编写一个日志信息的代理类LogProxy,这个代理类实现了接口InvocationHandler,可以对任何接口实现日志信息的输出。LogProxy.java的示例代码如下:
//******* LogProxy.java************** package com.gc.action; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.apache.log4j.Level; import org.apache.log4j.Logger; //代理类实现了接口InvocationHandler public class LogProxy implements InvocationHandler { private Logger logger = Logger.getLogger(this.getClass().getName()); private Object delegate; //绑定代理对象 public Object bind(Object delegate) { this.delegate = delegate; return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass(). getInterfaces(), this); } //针对接口编程 public Object invoke(Object proxy, Method method, Object[ ] args) throws Throwable { Object result = null; try { //在方法调用前后进行日志输出 logger.log(Level.INFO, args[0] + " 开始审核数据...."); result = method.invoke(delegate, args); logger.log(Level.INFO, args[0] + " 审核数据结束...."); } catch (Exception e){ logger.log(Level.INFO, e.toString()); } return result; } }
(2)使用com.gc.impl包中的接口TimeBookInterface。TimeBookInterface.java的示例代码如下:
//******* TimeBookInterface.java**************
package com.gc.impl;
import org.apache.log4j.Level;
//针对接口编程
public interface TimeBookInterface {
public void doAuditing(String name);
}
(3)使用com.gc.action包中的类TimeBook,doAuditing()方法中编写具体的考勤审核代码。TimeBook.java的示例代码如下:
//******* TimeBook.java**************
package com.gc.action;
import com.gc.impl.TimeBookInterface;
public class TimeBook implements TimeBookInterface {
public void doAuditing(String name) {
//审核数据的相关程序
……
}
}
(4)修改测试代码TestHelloWorld,使用日志代理类LogProxy 实现日志的输出。TestHelloWorld.java的示例代码如下:
//******* TestHelloWorld.java************** package com.gc.test; import com.gc.action.TimeBook; import com.gc.action.TimeBookProxy; import com.gc.impl.TimeBookInterface; import com.gc.action.LogProxy; public class TestHelloWorld { public static void main(String[ ] args) { //实现了对日志类的重用 LogProxy logProxy = new LogProxy(); TimeBookInterface timeBookProxy = (TimeBookInterface)logProxy.bind(new TimeBook()); timeBookProxy.doAuditing("张三"); } }
(5)运行测试程序,可以得到通过LogProxy类输出日志信息。
这种方式,对于其他的类也同样适用,这样就真正地实现了业务逻辑和输出日志信息代码的分离。
对这3种实现方式进行总结
第一种方式,需要在每个类里都增加对输出日志信息的代码;第二种方式,虽然实现了业务逻辑与输出日志信息代码的分离,但还是必须依赖于固定的接口;第三种方式,真正实现了对输出日志信息代码的重用,并且不依赖于固定的接口实现。
从第三种方式中,也可以看出Java动态代理机制的强大,而Spring的AOP正是建立在Java动态代理的基础上的,当读者通过上面的示例一步一步地对Java的动态代理机制有了一定的了解后,接下来就可以逐渐地进入AOP了。