功能代码的多余枝节
当我们书写执行一个功能的函数时,经常需要在其中写入与功能不是直接相关但很有必要的代码,如日志记录,信息发送,安全和事务支持等,以下代码是一个用户注册类的代码:
/** *//**
* 用於用戶注冊的服務類
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:15:25
*/
public class RegisterService{
/** *//**
* 注冊一個用戶
* @param name 用戶名
* @param pswd 用戶密碼
* @param email 用戶郵件地址
*/
public void register(String name,String pswd,String email){
Logger.log("將注冊一個新用戶"+name);
// 真正的,应该由本函數擔負的處理
System.out.println("存儲用戶信息到數據庫");
MailSender.send(email, "歡迎"+name+"注冊為本系統的用戶");
}
}
Logger类代码
/** *//**
* 模擬記錄器
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:17:56
*/
public class Logger{
/** *//**
* 模擬記錄信息到文件中
* @param str
*/
public static void log(String str){
System.out.println(getCurrTime()+"INFO:"+str);
}
/** *//**
* 取得當前時間
* @return
*/
private static String getCurrTime() {
Date date = new Date();
Format formatter = new SimpleDateFormat("HH时mm分ss秒");
return formatter.format(date);
}
}
MailSender类代码
/** *//**
* 模擬郵件發送器
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:23:31
*/
public class MailSender{
/** *//**
* 模擬發送郵件
* @param title
* @param msg
*/
public static void send(String email,String concept){
System.out.println("向"+email+"發送郵件 內容為:"+concept+"的郵件");
}
}
枝节性代码给功能性代码带来的麻烦
诸如日志记录,信息发送,安全和事务支持等枝节代码虽然是必要的,但它会带来以下麻烦:
1.枝节性代码游离在功能性代码之外,它们不是函数的目的,这对OO是一种破坏。
2.枝节性代码会造成功能性代码对其它类的依赖,加深类之间的耦合度,而这是OO系统所竭力避免的。
3.枝节性代码带来的耦合度会造成功能性代码移植困难,可重用性降低。
4.从法理上说,枝节性代码应该“监视”着功能性代码,然后采取行动;而不是由功能性代码“通知”枝节性代码采取行动。这好比吟游诗人应该是主动记述骑士的功绩而不是骑士主动要求诗人记录自己的功绩的。
如何两种代码分离开来
毫无疑问,枝节性代码和功能性代码(主干性代码)需要分离开来才能降低耦合程度,符合现代OO系统的要求,而java提供的动态代理机制可以帮助我们实现这一点。
动态代理机制主要的类是java.lang.reflect.Proxy,它从一诞生就受到了重视,并在RMI,EJB和AOP中都得到广泛的应用,其重要程度唯有反射能与之相比。
Proxy代理模式
在讲述动态代理之前我们可以回顾一下代理模式,它的定义是这样的:代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实。代理一般会实现它所表示的实际对象的接口。代理可以访问实际对象,但是延迟实现实际对象的部分功能,实际对象实现系统的实际功能,代理对象对客户隐藏了实际对象。客户不知道它是与代理打交道还是与实际对象打交道。
如果我们使用代理模式,把枝节性代码放入代理类中,这样主干性代码保持在真实的类中,这样不就能有效降低耦合度吗?这种通过在耦合紧密的类之间引入一个中间类是降低类之间的耦合度的常见做法。
具体来说就是把枝节性代码放入代理类中,它们由代理类负责调用,而真实类只负责主干的核心业务,它也由代理类调用,它并不知道枝节性代码的存在和作用,因为这本不是它的任务。对外来说,代理类隐藏在接口之后,客户并不清楚也不需要清楚具体的调用过程。通过这样的处理,主干与枝节之间的交叉解开了,外界的调用也没有复杂化,这就有效降低系统各部分间的耦合度。
下面让我们先看看代码
消除了枝节代码的注册类
/** *//**
* 用於用戶注冊的服務類
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:15:25
*/
public class RegisterService implements IService{
/** *//**
* 注冊一個用戶
* @param name 用戶名
* @param pswd 用戶密碼
* @param email 用戶郵件地址
*/
public void register(String name,String pswd,String email){
// 真正的,該由本函數擔負的處理
System.out.println("存儲用戶信息到數據庫");
}
}
注册类的代理类,枝节性代码都被转移到了这里
/** *//**
* 注冊服務代理類
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:45:10
*/
public class RegisterServiceProxy implements InvocationHandler {
// 代理對象
Object obj;
// 構造函數,傳入代理對象
public RegisterServiceProxy(Object o) {
obj = o;
}
/** *//**
* 调用被代理对象的将要被执行的方法,我们可以在调用之前進行日誌記錄,之后执行郵件發送
*/
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
Object result = null;
try {
// 進行日誌記錄
String name=(String)args[0];
Logger.log("將注冊一個新用戶"+name);
// 調用Object的方法
result = m.invoke(obj, args);
// 执行郵件發送
String email=(String)args[2];
MailSender.send(email, "歡迎"+name+"注冊為本系統的用戶");
} catch (InvocationTargetException e) {
} catch (Exception eBj) {
} finally {
// Do something after the method is called
}
return result;
}
}
代理类RegisterServiceProxy的解释
该代理类的内部属性为Object类,实际使用时通过该类的构造函数RegisterServiceProxy(Object obj)对其赋值;此外,在该类还实现了invoke方法,该方法中的
method.invoke(obj,args);
其实就是调用被代理对象的将要被执行的方法,这是通过反射实现的,方法参数obj是实际的被代理对象,args为执行被代理对象相应操作所需的参数。通过动态代理类,我们可以在调用之前或者之后执行一些相关操作。
如何生成一个代理类的实例
代理类的实例需要特殊的方式生成,代码如下:
public static IService genereteService(){
return (IService)Proxy.newProxyInstance(
IService.class.getClassLoader(),
new Class[]{IService.class},
new RegisterServiceProxy(new RegisterService()));
}
Proxy即为java中的动态代理类,其方法Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,其中loader是类加载器,interfaces是被代理的真实类的接口,h是具体的代理类实例。
所谓动态代理是这样一种class:它是在运行时生成的类,在生成它时你必须提供一组接口给它,然后该类就宣称它实现了这些接口。你当然可以把该类的实例当作这些接口中的任何一个实现类来用。当然啦,这个动态代理类其实就是一个代理,它不会做作实质性的工作,而是在生成它的实例时你必须提供一个真实的类的实例,由它接管实际的工作。
工厂方法的作用
对于代理类生成的细节,客户(需要使用RegisterService的程序员)是没有兴趣也没有必要知道的,我们可以让它隐藏在一个工厂方法中,对外返回一个接口,这样在调用时用户就不知道他是与代理打交道还是与实际对象打交道了。使用RegisterService类时示例代码如下:
IService service=RegisterServiceFactory.genereteService();
service.register("sitinspring","123456","junglesong@gmail.com");
执行完的结果和前面的代码的是一样的。
动态代理在AOP中的应用
Spring的AOP支持可以被用于从系统核心逻辑中分离交叉业务(cross-business)如日志,事务管理和安全等,使用AOP,你可以用各种功能层来覆盖核心业务层,这些功能层可以灵活的应用到你的系统中,甚至核心业务层都不知道它们的存在,这是一个强大的概念。
AOP(aspect-oriented programming)的核心就是动态代理,掌握它对于理解AOP尤为重要,犹如反射对理解IoC一样。
代码下载:
http://www.blogjava.net/Files/sitinspring/DynamicProxySample20080527235441.rar