In the last article, WE analyzed and studied The BeanFactoryPostProcessor in depth. The main body is the BeanFactory postprocessor. This time, we will study the Bean postprocessor: BeanPostProcessor.

Definition: It is alsoSpringAn external interface for extending user-defined functions. The timing of execution isbeanBefore and after the instantiation phase

This idea:

  1. BeanPostProcessordefine
  2. How to use
  3. Code implementation analysis
  4. This section describes the remaining extension functions

preface

Unlike BeanFactoryPostProcessor, which is both registered and executed within the same method, BeanPostProcessor splits two methods into two steps: registration and invocation.

The conventionalBeanFactoryIs not implemented after the processor automatic registration, so at the time of the call without manual registration is not available, but inApplicationContextAdded automatic registration in thisregisterBeanPostProcessorsMethod), and finally inbeanExecuted when instantiatedBeanPostProcessorCorresponding method.

This article will focus on BeanPostProcessor, and will also learn about the remaining context extensions


BeanPostProcessor

After learning from the previous article, you should have a better understanding of bean post-processing. Let’s get right to the point and see how it is used and combined with source code analysis


How to use

Create a new bean post-handler

The post-processor need reference InstantiationAwareBeanPostProcessor interface (inherited from actual BeanPostProcessor), and then reload the following two methods:

public class CarBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		// There is no distinction between bean types, just to test the order and time of printing
		System.out.println("Bean name : " + beanName + ", before Initialization, time : " + System.currentTimeMillis());
		return null;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("Bean name : " + beanName + ", after Initialization, time : " + System.currentTimeMillis());
		return null; }}Copy the code

Register bean-post-processor.xml in the configuration file

In the configuration file, configure the custom post-handler we wrote and two plain beans to test print time and order

<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<! -- beanPostProcessor -->
	<bean id="carPostProcessor" class="context.bean.CarBeanPostProcessor"/>

	<! Test print time and order with the following two beans
	<bean id="car" class="base.factory.bean.Car">
		<property name="price" value="10000"/>
		<property name="brand" value="Mercedes"/>
	</bean>

	<bean id="book" class="domain.ComplexBook"/>

</beans>
Copy the code

Start the code and print the results

public class CarBeanPostProcessorBootstrap {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("factory.bean/bean-post-processor.xml");
		Car car = (Car) context.getBean("car");
		ComplexBook book = (ComplexBook) context.getBean("book"); System.out.println(car); System.out.println(book); }}Copy the code

Output:

Bean name : car, before Initialization, time : 1560772863996
Bean name : car, after Initialization, time : 1560772863996
Bean name : book, before Initialization, time : 1560772863999
Bean name : book, after Initialization, time : 1560772863999
Car{maxSpeed=0, brand='Mercedes', price = 10000.0} domain.Com be656f plexBook @ 77Copy the code

From the output interface, it can be seen that the printing sequence starts from inside the framework and then to the application layer. Inside the framework, when each bean is instantiated sequentially, the execution timing is also mentioned above: Perform postProcessBeforeInitialization method first, and then instantiate the bean, execute postProcessAfterInitialization.

So the two interfaces we overloaded are printed out in reverse order


Registered BeanPostProcessor

The above introduction to the use of examples, should be easy to understand, then look at the source code registration method:

org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors

Entrusted to the actual PostProcessorRegistrationDelegate. RegisterBeanPostProcessors (the beanFactory, this);

public static void registerBeanPostProcessors( ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
		7.2 Retrieve the list of bean names with class type BeanPostProcessor from the registry
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true.false);

		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
		// Separate the beanPostProcessor with permissions, the order, and the rest
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		/ / type is MergedBeanDefinitionPostProcessor
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		for (String ppName : postProcessorNames) {
            // Sort, add to the corresponding array. }// First, register bean posthandlers that implement the PriorityOrdered interface
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
		// Next, register the bean postprocessor that implements the Ordered interface
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
		for (String ppName : orderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);
		// Now, after registering regular bean handlers, that is, out of order
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
		/ / in the end, after register MergedBeanDefinitionPostProcessor types of processors
		// It looks like the registration is repeated, but the underlying method called by each registration first removes the existing beanPostProcessor, then adds it, and finally saves the unique
		sortPostProcessors(internalPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);
		// Add the ApplicationContext probe
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}
Copy the code

Is it similar to BeanFactoryPostProcessor? It also sorts, adds weighted order, order, and normal BeanPostProcessor to the corresponding list, and sorts. Register at the end of the list of beanPostProcessors.

Comparing BeanPostProcessor with BeanFactoryPostProcessor before it, we find that there is no hard-coded registration code and only configuration file registered beans are handled. As explained in the book, there is some understanding of processing without hard coding:

For BeanFactoryPostProcessor processing, registration and implementation are implemented in a single method, so definitions in the configuration need to be loaded and activated; However, for BeanPostProcessor, it does not need to be called immediately. The function of hard-coding is to extract and call the post-processor. For BeanPostProcessor, it does not need to be called in the registration stage, so hard coding is not considered. Here you just need to extract the BeanPostProcessor from the configuration file and register it into the beanFactory.

And I in the testing process, want to have hard coded in the application code to register, found that because the ClassPathXmlApplicationContext last method is the bean instantiation of lazy loading, when the context is created, have been executed BeanPostProcessor, The post-processor of the hard-coded registration cannot be executed and can only be lazy loaded or registered in a configuration file configuration, or other BeanFactories can support hard coding.

BeanFactoryPostProcessor is a factoryPostprocessor that registers the Order postprocessor


summary

After studying and using the two extensions, BeanFactoryPostProcessor and BeanPostProcessor, there are still other extensions that I haven’t learned. The remaining methods were mentioned in the infrastructure section at the beginning:

Of these extensions, I feel event transmitters, listeners, and sending broadcast events are the three extensions that will be used most frequently, so the following sections will cover them in more detail.


Initialize the message resource

According to the book, the message resource messageSource is related to Spring internationalization.

For example, the Difference between Chinese and English between The United States and China shows different resources in different regions. For systems with internationalization requirements, a set of corresponding resource files should be provided for each system and stored in a specific directory in the form of a standardized name. The system automatically selects appropriate resource files based on the language or configuration of the client.

For example, 🌰 : defines two resource files. The simple configuration is as follows

  • 英 æ–‡ 版 : test= test
  • 英文 版 : test=test

So you can go throughApplicationcontext.getMessage()Method to access internationalization information and retrieve corresponding data in different environments.

As I feel that this configuration is related to the profile switch, SO I did not go to see and use, the specific implementation and use of students interested in in-depth understanding of it.


Event listeners

The use of event propagators is similar to the observer pattern in our design pattern, notifying the observer of the corresponding logical processing after the observer changes.

Before we look at how Spring initializes the event propagator, let’s look at the simple use of Spring listeners.

Define the listening Event Event

Create a new class that inherits from ApplicationEvent and calls the parent class’s constructor supre(source) in the constructor:

public class CarEvent extends ApplicationEvent {

	/** * Define a message */
	private String msg;

	public CarEvent(Object source) {
		super(source);
	}

	public CarEvent(Object source, String msg) {
		super(source);
		this.msg = msg; }}Copy the code

Define a Listener

Create a new class that references the ApplicationListener interface and override the onApplicationEvent method:

public class CarEventListener implements ApplicationListener {
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof CarEvent) {
			CarEvent carEvent = (CarEvent) event;
			System.out.println("source : " + event.getSource() + ", custom message : "+ carEvent.getMsg()); }}}Copy the code

Unlike kafka and other mainstream MQ messages, Spring’s message listeners can specify a sending queue or listening topic. Once a message is sent, all registered listeners will receive the message and process it.


The configuration file

<bean id="testListener" class="context.event.CarEventListener"/>
Copy the code

Register the listener you just wrote into the Spring container


The test code

public class EventBootstrap {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("factory.bean/bean-post-processor.xml");
		// The first parameter is the source, and the second parameter is custom
		CarEvent carEvent = new CarEvent("hello"."world");
		context.publishEvent(carEvent);
		// After the message is sent, print the following
		// source : hello, custom message : world}}Copy the code

Because the listener was registered in the configuration file, and then the listener event was initialized in the startup code summary, the message was finally sent through the context, and the output was as expected.

This observer mode is a classic implementation and is very simple to use. Let’s analyze how Spring implements message listening with source code.


Message listening code analysis

From the source analysis, it is found that the following three steps:

Initialize ApplicationEvenMulticaster

protected void initApplicationEventMulticaster(a) {
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	/ / if there is have their own registered the class Name is applicationEventMulticaster, using custom radio apparatus
	if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
		this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); }}else {
		// No customization, use the default event announcer
		this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
		beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); }}Copy the code

Radio apparatus is used to broadcast messages, in the default radio apparatus found this method multicastEvent SimpleApplicationEventMulticaster class:

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType ! =null ? eventType : resolveDefaultEventType(event));
	Executor executor = getTaskExecutor();
	// Iterate over registered message listeners
	for(ApplicationListener<? > listener : getApplicationListeners(event, type)) {if(executor ! =null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else{ invokeListener(listener, event); }}}Copy the code

As you can see, the call is made through all registered listeners when the event is broadcastinvokeListenerMethod, the underlying call is the listener reloadedlistener.onApplicationEvent(event), so again, if you useSpringFor built-in event monitoring, determine the event source on the service processor to avoid processing errors.


Register listeners

In the previous step, the listener was initialized, so the next step is to see the listener registration process. The entry method is as follows:

org.springframework.context.support.AbstractApplicationContext#registerListeners

protected void registerListeners(a) {
	// Here is the hard-coded registered listener
	for(ApplicationListener<? > listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); }// Don't initialize factoryBeans here: we need to leave all regular beans uninitialized for the post-handler to apply to them!
	// This step is the listener registered in the configuration file
	String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true.false);
	for (String listenerBeanName : listenerBeanNames) {
		getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
	}

	// Publish early application events, now we finally have a multicast =-=
	Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
	this.earlyApplicationEvents = null;
	if(earlyEventsToProcess ! =null) {
		for(ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); }}}Copy the code

This method code is not much, also do not have what nested function, according to the annotation order will flow out again, will we registered listeners to join applicationEventMulticaster list, after waiting for the call.


publishEvent

With both listeners and broadcasters ready, all that remains is to send an event that tells the listener to do the appropriate thing:

org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)

The core is this line of code:

getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
Copy the code

Broadcasting events is done by getting the event broadcaster and calling the multicastEvent method, as I described earlier without going into detail.


conclusion

The property editor, the SPEL language, and the initialization of non-lazy loading have been omitted from the book

We can also learn fromSpringProvided in these extensions learned through the reserved post-processor, can be inbeanModify the configuration information before instantiation or perform other customized operations, such as replacing placeholders and filtering sensitive information.

You can also broadcast events, define events and listeners, and realize business logic in listeners. Because listeners are not directly called, they are relayed through event broadcasters, so as to achieve the effect of code decoupling.

So in the later code design and writing, in the overall design, if necessary, consider the higher level of abstraction to reserve the extension function, and then let the subclass overload or implementation, implementation of the extension function.


Due to limited personal skills, if there is any misunderstanding or mistake, please leave a comment, and I will correct it according to my friends’ suggestions

Code and comments are in it, friends can download the code I uploaded, pro test can run ~

Gitee address: https://gitee.com/vip-augus/spring-analysis-note.git

Github address: https://github.com/Vip-Augus/spring-analysis-note


The resources

  1. — Beijing: Posts and Telecommunications Press

Portal:

  • Spring source learning – environment preparation

  • (1) The infrastructure of the container

  • Spring source code learning (2) default tag parsing

  • Spring source code learning (3) custom tags

  • Spring source code learning (four) bean loading

  • Spring source code learning (5) loop dependency

  • Spring source code learning (six) extension function part 1

  • Spring source code learning (seven) extension features part 2

  • Spring source learning (eight) AOP use and implementation principle

  • Spring source code learning (9) Transaction Transaction

  • (10) Spring MVC