首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

java反照与代理

2012-07-04 
java反射与代理java反射与代理?一. 关于数据库.?当今的数据处理大致可以分成两大类:联机事务处理OLTP(on-l

java反射与代理

java反射与代理?
一. 关于数据库.?
当今的数据处理大致可以分成两大类:联机事务处理OLTP(on-line transaction processing)、联机分析处理OLAP(On-Line Analytical Processing)。OLTP是传统的关系型数据库的主要应用,主要是基本的、日常的事务处理,例如银行交易。OLAP是数据仓库系统的主要应用,支持复杂的分析操作,侧重决策支持,并且提供直观易懂的查询结果。下表列出了OLTP与OLAP之间的比较。?

?????????????????????????????????????? OLTP????????????????????????????????????????????????????????????? OLAP?
用户?????????????????????????????? 操作人员,低层管理人员?????????????????????????????? 决策人员,高级管理人员?
功能????????????????????????????? 日常操作处理???????????????????????????????????????????????? 分析决策?
DB 设计??????????????????????? 面向应用??????????????????????????????????????????????????????? 面向主题?
数据?????????????????????????????? 当前的, 最新的细节的, 二维的分立的???????? 历史的, 聚集的, 多维的集成的, 统一的?
存取????????????????????????????? 读/写数十条记录?????????????????????????????????????????? 读上百万条记录?
工作单位?????????????????????? 简单的事务???????????????????????????????????????????????????? 复杂的查询?
用户数???????????????????????? 上千个???????????????????????????????????????????????????????????? 上百个?
DB 大小?????????????????????? 100MB-GB????????????????????????????????????????????????????? 100GB-TB?

二. Java中的类反射:?
反射就是把Java类中的各种成分映射成相应的java类.?
Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。?

1.检测类:?

1.1 reflection的工作机制?

考虑下面这个简单的例子,让我们看看 reflection 是如何工作的。?

import java.lang.reflect.*;?
public class DumpMethods {?
public static void main(String args[]) {?
?? try {?
??? Class c = Class.forName(args[0]);?
??? Method m[] = c.getDeclaredMethods();?
??? for (int i = 0; i < m.length; i++)?
??? System.out.println(m[i].toString());?
?? }?
?? catch (Throwable e) {?
??? System.err.println(e);?
?? }?
}?
}?

按如下语句执行:?

java DumpMethods java.util.Stack?

它的结果输出为:?

public java.lang.Object java.util.Stack.push(java.lang.Object)?

public synchronized java.lang.Object java.util.Stack.pop()?

public synchronized java.lang.Object java.util.Stack.peek()?

public boolean java.util.Stack.empty()?

public synchronized int java.util.Stack.search(java.lang.Object)?

这样就列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。?

这个程序使用 Class.forName 载入指定的类,然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。?

1.2 Java类反射中的主要方法?

对于以下三类组件中的任何一类来说 -- 构造函数、字段和方法 -- java.lang.Class 提供四种独立的反射调用,以不同的方式来获得信息。调用都遵循一种标准格式。以下是用于查找构造函数的一组反射调用:?

Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数,?

Constructor[] getConstructors() -- 获得类的所有公共构造函数?

Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关)?

Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)?

获得字段信息的Class 反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用了字段名:?

Field getField(String name) -- 获得命名的公共字段?

Field[] getFields() -- 获得类的所有公共字段?

Field getDeclaredField(String name) -- 获得类声明的命名的字段?

Field[] getDeclaredFields() -- 获得类声明的所有字段?

用于获得方法信息函数:?

Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法?

Method[] getMethods() -- 获得类的所有公共方法?

Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法?

Method[] getDeclaredMethods() -- 获得类声明的所有方法?


1.3开始使用 Reflection:?

用于 reflection 的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。?

下面就是获得一个 Class 对象的方法之一:?

Class c = Class.forName("java.lang.String");?

这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:?

Class c = int.class;?

或者?

Class c = Integer.TYPE;?

它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。?

第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。?

一旦取得这个信息,就可以进行第三步了——使用 reflection API 来操作这些信息,如下面这段代码:?

Class c = Class.forName("java.lang.String");?

Method m[] = c.getDeclaredMethods();?

System.out.println(m[0].toString());?

它将以文本方式打印出 String 中定义的第一个方法的原型。?

2.处理对象:?

如果要作一个开发工具像debugger之类的,你必须能发现filed values,以下是三个步骤:?

a.创建一个Class对象?
b.通过getField 创建一个Field对象?
c.调用Field.getXXX(Object)方法(XXX是Int,Float等,如果是对象就省略;Object是指实例).?

例如:?
import java.lang.reflect.*;?
import java.awt.*;?

class SampleGet {??
public static void main(String[] args) {?
?? Rectangle r = new Rectangle(100, 325);?
?? printHeight(r);??
}?

static void printHeight(Rectangle r) {?
?? Field heightField;?
?? Integer heightValue;?
?? Class c = r.getClass();?
?? try {?
??? heightField = c.getField("height");?
??? heightValue = (Integer) heightField.get(r);?
??? System.out.println("Height: " + heightValue.toString());?
?? } catch (NoSuchFieldException e) {?
??? System.out.println(e);?
?? } catch (SecurityException e) {?
??? System.out.println(e);?
?? } catch (IllegalAccessException e) {?
??? System.out.println(e);?
?? }?
}?
}?

顾名思义,反射 (reflection) 机制就像是在吴承恩所写的西游记中所提及的「照妖镜」,可以让类别或对象 (object) 在执行时期「现出原形」。我们可以利用反射机制来深入了解某类(class) 的构造函数 (constructor)、方法 (method)、字段 (field),甚至可以改变字段的值、呼叫方法、建立新的对象。有了反射机制,程序员即使对所想使用的类别所知不多,也能照样写程序。反射机制能够用来呼叫方法,这正是反射机制能够取代函数指针的原因。?

以 Java 来说,java.lang.reflect.Method (以下简称 Method) 类别是用来表示某类别的某方法。我们可以透过 java.lang.Class (以下简称 Class) 类别的许多方法来取得 Method 对象。Method 类别提供 invoke() 方法,透过 invoke(),此 Method 对象所表示的方法可以被呼叫,所有的参数则是被组织成一个数组,以方便传入 invoke()。?

举个例子,下面是一个名为 Invoke 的程序,它会将命令列的 Java 类别名称和要呼叫的方法名称作为参数。为了简单起见,我假定此方法是静态的,且没有参数:?

import java.lang.reflect.*;?
class Invoke {????????
public static void main(String[] args ) {???????????????
?? try {???????????????????????
??? Class c = Class.forName( args[0] );???????????????????????
??? Method m = c.getMethod( args[1], new Class [] { } );???????????????????????
??? Object ret = m.invoke( null, null );???????????????????????
??? System.out.println(args[0] + "." + args[1] +"() = " + ret );???????????????
?? } catch ( ClassNotFoundException ex ) {???????????????????????
??? System.out.println("找不到此类别");???????????????
?? } catch (NoSuchMethodException ex ) {???????????????????????
??? System.out.println("此方法不存在");???????????????
?? } catch (IllegalAccessException ex ) {???????????????????????
??? System.out.println("没有权限调用此方法");???????????????
?? } catch (InvocationTargetException ex ) {???????????????????????
??? System.out.println("调用此方法时发生下列例外:\n" + ex.getTargetException() );???????????????
?? }????????
}?
}?
我们可以执行 Invoke 来取得系统的时间:?

java Invoke java.lang.System CurrentTimeMillis执行的结果如下所示:?

java.lang.System.currentTimeMillis() = 1049551169474我们的第一步就是用名称去寻找指定的 Class。我们用类别名称 (命令列的第一个参数) 去呼叫 forName() 方法,然后用方法名称 (命令列的第二个参数) 去取得方法。getMethod() 方法有两个参数:第一个是方法名称 (命令列的第二个参数),第二个是 Class 对象的数组,这个阵例指明了方法的 signature (任何方法都可能会被多载,所以必须指定 signature 来分辨。) 因为我们的简单程序只呼叫没有参数的方法,我们建立一个 Class 对象的匿名空数组。如果我们想要呼叫有参数的方法,我们可以传递一个类别数组,数组的内容是各个类别的型态,依顺序排列。?

一旦我们有了 Method 对象,就呼叫它的 invoke() 方法,这会造成我们的目标方法被调用,并且将结果以 Object 对象传回。如果要对此对象做其它额外的事,你必须将它转型为更精确的型态。?

invoke() 方法的第一个参数就是我们想要呼叫目标方法的对象,如果该方法是静态的,就没有对象,所以我们把第一个参数设为 null,这就是我们范例中的情形。第二个参数是要传给目标方法作为参数的对象数组,它们的型态要符合呼叫 getMethod() 方法中所指定的型态。因为我们呼叫的方法没有参数,所以我们传递 null 作为 invoke() 的第二个参数。?


import?? java.lang.reflect.Constructor;?
import?? java.lang.reflect.Field;?
import?? java.lang.reflect.Method;?
import?? java.lang.reflect.Modifier;?
import?? java.util.HashMap;?

public??? class?? TestRef??? {?
????? public??? static??? void?? main(String[] args)?? throws?? Exception?? {?
???????? TestRef testRef?? =??? new?? TestRef();?
???????? Class clazz?? =?? TestRef. class ;?
???????? System.out.println( " getPackage() =?? "??? +?? clazz.getPackage().getName());?
???????? //?? getModifiers()的返回值可以包含类的种类信息。比如是否为public,abstract,static?
???????? int?? mod?? =?? clazz.getModifiers();?
???????? System.out.println( " Modifier.isAbstract(mod) =?? " + Modifier.isAbstract(mod));?
???????? System.out.println( " getName() =?? " + clazz.getName());?
???????? System.out.println( " getSuperclass() =?? " + clazz.getSuperclass().getName());?
???????? System.out.println( " getInterfaces() =?? " + clazz.getInterfaces()); // 实现了哪些Interface?
???????? System.out.println( " clazz.getDeclaredClasses() =?? " + clazz.getDeclaredClasses()); // 包含哪些内部类?
???????? System.out.println( " getDeclaringClass() =?? " + clazz.getDeclaringClass());?
???????? // 如果clazz是inner class 那么返回其outer class?
?????????
???????? System.out.println( " ---------- " );?
???????? Constructor[] constructor?? =?? clazz.getDeclaredConstructors(); // 返回一组构造函数 Constructor[]?
??????????? if?? (constructor?? !=??? null )?? {?
????????????? for?? ( int?? i?? =??? 0 ; i?? <?? constructor.length; i ++ )??? {?
???????????????? System.out.println(constructor[i].getName());?
???????????? }?
???????? }?
????????
???????? System.out.println( " ---------- " );?
???????? Method[] method?? =?? clazz.getDeclaredMethods();?? //?? Method[]?
??????????? if?? (method?? !=??? null )?? {?
????????????? for?? ( int?? i?? =??? 0 ; i?? <?? method.length; i ++ )??? {?
???????????????? System.out.println(method[i].getName());?
???????????? }?
???????? }?
????????
???????? System.out.println( " ---------- " );?
???????? Field[] field?? =?? clazz.getDeclaredFields();?? //?? Field[]?
??????????? if?? (field?? !=??? null )?? {?
????????????? for?? ( int?? i?? =??? 0 ; i?? <?? field.length; i ++ )??? {?
???????????????? System.out.println(field[i].getName());?
???????????????? System.out.println(field[i].getType().getName());?
???????????????? System.out.println(field[i].get(testRef));?
???????????? }?
???????? }?
????????
???????? //?? 动态生成instance(无参数)?
???????? Class clz?? =?? Class.forName( " reflection.TestRef " );?
???????? Object obj?? =?? clz.newInstance();?
???????? System.out.println(((TestRef)obj).getStr());?
????????
???????? //?? 动态生成instance(有参数)?
????????? Class[] params?? =??? new?? Class[]?? {String. class ,?? int . class ,?? double . class } ;?
???????? Constructor construct?? =?? clz.getConstructor(params);?
???????? //?? JDK1.5的情况下可以直接用{"haha",999,100.01}作为参数?????????
????????? Object obj2?? =?? construct.newInstance(new Object[]{"haha",new Integer( 999 ),?? new?? Double( 100.01 )} );?
???????? System.out.println(((TestRef)obj2).getStr());?
????????
???????? //?? 动态调用method(public method)?
????????? Class[] params2?? =??? new?? Class[]?? {String. class } ;?
???????? Method methods?? =?? clz.getMethod( " setStr " , params2);?
???????? methods.invoke(testRef,?? new?? Object[]?? { " invoke method " } );?
???????? System.out.println(testRef.getStr());?
????????
???????? //?? 动态改变field内容(public field)?
???????? Field fields?? =?? clz.getField( " str " );?
???????? fields.set(testRef,?? " set field's value " );?
???????? System.out.println(testRef.getStr());?
????????
???? }?

????? public?? TestRef()?? {?
???????? System.out.println( " --- complete TestRef() --- " );?
???? }?
????
???? public?? TestRef(String str,?? int?? i,?? double?? d)?? {?
????????? this .str?? =?? str;?
????????? this .i?? =?? i;?
????????? this .d?? =?? d;?
???????? System.out.println( " --- complete TestRef(String str, int i, double d) --- " );?
???? }?
????
???? public?? String str?? =??? " I'm a string " ;?

???? int?? i?? =??? 1 ;?

???? double?? d?? =??? 3.14 ;?

???? HashMap map?? =??? new?? HashMap();?

????? public??? double?? getD()??? {?
????????? return?? d;?
???? }?

?????? public??? void?? setD( double?? d)??? {?
????????? this .d?? =?? d;?
???? }?

?????? public??? int?? getI()??? {?
????????? return?? i;?
???? }?

?????? public??? void?? setI( int?? i)??? {?
????????? this .i?? =?? i;?
???? }?

?????? public?? HashMap getMap()??? {?
????????? return?? map;?
???? }?

?????? public??? void?? setMap(HashMap map)??? {?
????????? this .map?? =?? map;?
???? }?

?????? public?? String getStr()??? {?
????????? return?? str;?
???? }?

?????? public??? void?? setStr(String str)??? {?
????????? this .str?? =?? str;?
???? }?
}?

三. 代理(Proxy)?
1.?
我们直接从代码入手吧,我们可以使用一个动态代理类(Proxy),通过拦截一个对象的行为并添加我们需要的功能来完成。Java中的java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口为我们实现动态代理类提供了一个方案,但是该方案针对的对象要实现某些接口;如果针对的目的是类的话,cglib为我们提供了另外一个实现方案。等下会说明两者的区别。?
一、接口的实现方案:?
1)首先编写我们的业务接口(StudentInfoService.java):?
public interface StudentInfoService{?
void findInfo(String studentName);?
}?
????? 及其实现类(StudentInfoServiceImpl.java):?
public class StudentInfoServiceImpl implements StudentInfoService{?
public void findInfo(String name){?
?? System.out.println("你目前输入的名字是:"+name);?
}?
}?
2)现在我们需要一个日志功能,在findInfo行为之前执行并记录其行为,那么我们就首先要拦截该行为。在实际执行的过程中用一个代理类来替我们完成。Java中为我们提供了实现动态代理类的方案:?

1'处理拦截目的的类(MyHandler.java)?
import org.apache.log4j.Logger;?
import java.lang.reflect.InvocationHandler;?
import java.lang.reflect.Proxy;?
import java.lang.reflect.Method;?

public class MyHandler implements InvocationHandler{?
private Object proxyObj;?
private static Logger log=Logger.getLogger(MyHandler.class);?

public Object bind(Object obj){?
?? this.proxyObj=obj;?
?? return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);?
}?

public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{?
?? Object result=null;?
?? try{?
??? //请在这里插入代码,在方法前调用?
??? log.info("调用log日志方法"+method.getName());?
??? result=method.invoke(proxyObj,args); //原方法?
??? //请在这里插入代码,方法后调用?
?? }catch(Exception e){?
??? e.printStackTrace();?
?? }?
?? return result;?
}?
}?
2'我们实现一个工厂,为了方便我们使用该拦截类(AOPFactory.java):?
public class AOPFactory{?
private static Object getClassInstance(String clzName){?
?? Object obj=null;?
?? try{?
??? Class cls=Class.forName(clzName);?
??? obj=(Object)cls.newInstance();?
?? }catch(ClassNotFoundException cnfe){?
??? System.out.println("ClassNotFoundException:"+cnfe.getMessage());?
?? }catch(Exception e){?
??? e.printStackTrace();?
?? }?
?? return obj;?
}?

public static Object getAOPProxyedObject(String clzName){?
?? Object proxy=null;?
?? MyHandler handler=new MyHandler();?
?? Object obj=getClassInstance(clzName);?
?? if(obj!=null) {?
??? proxy=handler.bind(obj);?
?? }else{?
??? System.out.println("Can't get the proxyobj");?
??? //throw?
?? }?
?? return proxy;?
}?
}?

3)基本的拦截与其工厂我们都实现了,现在测试(ClientTest.java):?
public class ClientTest{?
public static void main(String[] args){?
?? StudentInfoService studentInfo=(StudentInfoService)AOPFactory.getAOPProxyedObject("StudentInfoServiceImpl");?
?? studentInfo.findInfo("阿飞");?
}?
}?
输出结果(看你的log4j设置):?
[INFO]调用log日志方法findInfo?
你目前输入的名字是:阿飞?
????? 这样我们需要的效果就出来了,业务处理自己在进行,但是我们实现了日志功能?

2.再看一个例子:?
  假设系统由一系列的BusinessObject所完成业务逻辑功能,系统要求在每一次业务逻辑处理时要做日志记录。这里我们略去具体的业务逻辑代码。?

public interface BusinessInterface {?
 public void processBusiness();?
}?

public class BusinessObject implements BusinessInterface {?
 private Logger logger = Logger.getLogger(this.getClass().getName());?
 public void processBusiness(){?
  try {?
   logger.info("start to processing...");?
   //business logic here.?
   System.out.println(“here is business logic”);?
   logger.info("end processing...");?
  } catch (Exception e){?
   logger.info("exception happends...");?
   //exception handling?
  }?
 }?
}?

  这里处理商业逻辑的代码和日志记录代码混合在一起,这给日后的维护带来一定的困难,并且也会造成大量的代码重复。完全相同的log代码将出现在系统的每一个BusinessObject中。?

按照AOP的思想,我们应该把日志记录代码分离出来。要将这些代码分离就涉及到一个问题,我们必须知道商业逻辑代码何时被调用,这样我们好插入日志记录代码。一般来说要截获一个方法,我们可以采用回调方法或者动态代理。动态代理一般要更加灵活一些,目前多数的AOP Framework也大都采用了动态代理来实现。这里我们也采用动态代理作为例子。?
JDK1.2以后提供了动态代理的支持,程序员通过实现java.lang.reflect.InvocationHandler接口提供一个执行处理器,然后通过java.lang.reflect.Proxy得到一个代理对象,通过这个代理对象来执行商业方法,在商业方法被调用的同时,执行处理器会被自动调用。?

  有了JDK的这种支持,我们所要做的仅仅是提供一个日志处理器。?

public class LogHandler implements InvocationHandler {?

 private Logger logger = Logger.getLogger(this.getClass().getName());?
  private Object delegate;?
  public LogHandler(Object delegate){?
   this.delegate = delegate;?
  }?

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {?
  Object o = null;?
  try {?
   logger.info("method stats..." + method);?
   o = method.invoke(delegate,args);?
   logger.info("method ends..." + method);?
  } catch (Exception e){?
   logger.info("Exception happends...");?
   //excetpion handling.?
  }?
  return o;?
 }?
}?

  现在我们可以把BusinessObject里面的所有日志处理代码全部去掉了。?

public class BusinessObject implements BusinessInterface {?

 private Logger logger = Logger.getLogger(this.getClass().getName());?
 public void processBusiness(){?
  //business processing?
  System.out.println(“here is business logic”);?
 }?
}?

  客户端调用商业方法的代码如下:?

BusinessInterface businessImp = new BusinessObject();?

InvocationHandler handler = new LogHandler(businessImp);?

BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(?
 businessImp.getClass().getClassLoader(),?
 businessImp.getClass().getInterfaces(),?
 handler);?

proxy.processBusiness();?

  程序输出如下:?

INFO: method stats...?
here is business logic?
INFO: method ends...?

  至此我们的第一次小尝试算是完成了。可以看到,采用AOP之后,日志记录和业务逻辑代码完全分开了,以后要改变日志记录的话只需要修改日志记录处理器就行了,而业务对象本身(BusinessObject)无需做任何修改。并且这个日志记录不会造成重复代码了,所有的商业处理对象都可以重用这个日志处理器。?

热点排行