`
senton
  • 浏览: 206651 次
  • 性别: Icon_minigender_1
  • 来自: 紫禁城
社区版块
存档分类
最新评论

反射和代理

    博客分类:
  • J2SE
阅读更多

一. 关于数据库.
当今的数据处理大致可以分成两大类:联机事务处理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)无需做任何修改。并且这个日志记录不会造成重复代码了,所有的商业处理对象都可以重用这个日志处理器。


 

分享到:
评论

相关推荐

    利用反射和代理实现简单的Spring

    代理,又分为静态代理和动态代理。静态代理是在编译时期就确定了代理类,需要为每个被代理的目标类手动创建一个代理类。而动态代理则是在运行时生成代理类,这通常是通过`java.lang.reflect.Proxy`类和`java.lang....

    Spring Ioc 使用java反射和代理

    Spring Ioc 使用java反射和代理

    JAVA反射与代理

    在Java中,有两种代理方式:静态代理和动态代理。 静态代理是通过定义一个代理类来实现的,代理类和目标类实现了相同的接口,代理类在调用目标方法之前或之后可以插入自定义的行为,比如添加日志。这种方式的缺点是...

    有关反射加代理的内容

    代理模式在Java中分为静态代理和动态代理。静态代理是一种在编译时就已经明确知道代理类和真实类关系的方式。代理类通常会扩展或实现真实类的接口,以此提供额外的功能,例如在调用真实方法前后添加日志记录。下面是...

    利用Java的反射与代理实现IOC模式

    Java提供两种类型的代理:基于接口的JDK动态代理和基于类的CGLIB代理。 1. JDK动态代理:通过实现`java.lang.reflect.InvocationHandler`接口,我们可以创建一个代理类,该类实现了目标接口。`java.lang.reflect....

    JAVA反射 代理 PPT

    通过Java反射机制和代理模式,我们可以实现诸如AOP(面向切面编程)、动态加载类、运行时代码增强等多种高级功能,极大地提高了代码的灵活性和可扩展性。然而,需要注意的是,反射和代理虽然强大,但也会带来性能...

    浪曦经典Java代码--反射+代理reflection_proxy源码

    Java编程语言以其强大的功能和广泛的应用领域而备受程序员喜爱,其中反射和代理是两个非常重要的概念,它们在实际开发中有着广泛的应用。本资源“浪曦经典Java代码--反射+代理reflection_proxy源码”提供了深入理解...

    JVM技术,反射与动态代理

    动态代理有两种实现方式:基于接口的代理和基于类的代理。前者要求目标类实现一个或多个接口,后者则利用CGLIB等库生成目标类的子类。 总结,JVM技术是Java程序运行的基础,反射和动态代理则是Java平台强大功能的...

    Java反射与动态代理

    结合反射和动态代理,开发者可以构建更加灵活和强大的应用程序,实现高度抽象和解耦。但需要注意的是,过度使用反射和动态代理可能会带来性能损失,因为它们都需要在运行时解析类信息,增加了额外的开销。因此,在...

    反射和动态代理(reflect and proxy)---含源码

    在Java编程语言中,反射(Reflection)和动态代理(Dynamic Proxy)是两个强大的特性,它们为程序员提供了在运行时检查和操作类、接口、对象的能力。这篇内容将深入讲解这两个概念,帮助初学者理解并掌握它们的应用...

    利用反射和动态代理机制实现自定义拦截器Interceptor

    在这里,我们使用 InvocationHandler 接口来定义代理和拦截器之间的关系。 ```java public class MHandler implements InvocationHandler { private Object object; private Interceptor interceptor = new ...

    反射和动态代理

    在Java编程语言中,反射和动态代理是两个强大的特性,它们极大地增强了代码的灵活性和可扩展性。本文将深入探讨这两个概念以及它们在实际开发中的应用。 **反射** 是Java提供的一种机制,允许程序在运行时检查类、...

    AOP动态代理(反射机制)

    在创建代理对象和处理方法调用时,都会用到反射。 4. **应用场景**:AOP动态代理常用于框架开发,例如Spring AOP。Spring AOP提供了基于代理的实现,可以在不修改原始代码的情况下,为业务方法添加额外的功能。此外...

    java反射机制和动态代理的原理

    java反射机制和动态代理的原理,熟悉反射机制和动态代理

    java反射与动态代理笔记

    Java反射和动态代理是Java编程中的重要特性,它们在实现高度灵活和动态的代码执行方面发挥着关键作用。本文将深入探讨这两个概念及其在实际开发中的应用。 **Java反射** Java反射API允许程序在运行时检查类、接口...

    Java 反射-动态代理

    Java提供两种方式实现动态代理:JDK动态代理和CGLIB代理。 1. JDK动态代理:基于接口的代理,需要目标对象实现至少一个接口。我们可以使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。...

Global site tag (gtag.js) - Google Analytics