“This is the 16th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”
1. Introduction
This article focuses on how field expressions in beans are parsed when the Spring IOC container is initialized.
Before formally looking at the source code, we need to know about BeanExpressionResolver: The bean’s expression parser, which is an interface with just one evaluate method, can be implemented if we want to customize an expression and want Sprig to parse it by overriding the expression parsing logic and registering the expression parser with the factory.
I’ll focus on spring’s default parser (the SPEL expression parser).
2. Where is the expression parser registered with the container?
Refresh () — prepareBeanFactory(beanFactory)
2.1 StandardBeanExpressionResolver
The source code is too long and I’m not going to paste it all but let’s take a look at the structure of it
It can be seen here StandardBeanExpressionResolver BeanExpressionResolver is realized.
So this is the constructor, and what we’re actually doing here is making a SpelParserConfiguration (SPEL parser configuration class)
Build a SpelParserConfiguration instance and then build a SpelExpressionParser
The instance is assigned to a expressionParser StandardBeanExpressionResolver can.
3. How do we set up our custom expression parser
Through the front We found the spring container to add the default view source, SPEL expression parser is StandardBeanExpressionResolver, custom that we should add in that place?
- We can customize a container, rewrite postProcessBeanFactory (ConfigurableListableBeanFactory the beanFactory). PostProcessBeanFactory () : postProcessBeanFactory () : postProcessBeanFactory
Such as:
- We can customize a BeanFactoryPostProcessor, Realize it void postProcessBeanFactory (ConfigurableListableBeanFactory the beanFactory) throws BeansException; BeanFactory PostProcessor call process, I also talked about in the previous, you can see interested
4. Where exactly is expression parsing performed?
refresh() –>
finishBeanFactoryInitialization(beanFactory) –> beanFactory.preInstantiateSingletons() –> getBean(beanName) –>
doGetBean(final String name, @Nullable final Class requiredType,@Nullable final Object[] args, boolean typeCheckOnly) –>
createBean(beanName, mbd, args) –>
resolveBeanClass(mbd, beanName) –> doResolveBeanClass(mbd, typesToMatch) –> evaluateBeanDefinitionString(className, mbd)
It may seem a bit convoluted, but the main code for parsing expressions is here. Here, for example, the default parser, the source code is as follows:
- If the current beanFactory’s BeanExpressionResolver is not null, attribute resolution is performed.
- If Beandefinition is not null, fetch the scope name first, and then fetch the scope by name.
- This. scopes: Saves the scope identifier string mapped to the corresponding scope
3. Construct an instance of BeanExpressionContext4. Call the evaluate() method of the parser.
4.1 Evaluate (@nullable String Value, BeanExpressionContext evalContext) throws BeansException
-
Returns if value is an empty string
-
Fetch the corresponding expression from expressionCache
- This. expressionCache: cache name –>Expression
-
When the Expression is null, parseExpression is called to generate an Expression object and put it in the cache
-
Again, take the expression from the cache and assign it to the variable SEC
-
EvaluationContext: indicates a context (the context in which an expression is executed), setting root objects, custom variables, custom functions, type converters, etc. The default implementation is org.springframework.expression.spel.support.StandardEvaluationContext
-
A default context is instantiated here.
-
-
PropertyAccessor: A property access controller that controls access to bean properties and is a key part of data binding. The source code is also to add several PropertyAccessor implementation classes.
-
ConversionService: the main function of this service is to convert the type. Here, we first remove its own type converter, if not, add a default to it.
-
CustomizeEvaluationContext (SEC) : here’s the default is an empty implementation, mainly to subclass implementation, used to do some custom configuration
-
Add the expression, and the corresponding context, to the cache again.
-
Return expr. GetValue (SEC) : This is mainly to process the expression, return the result value. You may have a look if you are interested.