Java 反射机制与动态代理我们平时写代码可能用得比较少,但在各种常见的框架(Spring、MyBatis 等)中却屡见不鲜。有句话叫“无反射,不框架;无代理,不框架”。
由于以后打算阅读和学习框架的源码,这里先简单回顾反射机制和动态代理(暂不深入分析实现原理),为后面做些准备。
Java 反射机制是在 Java 程序运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。
从面向对象的角度来看,我们平时用到的"类"、"构造器"、"属性"、"方法"其实也是一个"类",它们在 JDK 中分别对应 Class、Constructor、Field、Method 类。其中 Class 相当于"类的类",可称为"元类",从这个角度看,我们平时自定义的"类"可以理解为 Class 的一个对象。
简单来说(个人理解),反射机制就是通过 Class、Constructor 等"元类"来操作其他的普通类(创建对象、调用方法等)。下面以简单代码示例。
定义一个普通的 Java 类 Human,如下:
package com.jaxer.example.reflection;
public class Human { public String gender;
private int age;
protected void hello() { }
public void hi() { }}
定义一个普通的 Person 类,继承自 Human,如下:
public class Person extends Human { // 两个属性 public String name;
private int age;
// 各种修饰符的构造器 public Person() { }
private Person(String name) { this.name = name; }
protected Person(int age) { this.age = age; }
Person(String name, int age) { this.name = name; this.age = age; } // getter, setter 方法 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; }
// 自定义方法 private void test() { System.out.println("test is invoked."); }
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; }}
该 Person 类定义了 name 和 age 两个成员变量及其 getter/setter 方法,还有四个构造器,分别使用不同的修饰符,自定义了一个 test 方法,重写了 toString 方法。
PS: 这两个类仅供参考,只是为了演示。
获取 Person 的 Class 对象通常有下面三种方式:
// 方式一:从对象获取Person person = new Person();person.getClass();
// 方式二:类名.classPerson.class;
// 方式三:Class.forNameClass<?> aClass = Class.forName("com.jaxer.example.reflection.Person");
/* 这三种方式的获取到的都是: class com.jaxer.example.reflection.Person 而且可以证明,这三种方式得到的同一个 Class 对象,即同一个 Person 类*/
获取到了 Class 对象,就可以使用 Class 对象来创建 Person 对象或者做一些其他操作,例如:
// 获取 Class 对象Class<?> aClass = Class.forName("com.jaxer.example.reflection.Person");
// 使用 Class 创建 Person 对象System.out.println(aClass.newInstance()); // 创建 Person 对象
// 获取 Person 的父类(Human)System.out.println("superClass-->" + aClass.getSuperclass());
/* 输出结果: * Person{name='null', age=0} * superClass-->class com.jaxer.example.reflection.Human */
Class 中获取构造器(Constructor)的方法如下:
测试代码:
private static void testConstructors(Class<?> aClass) throws Exception { // 1. 获取所有public构造器 Constructor<?>[] constructors = aClass.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println("constructor-->" + constructor); }
// 2. 获取所有构造器 Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println("declaredConstructor-->" + declaredConstructor); }
// 4. 获取指定的构造器(根据参数类型) Constructor<?> constructor = aClass.getDeclaredConstructor(String.class); // 修改访问权限(用这种方式可以使用 private 类型构造器创建对象) constructor.setAccessible(true); // 使用该构造器创建 Person 对象 Object instance = constructor.newInstance("Ace"); System.out.println(instance);}
输出结果如下:
constructor-->public com.jaxer.example.reflection.Person()
declaredConstructor-->com.jaxer.example.reflection.Person(java.lang.String,int)declaredConstructor-->protected com.jaxer.example.reflection.Person(int)declaredConstructor-->private com.jaxer.example.reflection.Person(java.lang.String)declaredConstructor-->public com.jaxer.example.reflection.Person()
Person{name='Ace', age=0}
获取 Class 属性(Field)的方法如下:
测试代码:
private static void testFields(Class<?> aClass) throws Exception { // 1. 获取 public 属性(包含父类的 public 属性) Field[] fields = aClass.getFields(); for (Field field : fields) { System.out.println("field-->" + field); } // 2. 获取所有属性 Field[] declaredFields = aClass.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("declaredField-->" + declaredField); } // 4. 获取指定的属性(这里获取 age 属性) Field age = aClass.getDeclaredField("age"); System.out.println("age-->" + age); // 给指定对象的属性赋值(private 类型不能赋值,需要修改访问权限) Object obj = aClass.newInstance(); age.setAccessible(true); // 修改访问权限 age.set(obj, 18); // 设置新值 System.out.println("obj-->" + obj);}
输出结果:
field-->public java.lang.String com.jaxer.example.reflection.Person.namefield-->public java.lang.String com.jaxer.example.reflection.Human.gender
declaredField-->public java.lang.String com.jaxer.example.reflection.Person.namedeclaredField-->private int com.jaxer.example.reflection.Person.age
age-->private int com.jaxer.example.reflection.Person.ageobj-->Person{name='null', age=18}
获取 Class 方法(Method)的方法如下:
测试代码:
private static void testMethods(Class<?> aClass) throws Exception { // 1. 获取所有 public 方法(包括父类及 Object 类) Method[] methods = aClass.getMethods(); for (Method method : methods) { System.out.println("method-->" + method); } // 2. 获取该类声明的所有方法 Method[] declaredMethods = aClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("declaredMethod-->" + declaredMethod); } // 4. 获取指定的方法并调用 Method method = aClass.getDeclaredMethod("test"); method.setAccessible(true); // 修改访问权限 System.out.println("方法名:" + method.getName()); System.out.println("方法修饰符:" + Modifier.toString(method.getModifiers())); Object instance = aClass.newInstance(); System.out.println(method.invoke(instance));}
输出结果:
// getMethods 方法返回结果: // Person 类的 public 方法:method-->public java.lang.String com.jaxer.example.reflection.Person.toString()method-->public java.lang.String com.jaxer.example.reflection.Person.getName()method-->public void com.jaxer.example.reflection.Person.setName(java.lang.String)method-->public void com.jaxer.example.reflection.Person.setAge(int)method-->public int com.jaxer.example.reflection.Person.getAge() // Human 类的 public 方法:method-->public void com.jaxer.example.reflection.Human.hi() // Object 类的 public 方法:method-->public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptionmethod-->public final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptionmethod-->public final void java.lang.Object.wait() throws java.lang.InterruptedExceptionmethod-->public boolean java.lang.Object.equals(java.lang.Object)method-->public native int java.lang.Object.hashCode()method-->public final native java.lang.Class java.lang.Object.getClass()method-->public final native void java.lang.Object.notify()method-->public final native void java.lang.Object.notifyAll()
// getDeclaredMethods 方法返回结果:declaredMethod-->public java.lang.String com.jaxer.example.reflection.Person.toString()declaredMethod-->public java.lang.String com.jaxer.example.reflection.Person.getName()declaredMethod-->public void com.jaxer.example.reflection.Person.setName(java.lang.String)declaredMethod-->private void com.jaxer.example.reflection.Person.test()declaredMethod-->public void com.jaxer.example.reflection.Person.setAge(int)declaredMethod-->public int com.jaxer.example.reflection.Person.getAge()
// 调用 test 方法:方法名:private void com.jaxer.example.reflection.Person.test()方法修饰符:privatetest is invoked.null
框架中常用的反射机制主要是以上那些,这里暂不深究其实现原理,以后有需要再行补充。下面简要分析代理相关的内容。
使用代理的主要目的:对目标对象(的方法)进行功能增强,例如 Spring 的 AOP。
既然是对目标对象的方法进行增强,代理对象的方法中一定会调用目标对象的方法。而且一般会在目标对象的方法调用前后(或者其他时机)做一些其他的处理以达到增强的效果。
代理模式通常分为「静态代理」和「动态代理」,静态代理使用较少;而动态代理常用的有 JDK 动态代理和 CGLib 动态代理。下面简要分析。
一个普通的 Java 接口及其实现类如下:
// 接口public interface UserService { void save(String name);}
// 实现类public class UserServiceImpl implements UserService { @Override public void save(String name) { System.out.println("保存用户名:" + name); }}
无论静态代理还是动态代理,都是对该类的 save 方法进行增强。
此处以在该方法执行前后各打印一句话来演示。
主要思想:创建一个 UserService 接口的实现类 UserServiceProxy(代理对象),并且该类持有 UserServiceImpl 对象(目标对象),在 UserServiceProxy 类的 save 方法中调用 UserServiceImpl 的 save 方法,示例代码如下:
public class UserServiceProxy implements UserService { private UserService userService;
public UserServiceProxy(UserService userService) { this.userService = userService; }
@Override public void save(String name) { System.out.println("---静态代理:方法执行前---"); userService.save(name); // 调用目标类的方法 System.out.println("---静态代理:方法执行后---"); }}
测试代码:
private static void testStaticProxy() { UserService userService = new UserServiceImpl(); UserService proxy = new UserServiceProxy(userService); proxy.save("jack");}
/* 运行结果: ---静态代理:方法执行前--- 保存用户:jack ---静态代理:方法执行后---*/
显而易见,使用 UserServiceProxy 可以增强 UserServiceImpl 的 save 方法。但由于代码是固定的(编码期间写好的),不够灵活,因此静态代理使用较少,通常使用动态代理。
PS: 此处代码实现形式可能不尽相同,但思路相近。
与静态代理相比,动态代理则是在运行时动态生成一个代理类,该类可以对目标对象的方法进行功能增强。动态代理常用的有 JDK 动态代理和 CGLib 动态代理,下面简要分析。
JDK 的动态代理实现方式:
使用 Proxy 类的 newProxyInstance 方法创建代理对象,使用 InvocationHandler 来实现增强的逻辑(通常创建一个 InvocationHandler 接口的实现类,在其 invoke 方法中实现增强的逻辑)。示例代码如下(仅供参考):
// JDK 代理工厂,作用是生成代理对象public class JDKProxyFactory { // 获取 target 的代理对象(其中 target 为目标对象) public Object getProxy(Object target) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new MyInvocationHandler(target)); } // 自定义 InvocationHandler private static class MyInvocationHandler implements InvocationHandler { // 目标对象 private Object target;
public MyInvocationHandler(Object target) { this.target = target; } // 实现增强逻辑 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("目标类方法执行前-----"); Object result = method.invoke(target, args); // 调用目标对象的方法 System.out.println("目标类方法执行后-----"); return result; } }}
测试代码:
private static void testJdkProxy() { UserService userService = new UserServiceImpl(); // 目标对象 JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(); // 代理工厂 // 使用代理工厂生成代理对象 UserService proxy = (UserService) jdkProxyFactory.getProxy(userService); proxy.save("jack"); // 调用代理对象的方法(已对目标对象进行增强)}
/* 运行结果: 目标类方法执行前----- 保存用户:jack 目标类方法执行后-----*/
实现原理可参考:https://blog.csdn.net/yhl_jxy/article/details/80586785
主要思路:
创建一个目标类的子类 Enhancer,在子类中设置回调对象(MethodInterceptor),并在回调方法(intercept)中实现对目标对象的增强功能逻辑。示例代码如下:
// CGLIB 工厂,用于生成代理对象public class CglibProxyFactory { // 获取代理对象(对目标对象进行增强) public Object getProxy(Object target) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(new MyMethodInterceptor()); // 设置回调 return enhancer.create(); }
// 自定义方法拦截 MethodInterceptor private static class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("cglib方法调用前"); Object o = proxy.invokeSuper(obj, args); // 调用目标对象的方法 System.out.println("cglib方法调用后"); return o; } }}
测试代码:
private static void testCglibProxy() { UserService userService = new UserServiceImpl(); CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(); UserService proxy = (UserService) cglibProxyFactory.getProxy(userService); proxy.save("jack");}
/* 运行结果: cglib方法调用前 保存用户:jack cglib方法调用后*/
PS: 使用 CGLib 需要导入第三方 jar 包(cglib 和 asm)。
实现原理可参考:https://blog.csdn.net/yhl_jxy/article/details/80633194
二者主要区别:
反射机制:简单来说,反射机制主要是通过 Class、Constructor 等"元类"来操作其他的普通类,以达到在运行期间动态创建对象、动态调用方法等目的。
静态代理&动态代理:二者主要区别在于时机,静态代理在编码期已写好代理类,而动态代理则是在运行期间动态生成代理类。
JDK 动态代理&CGLib 动态代理的主要区别: