Organize how objects are created in Spring

When Spring is not used, developers generally create objects using new/reflection; Spring simplifies the development process by providing an implementation of an IoC container to help developers manage dependencies between objects in an injection-dependent manner, thereby transferring ownership of the objects created by developers to the IoC container. This makes Spring easy to integrate third-party modules, so Spring is a layered framework.

 

The way Spring creates objects is organized as follows:

Mode 1: Customize BeanPostProcessor to generate proxy objects

For example, implement the InstantiationAwareBeanPostProcessor interface, where InstantiationAwareBeanPostProcessor is a BeanPostProcessor that can be used to handle callbacks before and after the creation of a bean instance.

 

The tests are as follows:

@Test
public void instantiationAwareBeanPostProcessorTest() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

    GenericBeanDefinition definition1 = new GenericBeanDefinition();
    definition1.setBeanClass(DemoInstantiationAwareBPP.class);
    context.registerBeanDefinition("demoInstantiationAwareBPP", definition1);

    GenericBeanDefinition definition2 = new GenericBeanDefinition();
    definition2.setBeanClass(InstantiationDemo.class);
    context.registerBeanDefinition("instantiationDemo", definition2);

    context.refresh();
    InstantiationDemo bean = context.getBean(InstantiationDemo.class);
    bean.invoke();
}

  

public class DemoInstantiationAwareBPP implements InstantiationAwareBeanPostProcessor {
	private final static Log LOG = LogFactory.getLog(DemoInstantiationAwareBPP.class);

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		LOG.info("beanName:" + beanName + "implement postProcessBeforeInstantiation Method");
		// Proxy object generation using cglib
		if (beanClass == InstantiationDemo.class) {
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(beanClass);
			enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
				boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object");
				if (!objFlag) {

					LOG.info("Execution Method" + method + "Front");
					Object rtn = methodProxy.invokeSuper(o, objects);
					LOG.info("Execution Method" + method + "after");
					return rtn;
				} else {
					return methodProxy.invokeSuper(o, objects);
				}
			});
			InstantiationDemo bean = (InstantiationDemo) enhancer.create();
			LOG.info("Create Proxy Object:" + bean);
			return bean;
		}

		return null;
	}

	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		LOG.info("beanName:" + beanName + "implement postProcessAfterInstantiation Method");
		return false;
	}
}

 

public class InstantiationDemo {
	private final static Log LOG = LogFactory.getLog(InstantiationDemo.class);
	public void invoke() {
		LOG.info(this);
		LOG.info("InstantiationDemo invoke");
	}
}

  

 

Note:

MethodProxy#invokeSuper exits the current interceptor's processing and proceeds to the next callback processing;

MethodProxy#invoke will continue to call back the method, which is prone to recursive calls if the obj parameter passed to invoke is wrong;

 

Mode 2: Create by reflection

AbstractAutowireCapableBeanFactory#doCreateBean is the most sophisticated method for truly creating beans, including calling constructors, assigning properties to beans, calling initialization of beans, and generating proxy objects;

This method calls AbstractAutowireCapableBeanFactory#createBeanInstance to instantiate the object, as shown below;

 

AbstractAutowireCapableBeanFactory#createBeanInstance creates and processes parameterless constructors based on @Autowritten automatic injection/call;

  • instantiateBean calls parameterless constructors to create objects;
  • autowireConstructor @Autowired auto-injection, calling constructor injection;

 

Mode 3: Create objects through FactoryBean

AbstractBeanFactory#getObjectForBeanInstance

If you have an instance that implements the FactoryBean interface, get the instance from the FactoryBean;

 

The tests are as follows:
@Test
public void mainTest13() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
            MainConfig10.class);
 
    System.out.println("IOC Container creation complete...");
 
    // Call the FactoryBean#getBean method, enter the parameter as Bean id, and get the object as FactoryBean#getObjectType
    // FactoryBean to get the factory Bean itself needs to be preceded by an &id
    // org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance
    Object factoryBean1 = context.getBean("demoFactoryBean");
    Object factoryBean2 = context.getBean("demoFactoryBean");
    System.out.println("factoryBean1==factoryBean2 :" + (factoryBean1 == factoryBean2));
    System.out.println("bean Type of:" + factoryBean1.getClass());
 
    Object factoryBean3 = context.getBean("&demoFactoryBean");
    System.out.println("bean Type of:" + factoryBean3.getClass());
}

  

@Configuration
public class MainConfig10 {
    @Bean
    public DemoFactoryBean demoFactoryBean() {
        return new DemoFactoryBean();
    }
}

  

public class DemoFactoryBean implements FactoryBean<Person> {
 
    /**
     * Is Single Case Control
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
 
    /**
     * Return the object and place it in a container
     * @return
     * @throws Exception
     */
    @Override
    public Person getObject() throws Exception {
        return new Person();
    }
 
    /**
     * Return Object Type
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }
}

  

 

Mode 4: Create an object by setting the BeanDefinition property Supplier

AbstractAutowireCapableBeanFactory#createBeanInstance

Get the supplier of the bean; (

 

AbstractAutowireCapableBeanFactory#obtainFromSupplier

Get an instance through the Suupplier#get method, instead of using reflection to get the instance; (

 

The tests are as follows:

@Test
public void supplierTest() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

    GenericBeanDefinition definition = new GenericBeanDefinition();
    definition.setBeanClass(BeanC.class);
    definition.setInstanceSupplier((Supplier<BeanC>) BeanC::new);

    context.registerBeanDefinition("beanC", definition);
    context.refresh();
    Assertions.assertNotNull(context.getBean("beanC"));
}

  

public class BeanC {
	private final static Log LOG = LogFactory.getLog(BeanC.class);

	public BeanC() {
		LOG.info("BeanC constructor");
	}
}

  

 

Mode 5: Create an object by setting the BeanDefinition property factoryMethod

AbstractAutowireCapableBeanFactory#createBeanInstance

createBeanInstance is the process of instantiating objects. If beanDefinition sets factoryMethod, it instantiates from the set factoryMethod, and the supplier attribute takes precedence over the factoryMethod, that is, when both supplier and factoryMethod attributes exist in beanDefinition, the instantiation logic of supplier takes precedence.

 

How factoryMethod is used: Set beanName and factoryMethodName properties of the class to which factoryMethod belongs to to beanDefinition;

Parsing logic for instantiating objects using factoryMethod: ConstructorResolver#instantiateUsingFactoryMethod

 

SimpleInstantiationStrategy eventually calls factoryMethod through factoryBean reflection;

 

 

The tests are as follows:

beanDefinition has only factoryMethod attribute

public class FactoryDemo {
	private final static Log LOG = LogFactory.getLog(FactoryDemo.class);
	private int num;
	private String msg;

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public FactoryDemo() {
		LOG.info("FactoryDemo no params constructor");
	}

	public BeanDemo create() {
		BeanDemo beanDemo = new BeanDemo();
		LOG.info("invoke create without args,beanDemo:" + beanDemo);
		return beanDemo;
	}

	public BeanDemo create(int num, String msg) {
		BeanDemo beanDemo = new BeanDemo();
		beanDemo.setMsg(msg);
		beanDemo.setNum(num);
		LOG.info("invoke create with args,beanDemo:" + "[" + beanDemo + "]" + beanDemo.getMsg() + "," + beanDemo.getNum());
		return beanDemo;
	}
}

  

public class BeanDemo {
	private final static Log LOG = LogFactory.getLog(BeanDemo.class);

	private int num;
	private String msg;

	public BeanDemo() {
		LOG.info("BeanDemo constructor");
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
} 
 
Call factoryMethod without parameters
@Test
public void factoryBeanNameTestWithoutArgs() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(FactoryDemo.class);

    GenericBeanDefinition definition = new GenericBeanDefinition();
    // Set factoryBean
    definition.setFactoryBeanName("factoryDemo");
    definition.setFactoryMethodName("create");

    definition.setBeanClass(BeanDemo.class);
    context.registerBeanDefinition("beanDemo", definition);
    context.refresh();

    System.out.println(context.getBean(BeanDemo.class));
}

  

 

Call with parameter factoryMethod
@Test
public void factoryBeanNameWithArgsTest() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(FactoryDemo.class);

    GenericBeanDefinition definition = new GenericBeanDefinition();
    // Set factoryBean
    definition.setFactoryBeanName("factoryDemo");
    definition.setFactoryMethodName("create");

    ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
    argumentValues.addGenericArgumentValue("test", "java.lang.String");
    argumentValues.addGenericArgumentValue(1, "int");
    definition.setConstructorArgumentValues(argumentValues);

    definition.setBeanClass(BeanDemo.class);
    context.registerBeanDefinition("beanDemo", definition);
    context.refresh();

    System.out.println(context.getBean(BeanDemo.class));
}

  

 

beanDefinition has factoryMethod property and supplier property

@Test
public void supplierOrderTest() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(FactoryDemo.class);

    GenericBeanDefinition definition = new GenericBeanDefinition();
    // Set factoryBean
    definition.setFactoryBeanName("factoryDemo");
    definition.setFactoryMethodName("create");

    definition.setBeanClass(BeanDemo.class);
    context.registerBeanDefinition("beanDemo", definition);
    definition.setInstanceSupplier((Supplier<BeanDemo>) BeanDemo::new);

    context.refresh();
    Assertions.assertNotNull(context.getBean("beanDemo"));
}

 

The supplier logic is executed first at this time;

 

A collation diagram of how Spring creates objects

 

Spring Instantiation Policy Cleanup

 

Keywords: Spring

Added by soak on Sun, 09 Jan 2022 19:09:13 +0200