java、反射和动态代理 怼烎@ 2022-03-31 03:09 276阅读 0赞 \----------- [android培训][android]、[java培训][java]、java学习型技术博客、期待与您交流! ------------ 一、反射机制 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和放?对于任意一个对象能否调用它的任意一个方法?答案是肯定的。这种 动态获取类的信息,以及动态调用对象的方法的功能来自于Java语言的反射(Reflection机制)。 二、反射机制的功能 在运行时判断任意一个对象所属的类; 在运行时构造任意一个类的对象; 在运行时判断任意一个类所具有的成员变量和方法; 在运行时调用任意一个对象的方法; 生成动态代理。 反射实例: import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectDemo { /** * @param args * @throws SecurityException * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalArgumentException * @throws IllegalAccessException * @throws InstantiationException */ public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { //创建customer对象,并传入参数 Customer customer = new Customer("Tom", 21); customer.setId(new Long(1)); //将customer对象的所有属性和方法进行拷贝 Customer customerCopy = (Customer) new ReflectDemo().copy(customer); //打印拷贝后的对象的属性名称和年龄 System.out.println("Copy information:" + customerCopy.getName() + "" + customerCopy.getAge()); } public Object copy(Object object) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { // 获取对象的类型 Class classType = object.getClass(); //打印这个类的完整名字 System.out.println("Class:" + classType.getName()); // 通过默认构造方法创建一个新的对象,不带参数的构造方法 Object objectCopy = classType.getConstructor(new Class[] {}) .newInstance(new Object[] {}); // 获取对象的所有属性 Field[] fields = classType.getDeclaredFields(); //遍历所有属性 for (int i = 0; i < fields.length; i++) { Field field = fields[i]; //获取属性的名字 String fieldName = field.getName(); String firstLetter = fieldName.substring(0, 1).toUpperCase(); // 获得和属性对应的getXXX()方法的名字 String getMehodName = "get" + firstLetter + fieldName.substring(1); // 获得和属性对应的setXXX()方法的名字 String setMehodName = "set" + firstLetter + fieldName.substring(1); // 获得和属性对应的getXXX()方法 Method getMetod = classType.getMethod(getMehodName, new Class[] {}); // 获得和属性对应的setXXX()方法 Method setMetod = classType.getMethod(setMehodName, new Class[] {}); // 调用原对象的getXXX()方法 Object value = getMetod.invoke(object, new Object[] {}); System.out.println(fieldName + ":" + value); // 调用原对象的setXXX()方法 setMetod.invoke(objectCopy, new Object[] { value }); } return objectCopy; } } class Customer { private long id; private String name; private int age; public Customer(String name, int age) { super(); this.name = name; this.age = age; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } 总结:ReflectDemo类定义的copy方法通过getClass()方法,第一步获得对象类型,也就是该对象的字节码文件。第二步获取它的特定构造函数并用newInstance方法创建一个新的对象。第三步通过该字节码文件再获取它的所有属性和set、get方法,这里属性和set、get方法,可能不只一个,所以获取后用数组进行存储。第四步获得每个属性相应的getXXX()和setXXX()方法,然后执行这些方法,把原来对象的属性赋值到新的对象中; 注意:(Method类的invoke(Object obj,Object args\[\])用于动态执行一个对象的特定方法,第一个obj参数指定具有该方法的对象,第二个ergs参数指定向该方法传递的参数) Method类的invoke(Object obj,Object args\[\])方法接受的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象。 如果实际被调用的方法的返回类型是基本类型数据,那么invoke方法会把它转换为相应的包装类型的对象,再将其返回。 三、动态代理 动态代理类。对其他对象提供一种代理以控制对这个对象的访问。一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。在 程序运行时,运用反射机制动态创建而成,这些都是java虚拟机操作的。 动态代理的好处? 1.要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能 动态生成类的原理: 1.客户端调用代理,代理的构造方法接受一个Invocationhandler对象,接受进去了,然后客户端调用代理的各个方法, 代理的各个方法会把请求转发给Invocationhandler对象,Invocationhandler对象又会把各个请求分发给目标的相应方法。 nbsp; 动态代理实例: import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; /** * 切面编程:把对象的代码进行封装,以对象的形式传递给你,你执行了对象就等于执行切面的代码 * * */ public class Fanshe1 { public static void main(String[] args) throws InstantiationException, IllegalAccessException { ArrayList taget = new ArrayList(); Collection proxy4 = (Collection) getProxy(taget, new Advice()); proxy4.add("cxl"); proxy4.add("zzz"); System.out.println(proxy4.size()); // 结果打印三对方法,是因为,集合每使用一次proxy4对象的时候都从进入InvocationHandler对象一次 } public static Object getProxy(final ArrayList taget, final Advice advice) throws InstantiationException, IllegalAccessException { // 先获取该类的字节码文件,再获取该类的类加载器 // 然后获取接口的字节码文件 // 最后调用处理的程序,通过InvocationHandler Object className = Proxy.newProxyInstance(taget.getClass() .getClassLoader(), taget.getClass().getInterfaces(), new InvocationHandler() { @Override // 传入代理对象、对象的方法、对象的参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 放入代理对象方法的上面和下面 advice.look(); // invoke.使用taget的对象的args方法 Object result = method.invoke(taget, args); advice.show(); return result; } }); return className; } } /*Advic类*/ public class Advice implements MyAdvice { @Override public void look() { System.out.println("这是查功能"); } @Override public void show() { System.out.println("这是看功能"); } } /*MyAdvice接口*/ public interface MyAdvice { void look(); void show(); } 总结:以上程序你可以有些疑惑 注意:1.为什么要接受InvocationHandler对象呢? 答,之所以要接受InvocationHandler对象,是因为要定义一个它的成员变量,以便在程序后面应用 2.invoke方法是每调用一次对象的方法时,它都执行一次,那invoke方法后面的几个参数是什么呢? public Object invoke(Object proxy, Method method, Object\[\] args) 答:/ 1.(Object proxy)调用了代理对象。2.(Method method)调用了代理对象的哪个方法。3.(Object\[\] args)以及为这个方法传递的哪个参数 3.为什么在调用代理对象的方法的时候会出现异常呢? 答:因为我们在调用代理对象方法的时候,它会调用invoke方法,而invoke返回的是null,而代理对象的方法返回的是一个int类型,invoke方法返回的却是null,所以出现了空指针异常。 为什么修改了后没错呢?是因为目标返回的方法类型和你调用的代理对象方法的类型是一致的。所以没错。 注意:(打印代理对象的getClass.()getName()为什么返回的是$Proxy0,那是因为该对象属于Object,而Object只给三个方法(hashCode、equals 、toString)对InvocationHandler进行派 发,而其他的方法不进行派发,它自己有自己的实现) 4.什么叫切面编程呢? 答,就是把切面的代码用对象的形式进行封装,然后以对象的形式传递给你,你执行对象就等于你执行了切面的代码。 具体实现: 1.在InvocationHandler身上传递目标类与系统功能对象进去 public static Object getProxy(final ArrayList taget, final Advice advice) throws InstantiationException, IllegalAccessException {// 将系统功能封装成一个对象,对象的类名是什么?可以通过接口来约束。内部类访问局部变量应该在前面加final public Object invoke(Object proxy, Method method,Object[] args) throws Throwable { advice.look();// 系统功能可以放在1.在调用目标方法之前2.在调用目标方法之后 3.在调用目标方法前后 4.在处理目标方法异常的catch块中 Object result = method.invoke(taget, args); advice.show();//如果它实现了一个接口,有了接口就可以调用了,这叫通信的契约。 //步骤: 1.定义一个接口MyAdvice 2.定义它的抽象功能,也就是方法. 3.由于是功能必须实现它,所以再定义个Advice类实现它的方法,这样才能输出 \----------- [android培训][android]、[java培训][java]、java学习型技术博客、期待与您交流! ------------ [android]: http://edu.csdn.net/heima/ [java]: http://edu.csdn.net/heima
还没有评论,来说两句吧...