反射的官方定义是这样的:在运行状态中,对于任意的一个类,都可以知道这个类的全部属性和方法,对任意一个对象都可以经过反射机制调用一个类的任意方法,这种动态获取类信息及动态调用类对象方法的功能称为java的反射机制。 java
讲的通俗一点的话就是,对于jvm来讲,.java文件必需要先编译为.class文件才可以被jvm执行,因此在编译为.class文件的过程当中,对象的类型都会被指定好,好比说 User user。那么若是说我想在代码运行的过程当中获取到对象的类型呢?或者说程序在运行过程当中如何载入一个特定的类呢?这就涉及到了java的反射机制了,反射提供了一套可以让咱们在代码运行时也能获取到类型属性的方法。api
jdk提供了三种方式获取一个对象的Class,就User user来讲安全
1.user.getClass(),这个是Object类里面的方法app
2.User.Class属性,任何的数据类型,基本数据类型或者抽象数据类型,均可以经过这种方式获取类框架
3.Class.forName(""),Class类提供了这样一个方法,让咱们经过类名来获取到对象类jvm
这三种方法用的最多的就是第三种,那么获取到类以后,Class类提供了不少获取类属性,方法,构造方法的api。函数
1.获取全部的属性spa
//获取整个类 Class c = Class.forName("java.lang.Integer"); //获取全部的属性? Field[] fs = c.getDeclaredFields(); //定义可变长的字符串,用来存储属性 StringBuffer sb = new StringBuffer(); //经过追加的方法,将每一个属性拼接到此字符串中 //最外边的public定义 sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n"); //里边的每个属性 for(Field field:fs){ sb.append("\t");//空格 sb.append(Modifier.toString(field.getModifiers())+" ");//得到属性的修饰符,例如public,static等等 sb.append(field.getType().getSimpleName() + " ");//属性的类型的名字 sb.append(field.getName()+";\n");//属性的名字+回车 } sb.append("}"); System.out.println(sb);
2.获取特定的属性代理
//获取类 Class c = Class.forName("User"); //获取id属性 Field idF = c.getDeclaredField("id"); //实例化这个类赋给o Object o = c.newInstance(); //打破封装 idF.setAccessible(true); //使用反射机制能够打破封装性,致使了java对象的属性不安全。 //给o对象的id属性赋值"110" idF.set(o, "110"); //set //get System.out.println(idF.get(o));
3.获取方法日志
getDeclaredMethods() 获取全部的方法 getReturnType() 得到方法的放回类型 getParameterTypes() 得到方法的传入参数类型 getDeclaredMethod("方法名",参数类型.class,……) 得到特定的方法 getDeclaredConstructors() 获取全部的构造方法 getDeclaredConstructor(参数类型.class,……) 获取特定的构造方法 getSuperclass() 获取某类的父类 getInterfaces() 获取某类实现的接口
反射做用总结就是:1.动态地建立类的实例,将类绑定到现有的对象中,或从现有的对象中获取类型。2.应用程序须要在运行时从某个特定的程序集中载入一个特定的类。
那么什么是动态代理呢?先给出百度的答案:动态代理,就是根据对象在内存中加载的Class类建立运行时类对象,从而调用代理类方法和属性。
代理模式的定义:为其余对象提供一种代理以控制对这个对象的访问。在某些状况下,一个对象不适合或者不能直接引用另外一个对象,而代理对象能够在客户端和目标对象之间起到中介的做用。而代理模式又分为静态代理和动态代理,先说静态代理。
静态代理通俗点将就是本身手写一个代理类,而动态代理则不用咱们手写,而是依赖于java反射机制,下面以一个demo举例。
demo结构如图:
Subject是一个普通接口,里面有个抽象的doSomething()的方法,而SubjectImpl.java是它的普通的实现类,以下所示。
而HandProxy.java就是SubjectImpl的静态代理类,代替了SubjectImpl完成doSomething的工做,以下所示
这样作的好处是对于doSomething方法来讲,我用静态代理类来实现,能够任意的在其中插入我须要额外作的事情,好比说记录日志。
那么AutoProxy.java就是动态代理类了,具体以下所示。
这里面首先想要作到动态代理,必须先实现这个InvocationHandler接口,而后咱们主要看bind方法,参数tar是一个Object类型的对象,也就是须要被代理的对象SubjectImpl,
方法里面有一个Proxy类,这个Proxy类提供了不少方法,这里咱们用的是newProxyInstance方法,它有三个参数,第一个是被代理类的类构造器,第二个指的是被代理类的接口,也就是Subject接口,第三个是实现这个代理的类,这里就是本类。具体的来讲,这个方法执行了下面三步:
1.生成一个实现了参数interfaces里全部接口且继承了Proxy的代理类的字节码,而后用参数里的classLoader加载这个代理类。
2.使用代理类父类的构造函数 Proxy(InvocationHandler h)来创造一个代理类的实例,将咱们自定义的InvocationHandler的子类传入。
3.返回这个代理类实例,由于咱们构造的代理类实现了interfaces(也就是咱们程序中传入的subject.getClass().getInterfaces())里的全部接口,所以返回的代理类能够强转成Subject类型来调用接口中定义的方法。
而在调用每一个代理类每一个方法的时候,都用反射去调h的invoke方法(也就是咱们自定义的InvocationHandler的子类中重写的invoke方法),用参数传递了代理类实例、接口方法、调用参数列表,这样咱们在重写的invoke方法中就能够实现对全部方法的统一包装了。
总结一下,静态代理的优势是清晰易懂,可是若是说业务代码不少,那么在代理类里面必须所有从新调用一遍,很麻烦。而动态代理,利用java反射机制,动态的生成了一个代理类,直接调用代理方法便可。
反射这一块是博主最近在看框架源码的时候老是遇到这类问题因此刻意去整理了一下,里面有些东西其实我也研究的不是很深入,动态代理这边的源码还有待深刻挖掘,如有大神发现写的不对的地方,还请多多指教,谢谢。