Java反射和动态代理的使用解读,
分享于 点击 13783 次 点评:196
Java反射和动态代理的使用解读,
目录
- 1、反射
- 1.1 反射的概述
- 1.2 反射作用
- 1.3 获取字节码文件对象的方式
- 1.4 字节码文件和字节码文件对象
- 1.5 获取构造方法
- 1.6 获取构造方法并创建对象
- 1.7 获取成员变量并获取值和修改值
- 1.8 获取成员方法
- 1.9 获取成员方法并运行
- 2. 动态代理
- 2.1 动态代理好处
- 2.2 动态代理三要素
- 2.3 动态代理简单实现
- 2.4 动态代理扩展
- 总结
1、反射
1.1 反射的概述
是在运行状态中,不用创建对象就能够调用任意一个类的所有属性和方法;
1.2 反射作用
反射都是从class字节码文件中获取的内容。
- 获取class字节码文件的对象
- 利用反射如何获取构造方法(创建对象)
- 利用反射如何获取成员变量(赋值,获取值)
- 利用反射如何获取成员方法(运行)
1.3 获取字节码文件对象的方式
- Class这个类里面的静态方法forName(“全类名”)(最常用)
- 通过class属性获取,一般更多的是当做参数进行传递
- 通过对象获取字节码文件对象,当我们已经有了这个类的对象时,才可以使用。
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { //1. 第一种方式 //全类名 : 包名 + 类名 Class clazz1 = Class.forName("com.ya.reflect.demo01.Student"); //2. 第二种方式 Class clazz2 = Student.class; //3.第三种方式 Student s = new Student(); Class clazz3 = s.getClass(); System.out.println(clazz1 == clazz2); System.out.println(clazz2 == clazz3); } }
1.4 字节码文件和字节码文件对象
- java文件(源代码阶段):就是编写的java代码。
- 字节码文件(加载阶段):就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)
- 字节码文件对象(运行阶段):当class文件加载到内存之后,虚拟机自动创建出来的对象。这个对象里面至少包含了:构造方法,成员变量,成员方法。
反射获取的是字节码文件对象,这个对象在内存中是唯一的。
1.5 获取构造方法
规则:
- get表示获取
- Declared表示私有
- 最后的s表示所有,复数形式
- 如果当前获取到的是私有的,必须要临时修改访问权限setAccessible(true),否则无法使用
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //1.获取class字节码文件对象 Class clazz = Class.forName("com.ya.reflect.demo02.Student"); //2.获取构造方法 // 2.1 返回所有公共构造方法对象的数组 System.out.println("-----返回所有公共构造方法对象的数组----"); Constructor[] con1 = clazz.getConstructors(); for (Constructor con : con1) { System.out.println(con); } // 2.2 返回所有构造方法对象的数组 System.out.println("-------返回所有构造方法对象的数组------"); Constructor[] con2 = clazz.getDeclaredConstructors(); for (Constructor con : con2) { System.out.println(con); } // 2.3 返回所有构造方法对象的数组 System.out.println("-------返回单个公共构造方法对象------"); Constructor con3 = clazz.getConstructor(String.class); System.out.println(con3); //2.4 获取指定的空参构造 System.out.println("-------获取指定的空参构造------"); Constructor con4 = clazz.getConstructor(); System.out.println(con4); // 2.5 返回所有构造方法对象的数组 System.out.println("-------返回单个构造方法对象------"); Constructor con5 = clazz.getDeclaredConstructor(String.class,int.class); System.out.println(con5); System.out.println("-----获取构造方法的权限修饰符(返回整数)--------"); // 返回的是整数,public 1,private 2,protected 4,abstract 1024 int modifiers = con5.getModifiers(); System.out.println(modifiers); System.out.println("-----获取构造方法的参数--------"); Parameter[] parameters = con5.getParameters(); for (Parameter parameter : parameters) { System.out.println(parameter); } System.out.println("--------创建对象-----------"); //暴力反射:表示临时取消权限校验 con5.setAccessible(true); // 如果这个权限修饰符是私有的,就要取消权限校验 Student stu = (Student) con5.newInstance("张三", 23); System.out.println(stu); } }
1.6 获取构造方法并创建对象
public class ReflectDemo02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //需求1:获取空参,并创建对象(所创建的对象的属性是默认值) //1.获取整体的字节码文件对象 Class clazz = Class.forName("com.ya.reflect.demo02.Student"); //2.获取空参的构造方法 Constructor con = clazz.getConstructor(); //3.利用空参构造方法创建对象 Student stu = (Student) con.newInstance(); System.out.println(stu); System.out.println("----------------------------------"); //需求2:获取带参构造,并创建对象 //1.获取整体的字节码文件对象 Class clazz2 = Class.forName("com.ya.reflect.demo02.Student"); //2.获取有参构造方法 Constructor con2 = clazz2.getDeclaredConstructor(String.class, int.class); //3.临时修改构造方法的访问权限(暴力反射) con2.setAccessible(true); //4.直接创建对象 Student stu2 = (Student) con2.newInstance("zhangsan", 23); System.out.println(stu2); } }
1.7 获取成员变量并获取值和修改值
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { //1.获取class字节码文件的对象 Class clazz = Class.forName("com.ya.reflect.demo03.Student"); //2.获取所有的成员变量 System.out.println("-----获取所有的成员变量----"); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } //3.获取单个的成员变量 System.out.println("-----获取单个的成员变量----"); Field fieldName = clazz.getDeclaredField("name"); System.out.println(fieldName); System.out.println("-----获取成员变量的数据类型----"); Class<?> type = fieldName.getType(); System.out.println(type); System.out.println("-----获取成员变量记录的值----"); Student student = new Student("zhangsan", 23, "男"); fieldName.setAccessible(true); String value = (String) fieldName.get(student); System.out.println(value); System.out.println("-----修改对象里面记录的值---"); fieldName.set(student,"lisi"); System.out.println(student); } }
1.8 获取成员方法
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { //1. 获取class字节码文件对象 Class clazz = Class.forName("com.ya.reflect.demo04.Student"); //2. 获取里面所有的方法对象(包含父类中所有的公共方法) System.out.println("-------获取里面所有的方法对象(包含父类中所有的公共方法)-----"); Method[] methods = clazz.getMethods(); for (Method method : methods) { System.out.println(method); } // 3. 获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法) System.out.println("--获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)--"); Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); } // 4.获取指定的单一方法 System.out.println("-------获取指定的单一方法-----"); Method eatMethod = clazz.getDeclaredMethod("eat", String.class); System.out.println(eatMethod); // 5.获取方法的修饰符(返回整数) System.out.println("-------获取方法的修饰符(返回整数)-----"); int modifiers = eatMethod.getModifiers(); System.out.println(modifiers); // 6.获取方法的名字 System.out.println("-------获取方法的名字-----"); String eatMethodName = eatMethod.getName(); System.out.println(eatMethodName); // 7.获取方法的形参 System.out.println("-------获取方法的形参-----"); Parameter[] parameters = eatMethod.getParameters(); for (Parameter parameter : parameters) { System.out.println(parameter); } //8.获取方法的抛出的异常 System.out.println("-------获取方法的抛出的异常-----"); Class[] exceptionTypes = eatMethod.getExceptionTypes(); for (Class exceptionType : exceptionTypes) { System.out.println(exceptionType); } } }
1.9 获取成员方法并运行
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { //1. 获取class字节码文件对象 Class clazz = Class.forName("com.ya.reflect.demo04.Student"); //2. 获取指定的单一方法 Method eatMethod = clazz.getDeclaredMethod("eat", String.class); // 3. 方法运行 System.out.println("-------方法运行-----"); Student student = new Student(); eatMethod.setAccessible(true); String result = (String) eatMethod.invoke(student, "汉堡包"); System.out.println(result); } }
2. 动态代理
2.1 动态代理好处
- 无侵入式的给方法增强功能。
- 调用者(BitStar)–>代理(ProxyUtil)–>对象(Star)
2.2 动态代理三要素
- 接口:代理对象,被代理类和代理类都需要实现这个接口(本例是Star)
- 代理类:通过Proxy类动态生成的代理类(本例是ProxyUtil)
- 被代理类:代理类实例,它会代替被代理对象处理方法调用(本例是BigStar)
注:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。
2.3 动态代理简单实现
//代理对象 public interface Star { //可以把所有想要被代理的方法定义在接口当中 //唱歌 public abstract String sing(String name); //跳舞 public abstract void dance(); } //被代理类 public class BigStar implements Star{ private String name; public BigStar() { } public BigStar(String name) { this.name = name; } //唱歌 @Override public String sing(String name){ System.out.println(this.name + "正在唱" + name); return "谢谢"; } //跳舞 @Override public void dance(){ System.out.println(this.name + "正在跳舞"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "BigStar{name = " + name + "}"; } } //代理类 // 创建代理 java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 参数一:用于指定用哪个类加载器,去加载生成的代理类 参数二:指定接口,这些接口用于指定生成的代理有哪些方法 参数三:用来指定生成的代理对象要干什么事情 public class ProxyUtil { /** * 形参:被代理的明星对象 * 返回值:给明星创建的代理 */ public static Star createProxy(BigStar bigStar){ // 创建代理 // 这个代码没有显示实现Star接口,但通过 JDK动态代理机制 隐式地为生成的代理类添加了Star接口的实现,从而可以看作实现了Star接口 Star star = (Star) Proxy.newProxyInstance( ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类 new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理有哪些方法 new InvocationHandler() { //参数三:用来指定生成的代理对象要干什么事情 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /** * 参数一:代理的对象,即star变量引用的对象 * 参数二:要运行的方法,sing,dance * 参数三:调用sing方法时,传递的实参 */ if("sing".equals(method.getName())){ System.out.println("准备话筒,收钱"); }else if("dance".equals(method.getName())){ System.out.println("准备场地,收钱"); } //去找大明星开始唱歌或者跳舞 //代码的表现形式:调用大明星里面唱歌或者跳舞的方法 return method.invoke(bigStar,args); } } ); return star; } } //代理测试 public class Test { public static void main(String[] args) { //1. 获取代理的对象 BigStar bigStar = new BigStar("机哥"); Star proxy = ProxyUtil.createProxy(bigStar); //2. 调用唱歌的方法 String result = proxy.sing("只因你太美"); System.out.println(result); } }
2.4 动态代理扩展
动态代理,还可以拦截方法。比如:在这个故事中,经纪人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。
public class ProxyUtil { public static Star createProxy(BigStar bigStar){ public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) Star star = (Star) Proxy.newProxyInstance( ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("cleanWC".equals(method.getName())){ System.out.println("拦截,不调用大明星的方法"); return null; } //如果是其他方法,正常执行 return method.invoke(bigStar,args); } } ); return star; } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持3672js教程。
您可能感兴趣的文章:- Java的动态代理和静态代理及反射常用API详解
- Java使用反射和动态代理实现一个View注解绑定库
- Java反射的应用之动态代理深入理解
- Java反射(JDK)与动态代理(CGLIB)详解
- 详解Java中的反射机制和动态代理
- Java动态代理和反射机制详解
用户点评