实例分析Java的代理机制

(0)准备工作

本文使用log4j日志工具实现日志输出,添加log4j的步骤如下:

(1)下载地址:http://logging.apache.org/log4j/1.2/download.html,下载 【log4j-1.2.17.zip

(2)将 【log4j-1.2.17.jar】 文件,复制到 lib 下

(3)然后右击添加到资源文件。


(一)通用的日志输出方法

通用的日志输出方法即是在每个业务逻辑方法中,都编写记录日志的代码

  • (1)在每个业务逻辑方法中添加日志记录代码
  • (2)测试功能
//****************TimeBook.java*******************
//在业务逻辑中使用log4j作为日志输出的工具
import org.apache.log4j.Level;	
import org.apache.log4j.Logger;

public class TimeBook {
	private Logger logger = Logger.getLogger(this.getClass().getName());

	//doAuditing()方法用来处理实际业务中的考勤审核
	//参数name,用来传入是谁执行了类TimeBook中的doAuditing()方法
	public void doAuditing(String name) {
		logger.log(Level.INFO, name + " 开始审核数据...");
		// 审核数据的相关程序
		logger.log(Level.INFO, name + " 审核数据结束...");
	}
}
//****************TestHelloWorld.java******************
import com.gc.action.TimeBook;

public class TestHelloWorld {
	public static void main(String[] args){
		TimeBook timeBook = new TimeBook();
		timeBook.doAuditing("张三");
	}
}

结果
[ INFO ] 2020-04-19 21:42:44 [main] com.gc.action.TimeBook  - 张三 开始审核数据...
[ INFO ] 2020-04-19 21:42:44 [main] com.gc.action.TimeBook  - 张三 审核数据结束...

使用这种方法,假如程序中其他的代码都需要日志输出功能,则都需要添加上述代码,造成很大耦合。我们可以通过面向接口编程来解决这个问题。


(二)面向接口编程的日志输出方法

  • (1)将doAuditing()方法提取出来成为接口 TimeBookInterface
  • (2)通过TimeBook实现该接口,并编写具体的审核业务逻辑
  • (3)设置一个代理类进行日志输出
  • (4)测试
//*****************TimeBookInterface.java*******************
public interface TimeBookInterface {
	public void doAuditing(String name);
}
//****************TimeBookInterface.java**********************
public class TimeBook implements TimeBookInterface{
	//doAuditing()方法用来处理实际业务中的考勤审核
	//参数name,用来传入是谁执行了类TimeBook中的doAuditing()方法
	public void doAuditing(String name) {
		// 审核数据的相关程序
	}
}
//****************TimeBookProxy.java********************
//代理程序
public class TimeBookProxy {
	private Logger logger = Logger.getLogger(this.getClass().getName());
	private TimeBookInterface timeBookInterface;       //定义一个接口对象

	public TimeBookProxy(TimeBookInterface timeBookInterface) {
		this.timeBookInterface = timeBookInterface;    //使接口指向子对象
	}
	//实际业务处理
	public void doAuditing(String name) {
		logger.log(Level.INFO, name + " 开始审核数据...");
		timeBookInterface.doAuditing(name);            //调用子对象的方法
		logger.log(Level.INFO, name + " 审核数据结束...");
	}
}
//**************TestHelloWorld.java******************
public class TestHelloWorld {
	public static void main(String[] args){
		TimeBookProxy timeBookProxy = new TimeBookProxy(new TimeBook());
		timeBookProxy.doAuditing("张三");
	}
}

结果
[ INFO ] 2020-04-19 23:31:47 [main] com.gc.action.TimeBookProxy  - 张三 开始审核数据...
[ INFO ] 2020-04-19 23:31:47 [main] com.gc.action.TimeBookProxy  - 张三 审核数据结束...

该方法使用代理类实现日志的输出,实现了日志代码的重用性。但是仍具有局限性,因为要使用代理类就必须实现固定的接口。而 Java代理机制具有通用性,不管是不是实现这个接口,都可以实现日志的输出。


(三)使用Java代理机制的日志输出方法

  • (1)编写一个接口TimeBookInterface
  • (2)用TimeBook实现TimeBookInterface接口,并实现具体审核代码
  • (3)编写一个代理类LogProxy实现 InvocationHandler
  • (4)测试
//*****************TimeBookInterface.java*******************
public interface TimeBookInterface {
	public void doAuditing(String name);
}
//****************TimeBookInterface.java**********************
public class TimeBook implements TimeBookInterface{
	//doAuditing()方法用来处理实际业务中的考勤审核
	//参数name,用来传入是谁执行了类TimeBook中的doAuditing()方法
	public void doAuditing(String name) {
		// 审核数据的相关程序
	}
}
//****************LogProxy.java**********************
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);
	}
	//针对接口编程
	@Override
	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;
	}
}
//***************TestHelloWorld.java********************
public class TestHelloWorld {
	public static void main(String[] args){
		LogProxy logProxy = new LogProxy();
		TimeBookInterface timeBookProxy = (TimeBookInterface)logProxy.bind(new TimeBook());
		timeBookProxy.doAuditing("张三");
	}
}

结果
[ INFO ] 2020-04-20 00:09:48 [main] com.gc.action.LogProxy  - 张三 开始审核数据...
张三
[ INFO ] 2020-04-20 00:09:48 [main] com.gc.action.LogProxy  - 张三 审核数据结束...

输出结果的第二行,张三是通过代理类 LogProxy.java 中的 result = method.invoke(delegate, args); 去调用 doAufiting() 函数。通过Java代理机制,真正的实现了业务逻辑和输出日志信息代码的分离。


总结

  • 通用的方式:需要在每个类里都增加对输出日志信息的代码。
  • 面向接口编程:实现了业务逻辑与输出日志信息代码的分类,但必须依赖固定的接口。
  • Java代理机制:真正实现了日志信息代码的重用,且不依赖于固定接口。
扫描二维码关注公众号,回复: 11037422 查看本文章
发布了71 篇原创文章 · 获赞 38 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/u012839256/article/details/105624597