1. Preface
When developing Spring Boot applications, you can inject Bean into the Spring IoC container based on conditions. For example, if a configuration property exists in the configuration file, then the Bean is injected.
The red part of the diagram means that the class tagged with @Configuration
can only be injected into Spring IoC if ali.pay.v1.app-id
is present in the environment configuration of Spring.
The @ConditionalOnProperty
in this case is one of the conditional annotation family. It has many more conditional annotations to meet various scenarios.
There are actually a lot more than just the ones in the screenshot, they are also implemented in other frameworks in the Spring family.
This is a bit off topic, today is not about the usage of these conditional control annotations, just that I found a problem that could not be solved using the conditional annotation @ConditionalOnProperty
.
2. Scenarios where the configuration file has a Map structure
Here is a configuration file.
Corresponding configuration classes.
|
|
Here comes the special feature. The yml
configuration foo
and bar
are actually used as key
in the Map
to identify V1
, unlike other configuration parameters this key
can be defined by the user as a String
, maybe foo
, maybe bar
, depending on the developer’s preference.
At this point you want to make a @ConditionalOnProperty
determination based on app.v1.*.name
(using the wildcard *
for now), but it won’t work because you’re not sure of the value of *
, so what do you do?
3. Solution
I spent a day poking around here, at first I thought Spring provided wildcards (app.v1.*.name
) or even SpringEL
expressions to get them, but after half a day of messing with them I was left with no luck.
Suddenly it occurred to me that I was looking at Spring Security OAuth2 source code that had similar logic. Anyone who has used Spring Security OAuth2 knows that Spring Security OAuth2 also requires the user to customize a key
to identify their OAuth2 client. I use Gitee for example.
The
key
here isgitee
, but of course it depends on your mood, even if you usezhangshan
as thekey
.
Spring Security OAuth2 provides the relevant conditional injection ideas, and here are the core classes for their conditional injection judgments.
|
|
Obviously the structure of OAuth2ClientProperties
is the same as the structure of AppProperties
that we want to verify. So the logic above can be copied to bind the environment configuration with the uncertain key
to our configuration class AppProperties
. The core binding logic is this paragraph.
First, a bindable data structure is declared via Bindable
, where the mapOf
method is called to declare a Map
data binding structure. Then we extract the configuration properties starting with spring.security.oauth2.client.registration
from the configuration environment interface Environment
and inject them into Map
through the binding specific object Binder
. Now that we have access to the Map
, it is entirely in our hands to determine what policy to use.
Bindable
is a new data binding feature for Spring Boot 2.0, for those interested, you can get more information fromspring.io
.
I don’t need to tell you what to do next, so who doesn’t know how to do it? With the @Conditional
annotation, you can dynamically inject beans based on the actual parameters under app.v1
.
4. Summary
I spent a lot of time today solving a practical requirement using the data binding features of Spring Boot 2.0. When we get stuck in a problem, the first thing we need to do is think about whether there are similar scenarios and corresponding solutions. This also shows the importance of accumulation in general.