java反射JDK与CGLIB反射

反射的概念:

        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版本更新,两者之间的差距已经越来越小。

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值