SpringBoot对于依赖的管理
-
POM.XML中依赖控制:
-
SpringBoot采用集成化统一管理,项目中各种依赖,并且相关依赖中各个版本已经排除第三方插件的版本互不兼容的问题,spring-boot-starter-parent的父依赖是spring-boot-dependencies,在spring-boot-dependencies中定义了各种相关依赖和相关兼容版本,如果项目需要引入外部JAR包,是不需要指定版本号的,因为SpringBoot中已经存在各种JAR包的版本。
Starter场景启动器
- SpringBoot将各个应用和第三方框架,设置成为一个个场景“spring-boot-starter-xx”格式,需要哪种场景,就将那种场景添加进项目,并且SpringBoot会将该场景所需要的所有相关JAR包依赖,自动注入引入到项目中。
- springboot官网中场景举例:
- 例如web项目中的场景启动器为:spring-boot-starter-web,其中包含的依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.3</version>
<name>spring-boot-starter-web</name>
<description>Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container</description>
<url>https://spring.io/projects/spring-boot</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>https://spring.io</url>
</organization>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<developers>
<developer>
<name>Pivotal</name>
<email>info@pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-boot.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-boot.git</developerConnection>
<url>https://github.com/spring-projects/spring-boot</url>
</scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/spring-projects/spring-boot/issues</url>
</issueManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.6.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.6.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.6.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.15</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.15</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
springboot的注解配置类-@SpringBootConfiguration
- SpringBootApplication是在springboot主程序中的一个注解,标记此类为springboot程序的主类或者主应用,其中SpringBootApplication注解中包含两个重要注解:@SpringBootConfiguration和@EnableAutoConfiguration,其中SpringBootConfiguration该注解内部是被@Configuration注解标记的,证明SpringBootConfiguration为一个配置类,并且会将该类注入到springIOC容器中,在启动时进行加载。
- 源码如下:
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Indexed;
/**
* Indicates that a class provides Spring Boot application
* {@link Configuration @Configuration}. Can be used as an alternative to the Spring's
* standard {@code @Configuration} annotation so that configuration can be found
* automatically (for example in tests).
* <p>
* Application should only ever include <em>one</em> {@code @SpringBootConfiguration} and
* most idiomatic Spring Boot applications will inherit it from
* {@code @SpringBootApplication}.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.4.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
/**
* Specify whether {@link Bean @Bean} methods should get proxied in order to enforce
* bean lifecycle behavior, e.g. to return shared singleton bean instances even in
* case of direct {@code @Bean} method calls in user code. This feature requires
* method interception, implemented through a runtime-generated CGLIB subclass which
* comes with limitations such as the configuration class and its methods not being
* allowed to declare {@code final}.
* <p>
* The default is {@code true}, allowing for 'inter-bean references' within the
* configuration class as well as for external calls to this configuration's
* {@code @Bean} methods, e.g. from another configuration class. If this is not needed
* since each of this particular configuration's {@code @Bean} methods is
* self-contained and designed as a plain factory method for container use, switch
* this flag to {@code false} in order to avoid CGLIB subclass processing.
* <p>
* Turning off bean method interception effectively processes {@code @Bean} methods
* individually like when declared on non-{@code @Configuration} classes, a.k.a.
* "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore behaviorally
* equivalent to removing the {@code @Configuration} stereotype.
* @return whether to proxy {@code @Bean} methods
* @since 2.2
*/
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
@EnableAutoConfiguration注解解析
- EnableAutoConfiguration该注解用于加载配置@SpringBootApplication注解所在类的包,作用就是将SpringBootApplication注解所在类的包以及该类的所有子包纳入IOC容器。
- 其中 EnableAutoConfiguration注解中包含@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)这两个注解,AutoConfigurationPackage注解是加载自己编写的所有类注入到IOC容器中,AutoConfigurationImportSelector是将第三方JAR包或者框架依赖自动加载注入到IOC容器中。
EnableAutoConfiguration注解中@AutoConfigurationPackage注解
- AutoConfigurationPackage注解中包含@Import(AutoConfigurationPackages.Registrar.class),通过@SpringBootApplication注解所在类所属的包以及子包的包名,利用new PackageImports(metadata).getPackageNames()进行查找,找到后进行注册注入到IOC容器中,其具体事件代码如下:
/**
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
* configuration.
*/
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
EnableAutoConfiguration注解中@Import(AutoConfigurationImportSelector.class)注解
- AutoConfigurationImportSelector类负责将第三方的包和类注入到IOC容器中,通过AutoConfigurationImportSelector类中selectImports方法,内部调用getAutoConfigurationEntry(annotationMetadata),然后再调用getCandidateConfigurations(annotationMetadata, attributes),然后再调用SpringFactoriesLoader.loadFactoryNames()方法,利用类SpringFactoriesLoader中属性FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"定位到第三方JAR包的位置进行声明然后利用EnableAutoConfiguration注解开启使用,具体代码如下:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//进入下一步进行调用
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
----------------------------------------------------------------------
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//进入下一步进行调用
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
----------------------------------------------------------------------------------------
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//通过SpringFactoriesLoader类中属性进行第三方JAR包定位
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;
}
}
==上述三个方法均在同一个类中AutoConfigurationImportSelector类==
------------------------------------------------------------------------------------------
public final class SpringFactoriesLoader {
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
//利用FACTORIES_RESOURCE_LOCATION 属性进行定位
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();
private SpringFactoriesLoader() {
}
..................
}
如何实现自动配置
-
流程如下:
-
通过META-INF/spring.factories定位配置类时,并不是所有配置类都需要被加载注入IOC容器中,在配置类中有明确的该配置类的加载条件,只有达成所有的加载条件才能使该类开启自动加载,条件控制注解类别如下所示:
默认全局配置文件application.properties和application.yml文件
-
application.properties和application.yml文件都是springboot全局的配置文件,二者可以同时存在也可以独立存在,同时存在时可以起到互补的作用,但是application.properties文件的优先级高于application.yml文件的优先级。
application.properties文件中写法如下:
server.port=8080 -
application.yml文件的编写格式必须存在层次,按照首行缩进的方式区别层级,并且严格按照key:空格+value的方法编写,例如:
-
编写规则:
使用首行缩进表示层次区分;
value值可以默认不加引号
双引号中的转义符会被转义,但是单引号中的转义符会议字符串的形式直接输出
@ConfigurationProperties注解和@value注解
- @ConfigurationProperties注解和@value注解都可以在实体类中使用,用于将实体类中的属性与application.properties和application.yml文件中的值进行绑定,并且二者可以单独存在也可以同时存在,并且互补作用。
- @ConfigurationProperties注解和@value注解不同点
PropertySource注解(针对于非默认配置文件【.properties】中的属性)
- PropertySource注解的作用是为了加载默认配置文件(application.properties和application.yml)之外的配置文件中的属性,有些配置文件并非默认文件,或者一些属性写在非默认配置文件中,但是在代码某处需要使用该配置文件,可以使用PropertySource注解来进行加载,但是注意只能加载.properties文件,不能加载.yml配置文件可参考如下编写:
@PropertySource(value={“classpath:config.properties”})
ImporResource注解(针对于配置文件)
- springboot实现自动装配,只会默认识别application.properties和application.yml两种文件,如果需要引入非默认配置文件,并且在启动时进行加载,需要使用ImporResource注解,并且标注在主程序类之前:
package indi.dsl.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
//引入其他配置文件
@ImportResource(locations = {"classpath:spring.xml"})
@SpringBootApplication
public class SpringBootDomeApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDomeApplication.class, args);
}
}
- 虽然可以利用ImporResource注解将非配置配置文件引入项目,但是不推荐,可以利用配置类的方式进行引入加载,@Configuration标注在编写的类上面,@Bean标注在类中的方法上,同样可以起到将配置类和配置类中的方法注入到IOC容器中。
占位符
- 占位符种类
- 占位符可以在application.properties和application.yml中使用,使用方式如下:
多环境切换
-
springboot利用profiles属性可以实现多个环境进行切换的功能,通常有两个种类,一个是在配置文件中编写多个环境的信息(静态切换),另一个是使用动态的方式切换环境。
-
静态切换
-
application.properties配置文件方式:
- 存在application-dev.properties配置文件和application-test.properties配置文件,子环境的配置文件名必须为application-环境名.properties格式,application-dev.properties配置文件中端口配置端口信息为:server.prot=8888;application-test.properties文件中端口配置端口信息为:server.prot=8899,为实现环境切换,可以在application.properties主配置文件中添加使用如下语句进行相应环境的激活:
激活开发环境:
spring.profiles.active=dev
激活测试环境:
spring.profiles.active=dev
- application.yml配置文件方式:
注意:多个环境写在同一个文件中,以三个减号进行环境间的分割,如下所示:
#主环境端口配置
server:
port: 8080
#指定激活开发环境dev
spring:
profiles:
active: dev
---
#模拟开发环境端口
server:
port: 8888
spring:
profiles: dev
---
#模拟测试环境端口
server:
port: 8899
spring:
profiles: test
-
动态环境切换
- 利用编辑器配置,在其中添加运行时参数:–spring.profiles.active=环境名(例如dev/test)进行动态配置
- 先进行项目打包,然后在CMD命令行中利用语句:
java -jar 打包项目名.jar --spring.profiles.active=环境名(例如dev/test)
- 设置VM参数:在编辑器中设置VM参数,语句如下:
-Dspring.profiles.active=环境名(例如dev/test)
配置文件位置
- 对于application.properties和application.yml配置文件可以存放如下四个位置,并且四个位置的优先级是有区别的,优先级高的会优先读取。
加载外部配置文件
- springboot项目正常情况下是不需要填写项目名即可以访问,如果想要指定项目名,可以利用如下语句指定
server.servlet.context-path=/项目名
- 加载外部配置文件可以使用如下语句进行加载,同样可以在vm中设置,也可以在编译器中设置:
–spring.config.location=D:/xxx/xxx/spring.properties
- 注意:调用外部文件的配置优先级高于内部文件的配置,即外>内。
springboot处理静态资源
- springboot针对与静态资源处理与普通springweb项目不同,对于js文件等的引入只需要在https://www.webjars.org/all官网中查找相应的依赖,然后添加到POM.xml文件中即可,相应jar包maven会自动下载进项目,其中WebMvcAutoConfiguration类中的方法addResourceHandlers(),会将每个引入的js文件包中webjars目录下的文件进行加载;对于HTML、图片等等纯静态资源,可以放到约定的四个文件夹下:classpath:/META-INF/resources、classpath:/resources、classpath:/static、classpath:/public
- 具体源码:
public class WebMvcAutoConfiguration {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
//通过classpath:/META-INF/resources/webjars路径加载前端jar包
addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
//查找加载静态资源
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
registration.addResourceLocations(resource);
}
});
}
........
}
---------------------------------------------------------------------------
public class WebProperties {
public static class Resources {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/].
*/
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
........
}
}
- 解析图
设置默认欢迎页源码解析
- 解析图
- 源码:
public class WebMvcAutoConfiguration {
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
return welcomePageHandlerMapping;
}
private Resource getWelcomePage() {
for (String location : this.resourceProperties.getStaticLocations()) {
Resource indexHtml = getIndexHtml(location);
if (indexHtml != null) {
return indexHtml;
}
}
ServletContext servletContext = getServletContext();
if (servletContext != null) {
return getIndexHtml(new ServletContextResource(servletContext, SERVLET_LOCATION));
}
return null;
}
private Resource getIndexHtml(Resource location) {
try {
Resource resource = location.createRelative("index.html");
if (resource.exists() && (resource.getURL() != null)) {
return resource;
}
}
catch (Exception ex) {
}
return null;
}
..........
}
------------------------------------------------------------------
- 注意:所有静态资源只要放在默认的静态资源文件夹即可,程序能够自动加载到,同时可以利用如下语句自定义静态资源文件夹,当自定义文件夹后默认的文件夹将会失效,系统程序不会加载默认静态资源文件夹,具体自定义语句如下:
spring.web.resources.static-locations=classpath:/img
springboot整合JSP
- springboot默认是不支持JSP文件,如果想要springboot支持JSP,需要如下进行配置:
1.新建springboot项目,将打包类型由jar修改为war
2.将tomcat的依赖中compile改为provided
3建立必要目录webapps/WEB-INF(需要)
webapps/WEB-INF/web.xml(不需要)
webapps/index.jsp
4.在application.properties文件中配置文件的前后缀:(前缀)spring.mvc.view.prefix=/WEB-INF/
(后缀)spring.mvc.view.suffix=.jsp
5.新建tomcat,并打包部署项目
6.访问路径为端口名+项目名+文件名
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.6.4</version>
<scope>compile</scope>
</dependency>
<!--将compile改为provided-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.6.4</version>
<scope>compile</scope>
</dependency>
- 在新建能够整合JSP的springboot项目时,会在著配置类文件下多生成一个ServletInitializer类文件,该类属于监听器,在tomcat启动时会自动触发ServletInitializer类中的方法,该方法会调用调用springboot的主配置类。