杂谈下java的反射(反射,动态代理)

说起反射,应该很多学JAVA的人都或多或少的地听过,用过吧。那么,反射究竟是什么?我写这边文章主要是总结下JAVA的反射机制和JAVA反射的原理,以及反射涉及的其他东西。

1.JAVA的反射
什么是JAVA的反射呢?我的定义是:JAVA的反射允许JAVA的类在运行过程中查看这个类自身的功能,并进行动态地管理的过程,叫做JAVA的反射机制,也称为类自审机制。

JAVA的反射到底可以干什么呢?
先从JAVA反射的基础开始讲起:java.lang.Class 类是JAVA反射的核心类,java.lang.reflect 包是JAVA反射常用到的包,包中有Method类,Field类,Constructor 类。顾名思义,也该知道JAVA反射的基础应用是什么了:
1.JAVA反射可以获得类名,类加载器,父类,包名,获得类类型。
2.JAVA反射可以获得类的构造方法,成员方法,并动态调用
3.JAVA反射可以获得类的成员变量,并动态设置成员变量的值。
4.JAVA反射可以通过类所在的包名,动态地生成一个类的实例。

我们分别简单地看看一些示例代码:
1.Field:获得这个字段类的方法有:getField(String name),getFields() ,getDeclaredFields() ,getDeclaredField(String name) 四个方法。这四个方法里面前两个方法是只针对公有字段的,而后两个方法是针对全部字段都可以的:

public class Person {
         private int x;
         public int y;
            public Person(int x,int y){
               this.x=x;
               this.y=y;
         }
}

public static void main(String []args){
              try{
                  Person p=new Person(3,5);
                  Field fieldY=p.getClass().getField("y");//公有的
                  System.out.println(fieldY.get(p));
                  fieldY.set(p, 6);
                  Field fieldX=p.getClass().getDeclaredField("x");//获取公有,私有都可以
                  fieldX.setAccessible(true);
                  System.out.println(fieldX.get(p));
                  System.out.println(fieldY.get(p));
                  }

Filed.set(Object obj,Object value)用于动态设置字段的值。
2.Method : 方法类的主要目标是获得方法名,然后做动态调用(invoke),获取Method类的方法有:getMethod(String name, Class

public class Teacher {

        public String teacherhName;
        public String teachiingSubject;

        public Integer teacherNumber;

        public void setTeacherhName(String teacherhName){
              this.teacherhName=teacherhName;
        }

        public String getTeacherhName(){
             return(this.teacherhName);
        }

        public void setTeachiingSubject(String teachiingSubject){
              this.teachiingSubject=teachiingSubject;
        }

        public String getTeachiingSubject(){
              return (this.teachiingSubject);
        }

        public void setTeacherNumber(Integer teacherNumber){
              this.teacherNumber=teacherNumber;
        }

        public Integer getTeacherNumber(){
               return(this.teacherNumber);
        }

}

 使用Method类的代码:
             Teacher teacher=new Teacher();
                  Class teacherClass=teacher.getClass();
                  Method[] methods=teacherClass.getDeclaredMethods();//method类所有的公有私有的方法都获取
                  for(Method method :methods){
                          if(method.getName().equals("setTeacherhName")){
                              method.invoke(teacher,"lzw");
                          }else if(method.getName().equals("setTeachiingSubject")){
                              method.invoke(teacher,"computer science");
                          }else if(method.getName().equals("setTeacherNumber")){
                              method.invoke(teacher,new Integer("2"));
                          }
                  }
                  System.out.println(teacher.getTeacherhName());
                  System.out.println(teacher.getTeachiingSubject());
                  System.out.println(teacher.getTeacherNumber());

可以看到,Method类最重要的一个方法就是invoke,这个方式的意思是(jdk官方文档解释:对带有指定参数的指定对象调用由此 Method 对象表示的底层方法),其实通俗的理解就是,通过这个类,激活调用Method方法。
3.Constructor :构造方法类,
这个类主要是是可以通过反射动态地生成的一个类的实例。

好,我们讲述了关于的类的反射的基础应用,大家需要用的时候,可以去百度或者jdk文档里面查看。接下来说下java的反射的一些应用:
1.关于java反射在后台(JDBC)上的应用,请大家查看这篇博客:http://www.cnblogs.com/jqyp/archive/2012/03/29/2423112.html
2.关于java反射在android上的应用,大家可以查看这篇博客,http://blog.csdn.net/nokiaguy/archive/2010/07/27/5770263.aspx,这篇博客说明了我们如何使用java反射来到达我们需要的目的,比如修改一个类的内部运行机制。

讲了反射的应用,就该说说java反射的原理了。
原理就是基于Class类实现,也许大家的会觉得很拗口,Class本来就是一个类,居然还有Class类。这个类位于java.lang包下,Class 我们可以看到,这类有个泛型,什么意思呢?其实根据jdk官方文档的解释来说:Class 类的实例表示正在运行的 Java 应用程序中的类和接口。说白了,这个类其实就是每个运行时类的模版。JVM在类加载的时候,是从每个类编译生成的class文件中去加载一个类:
比如:
Teacher.java 是源文件,经过编译后是Teacher.class 文件,运行时候被类加载器读入。这时候,Teacher 类的所有信息都放在了Teacher.class文件中(比如类的静态字段,方法,常量等等),所以,java语言借助这种原理提供了反射机制,就是Class类,每个类都有一个Class类,表示的就是Class文件中内容的映射关系,而我们通过class类我们可以获取到静态字段,成员变量,构造方法,成员方法等。这就是JAVA的反射机制的原理所在。

2.动态代理
代理模式大家可能在设计模式中都听过,如果没有听过,去看看《Head First 设计模式》这本书,讲的很好。
这里简单说下代理模式:代理模式的具体就是用一个接口,然后一个具体实现接口方法的类A,然后一个实现类接口方法的代理类Proxy,代理类在构造函数的开始的时候,传入那个具体实现类接口的类A,在代理类Proxy覆写接口的方法中,调用的实现接口的类A的方法。

代理模式顾名思义就是用一个代理来控制对具体的类的方法,方便接口灵活转换,也体现了封装的特性。

java的API中还提出类动态代理的概念:这是一种什么意思呢?
我们先上代码:
代理的接口类:

package com.proxy;

/**
 * Created by asus on 2015/9/4.
 */
public interface Subject {

           public void dosomeThing();
}

实现接口的具体类

package com.proxy;

/**
 * Created by asus on 2015/9/4.
 */
public class RealSubject implements Subject {

         public void  dosomeThing(){
               System.out.println("do somethind find practice job!!!!");
         }
}

接下来是如何生成动态代理类:

package com.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by asus on 2015/9/4.
 */
public class ProxyHandler implements InvocationHandler {

        private  Object proxied;

        public ProxyHandler(Object proxied){
               this.proxied=proxied;
        }
        public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable{
              return( method.invoke(this.proxied,args));
        }


        public static void main(String []args){
              RealSubject realSubject=new RealSubject();
              ProxyHandler handler=new ProxyHandler(realSubject);
              Subject proxySubject=(Subject)
                      Proxy.newProxyInstance(Subject.class.getClassLoader(),
                              new Class[]{Subject.class},handler);
              proxySubject.dosomeThing();
        }
}

(注:如果不太了解动态代理请先看下是怎么回事,这里主要是说下动态代理的原理)
大家可以看到,上述代码利用了一个ProxyHandler和Proxy类实现了动态代理。大家可能觉得有两个疑问?(最起码我一开始是这样觉得的):1.ProxyHandler的invoke方法到底是干嘛的?2.怎么就通过Proxy类生成了一个动态代理的实例?

我们一一阐述问题,首先,ProxyHandler是java的一个接口,这个接口有个空方法:invoke,看着好眼熟,对,这个方法其实就是用于给动态代理类使用反射来调用被代理对象的方法。
我们可以看参数: public Object invoke( Object proxy, Method method, Object[] args ),第一个参数是动态代理类,第二个参数是Method对象,第三个参数反射调用的方法需要的参数。但是ProxyHandler是一个接口,其实真的要去实现反射使我们自己写的的代码,比如上述代码中的method.invoke(proxied,args),就是我们自己写Method动态调用的方法.
2.动态代理类是怎么被new出来的?这就我们好好查看下Proxy的静态newInstance方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException { 
    // 检查 h 不为空,否则抛异常
    if (h == null) { 
        throw new NullPointerException(); 
    } 

    // 获得与指定类装载器和一组接口相关的代理类类型对象
    Class cl = getProxyClass(loader, interfaces); 

    // 通过反射获取构造函数对象并生成代理类实例
    try { 
        Constructor cons = cl.getConstructor(constructorParams); 
        return (Object) cons.newInstance(new Object[] { h }); 
    } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); 
    } catch (IllegalAccessException e) { throw new InternalError(e.toString()); 
    } catch (InstantiationException e) { throw new InternalError(e.toString()); 
    } catch (InvocationTargetException e) { throw new InternalError(e.toString()); 
    } 
}

大家可以看到,上述代码就是做了两件事情:1.利用传入的接口的类加载器和接口的Class类,构造了一个动态代理类的Class类 cl。2.然后再次通过反射,得到动态代理类的构造方法类,然后传入PorxyHanlder作为构造方法的参数,生成了动态代理类的实例。
其实,最关键的一行代码就是 Class cl = getProxyClass(loader, interfaces); 这到底构造的动态代理类对象是什么样的呢?我们马上附上代码:

import java.lang.reflect.*;   
public final class ProxySubject extends Proxy   
    implements Subject   
{   
    private static Method m1;   
    private static Method m0;   
    private static Method m3;   
    private static Method m2;   
    public ProxySubject(InvocationHandler invocationhandler)   
    {   
        super(invocationhandler);   
    }   
    public final boolean equals(Object obj)   
    {   
        try  
        {   
            return ((Boolean)super.h.invoke(this, m1, new Object[] {   
                obj   
            })).booleanValue();   
        }   
        catch(Error _ex) { }   
        catch(Throwable throwable)   
        {   
            throw new UndeclaredThrowableException(throwable);   
        }   
    }   
    public final int hashCode()   
    {   
        try  
        {   
            return ((Integer)super.h.invoke(this, m0, null)).intValue();   
        }   
        catch(Error _ex) { }   
        catch(Throwable throwable)   
        {   
            throw new UndeclaredThrowableException(throwable);   
        }   
    }   
    public final void doSomething()   
    {   
        try  
        {   
            super.h.invoke(this, m3, null);   
            return;   
        }   
        catch(Error _ex) { }   
        catch(Throwable throwable)   
        {   
            throw new UndeclaredThrowableException(throwable);   
        }   
    }   
    public final String toString()   
    {   
        try  
        {   
            return (String)super.h.invoke(this, m2, null);   
        }   
        catch(Error _ex) { }   
        catch(Throwable throwable)   
        {   
            throw new UndeclaredThrowableException(throwable);   
        }   
    }   
    static    
    {   
        try  
        {   
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {   
                Class.forName("java.lang.Object")   
            });   
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);   
            m3 = Class.forName("Subject").getMethod("doSomething", new Class[0]);   
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);   
        }   
        catch(NoSuchMethodException nosuchmethodexception)   
        {   
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());   
        }   
        catch(ClassNotFoundException classnotfoundexception)   
        {   
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());   
        }   
    }   
}  

大家多读几遍代码,现在明白了没有?这个动态代理类ProxySubject继承了Proxy类,还实现了Subject(代理模式类的接口),并且利用静态代码段生成了一堆代理接口的method类对象。然后呢?比如我们在main函数中调用的proxySubject.dosomeThing();是怎么做的呢? super.h.invoke(this, m3, null); ,哈哈,就是使用了ProxyHandlerinvoke方法,我们之前已经覆写了这个方法,所以,哈哈,就是利用反射来调用RealSubject类中的方法了。

以上就是整个动态代理类的原理:总结下
1.动态代理和静态代理的最大区别就是利用了反射机制。
2.动态代理中,两个很重要的类:1.ProxyHanlder,一个接口,担任中间件角色,把要代理的对象放入其中,在让实现其子类的接口其invoke方法,利用反射调用被代理类的方法
2.Proxy类:核心,生成了一个继承了Proxy的类的动态代理类,并暴露ProxyHandler,让动态代理类使用,完成动态代理。

关于动态代理,大家还可以去查看这篇博客,写的很好:http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html

这里可能有人会问,那个ProxyHandler为啥是一个接口啊?直接帮我们写好不久行了吗?哈哈,这就是动态代理的优势所在,invoke方法是任我们发挥的,我们既然得到了被代理的对象的Method方法,我们就能在运行时候知道类的信息,然后决定是否触发Metho的方法。这就是Spring中常常说到的AOP编程。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值