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 alsoSpring
An external interface for extending user-defined functions. The timing of execution isbean
Before and after the instantiation phase
This idea:
BeanPostProcessor
define- How to use
- Code implementation analysis
- 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 conventionalBeanFactory
Is not implemented after the processor automatic registration, so at the time of the call without manual registration is not available, but inApplicationContext
Added automatic registration in thisregisterBeanPostProcessors
Method), and finally inbean
Executed when instantiatedBeanPostProcessor
Corresponding 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 broadcastinvokeListener
Method, the underlying call is the listener reloadedlistener.onApplicationEvent(event)
, so again, if you useSpring
For 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 fromSpring
Provided in these extensions learned through the reserved post-processor, can be inbean
Modify 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
- — 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