Java_反射(反射及原理 / 动态代理及原理)

news/2024/5/19 5:19:43 标签: java, 反射, 动态代理

Java_反射(反射及原理 / 动态代理及原理)


本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.csdn.net/Rozol/article/details/77511333


反射: 在运行状态时, 能够获取任意一个类的所有属性和方法; 能够调用任意一个对象的所有属性和方法.
反射性能较低, 一般情况下不考虑用反射.
Proxy 动态代理: 用来修改已经具有的对象的方法, 控制方法是否执行, 或在方法执行之前和执行之后做一些额外的操作

Bean类

java">public class Bean {
	/**
	 * 公有属性
	 */
	public int number = 10;
	/**
	 * 私有属性
	 */
	private String name = Bean.class.getSimpleName();
	/**
	 * 静态属性
	 */
	public static int max = Integer.MAX_VALUE;
	/**
	 * 常量属性
	 */
	protected static final int min = Integer.MIN_VALUE;
	
	/**
	 * 无参构造
	 */
	public Bean(){}
	
	/**
	 * 有参构造
	 * @param numbder
	 */
	public Bean(int number){
		this.number = number;
	}
	
	/**
	 * 私有构造
	 * @param number
	 * @param name
	 */
	private Bean(int number, String name){
		this.number = number;
		this.name = name;
	}
	
	protected Bean(int number, String name, int max){
		this.number = number;
		this.name = name;
		Bean.max = max;
	}
	
	/**
	 * 公有方法
	 * @param number
	 */
	public void setNumber(int number){
		this.number = number;
	}
	
	public int getNumber(){
		return this.number;
	}
	
	/**
	 * 私有方法
	 */
	private void setName(String name){
		this.name = name;
	}
	
	private String getName(){
		return this.name;
	}
	
	/**
	 * 静态方法
	 * @param max
	 */
	public static void setMax(int max){
		Bean.max = max;
	}
	
	public static int getMax(){
		return Bean.max;
	}
	
	/**
	 * protected方法
	 * @param min
	 * @return
	 */
	protected int getMin(){
		return Bean.min;
	}

	@Override
	public String toString() {
		return "Bean [number=" + number + ", name=" + name + ", max=" + Bean.max + ", min =" + Bean.min + "]";
	}
}
java">@Deprecated
public class SubBean extends Bean implements Serializable{

	/**
	 * 调用父类方法, 并抛出一个异常
	 * @return
	 * @throws Exception
	 */
	public int getSuperNumber(int number) throws Exception{
		return super.getNumber();
	}
}

Class文件对象

在使用反射之前, 首先要获取Class类的文件对象, 然后才能通过反射去获取类的方法, 之后才能调用该方法.

java">/**
 * 获取Class文件对象的几种方式
 * @author Luzhuo
 */
public class GetClass {
	public static void main(String[] args) throws ClassNotFoundException {
		
		// 方式1: 通过对象获得
		GetClass getClass = new GetClass();
		Class<? extends GetClass> class1 = getClass.getClass();
		
		// 方式2: 通过数据类型的一个静态calss属性
		Class<GetClass> class2 = GetClass.class;
		
		// 方式3: 通过全类名获得
		Class<?> class3 = Class.forName("反射.reflect.GetClass");
		
		// 方式4: 通过 类加载器 加载
		Class<?> class4 = GetClass.class.getClassLoader().loadClass("反射.reflect.GetClass");
		
		System.out.println(class1 == class2 && class2 == class3 && class3 == class4); // true
	}
}

反射

反射: 在运行状态时, 能够获取任意一个类的所有属性和方法; 能够调用任意一个对象的所有属性和方法.

java">/**
 * 反射: 在运行状态时, 能够获取任意一个类的所有属性和方法;
 * 		 			    能够调用任意一个对象的所有属性和方法.
 * 
 * 反射性能较低, 一般情况下不考虑用反射.
 * @author Luzhuo
 */
public class reflect {
	public static void main(String[] args) throws Exception {
		// 创建对象(构造方法)
		constructor();
		
		// 属性
		field();
		
		// 方法
		method();
		
		// 案例: 通过反射调用已存在对象的方法
		callOldInstance();
	}


	/**
	 * 对象的创建(构造方法)
	 * 访问 private / protected 修饰的构造方法时, 需要取消语法检查
	 */
	private static void constructor() throws Exception {

		// 方式1: Class文件对象
		Class<?> clazz = Bean.class;
		Bean instance1 = (Bean) clazz.newInstance();
		
		// 方式2: Class文件对象的构造方法
		Constructor<?>[] cons = clazz.getConstructors(); // 所有公共构造方法
		Constructor<?>[] allcons = clazz.getDeclaredConstructors(); // 所有构造方法(public / private / protected)
		Constructor<?> con = clazz.getConstructor(int.class);  // 获取指定公有构造方法
		Constructor<?> decCon = clazz.getDeclaredConstructor(int.class, String.class); // 获取指定私有构造方法
		
		decCon.setAccessible(true); // 取消Java语言访问检查(获取 private / protected 构造时使用)
		
		Bean instance2 = (Bean) cons[0].newInstance(200);
		Bean instance3 = (Bean) cons[1].newInstance();

		System.out.println(instance1 == instance2 || instance2 == instance3); // false
		
		// 访问 private / protected 修饰的构造方法时, 需要取消语法检查
	}
	
	/**
	 * 属性
	 * 访问 private / protected / static final 修饰的属性时, 需要取消语法检查
	 * 修改 private / protected 修饰的属性时, 需要取消语法检查, 不能修改 static final 修饰的属性
	 * 能获取本类所有字段, 但不能获取父类除public外的所有字段
	 */
	private static void field() throws Exception {
		Class<?> clazz = Bean.class;
		
		// 获取成员变量
	    Field[] fields = clazz.getFields(); // 所有公共字段(public)(含父类)
		Field[] allfields = clazz.getDeclaredFields(); // 本类所有字段(public / private / protected)
		
		Object obj = clazz.getConstructor().newInstance(); // 创建对象

		// 使用成员变量
		// 公有属性
		Field number = clazz.getField("number"); // 获取指定公有字段(public)
		System.out.println("number: " + number.getInt(obj)); // 获取字段的值
		
		number.set(obj, 1000); // 设置字段的值
		System.out.println(obj.toString());
		
		// 私有属性
		Field name = clazz.getDeclaredField("name"); // 获取指定字段(...)
		name.setAccessible(true);
		System.out.println("name: " + name.get(obj));
		
		name.set(obj, "set name");
		System.out.println(obj.toString());
		
		// 静态属性
		Field max = clazz.getField("max");
		System.out.println("max: " + max.getInt(obj));
		
		max.set(obj, 123);
		System.out.println(obj.toString());
		
		// 常量属性(protected)
		Field min = clazz.getDeclaredField("min");
		min.setAccessible(true);
		System.out.println("min: " + min.getInt(obj));
		
		// min.set(obj, 321); // static final 修饰的属性不允许修改
		System.out.println(obj.toString());
		
		// 访问 private / protected / static final 修饰的属性时, 需要取消语法检查
		// 修改 private / protected 修饰的属性时, 需要取消语法检查, 不能修改 static final 修饰的属性
		// 能获取本类所有字段, 但不能获取父类除public外的所有字段
	}
	
	/**
	 * 方法
	 * 能获取该类的所有方法, 但是不能获取父类除public外的所有方法
	 * 访问 private / protected 修饰的方法时, 需要取消语法检查
	 */
	private static void method() throws Exception{
		Class<?> clazz = Bean.class;
		Method[] allmethods = clazz.getMethods(); // 获取所有公共方法(public / public final / public static)(含父类)
		Method[] methods = clazz.getDeclaredMethods(); // 获取本类所有方法(public / private / public static / private / protected ...)
		
		Object obj = clazz.getConstructor().newInstance(); // 创建对象
		
		// 方法
		// 公有
		Method setNumber = clazz.getMethod("setNumber", int.class); // 获取指定公共方法(public)
		setNumber.invoke(obj, 1000); // 调用方法
		
		Method getNumber = clazz.getMethod("getNumber");
		int number = (int) getNumber.invoke(obj);
		System.out.println("getNumber: " + number);
		
		// 私有
		Method setName = clazz.getDeclaredMethod("setName", String.class); // 获取指定方法(...)
		setName.setAccessible(true);
		setName.invoke(obj, "abc");
		
		Method getName = clazz.getDeclaredMethod("getName");
		getName.setAccessible(true);
		String name = (String) getName.invoke(obj);
		System.out.println("getName: " + name);
		
		// 静态
		Method setMax = clazz.getMethod("setMax", int.class);
		setMax.invoke(obj, 123); // 调用方法
		
		Method getMax = clazz.getMethod("getMax");
		int max = (int) getMax.invoke(obj);
		System.out.println("getMax: " + max);
		
		// protected
		Method getMin = clazz.getDeclaredMethod("getMin");
		getMin.setAccessible(true);
		int min = (int) getMin.invoke(obj);
		System.out.println("getMin: " + min);

		// 能获取该类的所有方法, 但是不能获取父类除public外的所有方法
		// 访问 private / protected 修饰的方法时, 需要取消语法检查
	}
	
	
	
	/**
	 * 通过反射调用已存在对象的方法
	 */
	private static void callOldInstance() throws Exception {
		Bean bean = new Bean();
		Class<? extends Bean> clazz = bean.getClass();
		
		// 通过反射调用私有方法
		Method getName = clazz.getDeclaredMethod("getName");
		getName.setAccessible(true);
 		String name = (String) getName.invoke(bean);
		System.out.println("getName: " + name);
		
		bean.number = 100000000;
		// 通过反射调用已存在对象的公有方法
		Method getNumber = clazz.getMethod("getNumber");
		int number = (int) getNumber.invoke(bean);
		System.out.println("getNumber: " + number);
	}
}

反射补充

反射相关的一些补充

java">/**
 * 反射的一些补充
 * @author Luzhuo
 */
public class Other {
	public static void main(String[] args) throws Exception {
		Class<?> clazz = SubBean.class;
		
		
		// 包名
		String packageName = clazz.getPackage().getName();
		System.out.println("包名: " + packageName); // 包名: 反射.Class
		
		// 完整类名
		String className = clazz.getName();
		System.out.println("完整类名: " + className); // 完整类名: 反射.Class.Clazz
		
		// 类名
		String classSimpleName = clazz.getSimpleName();
		System.out.println("类名: " + classSimpleName); // 类名: Clazz
		
		
		
		// 获取接口
		Class<?>[] interfaces = clazz.getInterfaces();
		System.out.println("Interfaces: " + Arrays.toString(interfaces));
		
		// 获取父类
		Class<?> superClass = clazz.getSuperclass();
		System.out.println("SuperClass: " + superClass);
		
		// 获取异常
		Method getSuperNumber = clazz.getMethod("getSuperNumber", int.class);
		Class<?>[] exceptions = getSuperNumber.getExceptionTypes();
		System.out.println("Exceptions: " + Arrays.toString(exceptions));
		
		// 获取权限修饰符
		int mod = getSuperNumber.getModifiers();
		System.out.println("Mod: " + Modifier.toString(mod));
		
		// 获取返回值类型
		Class<?> returnType = getSuperNumber.getReturnType();
		System.out.println("Return Type: " + returnType);
		
		// 获取参数列表
		Class<?>[] parameterTypes = getSuperNumber.getParameterTypes();
		System.out.println("ParameterTypes: " + Arrays.toString(parameterTypes));
		
		// 方法名
		System.out.println("GetSupperNumber Name: " + getSuperNumber.getName());
		
		// 获取属性类型
		Field number = clazz.getField("number");
		Class<?> type = number.getType();
		System.out.println("Field Type: " + type);
		
		
		
		// 获取类的注解
		// 其他获取注解的方法, 详见 注解 http://blog.csdn.net/rozol/article/details/77758192 文章
		Annotation[] classAnnotations = clazz.getAnnotations();
		System.out.println("ClassAnnotations: " + Arrays.toString(classAnnotations));
	}
}

反射的原理

先看下使用方式

java">// 使用正常的方式创建对象
Bean bean1 = new Bean(100);

// 使用反射的方式创建对象
Class clazz = Bean.class; // 获得字节码文件对象
Constructor constructor = clazz.getConstructor(int.class); // 获得构造对象
Bean bean2 = (Bean) constructor.newInstance(100); // 创建实例

同一个字节码对象

通过Java的双亲委托模型加载类的机制可知, 内存中只有一份同全类名的字节码文件对象, 所以以下这两种方式获得的字节码文件对象同一个.

java">Bean bean1 = new Bean(100);
Class clazz1 = bean1.getClass();

Class clazz2 = Bean.class;

System.out.println(clazz1 == clazz2); // true

new Bean()的原理:

当我们使用以下代码创建对象时,

java">Bean bean1 = new Bean(100);
  1. 获取 Bean.class字节码文件对象 (与反射用的是同一个字节码文件对象);
  2. Bean(100)就是构造方法
  3. new 构造方法, 创建了一个实例

怎么样, 是不是与反射是一致的.

字节码对象

字节码文件对象的产生:

  1. 编写代码文件 Bean.java
  2. javac编译成字节码文件 Bean.class
  3. Jvm加载Bean.class到内存, 字节码文件再由ClassLoader转成字节码文件对象

字节码文件对象的类型:

字节码文件对象的类型是 Class类型

字节码文件对象的加载时机

  1. new 出一个实例的时候
  2. 调用 静态成员变量 或 方法 的时候
  3. 反射获取字节码文件对象的时候
  4. 创建子类实例时, 会加载父类的字节码文件对象
  5. 使用java xxx.class命令运行该类的时候

字节码文件对象的组成:

类里面有什么, 字节码文件对象里就有什么.

字节码文件对象
成员变量成员变量对象(Constructor)
构造函数构造函数对象(Method)
成员函数成员函数对象(Field)
注解注解对象(Annotation)
.etc.etc对象

反射为什么会慢

比喻:
正常的使用相当于你已经知道这个东西就在房间的那个位置, 直接去拿就好了.
反射的使用相当于你不知道这个东西在哪, 但就在这个房间里, 你得一个一个的翻.

你们说哪个快?哪个慢?

这么多构造方法你得找吧

java">Constructor<?>[] allcons = clazz.getDeclaredConstructors();

这么多变量你得找吧

java">Field[] fields = clazz.getFields();

这么多方法哪个才是呢, 你这里没有, 你父亲那可能会有呢

java">Method[] allmethods = clazz.getMethods();

就算找到了这个方法, 你这个invoke()的可变Object参数列表, 还得帮你去匹配呀.

java">Method setNumber = clazz.getMethod("setNumber", int.class);
setNumber.invoke(obj, 1000);

动态代理

Proxy 动态代理: 用来修改已经具有的对象的方法, 控制方法是否执行, 或在方法执行之前和执行之后做一些额外的操作
jdk动态代理是由Java内部的反射机制来实现的
jdk动态代理需要基于同一的 接口

java">/**
 * Proxy 动态代理
 * 用来修改已经具有的对象的方法, 控制方法是否执行, 或在方法执行之前和执行之后做一些额外的操作
 * jdk动态代理是由Java内部的反射机制来实现的
 * jdk动态代理需要基于同一的 接口
 * @author Luzhuo
 */
public class ProxyTest {
	public static void main(String[] args) {
        User userProxy = initAccess(new ProxyTest.UserImpl());
  
        System.out.println(userProxy.getName());
        System.out.println(userProxy.getAge()); 
	}
	
	public static User initAccess(final User user){
        User userProxy = (User)Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), new InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				
				if ("getName".equals(method.getName())){
					System.out.println("getName() 调用之前");
					Object result = method.invoke(user, args);
					System.out.println("getName() 调用之后");
		            return result;
				}
		        
		        return method.invoke(user, args);
			}
		});
        return userProxy;
	}
	
	public static interface User {  
	    public String getName();  
	    public int getAge();  
	}  
	
	public static class UserImpl implements User {  
	    @Override  
	    public String getName() {  
	        System.out.println("UserServiceImpl - getName");  
	        return "name";  
	    }  
	  
	    @Override  
	    public int getAge() {  
	        System.out.println("UserServiceImpl - getAge");  
	        return 12;  
	    }
	}
}

输出结果:

getName() 调用之前
UserServiceImpl - getName
getName() 调用之后
name
UserServiceImpl - getAge
12

Java Proxy 的原理

通过动态构建了新的Class实现的, 在我们调用接口时, 才会去生成字节码, 所以对性能消耗较大.

由ProxyGenerator构建代理Class字节码文件, 再由ClassLoader转成Class字节码文件对象.

生成的字节码文件对象会被放在内存的方法区, 不会被回收掉.

新生成的字节码文件对象

新生成给的类, 类名为"接口名$Proxy", 同时该类实现User接口.
由ProxyGenerator构建, 可通过以下代码写入文件.

java">@Test
public void buildProxyClass() throws Exception{
	byte[] bytes = ProxyGenerator.generateProxyClass("User$Proxy", new Class[]{User.class});
	FileOutputStream os = new FileOutputStream(new File("User$Proxy.class"));
	os.write(bytes);
	os.close();
}

User$Prox.class

我们用反编译工具对该文件进行查看, 得到以下代码

java">package p000;

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

/* renamed from: User$Proxy */
public final class User$Proxy extends Proxy implements User {
    /* renamed from: m0 */
    private static Method f0m0;
    /* renamed from: m1 */
    private static Method f1m1;
    /* renamed from: m2 */
    private static Method f2m2;
    /* renamed from: m3 */
    private static Method f3m3;
    /* renamed from: m4 */
    private static Method f4m4;

    static {
        try {
            f1m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            f3m3 = Class.forName("ProxyTest$User").getMethod("getName", new Class[0]);
            f2m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            f4m4 = Class.forName("ProxyTest$User").getMethod("getAge", new Class[0]);
            f0m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        } catch (ClassNotFoundException e2) {
            throw new NoClassDefFoundError(e2.getMessage());
        }
    }

    public User$Proxy(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    public final boolean equals(Object obj) {
        try {
            return ((Boolean) this.h.invoke(this, f1m1, new Object[]{obj})).booleanValue();
        } catch (Error | RuntimeException e) {
            throw e;
        } catch (Throwable e2) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(e2);
        }
    }

    public final int getAge() {
        try {
            return ((Integer) this.h.invoke(this, f4m4, null)).intValue();
        } catch (Error | RuntimeException e) {
            throw e;
        } catch (Throwable e2) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(e2);
        }
    }

    public final String getName() {
        try {
            return (String) this.h.invoke(this, f3m3, null);
        } catch (Error | RuntimeException e) {
            throw e;
        } catch (Throwable e2) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(e2);
        }
    }

    public final int hashCode() {
        try {
            return ((Integer) this.h.invoke(this, f0m0, null)).intValue();
        } catch (Error | RuntimeException e) {
            throw e;
        } catch (Throwable e2) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(e2);
        }
    }

    public final String toString() {
        try {
            return (String) this.h.invoke(this, f2m2, null);
        } catch (Error | RuntimeException e) {
            throw e;
        } catch (Throwable e2) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(e2);
        }
    }
}

可见内部是通过反射获得方法对象

java">f3m3 = Class.forName("ProxyTest$User").getMethod("getName", new Class[0]);

当我们通过接口调用

java">userProxy.getName()

时, User$Proxy类内部其实是用InvocationHandler接口进行回调

java">return (String) this.h.invoke(this, f3m3, null);

喏, 就回调执行我们写的这些代码.

java">new InvocationHandler() {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if ("getName".equals(method.getName())){
            System.out.println("getName() 调用之前");
            Object result = method.invoke(user, args);
            System.out.println("getName() 调用之后");
            return result;
        }

        return method.invoke(user, args);
    }
}

http://www.niftyadmin.cn/n/996540.html

相关文章

bootstrap学习——javascript插件篇

飞近期做的一个小项目须要用到一个模态框和一个图片浏览插件&#xff0c;并把二者结合&#xff0c;刚好bootstrap有相应插件&#xff0c;下面是学习应用流程&#xff1a; 1. 引入js文件&#xff1a; 能够单个引入相应插件文件&#xff0c;或一次所有引入。飞是选择的后者&am…

group user

例子 groupadd test 创建test用户组 useradd user1 创建user1用户 passwd user1 设置user1的密码 useradd user2 创建user2用户 passwd user2 设置user2的密码 gpasswd -a user1 test 把user1用户添加到test用户组 gpasswd -a user2 test 把user2用…

Java_注解 (元注解 / Java内置注解 / Android内置注解)

Java_注解 (元注解 / Java内置注解 / Android内置注解) 本文由 Luzhuo 编写,转发请保留该信息. 原文: http://blog.csdn.net/rozol/article/details/77758192 注解: 给程序看的提示. (注释: 给程序员看的提示) Java1.5开始引入注解功能. 元数据: 描述数据的数据, 可以说注解…

go语言中container容器数据结构heap、list、ring

heap堆的使用&#xff1a; package mainimport ("container/heap""fmt" )type IntHeap []int//我们自定义一个堆需要实现5个接口 //Len(),Less(),Swap()这是继承自sort.Interface //Push()和Pop()是堆自已的接口//返回长度 func (h *IntHeap) Len() int {r…

Java_ClassLoader(ClassLoader / 自定义ClassLoader / DexClassLoader)

Java_ClassLoader(ClassLoader / 自定义ClassLoader / DexClassLoader) 本文由 Luzhuo 编写,转发请保留该信息. 原文: http://blog.csdn.net/rozol/article/details/77758194 ClassLoader 用来动态加载class文件到内存中使用的 ClassLoader 使用 双亲委托模型 来搜索类的 C…

JavaScript中操作数组的常用函数汇总

1.concat()语法&#xff1a;arrayObject.concat(array1,array2,......,arrayn)作用&#xff1a;concat() 方法用于连接两个或多个数组。特征&#xff1a;该方法不会改变现有的数组&#xff0c;而仅仅会返回被连接数组的一个副本。示例&#xff1a; var arr1 [1,2,3];var arr2 …

Asp.Net Core 集成 Hangfire 配置使用 Redis 存储

Hangfire 官方支持 MSSQL 与 Redis(Hangfire.Pro.Redis) 两种 &#xff0c;由于我的数据库是 MYSQL &#xff0c;粗略查询了一下文档&#xff0c;现在对 .NET Core 支持的并不够好&#xff0c;所有就选择了 Redis;当然也可以使用第三方来支持 PostgreSql,Mongo等 安装 Redis su…

Java_内存

Java_内存 本文由 Luzhuo 编写,转发请保留该信息. 原文: http://blog.csdn.net/Rozol/article/details/78050885 运行时数据区域分为: 方法区 / 堆 / Java栈 / Native方法栈 / 程序计数器 内存 判断 / 回收 算法 新生代内存划分为 Eden[‘i:dn] 和 Survivor[sərˈvaɪvə(…