You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java代理模式内部工作原理及Spring自动装配等技术疑问

Java代理模式原理、懒加载对象创建与Spring自动装配详解

Hey there! Let's break down your questions step by step—starting with how proxy patterns work under the hood in Java, then addressing your confusion about lazy-loaded object creation, and finally explaining Spring's autowiring mechanism.

一、Java代理模式的内部工作原理

代理模式的核心是用一个代理对象包裹真实对象,控制对真实对象的访问。Java里主要有两种代理方式:

1. 静态代理

这是最简单的形式:你手动编写一个代理类,实现和真实对象相同的接口(或继承相同类),在代理类的方法里调用真实对象的方法。但这种方式灵活性差,每个真实类都要对应一个代理类,框架几乎不会用它。

2. 动态代理(框架常用)

框架(Spring、Hibernate)依赖动态代理实现懒加载、AOP等功能,主要有两种实现:

  • JDK动态代理:基于接口实现,JDK会在运行时动态生成一个实现目标接口的代理类,通过InvocationHandler接口的invoke方法拦截所有方法调用。
  • CGLIB代理:基于继承实现,通过字节码生成技术生成目标类的子类作为代理类,用MethodInterceptor拦截方法调用(适合没有实现接口的类)。

两种动态代理的核心逻辑一致:代理对象会拦截所有方法调用,在调用真实对象的方法前后插入自定义逻辑(比如懒加载时创建真实对象)。


二、为什么没写new B(),调用b.bgh时能创建B对象?

你的困惑点在于懒加载(Lazy Loading),框架(比如Spring、Hibernate)默认对某些Bean启用懒加载,不会在容器启动时创建真实对象,而是生成一个代理对象代替真实对象注入到依赖中。

具体流程(以Spring为例):

  1. 当Spring容器初始化时,扫描到B类是懒加载Bean,不会直接创建B的实例,而是用动态代理(CGLIB或JDK)生成一个B的代理对象。
  2. 这个代理对象被注入到A类的b属性中(也就是你代码里的A中的B b其实是代理对象,不是真实的B实例)。
  3. 当你第一次调用b.bgh时:
    • 代理对象的拦截方法(比如CGLIB的intercept或JDK的invoke)被触发。
    • 拦截器检查到真实的B对象还未创建,就通过反射调用B的构造方法创建实例。
    • 如果B还有依赖(比如你的代码里的C c),Spring会同时完成C的创建和注入。
    • 最后把方法调用转发给真实的B对象,返回bgh的值。

简化代码示例模拟这个过程:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 模拟Spring的懒加载代理逻辑
class LazyBeanLoader<T> implements MethodInterceptor {
    private final Class<T> targetClass;
    private T realInstance;

    public LazyBeanLoader(Class<T> targetClass) {
        this.targetClass = targetClass;
    }

    @Override
    public Object intercept(Object proxyObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 第一次调用时才创建真实对象
        if (realInstance == null) {
            realInstance = targetClass.getDeclaredConstructor().newInstance();
            // 模拟Spring自动注入B的依赖C
            if (realInstance instanceof B) {
                ((B) realInstance).c = new C();
            }
        }
        // 调用真实对象的方法
        return methodProxy.invoke(realInstance, args);
    }

    // 获取代理对象
    public T getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }
}

// 你的原始类
class A {
    int abc;
    B b;
    A() {}
}

class B {
    int bgh;
    C c;
    B() {}
}

class C {
    String jkl;
}

// 测试
public class LazyLoadingDemo {
    public static void main(String[] args) {
        A a = new A();
        // 注入代理对象,而非真实B实例
        a.b = new LazyBeanLoader<>(B.class).getProxy();
        
        // 第一次调用b.bgh,触发真实B对象的创建
        System.out.println(a.b.bgh); 
    }
}

三、Spring自动装配的工作原理

Spring的自动装配(Autowiring)是容器帮你自动完成Bean之间的依赖注入,核心流程如下:

  1. Bean定义扫描:Spring容器启动时,扫描指定包下带有@Component(或@Service、@Repository等)注解的类,将它们注册为BeanDefinition(包含类信息、作用域、懒加载配置等)。
  2. 依赖解析:当容器处理A类的Bean时,发现它有一个B b的属性带有@Autowired注解,就会在容器中查找匹配的B类型Bean。
  3. 代理对象注入:如果B是懒加载Bean,容器不会创建真实的B实例,而是注入前面提到的动态代理对象。
  4. 懒加载触发:当你第一次使用A中的b(比如调用b.bgh),代理对象的拦截逻辑会触发真实B对象的创建,同时自动完成B对C的依赖注入(如果C也是容器管理的Bean)。

自动装配的匹配规则:

  • 优先按类型匹配容器中的Bean,如果有多个同类型Bean,再按名称匹配。
  • 可以用@Qualifier("beanName")指定具体要注入的Bean名称,用@Primary标记优先选择的Bean。

内容的提问来源于stack exchange,提问作者dgupta3091

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:25:44