一、定义
Java 反射(Reflection)是一个强大的机制,它允许程序在运行时查询、访问和修改类、接口、字段和方法信息。反射提供了一种动态地操作类的能力,这在很多框架和库中被使用,例如 Spring 框架的依赖注入,Spring Boot、MyBatis 等等框架中也都使用了反射机制,主要是通过反射机制实现动态代理。
1.1 代理模式
代理模式就是为其他对象提供一种代理以控制这个对象的访问。所有访问这个对象的资源都需要通过代理。

1.2 静态代理的缺点
-
代码灵活度不高 :
静态代理的时候,如果接口上定义了很多方法,代理类里面也要写很多方法;而动态代理实现的时候虽然接口上定义了很多方法,但是动态代理类始终只有一个 invoke() 方法。这样,当需要代理的接口发生变化的时候,动态代理的接口就不需要跟着变化。
-
开发及维护成本大 :
在静态代理中,需要在代理类中,将原始类的所有方法都重新实现一遍,如果要添加附加功能的类不止一个,还需要针对每个类都创建一个代理类,这样大大增加了开发及维护成本。
1.3 动态代理
Java 对代理模式提供了内建的支持,在 java.lang.reflect 包下面,提供了一个 Proxy 类和一个 InvocationHandler 的接口。下面是实现步骤:
- 实现一个 InvocationHandler 接口。
- 需要提供一个方法来实现:把具体的目标对象和动态代理绑定起来,并在绑定好后返回被代理的目标对象的接口,以便于客户端的操作。
- 需要实现 invoke() 方法。在这个方法中实现,判断具体调用的方法以及处理逻辑。
实例代码:
java
public class UserInvocationHandler implements InvocationHandler{
/**
*
* 被代理的对象
*/
private Object target;
public Object newInstance(Object target) throws Exception{
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
System.out.println("开始事务....");
method.invoke(this.target,args);
}catch (Exception e){
System.out.println("回滚事务....");
}finally {
System.out.println("提交事务....");
}
return null;
}
}
客户端使用这个动态代理,代码如下:
java
IUserService userService = new UserServiceImpl();
IUserService userServiceProxy = (IUserService) Proxy.
newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),
new UserInvocationHandler());
userServiceProxy.save();
二、反射机制的优缺点
- 优点
可以让代码更灵活、为各种框架提供开箱即用的功能提供了便利。 - 缺点
增加了安全问题,比如可以无视泛型参数的安全检查(发生在编译时)。另外,反射的性能也要稍差一点。不过对于框架来说,实际影响不大。
三、工作流程
- 获取 Class 对象:首先获取目标类的 Class 对象。
- 获取成员信息:通过 Class 对象,可以获取类的字段、方法、构造函数等信息。
- 操作成员:通过反射 API 可以读取和修改字段的值、调用方法以及创建对象。
3.1 Class 对象
在一个JVM中,每个类都只会有一个类对象,所以无论使用哪种方法获得的类对象都是同一个。 准确的讲是一个ClassLoader下,一种类,只会有一个类对象存在。通常一个JVM下,只会有一个ClassLoader。
3.2 获取 Class 对象的四种方式
-
知道具体类的情况下
javaClass testClass = Test.class;
-
通过 Class.foeName() 传入类的全路径获取
javaClass test = Class.forName("com.org.example.Test");
-
通过对象实例 instance.getClass() 获取
javaTest t = new Test(); Class t2 = t.getClass();
-
通过类加载器 xxxClassLoader.loadClass() 传入类路径获取
javaClassLoader.getSystemClassLoader().loadClass("com.org.example.Test")
通过类加载器获取 Class 对象不会进行初始化,这表示不会进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行。
四、应用
4.1 创建对象
可以使用反射动态创建对象:
Class<?> clazz = Class.forName("java.lang.String");
Object obj = clazz.getDeclaredConstructor().newInstance();
4.2 访问字段
可以通过反射访问和修改类的字段:
java
Class<?> clazz = Person.class;
Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // 如果字段是私有的,需要设置为可访问
Object value = field.get(personInstance); // 获取字段值
field.set(personInstance, "New Name"); // 设置字段值
4.3 调用方法
可以通过反射调用类的方法:
java
Class<?> clazz = Person.class;
Method method = clazz.getMethod("sayHello");
method.invoke(personInstance);
Method methodWithArgs = clazz.getMethod("greet", String.class);
methodWithArgs.invoke(personInstance, "World");
4.4 获取构造函数
可以使用反射获取和调用构造函数:
java
Class<?> clazz = Person.class;
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("John", 30);
4.5 获取接口和父类
可以使用反射获取类实现的接口和父类:
java
Class<?> clazz = Person.class;
// 获取所有接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> i : interfaces) {
System.out.println("Interface: " + i.getName());
}
// 获取父类
Class<?> superClass = clazz.getSuperclass();
System.out.println("Superclass: " + superClass.getName());