个人随笔
目录
Java对象的代理,让我差点陷入了最最最简单的引用误区
2022-11-17 22:09:51

工作五六年了,我是第一次被自己蠢到,连java最基础最基本对象引用都搞混了,可能是最近没有怎么写代码练习的原因,真是罪过罪过。

事情是这样的,在看spring的源码,开始看到对象初始化了。

  1. //1、实例化
  2. instanceWrapper = createBeanInstance(beanName, mbd, args);
  3. //2、获取对象
  4. Object bean = instanceWrapper.getWrappedInstance();
  5. //3、加入singletonObjects(三级缓存中),顺便弄个回调方法。
  6. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  7. //4、将引用指向暴露到一个对象中
  8. Object exposedObject = bean;
  9. try {
  10. //5、给对象的属性赋值
  11. populateBean(beanName, mbd, instanceWrapper);
  12. //6、初始化
  13. exposedObject = initializeBean(beanName, exposedObject, mbd);
  14. }

我们知道,在第3步骤将我们的bean放入到了三级缓存(singletonObjects)中,其中key为beanName,value为一个ObjectFactory对象,这个类为一个支持方法接口的类

  1. @FunctionalInterface
  2. public interface ObjectFactory<T> {
  3. T getObject() throws BeansException;
  4. }

也就是在什么地方如果调用了这个对象的getObject方法那么就会执行

  1. getEarlyBeanReference(beanName, mbd, bean)

然后我发现,如果有循环依赖,也就是在第五步骤给对象赋值初始属性的时候,有循环依赖比如此时是在初始化A对象,但是有如下循环依赖

  1. public class A{
  2. private B b;
  3. }
  4. public class B{
  5. private A a
  6. }

那么接下来的流程就是会去设置B属性

  1. //1、设置A的属性
  2. populateBean(beanName, mbd, instanceWrapper);
  3. //2、设置属性B
  4. applyPropertyValues(beanName, mbd, bw, pvs)
  5. //3、解析属性B,如果需要的话
  6. Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
  7. //4、初始化属性B对象
  8. bean = this.beanFactory.getBean(resolvedName);

上面兜兜转转又回到了初始化B的方法,也会走一开始A的流程,然后也会到达

  1. //1、设置B的属性
  2. populateBean(beanName, mbd, instanceWrapper);
  3. //2、设置属性A
  4. applyPropertyValues(beanName, mbd, bw, pvs)
  5. //3、解析属性A,如果需要的话
  6. Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
  7. //4、初始化属性A对象
  8. bean = this.beanFactory.getBean(resolvedName);

但是在此时的第4步骤,会走到逻辑

  1. Object sharedInstance = getSingleton(beanName);

里面对应的代码如下

  1. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  2. Object singletonObject = this.singletonObjects.get(beanName);
  3. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  4. synchronized (this.singletonObjects) {
  5. singletonObject = this.earlySingletonObjects.get(beanName);
  6. if (singletonObject == null && allowEarlyReference) {
  7. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  8. if (singletonFactory != null) {
  9. singletonObject = singletonFactory.getObject();
  10. this.earlySingletonObjects.put(beanName, singletonObject);
  11. this.singletonFactories.remove(beanName);
  12. }
  13. }
  14. }
  15. }
  16. return singletonObject;
  17. }

在前面我们知道,我们已经把属性A放到三级缓存(singletonObjects)中,其中key为beanName,value为一个ObjectFactory对象,此时上面的方法调用

  1. singletonObject = singletonFactory.getObject();

也就是会调用

  1. getEarlyBeanReference(beanName, mbd, bean)

那上面这段逻辑干了啥呢?

  1. protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
  2. Object exposedObject = bean;
  3. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  4. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  5. if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
  6. SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
  7. exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
  8. }
  9. }
  10. }
  11. return exposedObject;
  12. }

好吧,原来是这样,因为前面要设置B的属性A了,所以一定要有一个比较完整一点的A,也就是比如我们有Aop的话,可以在这里做增强,然后返回代理对象A回去。

问题来了?

这里对A进行了增强,那刚开始的A,也就是一开始实例化后的A

  1. Object exposedObject = bean;

exposedObject这个怎么办,两个对象都不一样了,后面还是用这个啊!

然后这个蠢问题,这个最基础最基本的蠢问题,让我想了一天,我竟然忘记了Java的基础。忘记了上面的exposedObject不是对象,是引用,它指向的内存中的对象被代理了后,其实自己就开始指向代理,所以是一样的啊!

然后为了这个蠢问题,我还去证明了一下,如下代码

  1. public class DynamicProxyDemonstration
  2. {
  3. public static void main(String[] args)
  4. {
  5. //代理的真实对象
  6. Subject realSubject = new RealSubject();
  7. InvocationHandler handler = new InvocationHandlerImpl(realSubject);
  8. ClassLoader loader = realSubject.getClass().getClassLoader();
  9. Class[] interfaces = realSubject.getClass().getInterfaces();
  10. //获得代理后的对象
  11. Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
  12. System.out.println(subject);
  13. System.out.println(realSubject);
  14. }
  15. }

运行,输出结果

  1. mydemo.jdk.proxy.RealSubject@75b84c92
  2. mydemo.jdk.proxy.RealSubject@75b84c92

这两个是一个东西。

哎!真是人有失手,马有失蹄!

后面突然又纠结起来了!

如果一直共用一个对象的话,那

  1. getEarlyBeanReference(beanName, mbd, bean)

岂不是很多余?

毕竟就算先设置B的属性a,不对a进行代理先也可以啊,反正引用的是最同一个对象,在最后初始化A的时候再给A设置代理也是可以的啊,为啥还要多此一举?看来不仔细研读源码是不知道了!

 152

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2