反射的概念:
Java中反射机制是一种强大的运行时元编程工具,它允许程序在运行时获取关于类、接口、字段和方法的信息,并能动态地操作它们。主要核心作用为:
1. 动态加载与检查类信息:
在编译期并不需要知道所有类型的具体信息,可以在运行时根据需求加载类,并通过Class对象查看类的详细结构,如包名、父类、实现的接口、声明的方法和变量等。
2. 创建对象和调用方法:
反射API可以绕过通常的构造函数创建对象的方式,通过Class对象的newInstance()方法或Constructor对象的newInstance()方法来动态创建对象。
也可以在运行时查找并调用类的方法,包括私有方法。
3. 访问和修改字段:
即使是私有的字段,反射也能让我们在运行时获取其值,修改其值,或者设置为可访问(setAccessible(true))。
4. 动态代理:
Java反射机制在实现动态代理时扮演了重要角色,通过InvocationHandler接口和Proxy类配合使用,可以在运行时创建一个能够动态处理方法调用的对象。
5. 框架与中间件中的应用:
在许多Java框架中广泛运用反射机制,例如Spring框架依赖注入时会通过反射实例化Bean并对属性进行赋值;ORM框架(如Hibernate)则利用反射将Java对象与数据库表进行映射。
6. 安全性与性能考量:
虽然反射提供了很大的灵活性,但过度使用可能带来安全风险(如破坏封装性)以及性能上的损失(因为反射操作相比直接调用要慢很多)。
因此,Java反射机制提供了一种灵活的方式来操作已加载类的内部结构,使得代码能够在不预先知道具体类型的情况下执行更复杂的操作,增强了程序的适应性和扩展性。
JDK动态代理:
* JDK反射中的动态代理主要通过java.lang.reflect.Proxy类来实现。
* 使用JDK动态代理的前提条件是目标类必须至少实现一个接口,因为JDK动态代理生成的代理类会实现与目标类相同的接口。
* 在运行时,JDK会根据接口生成一个新的代理类(如$Proxy0.class),这个代理类会重写接口中定义的所有方法,并在这些方法内部添加额外的处理逻辑,通常这个逻辑由InvocationHandler接口的一个实现类提供。
代码实现:
1. 先假设一个存在的接口SomeInterface与它的实现类TargetImplementation
public interface SomeInterface {
void someMethod();
}
public class TargetImplementation implements SomeInterface {
@Override
public void someMethod() {
System.out.println("Inside TargetImplementation.someMethod()");
}
}
2. 创建MyInvocationHandler类 继承InvocationHandler 类
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("Before method call");
Object result = method.invoke(target, args);
System.out.println("After method call");
return result;
}
}
3. 使用代理
public class JdkProxyExample {
public static void main(String[] args) {
// 目标类实现了某个接口
SomeInterface target = new TargetImplementation();
// 创建InvocationHandler实现类实例
MyInvocationHandler handler = new MyInvocationHandler(target);
// 动态创建并返回代理对象
SomeInterface proxy = (SomeInterface) Proxy.newProxyInstance(
SomeInterface.class.getClassLoader(),
new Class<?>[]{SomeInterface.class},
handler
);
// 通过代理对象调用方法
proxy.someMethod();
}
}
执行结果:
Before method call
Inside TargetImplementation.someMethod()
After method call
CGLIB代理:
* CGLIB(Code Generation Library)是一个第三方库,它使用ASM字节码生成框架来动态生成字节码,从而创建出目标类的子类。
* CGLIB代理不依赖于目标类是否实现了接口,因此可以对没有接口的类进行代理。
* CGLIB代理会在运行时为目标类生成一个继承自目标类的子类,在子类中覆盖父类的方法并在方法内部插入增强代码,通常这个逻辑由MethodInterceptor接口的一个实现类提供。
代码实现:
1. 实现 CGLIB需要引入对应的pom文件
<dependencies>
<!-- CGLIB -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!-- ASM, CGLIB内部依赖 -->
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.2</version>
</dependency>
<!-- 如果需要处理Java 8及以上版本的方法引用、lambda表达式等特性,还需要添加ASM分析器 -->
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-analysis</artifactId>
<version>9.2</version>
</dependency>
<!-- 若使用的是Spring框架,Spring已包含了对CGLIB的支持,无需单独引入 -->
<!-- 若不是Spring环境,请确保按照上述方式引入CGLIB和ASM依赖 -->
</dependencies>
2. 创建TargetClass类,由于CGLIB并不依赖于接口,因此即使TargetClass不实现任何接口
public class TargetClass {
public void someMethod() {
System.out.println("Inside TargetClass.someMethod()");
}
}
3. 创建MyMethodInterceptor 继承 MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyMethodInterceptor implements MethodInterceptor {
private Object target;
public MyMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method call");
Object result = proxy.invokeSuper(obj, args); // 调用父类(即目标类)的方法
System.out.println("After method call");
return result;
}
}
4. 使用:
public class CglibProxyExample {
public static void main(String[] args) {
// 目标类,无需实现接口
TargetClass target = new TargetClass();
// 创建MethodInterceptor实现类实例
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class);
enhancer.setCallback(new MyMethodInterceptor(target));
// 动态创建并返回代理对象
TargetClass proxy = (TargetClass) enhancer.create();
// 通过代理对象调用方法
proxy.someMethod();
}
}
总结一下:
1. JDK动态代理基于接口实现,适合面向接口编程的场景,代理效率相对较高且内存占用较小。
2. CGLIB代理适用于无接口或需要更灵活代理策略的情况,但它的代理对象生成速度较慢,且由于创建了新的类导致内存占用稍大。不过对于方法调用性能上,随着JDK版本更新,两者之间的差距已经越来越小。