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为例):
- 当Spring容器初始化时,扫描到
B类是懒加载Bean,不会直接创建B的实例,而是用动态代理(CGLIB或JDK)生成一个B的代理对象。 - 这个代理对象被注入到
A类的b属性中(也就是你代码里的A中的B b其实是代理对象,不是真实的B实例)。 - 当你第一次调用
b.bgh时:- 代理对象的拦截方法(比如CGLIB的
intercept或JDK的invoke)被触发。 - 拦截器检查到真实的
B对象还未创建,就通过反射调用B的构造方法创建实例。 - 如果
B还有依赖(比如你的代码里的C c),Spring会同时完成C的创建和注入。 - 最后把方法调用转发给真实的
B对象,返回bgh的值。
- 代理对象的拦截方法(比如CGLIB的
简化代码示例模拟这个过程:
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之间的依赖注入,核心流程如下:
- Bean定义扫描:Spring容器启动时,扫描指定包下带有
@Component(或@Service、@Repository等)注解的类,将它们注册为BeanDefinition(包含类信息、作用域、懒加载配置等)。 - 依赖解析:当容器处理
A类的Bean时,发现它有一个B b的属性带有@Autowired注解,就会在容器中查找匹配的B类型Bean。 - 代理对象注入:如果
B是懒加载Bean,容器不会创建真实的B实例,而是注入前面提到的动态代理对象。 - 懒加载触发:当你第一次使用
A中的b(比如调用b.bgh),代理对象的拦截逻辑会触发真实B对象的创建,同时自动完成B对C的依赖注入(如果C也是容器管理的Bean)。
自动装配的匹配规则:
- 优先按类型匹配容器中的Bean,如果有多个同类型Bean,再按名称匹配。
- 可以用
@Qualifier("beanName")指定具体要注入的Bean名称,用@Primary标记优先选择的Bean。
内容的提问来源于stack exchange,提问作者dgupta3091
相关产品推荐
相关产品推荐

