Explain in detail how to quickly implement functions like automatic configuration in the lower version of Spring
- 2021-07-24 10:57:20
- OfStack
After Spring 4, conditional annotations such as @ Conditional were introduced, which is the biggest contributor to the automatic configuration in Spring Boot!
Then the question arises: If we are still using the old version of Spring 3. x, how do we implement an automatic configuration at this time?
Needs and issues
The core appeal
Existing system, not intended to be reconstructed The version of Spring is 3. x, and there is no plan to upgrade the version and introduce Spring Boot It is expected that the function enhancement can be realized under the premise of less code changeFor example:
I hope to add log records to the whole station system 1 (such as the summary information of RPC framework Web call and the summary information of database access layer), which is actually a general function. We have cited some infrastructures and want to make further enhancements to the functions of these infrastructures. At this time, we should solve this problem from the framework level.
Problems faced
Because there are no conditional annotations, we don't know when these things need/don't need to be configured
Unable to automatically locate the automatic configuration that needs to be loadedThere is no way for the framework to automatically load our configuration like the Spring Boot auto-configuration. We need to use one other means for Spring to load our customized functions.
Core solution ideas
Conditional judgment
Spring provides us with an extension point, and we can solve the problem of condition judgment through BeanFactoryPostProcessor. It allows us to do some post-processing on the definition of Bean after the definition of BeanFactory and before the initialization of Bean. At this time, we can judge our Bean definition, see which Bean definitions exist/are missing, and add one Bean definition-add one Bean customized by ourselves.
Configuration load
You can consider writing your own Java Config class and adding it to component-scan, and then find a way to make the component-scan of the current system include the Java Config class we wrote; You can also write XML file. If the current system uses XML, can we load our XML file on its loading path? If not, you can use manual import.
Two extension points provided by Spring
BeanPostProcessor
For the Bean instance Provide custom logical callbacks after Bean is created
BeanFactoryPostProcessor
Some customizations about Bean
Since the two extension points of Spring have been mentioned above, here are some customization methods for Bean under 1.
Lifecycle Callback
InitializingBean / @PostConstruct / init-method
This part is about initialization. You can do some customization after the initialization of Bean. There are three ways here:
Implement InitializingBean interface Use @ PostConstruct annotations Specify 1 init-method in the ES1120EN file defined by Bean; Or specify init-method when using the @ Bean annotationAll these allow us to call specific methods after the Bean is created.
DisposableBean / @PreDestroy / destroy-method
This part is one of the operations we should do when Bean is recycled. You can specify that when this Bean is destroyed, if:
It implements the interface DisposableBean, so Spring will call its corresponding method You can also add an @ PreDestroy annotation to a method, which will be called on destruction Specify an destroy-method in the XML file defined by Bean; Or destroy-method is specified when using the @ Bean annotation, this method is called on destruction
XxxAware interface
The whole ApplicationContext can be injected through the interface, and we can get a complete ApplicationContext in this Bean.
BeanFactoryAwareSimilar to ApplicationContextAware.
BeanNameAwareYou can inject the name of Bean into this instance.
If you are interested in the source code, see: org. springframework. beans. factory. support. AbstractBeanFactory. doGetBean\
If the current Bean has a method with close or shutdown method name, it will be treated as destroy-method by Spring and will be called on destruction.
1 Some common operations
Determine whether a class exists
Call ClassUitls. isPresent () provided by Spring to determine whether a class exists under the current Class Path.
Determine whether Bean has been defined
ListableBeanFactory. containsBeanDefinition (): Determine whether Bean has been defined. ListableBeanFactory. getBeanNamesForType (): You can see which names have been defined for certain types of Bean.
Register an Bean definition
Roll up your sleeves and work hard
The theory is finished, so let's start practice.
In the current example, we assume 1 that the current environment is not using Spring, Boot and a higher version of Spring.
Step 1: Simulates a lower version of the Spring environment
The spring-context dependency is simply introduced here, and the version of Spring 3. x is not really used, but the features above Spring 4 are not used either.
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>io.github.y0ngb1n.samples</groupId>
<artifactId>custom-starter-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
Step 2: Taking the implementation of BeanFactoryPostProcessor interface as an example
@Slf4j
public class GreetingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
// Judge the current Class Path Is there a required GreetingApplicationRunner So 1 Category
boolean hasClass = ClassUtils
.isPresent("io.github.y0ngb1n.samples.greeting.GreetingApplicationRunner",
GreetingBeanFactoryPostProcessor.class.getClassLoader());
if (!hasClass) {
// Class does not exist
log.info("GreetingApplicationRunner is NOT present in CLASSPATH.");
return;
}
// Does it exist id For greetingApplicationRunner Adj. Bean Definition
boolean hasDefinition = beanFactory.containsBeanDefinition("greetingApplicationRunner");
if (hasDefinition) {
// Current context already exists greetingApplicationRunner
log.info("We already have a greetingApplicationRunner bean registered.");
return;
}
register(beanFactory);
}
private void register(ConfigurableListableBeanFactory beanFactory) {
if (beanFactory instanceof BeanDefinitionRegistry) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(GreetingApplicationRunner.class);
((BeanDefinitionRegistry) beanFactory)
.registerBeanDefinition("greetingApplicationRunner", beanDefinition);
} else {
beanFactory.registerSingleton("greetingApplicationRunner", new GreetingApplicationRunner());
}
}
}
To register our Bean (see CustomStarterAutoConfiguration), there are several points to note:
The method here is defined as static When using, if the two projects are not in the same package, you need to actively add the current class to the component-scan of the project
@Configuration
public class CustomStarterAutoConfiguration {
@Bean
public static GreetingBeanFactoryPostProcessor greetingBeanFactoryPostProcessor() {
return new GreetingBeanFactoryPostProcessor();
}
}
Step 3: Verify that the automatic configuration is in effect
Add dependencies to other projects:
<dependencies>
...
<dependency>
<groupId>io.github.y0ngb1n.samples</groupId>
<artifactId>custom-starter-spring-lt4-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>io.github.y0ngb1n.samples</groupId>
<artifactId>custom-starter-core</artifactId>
</dependency>
...
</dependencies>
Start the project and observe the log (see custom-starter-examples) to verify that automatic configuration is in effect:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.0.RELEASE)
2019-05-02 20:47:27.692 INFO 11460 --- [ main] i.g.y.s.d.AutoconfigureDemoApplication : Starting AutoconfigureDemoApplication on HP with PID 11460 ...
2019-05-02 20:47:27.704 INFO 11460 --- [ main] i.g.y.s.d.AutoconfigureDemoApplication : No active profile set, falling back to default profiles: default
2019-05-02 20:47:29.558 INFO 11460 --- [ main] i.g.y.s.g.GreetingApplicationRunner : Initializing GreetingApplicationRunner.
2019-05-02 20:47:29.577 INFO 11460 --- [ main] i.g.y.s.d.AutoconfigureDemoApplication : Started AutoconfigureDemoApplication in 3.951 seconds (JVM running for 14.351)
2019-05-02 20:47:29.578 INFO 11460 --- [ main] i.g.y.s.g.GreetingApplicationRunner : Hello everyone! We all like Spring!
At this point, auto-configuration-like functionality has been successfully implemented in a lower version of Spring. clap
The code is hosted by GitHub, welcome Star
Reference link
https://github.com/y0ngb1n/spring-boot-samples
https://github.com/digitalsonic/geektime-spring-family