Hi, I’m crooked.
While I was hanging out on Spring’s Github this week, an issue caught my eye.
This article, as I follow the issues, begins with it, but ends with it:
Github.com/spring-proj…
The @async annotation is expected to support placeholders or SpEL expressions.
The reason why I pay attention to this issues is that I have written an article related to @async before. It looks familiar to me, so I clicked it in and looked at it.
In this question, there is a issue no. 27775:
Github.com/spring-proj…
What is this about?
As you can see from my screenshot, he wants to put the name of the thread pool in the configuration file. This requirement is not surprising to me and is a reasonable requirement based on the Spring framework.
Have a Demo
I’ll give you a Demo, see what it wants to do.
First, a thread pool named WHY is injected.
Then there is a method decorated with the @async annotation, which specifies a value of why to use the thread pool named why:
And then we need a Controller that fires:
Finally, add the @enableAsync annotation to the startup class to start the project.
Invoke the following link to initiate the call:
http://127.0.0.1:8085/insertUser?age=18
The following output is displayed:
The configuration takes effect.
Then, the guy who raised Issues wanted a feature like this:
That is, make @async annotations work with configuration files.
The current version of Spring does not support this, such as when I start a project:
Throw the NoSuchBeanDefinitionException directly, illustrate the value of the @ Async annotation is not the function of the analytic expression.
Support a wave
Well, now the requirement is clear: it’s not currently supported, someone in the community is asking for it and wants Spring to support it.
And then this guy, Sbrannen, comes out:
He said two words:
- 1. If the BeanFactory provided is ConfigurableBeanFactory, we can probably change it
Org. Springframework. Aop) interceptor. AsyncExecutionAspectSupport. FindQualifiedExecutor (the BeanFactory, String)
Code, using embeddedValue esolver to support. - You can take a look at it
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.setBeanFactory(BeanFactory)
This is a corresponding example.
The findQualifiedExecutor method he mentioned in the first sentence, which is the code that needs to be modified, looks like this in my 5.3.16 version:
Just remember that there is a beanFactory in the input.
The setBeanFactory method in the second sentence looks like this:
What he said, “for an example,” is the part that I’ve framed.
There are two key points:
- ConfigurableBeanFactory
- EmbeddedValueResolver
ConfigurableBeanFactory first, ConfigurableBeanFactory is a very important class in Spring, but not the focus of this article: you can think of it as a large, fully functional factory interface.
The point is embeddedValue Esolver this thing:
You can see from the annotations that this class is used to parse placeholders and expressions. Spring gives you a packaged utility class.
EmbeddedValueResolver there is only one method within:
This method calls a resolveEmbeddedValue method:
org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue
This method is the core code for parsing expressions in Spring.
Let me show you.
First let’s add a bit of code:
This code does not need to be explained, it is already clear.
I just need to put a breakpoint on the code we analyzed earlier and run the program:
Is that clear?
The input parameter is the ${user.age} expression and the output parameter is the corresponding 18 in the configuration file.
All the secrets about how to parse are buried in one line of code:
You think I’m gonna give you all the details?
No way. It’s just a point. Look for yourself.
Now I’m going to turn, and turn back to this reply:
Now let me walk you through this.
@async (“${thread.name}”) {${thread.name}}
The findQualifiedExecutor method can be modified to use the EmbeddedValueResolver tool class to support it. For example, the setBeanFactory method in this class:
Then I took you through the method and saw the use of embeddedValue Server solver.
Ok, so now the question is: how do we use it in the findQualifiedExecutor method?
Let’s go back to the issues we started with:
The old man said he was based on a tip from sbrannen, an official. Commit this change.
How did you modify it?
See his Files changed:
Three files were modified, one of which tested the class.
There are two left, one with the @async annotation:
This is just a change in Javadoc to show that the annotation supports the way expressions are configured.
The other is AsyncExecutionAspectSupport this class:
This is done by adding five lines of code to the findQualifiedExecutor method.
Finally, another line of code was deleted when the official code was reviewed:
That’s 4 lines of code, 2 lines of core code, to make @async support expressions.
And the official first told you what the solution was. As long as you follow up a little and start your little brain to think about it, I think it is not difficult for you to write these four lines of code.
This is the source code contribution to Spring, and a valuable one at that. If you’re the one who jumped at the chance, you might want to say on your resume that you contributed source code to Spring to make @async annotations support the way expressions are configured.
Generally speaking, those who do not know much about Spring will only think it is awesome when they see this sentence, thinking that they should be a big man.
But in reality, two lines of core code are all it takes.
So you say contributing source code to Spring is difficult?
There’s always a chance. It’s up to you to pay attention.
What, did I contribute source code to Spring?
I’m not. I just don’t care. What the hell.
This is the first point I want to make in writing this article:
It’s not too difficult to contribute to an open source project, so don’t try to commit an entire feature at once. Every little improvement is a good thing.
Debugging technique
Spring has yet to release an official package for the aforementioned code improvements, but I want to try them out for myself. What should I do?
You can certainly pull down the Spring source code, compile a wave yourself, and try it out locally.
But the process is so complicated that it’s basically a process of persuasion.
It’s not worth it for such a small test.
So I will teach you a “SAO” operation that I have researched.
First of all, my native Spring version is 5.3.16. The corresponding source code for this section is as follows:
Let’s change the program:
It then runs the program, fires a call, and stops at the breakpoint:
At this point we can see that qualifier is still an expression.
Then the SAO operation came.
When you click on the icon, the shortcut is Alt+F8:
This is the Evaluate Expression function provided by the IDE, in which you can write code.
Like this:
It can also be used as a backdoor substitution. Here I changed the qualifier to “yyds” string:
Then run over the breakpoint, and you can see from the exception message that it has really changed:
So, if I implement Evaluate Expression with the 4 lines of code I submitted this time, would I simulate the corresponding modified function?
I ask you: this method “SAO” not “SAO”.
Now, let’s do it.
Fill in Evaluate with these lines:
if (beanFactory instanceof ConfigurableBeanFactory) {
EmbeddedValueResolver embeddedValueResolver = new EmbeddedValueResolver((ConfigurableBeanFactory)beanFactory);
qualifier = embeddedValueResolver.resolveStringValue(qualifier);
}
Copy the code
Enter the code snippet and remember to click on this icon:
Click Execute and it looks like this:
Then looking at the output log, you can see a line like this:
It means that my trick of replacing the old guard worked.
Isn’t it easier to compile a copy of Spring source code?
And this way of debugging, you can actually execute some extra code while you’re debugging, so sometimes it really works wonders.
This is my second purpose in writing this article, to share this debugging method with you.
The difference
Careful readers will have noticed that the official code is a bit odd:
Instanceof is a Java reserved keyword that tests whether the object to its left is an instanceof the class to its right, returning a Boolean data type.
But that’s not how instanceof works, RIGHT? What kind of operation is this?
Don’t panic, paste it out first and put it into the IDE to see what happens:
When I write code labeled ② in my environment, the IDE gives me a hint:
Patterns in ‘instanceof’ are not supported at language level ‘8’
Instanceof is not supported in JDK 8.
As SOON as I saw the prompt, IT occurred to me that this notation was supported by some advanced version of the JDK and I had glanced at it somewhere a long time ago.
I then looked it up with the keywords “Patterns Instanceof” and found a new feature supported in JDK release 14.
www.baeldung.com/java-patter…
I’m just going to give you an example from the article.
When we use instanceof, we basically need to check the type of the object, and different types correspond to different logic.
Ok, so I asked you, you use instanceof, after the type matches, what do you do next?
Do you cast objects?
Like this:
In the code screenshot above, we determine the specific type of Animal by instanceof in each case, then enforce the cast declaration as a local variable, and then execute the specified function based on the specific type.
Some of these have many disadvantages:
- It’s tedious to write this, to detect the type and cast it.
- The type name appears three times for each if.
- Type conversions and variable declarations are poorly readable
- Repeating a type name means it is error-prone and can result in unexpected runtime errors.
- Every time I add an Animal type I have to change this function here.
Notice my bold, which is the same as the original, the wave of emphasis and detail is full:
To address some of the shortcomings mentioned above, Java 14 provides an Instanceof operation that combines parameter type checking with binding local variable types.
Like this:
Start by matching animal’s type with Cat in the if block. If the animal variable is an instance of Cat, force it to Cat and assign it to Cat.
Note that the variable name cat is not a real variable, just a declaration of the schema variable. You can think of it as fixed grammar.
The variables cat and dog are valid and assigned only if the result of the pattern matching expression is true. So if you accidentally use a variable in a different way, it will alert you to a compilation error.
So if you compare the above two versions of the code, the Java 14 version of the code is more concise and easier to understand. A lot of type conversions are reduced, and readability is greatly improved.
Back to the Spring
You see, originally read Spring, why suddenly write a new JDK feature?
I must have foreshadowed that.
Let me show you something:
Spring. IO/blog / 2021/0…
It was officially announced at last year’s SpringOne Conference that the JDK baseline for Spring 6.0 and Spring Boot 3 is 17.
That said: it’s quite possible that the next release we’ll embrace after JDK 8 will be JDK 17.
And I, as a technology enthusiast: it’s a good thing to support, to support.
However, as a Java practitioner who writes CRUD: It’s a headache to think about all the compatibility issues after upgrading, so hopefully this embrace doesn’t happen in my short career. Let the young, young, new boys do it.
When I limited my perspective to this article, I came up with a “SAO” operation that contributes source code to Spring.
Where instanceof is used so much in the history code, I just change these places to the new feature in the 6.0 branch. Wouldn’t that be an easier way to contribute source code?
However, before submitting issues, the usual procedure is to check for similar submissions.
So before I did this, I did some cool-headed research.
A check, I laughed…
I can think of it, I’m sure everyone else can think of it, and someone’s already there.
Here, for example:
Github.com/spring-proj…
The corresponding submitted code looks like this:
Then, there was a little official joke:
To put it simply: brother, such a small improvement, or do not mention the issue. You need the whole big one, not just one class.
If you want to change a module, you can change a module. For example, if you want to change a module, you can change 8 files in the spring-beans module.
This is the correct posture for this type of change.
If you’re interested, you can check out the Spring 6.0 code and see if there are any changes that haven’t been made. You can try to submit one.
Which brings me back to my first point:
It’s not too difficult to contribute to an open source project, so don’t try to commit an entire feature at once. Every little improvement is a good thing.
It’s true that the submissions don’t have much to do with the Spring framework, but at least you’ll get a taste of what it’s like to contribute to an open source project, and the larger the project, the more detailed the process, the more you’ll learn.
There’s a lot more you can learn from this process than if you submitted an Instanceof improvement, so can you argue that such a submission is not nutritious?
For example, in my article last year, I mentioned that Dubbo performs an unnecessary duplication of decoding a response message by removing a line of checkbid-related code.
I didn’t mention the corresponding PR, but I wrote it in the article.
A reader saw it and submitted it at noon that day, and it was officially put into storage soon.
At the end of last year, the Dubbo community gave him a coffee cup as part of a “give back” campaign:
Surprise, one line of code, not only can you learn something, but you can also get a free coffee cup, just ask if it smells good.
A sublimation
Ok, review this article.
I started with @async support expressions as an introduction to instanceof’s new features, and then Spring 6 will use JDK 17 as the base release.
In fact, when I write this article, the mind has been lingering a sentence: the wind starts at the end of the green ping.
Instanceof is the end of green duckweed.
The gale is JDK 17 as the baseline release.
In terms of why JDK 17 was used as the baseline release, it was a coup for the young Java. Whether the robbery is successful or not concerns each of us.
Under the “hubbub” of cloud origin, the person walking in front has already felt: the wind has blown up.
For example, Dr. Zhou Zhiming said in a speech titled “Cloud Native Era, Java’s Risks and Opportunities” :
Icyfenix. Cn/tricks / 2020…
If Java 17, the next LTS release, successfully integrates the new capabilities and features of Amber, Portola, Valhalla, Loom, and Panama, With sufficient GraalVM support, Java 17 LTS is likely to be a milestone release, leading the entire Java ecosystem to transition from large-scale server applications to new cloud-native software systems.
It could be a milestone similar to the transition from Java 1, which went from Web Applets for embedded devices and browsers, to Java 2, which established the embryonic direction of the modern Java language (Java SE/EE/ME and JavaCard).
However, if Java does not accelerate its growth, the moat built by the powerful ecosystem will eventually be consumed by newer languages like Golang and Rust, as well as older rivals like C, C++, C#, and Python. And was forced to dethrone as the “number one” programming language.
Whether the future of Java will continue to move forward and scale new heights, or turn from boom to bust and shrink, you and I will wait and see.
And I, still just saw the end of green ping.
Finally, the article was published on the public account [WHY Technology], welcome to pay attention to, the first time to receive the latest article.