Source Understanding FactoryBean and BeanFactory Differences in Spring

Posted by thebopps on Mon, 14 Feb 2022 19:12:38 +0100

Catalog

Preface:

Text:

What is BeanFactory?

What is FactoryBean?

Use of FactoryBean

Underlying Source Code Interpretation of FactoryBean

The difference between with and without an identifier

Summary:

Preface:

Now it's also a good time for Gold, Silver and Silver to jump around. When asked about the Spring Framework during an interview, many of your small partners may be asked about the differences between BeanFactory and FactoryBean, so write a post to carefully explain the differences between the two.

Text:

What is BeanFactory?

BeanFactory understands that it is a bottom-level interface that generates a bean object factory, a base interface implemented by any bean factory. It defines a series of methods for creating beans and specifies a life cycle for beans on the interface notes.

What is FactoryBean?

FactoryBean is a bean object that is produced into an IOC container through BeanFactory. The FactoryBean interface is also a high extension point for Spring, where users can produce their own beans by using the FactoryBean interface, which in some ways avoids a complex process of IOC context loading and bean factory creation.

Use of FactoryBean

// Implement class for FactoryBean interface, be careful to inject @Component into IOC container
@Component
public class MyFactoryBean implements FactoryBean<User> {


    // Manually create the object, and if the isSingleton() method is set to a singleton, it will only run once at the bottom level
    @Override
    public User getObject() throws Exception {
        return new User();
    }

    // Type of object
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }

    // Is it singleton, which affects getObject calls at the bottom level
    @Override
    public boolean isSingleton() {
        return true;
    }

}


// Configuration Class Scan Annotation
@Configuration
@ComponentScan("test")
public class MyConfig {
    
}



// Bean Object
public class User {
    public User(){
        System.out.println("No parameters called");
    }
}



// Main Startup Class
public class Application {


    public static void main(String[] args)  {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        Object bean1 = applicationContext.getBean("myFactoryBean");
        Object bean2 = applicationContext.getBean("myFactoryBean");
        System.out.println(bean1);
        System.out.println(bean2);
        System.out.println(bean1==bean2);
        
    }
}

By implementing the FactoryBean interface to override his three methods to manually new and control objects, I've explained the comments for the three methods in the code.

Here we may have a question, my code is applicationContext.getBean("myFactoryBean"), why is the User object returned? How does the bottom level control the singleton?

This question will be addressed later in the source code, and think again, maybe some little buddies have known before about BeanFactory factories to identify normal bean objects and FactoryBean objects as FactoryBean objects whose name s are preceded by & (but not injected into the IOC container, which the source code will explain later), so when the container is loaded, The User object is obtained by the getBean() method, so do we want to get the FactoryBean object by &+beanName? Can you get a User object later through the getObject() of FactoryBean? Is the User object singleton?

We want to verify the above by code and by source.

// Main boot class code
public class Application {


    public static void main(String[] args) throws Exception {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        MyFactoryBean fb1 = (MyFactoryBean)applicationContext.getBean("&myFactoryBean");

        User user1 = fb1.getObject();
        User user2 = fb1.getObject();

        System.out.println(user1==user2);

    }
}

Changing the main startup class knows by running that FactoryBean is obtained by &+beanName, but User is not singular, so we answer all the above questions in the source phase.

Underlying Source Code Interpretation of FactoryBean

Partners who understand how IOC containers start know that they start the entire context around the refresh() method of the AbstractApplicationContext class. And our FactoryBean said earlier that a Bean object will be created by a BeanFactory factory object to be added to the IOC container, so our entry is in the finishBeanFactoryInitialization() method.

Next you enter the preInstantiateSingletons() method

Here we get the BeanDefinition object parsed by the first few methods in the refresh() method, and the splitting occurs, a FactoryBean object, a normal Bean object, and a FactoryBean. Let's see how we can tell if it's a FactoryBean object.

Clearly MyFactoryBean is a subclass of FactoryBean, so return to true and set isFactoryBean to true

if (isFactoryBean(beanName)) {
   Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
   if (bean instanceof FactoryBean) {
      final FactoryBean<?> factory = (FactoryBean<?>) bean;
      boolean isEagerInit;
      if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
         isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
                     ((SmartFactoryBean<?>) factory)::isEagerInit,
               getAccessControlContext());
      }
      else {
         isEagerInit = (factory instanceof SmartFactoryBean &&
               ((SmartFactoryBean<?>) factory).isEagerInit());
      }
      if (isEagerInit) {
         getBean(beanName);
      }
   }
}

The getBean() method simply looks up the cache, creates it without it, and creates it with the name &+beanName.

Didn't that precede the blogger by saying that it wasn't added to the IoC container? So let's chase one of the getBean() methods.

You can prove that he has removed the identifier, followed by checking whether the first level cache already exists, returns if it exists, and creates if it does not exist. Not chasing here is not our focus, so we go back to the code in the main boot class.

The preceding code is the creation of the IoC container context and the creation of the Bean. After execution, so now run the other code in the main method, so we trace to the getBean() method in the breakpoint line.

 

protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

		// Don't let calling code try to dereference the factory if the bean isn't a factory.
		if (BeanFactoryUtils.isFactoryDereference(name)) {
			if (beanInstance instanceof NullBean) {
				return beanInstance;
			}
			if (!(beanInstance instanceof FactoryBean)) {
				throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
			}
			if (mbd != null) {
				mbd.isFactoryBean = true;
			}
			return beanInstance;
		}

		// Now we have the bean instance, which may be a normal bean or a FactoryBean.
		// If it's a FactoryBean, we use it to create a bean instance, unless the
		// caller actually wants a reference to the factory.
		if (!(beanInstance instanceof FactoryBean)) {
			return beanInstance;
		}

		Object object = null;
		if (mbd != null) {
			mbd.isFactoryBean = true;
		}
		else {
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			// Return bean instance from factory.
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			// Caches object obtained from FactoryBean if it is a singleton.
			if (mbd == null && containsBeanDefinition(beanName)) {
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

The core code is here, and we wrote two writings on the startup class when we demonstrated FactoryBean's code.

One is &+beanName, the other is beanName directly, and the core code with different return values is also here.

See if (BeanFactoryUtils.isFactoryDereference(name))

This is to determine if name is the beginning of the & flag bit. Obviously, the only code we're testing right now is beanName, so we can't get into the if code block, and the other test code has the & flag bit on it, so we're going into the if code block.

We currently don't have a marker, so the code to go is as follows

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
   if (factory.isSingleton() && containsSingleton(beanName)) {
      synchronized (getSingletonMutex()) {
         Object object = this.factoryBeanObjectCache.get(beanName);
         if (object == null) {
            object = doGetObjectFromFactoryBean(factory, beanName);
            // Only post-process and store if not put there already during getObject() call above
            // (e.g. because of circular reference processing triggered by custom getBean calls)
            Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
            if (alreadyThere != null) {
               object = alreadyThere;
            }
            else {
               if (shouldPostProcess) {
                  if (isSingletonCurrentlyInCreation(beanName)) {
                     // Temporarily return non-post-processed object, not storing it yet..
                     return object;
                  }
                  beforeSingletonCreation(beanName);
                  try {
                     object = postProcessObjectFromFactoryBean(object, beanName);
                  }
                  catch (Throwable ex) {
                     throw new BeanCreationException(beanName,
                           "Post-processing of FactoryBean's singleton object failed", ex);
                  }
                  finally {
                     afterSingletonCreation(beanName);
                  }
               }
               if (containsSingleton(beanName)) {
                  this.factoryBeanObjectCache.put(beanName, object);
               }
            }
         }
         return object;
      }
   }
   else {
      Object object = doGetObjectFromFactoryBean(factory, beanName);
      if (shouldPostProcess) {
         try {
            object = postProcessObjectFromFactoryBean(object, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
         }
      }
      return object;
   }
}

The execution code is here, and the FactoryBean object is also obtained. Is it not possible to execute the three methods in the FactoryBean for whatever you want, first to determine if it is a single bean, then to look up the cache. If there is no direct return in the cache, execute the getObject () method and return the object and add it to the cache? If the front and back operations of the BeanPostProcessor interface are implemented, they will go forward and overwrite the bean object returned by the getObject() method. When you see a non-singleton object in the else code block, you don't walk the cache, so each time a new bean object is generated, and if you implement the BeanPostProcessor interface, it also moves forward and backward, and the resulting bean object overwrites the getObject () object. The final bean object is returned as a return value.

Finished without markers, let's catch up with markers.

// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name)) {
   if (beanInstance instanceof NullBean) {
      return beanInstance;
   }
   if (!(beanInstance instanceof FactoryBean)) {
      throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
   }
   if (mbd != null) {
      mbd.isFactoryBean = true;
   }
   return beanInstance;
}

There's nothing to say here but to decide whether to carry the &flag bit and return to the FactoryBean object. Then back to the main startup class, the FactoryBean object calls the getObject() object.

Each call to the getObject() method manually new s an object, so it's certainly not a singleton, it's different each time. (

The difference between with and without an identifier

In fact, you can see here that the return value of the getObejct() method is cached in the cache with and without an identifier, so it is only created once and the return value is the object of the getObejct() method new, provided it is a singleton. In the case of an &identifier, the FactoryBean object is returned directly.

Summary:

It's an easy source. It is not easy for the author to write a post. I hope you can support the author at the same time. Hope to Praise+Focus

Topics: Java IntelliJ IDEA Spring Back-end Interview