Part I: A review of SpringBoot applications
1.1 Overview
Convention over Configuration, also known as programming by convention, is a software design specification.
Benefits: Configuration items are significantly reduced.
This idea runs through springboot.
springboot: With Spring Boot, you can easily create stand-alone, production-level Spring-based applications based on Spring 4. 0 Design
1.2 Main features
- SpringBoot Starter: He consolidates common dependency groupings into one dependency so that they can be used once
Sex is added to the project's Maven or Gradle build; - To make coding easy, SpringBoot configures Spring in a JavaConfig way and provides a lot of annotations.
Greatly improves work efficiency. - Automatic Configuration: SpringBoot's automatic configuration feature leverages Spring's support for conditional configuration to reasonably guess what your application needs
Beans and automatically configure them; - To make deployment easy, SpringBoot has three built-in Servlet containers, Tomcat, Jetty,undertow. We only need one
The Java runtime environment is ready to run SpringBoot projects, which can be packaged as a jar package.
1.3 Question throwing
- What is starter? How do we use these starters?
- Why do package scans only scan packages and their subpackages where the core boot class resides
- How do I complete the automatic assembly during springBoot startup?
- How was the embedded Tomcat created and started?
- How does springmvc automatically assemble using the starter corresponding to the web scene?
The source code analysis section below answers one by one.
1.4 Hot Deployment
Update service capabilities without manually restarting the application after modifying the code
1. Introducing Dependencies
<!-- Introducing hot deployment dependency --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency>
2. Configure IEDA
Open the Compiler panel settings page by selecting the File->[Settings] option on the IDEA tool interface
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-ZFhGPAlO-16236523162) (/Users/jarry/Library/Application Support/typora-user-images/image-20210611105519061.png)]
Select the Compiler option under Build and check the "Build project automatically" option on the right to set the project to be auto-programmed
Translate, click the Apply[OK] button to save the settings
Use the combined shortcut "Ctrl+Shift+Alt+/" to open the Maintenance Options box on any page of the project, select and open it
Registry page, as shown in the figure
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-jQTZwg7q-1623652323169)(/Users/jarry/Library/Application Support/typora-user-images/image-20210611111018960.png)]
"compiler.automake.allow.when.app.running" is found in the list, and the Value value after this option is checked to indicate that
The fixed IDEA tool compiles automatically when the program is running, and then clicks the Close button to complete the setup.
Thermal Deployment Principle
It's when we start the project on the editor, change the code, and the editor automatically triggers the compilation
Replace historical. After the class file, the srpring-boot project is restarted when the project detects file changes.
With the introduction of hot deployment plug-ins, there are two class loaders for class loading, baseclassloader for third-party jar packages, and restartClassLoader for code developed by developers, which makes the comparison stop
It's much quicker to restart services without them, because using plug-ins is only part of restarting code written by developers.
Some resources do not necessarily need to trigger a restart after a change. Use the following configuration to customize the directory of resources that do not need to be restarted after a change:
spring.devtools.restart.exclude=static/**,public/**
1.5 Global Profile
springboot defaults from
–file:./config/ –file:./ –classpath:/confi –classpath:/
These directories look for global profiles (in order of priority from high to low):
- Go to the project root first to find the configuration file file under the config folder
- Go to the root directory to find the configuration file
- Go to resources to find the configuration file in the cofnig folder
- Go to resources to find the configuration file
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-AOaLmtiu-1623652323171)(/Users/jarry/Library/Application Support/typora-user-images/image-20210611112641960.png)]
Remarks:
1. If the profile attributes in high priority do not conflict with those in low priority, they will coexist - complementary configuration
2. If the same configuration property is configured in more than one configuration file, the first read is used by default, and the subsequent read does not overwrite the previous read
Yes.
3. If there is an application in the same directory. YML also has applications. Properties, if prior to version 2.4.0, priority Properties > yaml
But if it's version 2.4.0, priority yaml > properties, or if you want to continue using Spring Boot 2.3 configuration logic, you can also use application.properties or application. Add the following parameters to the YML configuration file:
spring.config.use-legacy-processing = true
If our profile name is not application.properties or application.yml, which can be specified with the following parameters
The name of the profile, myproject is the profile name
$ java -jar myproject.jar --spring.config.name=myproject
We can also specify another location for the profile to take effect
Specify the profiles and the default loaded profiles that work together to form complementary configurations
ava -jar run-0.0.1-SNAPSHOT.jar -- spring.config.location=D:/application.properties
When using @Configuration Properties, you can add the following dependencies:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
Then restart or build the project, which you can use in the application.yml file appears with associated configuration prompts
1.6 Attribute Injection
1.6.1 Attribute Injection Common Comments
@Configuration: Declare a class as a configuration class
@Bean: Declares that on a method, the return value of the method is added to the Bean container
@Value: Property Injection
@ConfigurationProperties(prefix = "jdbc"): Bulk property injection, used with either @Component or @EnableConfigurationProperties, can be placed on a class or on a method with @Bean
@PropertySource("classpath:/jdbc.properties") specifies the external property file. Add on Class
1.6.2 How to override third-party configurations
In addition to @ConfigurationProperties being used to annotate classes, you can also use it on the public @Bean method. When the genus is to be
This is particularly useful when sex is bound to third-party components other than controls. As shown below
@Configuration public class MyService { @ConfigurationProperties("another") @Bean public AnotherComponent anotherComponent(){ return new AnotherComponent(); } }
another.enabled=true another.remoteAddress=192.168.10.11
1.7 Log Framework
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-ICWJLgKu-16236523173) (/Users/jarry/Library/Application Support/typora-user-images/image-20210611140638780.png)]
The Spring framework chose to use JCL as the default log output. Spring Boot defaults to SLF4J + LogBack
The corresponding dependencies are as follows:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <version>2.4.0.RELEASE</version> </dependency>
Replace Log Framework
Modify the pom file as follows:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-logging</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
Part Two: Source Analysis of SpringBoot
2.1 [Key] Automatic Configuration
@SpringBootApplication
@Target({ElementType.TYPE}) //Scope of application of annotations, Type means that annotations can be described in classes, interfaces, annotations, or enumerations @Retention(RetentionPolicy.RUNTIME) //Represents the life cycle of the comment, Runtime Runtime @Documented //Indicates that annotations can be recorded in javadoc @Inherited //Indicates that the annotation can be inherited by a subclass @SpringBootConfiguration // Mark this class as a configuration class @EnableAutoConfiguration // Start auto-configuration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication
@SpringBootConfiguration
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration // Configuration classes act as configuration files and are an object in a container public @interface SpringBootConfiguration { }
Is a configuration class
@EnableAutoConfiguration
// Automatic Configuration Package @AutoConfigurationPackage // Spring's underlying comment, @Import, imports a component into the container; // The imported component is AutoConfiguration Packages. Registrar. Class @Import(AutoConfigurationImportSelector.class) // Tell SpringBoot to turn on auto-configuration so that it will take effect. public @interface EnableAutoConfiguration
@EnableAutoConfiguration uses @Import to collect and load all bean definitions that meet the automatic configuration criteria
IoC Container
@AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class) // Import components registered in Registrar public @interface AutoConfigurationPackage
@Import (AutoConfiguration Packages.Registrar.class), which imports the Registrar component class
In the container, you can see the registerBeanDefinitions method in the Registrar class:
@Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // Pass in the meta information of the annotation label to get the corresponding package name register(registry, new PackageImport(metadata).getPackageName()); }
Register a Bean that is
Org. Springframework. Boot. Autoconfigure. AutoConfiguration Packages. BasePackages, which holds the base package path that spring will scan.
@Import(AutoConfigurationImportSelector.class)
AutoConfiguration ImportSelector focuses on implementing the DeferredImportSelector interface and various
The Aware interface, then the DeferredImportSelector interface inherits the ImportSelector interface.
According to org.springframework.context.annotation.ConfigurationClassParser#processImports
... if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); if (selector instanceof DeferredImportSelector) { //Processing for @Import(AutoConfigurationImportSelector.class) goes here this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false); } } ...
Continue to follow org.springframework.context.annotation.ConfigurationClassParser#handle
... DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder( configClass, importSelector); if (this.deferredImportSelectors == null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); handler.register(holder); //Will come here to process import handler.processGroupImports(); } ...
Continue to follow org.springframework.context.annotation.ConfigurationClassParser#processGroupImports
//Focus on grouping.getImports() grouping.getImports().forEach(entry -> { ... });
Continue to follow org.springframework.context.annotation.ConfigurationClassParser#getImports
... //Configuration class handling DeferredImportSelector import this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); ...
Continue to follow org. Springframework. Boot. Autoconfigure. AutoConfiguration ImportSelector. AutoConfiguration Group#process
... AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); ...
Continue to follow org. Springframework. Boot. Autoconfigure. AutoConfiguration ImportSelector#getAutoConfiguration Entry
... List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); ... protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //A key!!! Use SpringFactoriesLoader to load META-INF/spring under classpath. Factories file List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }
Summarize the main things the AutoConfiguration Entry method does:
-
From spring. Load the auto-configuration class in the factories configuration file;
-
Exclude the auto-configuration specified by the exclude property of the @EnableAutoConfiguration annotation from the loaded auto-configuration class
Class; -
Then use the AutoConfiguration ImportFilter interface to filter whether the auto-configuration class meets its label annotations (if any)
If marked)@ConditionalOnClass, @ConditionalOnBean, and
The @ConditionalOnWebApplication criteria, if all are met, return the matching result; -
Then trigger the AutoConfiguration ImportEvent event to tell ConditionEvaluationReport Condition Evaluation
Evaluate the Reporter object to record eligible and exclude auto-configuration classes, respectively. -
Finally spring imports the last filtered auto-configuration class into the IOC container
Conditional Notes
@Conditional is a newly provided comment from Spring4 that serves to register containers on certain criteria
bean.
@ConditionalOnBean: A Bean is instantiated only if an object exists in the current context.
@ConditionalOnClass: A class is on the class path before it instantiates a Bean.
@ConditionalOnExpression: A Bean is instantiated when the expression is true. Based on SpEL expression
Conditional judgment.
@ConditionalOnMissingBean: A Bean is instantiated only if an object does not exist in the current context.
@ConditionalOnMissingClass: A Bean is instantiated when a class class class path does not exist.
@ConditionalOnNotWebApplication: A Bean is not instantiated as a web application.
@ConditionalOnWebApplication: instantiates a project when it is a Web project.
@ConditionalOnNotWebApplication: instantiates a project when it is not a Web project.
@ConditionalOnProperty: instantiates a specified property when it has a specified value.
@ConditionalOnJava: Triggers instantiation when the JVM version is in the specified version range.
@ConditionalOnResource: Triggers instantiation when there is a specified resource under the class path.
@ConditionalOnJndi: Triggers instantiation in the presence of JNDI.
@ConditionalOnSingleCandidate: When a specified Bean has only one or more beans in the container but has a beginning specified
Trigger instantiation when selected Bean.
@ComponentScan comment
Mainly from the defined scan path, find out the bean s container that identifies the classes that need to be assembled automatically into spring.
The @ComponentScan annotation does not label basePackages and value s, so the scan path defaults to @ComponentScan
The annotated class is in a package that is the basic scan path (that is, the project startup class labeled with the @SpringBootApplication annotation)
Path)
2.2 Run Method Execution Process
2.2.1 SpringApplication() construction method
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { //Set resource loader to null this.resourceLoader = resourceLoader; //Assertion Loading Resource Class cannot be null Assert.notNull(primarySources, "PrimarySources must not be null"); //Convert the primarySources array to a List and place it in the LinkedHashSet collection this.primarySources = new LinkedHashSet<> (Arrays.asList(primarySources)); //[1.1 infers the application type, then initializes the corresponding environment based on the type. Typically, servlet environments are used.) this.webApplicationType = WebApplicationType.deduceFromClasspath(); //[1.2 Initialize the configured META-INF/spring.factories under classpath ApplicationContextInitializer ] setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //[1.3 Initialize all configured ApplicationListener s under classpath] setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //[1.4 Derive the class name of the main method from the call stack] this.mainApplicationClass = deduceMainApplicationClass(); }
2.2.2 run(args)
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} * * Run the spring application and refresh a new ApplicationContext (Spring's context) * ConfigurableApplicationContext Is the subinterface of the ApplicationContext interface. stay ApplicationContext * A tool for configuring context is added. Configurable ApplicationContext is the advanced interface for containers */ public ConfigurableApplicationContext run(String... args) { //Record program run time StopWatch stopWatch = new StopWatch(); stopWatch.start(); // Context of Configurable Application Context Spring ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<> (); configureHeadlessProperty(); //From META-INF/spring. Getting listeners in factories //1. Get and start the listener SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //2. Construct application context ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //Handling beans that need to be ignored configureIgnoreBeanInfo(environment); //Print banner Banner printedBanner = printBanner(environment); ///3. Initialize application context context = createApplicationContext(); //Instantiate SpringBootExceptionReporter.class, used to support reporting errors on startup exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); //4. Preparations before refreshing the application context prepareContext(context, environment, listeners, applicationArguments, printedBanner); //5. Refresh Application Context refreshContext(context); //Extension interface after refreshing application context afterRefresh(context, applicationArguments); //Time Record Stop stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } / /Publish Container Launch Completion Event listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } t ry { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } r eturn context; }
- Step 1: Get and start the listener, ApplicationListener
- Step 2: Construct the application context and create the StandardServletEnvironment object
- Step 3: Initialize the application context, initialize the AnnotationConfigServletWebServerApplicationContext object, and set its beanFactory property to DefaultListableBeanFactory
- Step 4: In the preparation phase before refreshing the application context, set the context constructed above to the AnnotationConfigServletWebServerApplicationContext, execute the ApplicationContextInitializer, and register the startup class with the beanDefinitionMap in the DefaultListableBeanFactory
- Step 5: Refresh the application context, start the spring container operation, and start the embedded tomcat in onRefresh()
- Step 6: Refresh Extension Interface after Application Context
ConfigFileApplicationListener.java: add user-defined profile properties to PropertySource in the environment object
2.3 Embedded Tomcat
Spring Boot supports Tomcat, Jetty, and Undertow as bottom containers by default.
Spring Boot uses Tomcat by default, and once the spring-boot-starter-web module is introduced, Tomcat content is used by default.
Apparatus.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
This dependency contains dependencies related to tomcat and Spring Mvc
How do I replace the default tomcat container?
- Remove tomcat dependencies
- Introducing additional Servlet container dependencies
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <!--remove spring-boot-starter-web In tomcat--> <artifactId>spring-boot-starter-tomcat</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <!--Introduce jetty--> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
2.3.1 Embedded Tomcat AutoConfiguration Principle
In spring. tomcat autoconfiguration class ServletWebServerFactoryAutoConfiguration found in factories
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-aSYryvTP-16236523174) (/Users/jarry/Library/Application Support/typora-user-images/image-20210613130741534.png)]
Continue into the EmbeddedTomcat class, as shown in the following figure:
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-R1lLiYXo-1623652323175) (/Users/jarry/Library/Application Support/typora-user-images/image-202106131318337.png)]
Enter the TomcatServletWebServerFactory class, where getWebServer () is the key method, as shown in the following figure:
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-50mtYwZB-16236523176) (/Users/jarry/Library/Application Support/typora-user-images/image-202106131437891.png)]
Continue into methods such as getTomcatWebServer(), follow along to the Tomcat initialization method, and call tomcat.start()
Method, tomcat is officially turned on, see Figure
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-qdC6ewic-16236523177) (/Users/jarry/Library/Application Support/typora-user-images/image-202106131535947.png)]
Where does call analysis for getWebServer() call?
A: AbstractApplicationContext#onRefresh()
onRefresh() calls createWebServer() in the ServletWebServerApplicationContext
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { //Emphasis, factory = TomcatServletWebServerFactory ServletWebServerFactory factory = getWebServerFactory(); //A key this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
TomcatServletWebServerFactory#getWebServer
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-dKCLHZwG-16236523178) (/Users/jarry/Library/Application Support/typora-user-images/image-20210613132402400.png)]
Summary:
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-qNQZ2d6w-1623652323180)(/Users/jarry/Library/Application Support/typora-user-images/image-20210613133024432.png)]
2.3.3 Auto-configure SpringMVC
How to use SpringMVC in a normal WEB project, I
We need to start with the web. Configure the following configuration in XML
<servlet> <description>spring mvc servlet</description> <servlet-name>springMvc</servlet-name> <servletclass>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
But in SpringBoot, we don't have a web.xml file, how do we configure a Dispatcher servlet?
Actually Servlet3. The 0 specification specifies that to add a Servlet, in addition to the xml configuration, there is also a code-enabled
Method, pseudocode as follows
servletContext.addServlet(name, this.servlet);
Automatically configure Dispatcher Servlet and Dispatcher Servlet Registry
In spring. springmvc autoconfiguration class Dispatcher ServletAutoConfiguration found in factories
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) public class DispatcherServletAutoConfiguration { //... }
The Dispatcher ServletAutoConfiguration class contains two main internal classes:
1. Dispatcher Servlet Configuration, Configure Dispatcher Servlet
2. Dispatcher Servlet RegistrationConfiguration, which configures the registration class for Dispatcher Servlet, is responsible for
Dispatcher Servlet registered to ServletContext
Register Dispatcher Servlet to ServletContext
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-d4cer39Y-1623652323182)(/Users/jarry/Library/Application Support/typora-user-images/image-20210613134236921.png)]
Register Dispatcher Servlet process:
ServletContextInitializer
As we can see, at the top is a ServletContextInitializer interface. We know that implementing this interface means using
To initialize the ServletContext. Let's look at this interface
public interface ServletContextInitializer { void onStartup(ServletContext servletContext) throws ServletException; }
RegistrationBean
See how RegistrationBean implements the onStartup method
@Override public final void onStartup(ServletContext servletContext) throws ServletException { String description = getDescription(); if (!isEnabled()) { logger.info(StringUtils.capitalize(description) + " was not registered (disabled)"); return; } r egister(description, servletContext); }
Called the internal register method to follow it up
protected abstract void register(String description, ServletContext servletContext);
This is an abstract method
DynamicRegistrationBean
Look again at how DynamicRegistrationBean implements the register method
@Override protected final void register(String description, ServletContext servletContext) { D registration = addRegistration(description, servletContext); if (registration == null) { logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)"); return; } configure(registration); }
Follow up on the addRegistration method
protected abstract D addRegistration(String description, ServletContext servletContext);
The same is an abstract method
ServletRegistrationBean
Look again at how ServletRegistrationBean implements the addRegistration method
@Override protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) { String name = getServletName(); return servletContext.addServlet(name, this.servlet); }
As we can see, here the Dispatcher Servlet is add ed directly into the servletContext.
Reflected in the SpringBoot startup process
getSelfInitializer().onStartup(servletContext);
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-j0gg5jPq-16236523183) (/Users/jarry/Library/Application Support/typora-user-images/image-20210613135915400.png)]
getSelfInitializer() will eventually
To call the selfInitialize method to the ServletWebServerApplicationContext, the method code is as follows
private void selfInitialize(ServletContext servletContext) throws ServletException { prepareWebApplicationContext(servletContext); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes( beanFactory); WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, getServletContext()); existingScopes.restore(); WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, getServletContext()); for (ServletContextInitializer beans : getServletContextInitializerBeans()/**Focus**/) { beans.onStartup(servletContext); } }
By debugging, we know that getServletContextInitializerBeans() returns a
ServletContextInitializer collection, then call the onStartup method of the object in turn
Part Three: SpringBoot Data Access
3.1 Data Source Automatic Configuration Source Profiling
SpringBoot provides three database connection pools:
- HikariCP
- Commons DBCP2
- Tomcat JDBC Connection Pool
Where spring boot2. HikariCP is used by default in version x and is configured as follows in maven:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
If you use Commons DBCP2 instead of HikariCP, the configuration is as follows
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <exclusions> <exclusion> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </exclusion> </exclusions> </dependency>
Data source auto-configuration
Spring. Configuration class for data source found in factories: DataSourceAutoConfiguration
@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @EnableConfigurationProperties(DataSourceProperties.class) @Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class }) public class DataSourceAutoConfiguration { @Configuration(proxyBeanMethods = false) @Conditional(EmbeddedDatabaseCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import(EmbeddedDataSourceConfiguration.class) protected static class EmbeddedDatabaseConfiguration { } @Configuration(proxyBeanMethods = false) @Conditional(PooledDataSourceCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class }) protected static class PooledDataSourceConfiguration { } ...
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
@ConditionalOnMissingBean({DataSource.class}) @ConditionalOnProperty( name = {"spring.datasource.type"}, havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true ) static class Hikari { Hikari() { } @Bean @ConfigurationProperties( prefix = "spring.datasource.hikari" ) public HikariDataSource dataSource(DataSourceProperties properties) { HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class); if (StringUtils.hasText(properties.getName())) { dataSource.setPoolName(properties.getName()); } return dataSource; } }
If spring is not configured. Datasource. Type, the default value is com.zaxxer.hikari.HikariDataSource
Database configuration spring. The datasource (DataSourceProperties.java) prefix can be used to configure spring.datasource. The Hikari (HikariConfig.java) prefix is also possible
spring.datasource.hikari will cover spring. Configuration of datasource
3.2 Druid Connection Pool Configuration
1. Add Dependency
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </dependency>
2. In application. Introducing druid-related configurations in YML
spring: datasource: username: root password: root url: jdbc:mysql:///springboot_h?useUnicode=true&characterEncoding=utf- 8&useSSL=true&serverTimezone=UTC driver-class-name: com.mysql.cj.jdbc.Driver initialization-mode: always # Use druid data source type: com.alibaba.druid.pool.DruidDataSource # Data Source Other Configurations initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # Configure filters intercepted by monitoring statistics, sql cannot be counted after removal,'wall'for firewall filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
3. Write configuration classes
@Configuration public class DruidConfig { @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druid(){ return new DruidDataSource(); } }
3.3 SpringBoot Integration Mybatis
1. Add Dependency
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
2. Configure application.yml
spring: datasource: username: root password: root url: jdbc:mysql:///springboot_h?useUnicode=true&characterEncoding=utf- 8&useSSL=true&serverTimezone=UTC driver-class-name: com.mysql.jdbc.Driver # Use druid data source type: com.alibaba.druid.pool.DruidDataSource
3.4 SpringBoot + Mybatis for dynamic data source switching
Principle:
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-hRIPk3cj-162323184) (/Users/jarry/Library/Application Support/typora-user-images/image-20210613191254854.png)]
Spring has a built-in AbstractRoutingDataSource that configures multiple data sources into one Map, then root
Returns different data sources based on different key s. Because AbstractRoutingDataSource is also a DataSource interface, because
This allows the application to set the key first so that the code to access the database can be retrieved from the AbstractRoutingDataSource
Corresponding to a real data source to access the specified database
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean { ....... /** * Specify the map of target DataSources, with the lookup key as key. * The mapped value can either be a corresponding {@link javax.sql.DataSource} * instance or a data source name String (to be resolved via a * {@link #setDataSourceLookup DataSourceLookup}). * <p>The key can be of arbitrary type; this class implements the * generic lookup process only. The concrete key representation will * be handled by {@link #resolveSpecifiedLookupKey(Object)} and * {@link #determineCurrentLookupKey()}. */ //Translate as follows /** *Specifies the mapping of the target data source with the lookup key as the key. *The mapped value can be the corresponding {@link javax.sql.DataSource} *Instance or data source name string (to pass * {@link #setDataSourceLookup DataSourceLookup}). *Keys can be of any type; This class implements *Universal search process only. Specific key representations will *By {@link #resolveSpecifiedLookupKey(Object)} and * {@link #determineCurrentLookupKey()}. */ public void setTargetDataSources(Map<Object, Object> targetDataSources) { this.targetDataSources = targetDataSources; } . ..... /** * Determine the current lookup key. This will typically be * implemented to check a thread-bound transaction context. * <p>Allows for arbitrary keys. The returned key needs * to match the stored lookup key type, as resolved by the * {@link #resolveSpecifiedLookupKey} method. */ //Translate as follows /** * Determines the current lookup key. This usually happens * Implemented to check the transaction context of a thread binding. * <p> Allow any key. The returned key needs * Matches the stored lookup key type, such as * {@link #resolveSpecifiedLookupKey} Method. */ protected abstract Object determineCurrentLookupKey(); }
The recommended way is to create a class to inherit it and implement its determineCurrentLookupKey() method
Step 1: Configure multiple data sources
spring.druid.datasource.master.password=root spring.druid.datasource.master.username=root spring.druid.datasource.master.jdbcurl=jdbc:mysql://localhost:3306/product_master? useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC spring.druid.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver spring.druid.datasource.slave.password=root spring.druid.datasource.slave.username=root spring.druid.datasource.slave.jdbcurl=jdbc:mysql://localhost:3306/product_slave? useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC spring.druid.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
In the configuration code for SpringBoot, we initialize two data sources
@Configuration public class MyDataSourceConfiguratioin { Logger logger = LoggerFactory.getLogger(MyDataSourceConfiguratioin.class); /** * Master data source. */ @Bean("masterDataSource") @ConfigurationProperties(prefix = "spring.druid.datasource.master") DataSource masterDataSource() { logger.info("create master datasource..."); return DataSourceBuilder.create().build(); } / ** * Slave data source. */ @Bean("slaveDataSource") @ConfigurationProperties(prefix = "spring.druid.datasource.slave") DataSource slaveDataSource() { logger.info("create slave datasource..."); return DataSourceBuilder.create().build(); } } @Bean @Primary DataSource primaryDataSource( @Autowired @Qualifier("masterDataSource") DataSource masterDataSource, @Autowired @Qualifier("slaveDataSource") DataSource slaveDataSource ) { logger.info("create routing datasource..."); Map<Object, Object> map = new HashMap<>(); map.put("masterDataSource", masterDataSource); map.put("slaveDataSource", slaveDataSource); RoutingDataSource routing = new RoutingDataSource(); routing.setTargetDataSources(map); routing.setDefaultTargetDataSource(masterDataSource); return routing; }
Step 2: Write the RoutingDataSource
public class RoutingDataSourceContext { // holds data source key in thread local: static final ThreadLocal<String> threadLocalDataSourceKey = new ThreadLocal<>(); public static String getDataSourceRoutingKey() { String key = threadLocalDataSourceKey.get(); return key == null ? "masterDataSource" : key; } public RoutingDataSourceContext(String key) { threadLocalDataSourceKey.set(key); } public void close() { threadLocalDataSourceKey.remove(); } }
Use ThreadLocal to store the dynamically selected data source name "master" or "slave"
public class RoutingDataSource extends AbstractRoutingDataSource { protected Object determineCurrentLookupKey() { return RoutingDataSourceContext.getDataSourceRoutingKey(); } }
Write a @RoutingWith("slaveDataSource") comment and place it on the side of a Controller
In law, AOP is used to automatically select the corresponding data source.
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RoutingWith { String value() default "master"; }
Add aop dependency
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
Face class
@Aspect @Component public class RoutingAspect { @Around("@annotation(routingWith)") public Object routingWithDataSource(ProceedingJoinPoint joinPoint, RoutingWith routingWith) throws Throwable { String key = routingWith.value(); RoutingDataSourceContext ctx = new RoutingDataSourceContext(key); return joinPoint.proceed(); } }
Use
@RoutingWith("masterDataSource") @GetMapping("/findAllProductM") public String findAllProductM() { /* String key = "masterDataSource"; RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key);*/ productService.findAllProductM(); return "lagou"; } @RoutingWith("slaveDataSource") @GetMapping("/findAllProductS") public String findAllProductS() { /*String key = "slaveDataSource"; RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key);*/ productService.findAllProductS(); return "lagou"; }
Part 4: SpringBoot Cache Depth
4.1 JSR107
Java Caching (JSR-107) defines five core interfaces: CachingProvider, CacheManager, Cache, Entry, and Expiry.
CachingProvider: Create, configure, acquire, manage, and control multiple CacheManager s
Cache Manager: Create, configure, acquire, manage, and control multiple uniquely named aches.
Cache exists in the context of CacheManager. A CacheManager corresponds to only one CachingProvider
Cache: managed by the Cache Manager, which manages the life cycle of the Cache.
Cache exists in the context of CacheManager, is a map-like data structure, and is temporarily stored as key
The value of the index. A Cache is owned by only one CacheManager
Entry: A key-value pair stored in a Cache
Expiry: Each entry stored in a Cache has a defined expiration date. Once you exceed this
Time, entries automatically expire, after which they will not be accessible, updated, and deleted. Cache validity period is passable
ExpiryPolicy Settings Passed
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-sakL1zek-16236523186) (/Users/jarry/Library/Application Support/typora-user-images/image-20210613193514354.png)]
4.2 Cache abstraction for Spring
Spring has defined org since 3.1. Springframework. Cache. Cache
And org.springframework.cache.CacheManager interface to unify different caching technologies; And support for using Java
The Caching (JSR-107) annotation simplifies our cache development.
Spring Cache is only responsible for maintaining the abstraction layer, and the specific implementation is determined by its own technical choices. Cache processing and caching techniques
Decoupling.
4.3 Spring Cache Usage
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-qbvac8ct-16236523187) (/Users/jarry/Library/Application Support/typora-user-images/image-20210613201500633.png)]
Description:
(1) The @Cacheable label is on the method, indicating that the result of the method needs to be cached, with the cached key being owned by keyGenerator
The format of the cached values is determined by the serialize serialization strategy (serialization or json format). Label the note
After that, when the method is called again during the cache lifetime, the result is obtained directly from the cache instead of calling the method itself
(2) @CachePut is also labeled on the method, and similar to @Cacheable, the return value of the method is cached, unlike labels
Note @CachePut's method is called each time, and the results are cached each time, suitable for object updates
Properties of the @Cacheable annotation
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-N3U8lvOE-16236523188) (/Users/jarry/Library/Application Support/typora-user-images/image-20210613201754103.png)]
Note:
1. neither condition nor unless condition is satisfied nor cached
(2) When caching in asynchronous mode (sync=true):unless condition will not be supported
Available SpEL expressions are shown in the following table:
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-M8fbJhvk-162323188) (/Users/jarry/Library/Application Support/typora-user-images/image-20210613201858407.png)]
4.4 Cache Automatic Configuration Principle Source Profiling
Spring. CacheAutoConfiguration found in factories
There is a static internal class CacheConfigurationImportSelector in this class. He has a selectImport party
Method is used to add some components to the container for caching;
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-um86kceJ-16236523190) (/Users/jarry/Library/Application Support/typora-user-images/image-202106132120751.png)]
Let's break in here and debug to see what cached components are in imports
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-aXNEWQgx-16236523191) (/Users/jarry/Library/Application Support/typora-user-images/image-20210613202142191.png)]
SimpleCacheConfiguration is used by default;
Then we enter SimpleCacheConfiguration:
@Configuration(proxyBeanMethods = false) @ConditionalOnMissingBean(CacheManager.class) @Conditional(CacheCondition.class) class SimpleCacheConfiguration { @Bean ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers) { ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); List<String> cacheNames = cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { cacheManager.setCacheNames(cacheNames); } return cacheManagerCustomizers.customize(cacheManager); } }
We'll find that he added a bean to the springBoot container, which is a CacheManager;
ConcurrentMapCacheManager implements the CacheManager interface
Look again at the getCache method of ConcurrentMapCacheManager
@Override @Nullable public Cache getCache(String name) { Cache cache = this.cacheMap.get(name); if (cache == null && this.dynamic) { synchronized (this.cacheMap) { cache = this.cacheMap.get(name); if (cache == null) { cache = createConcurrentMapCache(name); this.cacheMap.put(name, cache); } } } return cache; }
The getCache method uses a double lock check (this validation mechanism is typically used in single-case mode)
We can see that without Cache we would call
cache = this.createConcurrentMapCache(name);
protected Cache createConcurrentMapCache(String name) { SerializationDelegate actualSerialization = (isStoreByValue() ? this.serialization : null); return new ConcurrentMapCache(name, new ConcurrentHashMap<>(256), isAllowNullValues(), actualSerialization); }
This method creates a ConcurrentMapCache, which is what we call a Cache;
public class ConcurrentMapCache extends AbstractValueAdaptingCache { private final String name; //Store cache key-value pairs private final ConcurrentMap<Object, Object> store; @Nullable private final SerializationDelegate serialization;
4.5 @Cacheable Source Analysis
@Cacheable Running Process:
(1) Before the method runs, query the Cache and get it by the name specified by the cacheNames (CacheManager)
Get the corresponding cache first, and get the cache for the first time automatically if there is no Cache component)
(2) To find the contents of the Cache in the Cache, the default key used is the parameter of the method:
key is generated using keyGenerator by default and SimpleKeyGenerator by default
The default policy for SimpleKeyGenerator to generate key s:
If there are no parameters: key = new SimpleKey();
If there is one parameter: key = the value of the parameter
If there are multiple parameters: key = new SimpleKey(params);
(3) Call the target method without finding the cache
(4) Put the results returned by the target method in the cache
_Total
Summary: @Cacheable labeling checks the cache for this data before executing, defaulting to the value of the parameter
Query the cache for the key, run the method and put the results into the cache if it is not present, then use the number in the cache directly when you call it later
according to
Core:
1. Use CacheManager(ConcurrentMapCacheManager) to get by name
Cache(ConcurrentMapCache) component
2. key is generated using keyGenerator and SimpleKeyGenerator is used by default
4.6 Redis-based cache implementation
starter with Redis
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
You can use the redis-based cache directly, and the cache's configuration class uses RedisCacheConfiguration
4.7 Custom RedisCacheManager
Open the cache auto-configuration class provided by the Spring Boot integration Redis component
RedisCacheConfiguration (under the package org.springframework.boot.autoconfigure.cache), view the
The source code information for the class, whose core code is as follows
@Configuration class RedisCacheConfiguration { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,ResourceLoader resourceLoader) { RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory) .cacheDefaults(this.determineConfiguration(resourceLoader.getClassLoader())) ; List<String> cacheNames = this.cacheProperties.getCacheNames(); if(!cacheNames.isEmpty()) { builder.initialCacheNames(new LinkedHashSet(cacheNames)); } r eturn (RedisCacheManager)this.customizerInvoker.customize(builder.build()); } p rivate org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(ClassLoader classLoader){ if(this.redisCacheConfiguration != null) { return this.redisCacheConfiguration; } else { Redis redisProperties = this.cacheProperties.getRedis(); org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheCo nfig(); config = config.serializeValuesWith(SerializationPair.fromSerializer( new JdkSerializationRedisSerializer(classLoader))); ... return config; } } }
As you can see from the core source above, RedisCacheConfiguration also connects factories internally through Redis
RedisConnectionFactory defines a cache manager, RedisCacheManager; Simultaneous customization
When RedisCacheManager is used, the JdkSerializationRedisSerializer serialization method is also used by default.
If you want to use RedisCacheManager with custom serialization for data caching, you can refer to the core generation above.
Code creates a Bean component named cacheManager and sets the serialization method in that component.
Customize RedisCacheManager
@Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { // Create String and JSON format serialized objects to convert cached data key and value, respectively RedisSerializer<String> strSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class); // Resolve Query Cache Conversion Exceptions ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jacksonSeial.setObjectMapper(om); // Customize the serialization and timeliness of cached data RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofDays(1)) .serializeKeysWith(RedisSerializationContext.SerializationPair .fromSerializer(strSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair .fromSerializer(jacksonSeial)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager .builder(redisConnectionFactory).cacheDefaults(config).build(); return cacheManager; }
In the above code, a method name with a default name is injected into the RedisConfig configuration class using the @Bean annotation
cacheManager component. In the defined Bean component, the key and the
value is customized for serialization, where the key for cached data is customized for String RedisSerializer (String)
Format), while value is customized to Jackson2JsonRedisSerializer (that is, JSON format) and uses
The entryTtl(Duration.ofDays(1)) method sets the cache data validity period to 1 day