配置
yaml、yml、properties 文件都可以配置,还可以通过 Java 系统属性和命令行参数
优先级:命令行参数 > Java 系统属性 > properties > yml > yaml
命令行使用,需要首先运行 Maven 的打包指令,然后在命令行运行
java -jar path_to_jar.jar
# Java 系统属性,端口为例
java -Dserver.port=9000 -jar path_to_jar.jar
# 命令行参数,端口为例
java -jar path_to_jar.jar --server.port=9000
SpringBoot 项目打包时需要引入插件 spring-boot-maven-plugin (基于官网骨架创建项目,会自动添加该插件)
Bean 管理
获取 bean
对于默认的单例非延迟加载的 bean 而言,Spring 项目启动时,会把 bean 都创建好放在 IOC 容器中 (如加上 @Lazy 注解将会在第一次被使用时实例化)
如果想主动获取这些 bean,可以通过如下方式
- 根据 name 获取
Object getBean(String name)
- 根据类型获取
<T> T getBean(Class<T> requiredType)
- 根据 name 和类型获取 (类型转换)
<T> T getBean(String name, Class<T> requiredType)
为了使用该方法,需要先获取 IOC 容器对象
@Autowired
private ApplicationContext applicationContext; //IOC容器对象
public void testGetBean(){
// 根据bean的名称获取
DeptController beanl = (DeptController) applicationContext.getBean("deptController");
// 根据bean的类型获取
DeptController bean2 = applicationContext.getBean(DeptController.class);
// 根据bean的名称及类型获取
DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
}
bean 作用域
Spring 支持五种作用域,后三种在 web 环境才生效
作用域 | 描述 |
---|---|
singleton | 容器内同名称的 bean 只有一个实例 (单例) |
prototype | 每次使用该 bean 时会创建新的实例 (非单例) |
request | 每个请求范围内会创建新的实例 |
session | 每个会话范围内会创建新的实例 |
application | 每个应用范围内会创建新的实例 |
使用注解 @Scope 设置作用域
// 设置非单例
@Scope("prototype")
@RestController
public class xxxController{
}
实际开发当中,绝大部分的 Bean 是单例的,也就是说绝大部分 Bean 不需要配置 scope 属性
第三方 Bean
如果要管理的 bean 对象来自于第三方 (不是自定义的),是无法用 @Component 及衍生注解声明 bean 的,就需要用到 @Bean 注解。例如解析 XML 文件的 dom4j
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
依赖如上
@SpringBootApplication
public class xxxApplication{
@Bean // 将方法返回值交给IOC容器管理,成为IOC容器的bean对象
public SAXReader saxReader(){
return new SAXReader;
}
}
不过若要管理的第三方 bean 对象,建议对这些 bean 进行集中分类配置,可以通过 @Configuration 注解声明一个配置类
@Configuration
public class CommonConfig {
@Bean
public SAXReader saxReader(){
return new SAXReader;
}
}
通过 @Bean 注解的 name 或 value 属性可以声明 bean 的名称,如果不指定,默认 bean 的名称就是方法名。如果第三方 bean 需要依赖其它 bean 对象,直接在 bean 定义方法中设置形参即可,容器会根据类型自动装配
@Configuration
public class CommonConfig {
@Bean
public SAXReader saxReader(XxService xxService){
return new SAXReader;
}
}
起步依赖
在开发中若直接使用 Spring 需要引入相关依赖,并且保证版本匹配,而使用 SpringBoot 则只需要引入起步依赖依赖即可。原理是 Maven 的传递依赖,其他的依赖都会自动的通过 Maven 的依赖传递进来
自动配置
SpringBoot 的自动配置就是当 Spring 容器启动后,一些配置类、bean 对象就自动存入到了 IOC 容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作
配置类 @Configuration 的底层是 @Component,也是容器中一个 bean 对象
在引入依赖之后是如何将依赖 jar 包当中所定义的配置类以及 bean 加载到 SpringIOC 容器中的
@ComponentScan
使用 @ComponentScan 可以指定要扫描的包,例如依赖导入了 com.example
包
@SpringBootApplication
@ComponentScan({"net.yexca","com.example"})
不过当需要引入大量的第三方的依赖,上方要配置大量的包,而大面积的扫描性能也比较低
@Import
可以导入普通类、配置类以及 ImportSelector 接口实现类
普通类
@Import(TokenParser.class) //导入普通类
@SpringBootApplication
配置类
配置类内容
@Configuration
public class HeaderConfig {
@Bean
public HeaderParser headerParser(){
return new HeaderParser();
}
@Bean
public HeaderGenerator headerGenerator(){
return new HeaderGenerator();
}
}
启动类
@Import(HeaderConfig.class) //导入配置类
@SpringBootApplication
ImportSelector 接口实现类
ImportSelector 接口实现类内容
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//返回值字符串数组(数组中封装了全限定名称的类)
return new String[]{"com.example.HeaderConfig"};
}
}
启动类
@Import(MyImportSelector.class) //导入ImportSelector接口实现类
@SpringBootApplication
@EnableXxxxx
上述 @Import 需要首先知道第三方依赖中有哪些配置类或 bean 才可,而第三方依赖可以提供 @EnableXxxxx 注解,封装 @Import 注解提供一些常用 bean,使用时只需要 @EnableXxxxx 注解即可
如上述 @Import 的配置类,封装一个 @EnableHeaderConfig 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)//指定要导入哪些bean对象或配置类
public @interface EnableHeaderConfig {
}
然后只需要在启动类加上 @EnableHeaderConfig 注解即可导入相应 bean
@EnableHeaderConfig //使用第三方依赖提供的Enable开头的注解
@SpringBootApplication
此方法也是 SpringBoot 所采用的方式
SpringBoot 的自动配置
在 @SpringBootApplication 注解里有 @EnableAutoConfiguration,其中 @Import({AutoConfigurationImportSelector.class}) 导入了 ImportSelector 接口的实现类 AutoConfigurationImportSelector.class
在该实现类中重写了 selectImports() 方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 获取自动配置的配置类信息集合
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
调用 getAutoConfigurationEntry() 方法获取了自动配置的配置类信息集合
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 获取在配置文件中配置的所有自动配置类的集合
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
其中 getCandidateConfigurations(annotationMetadata, attributes) 方法获取在配置文件中配置的所有自动配置类的集合
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
可以看到是获取 META-INF/spring.factories 和 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中配置类的集合
上述俩文件通常在引入的起步依赖中
也就是说当 SpringBoot 程序启动时,就会加载配置文件当中所定义的配置类,并将这些配置类信息 (类的全限定名) 封装到 String 类型的数组中,最终通过 @Import 注解将这些配置类全部加载到 Spring 的 IOC 容器中,交给 IOC 容器管理
@Conditional
可文件中的配置类那么多,每个 bean 都会注册到 IOC 容器中吗。并不是,使用 @Conditional 注解可以使得 bean 对象按照条件进行装配
@Conditional 是一个父注解,有许多子注解
@ConditionalOnClass
判断环境中有对应字节码文件,才注册 bean 到 IOC 容器
@Configuration
public class HeaderConfig {
@Bean
//环境中存在指定的这个类,才会将该bean加入IOC容器
@ConditionalOnClass(name="io.jsonwebtoken.Jwts")
public HeaderParser headerParser(){
return new HeaderParser();
}
}
上述 bean 需要引入 jwt 令牌的依赖才会注入到 IOC 容器中
<!--JWT令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
测试
@SpringBootTest
public class AutoConfigurationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
public void testHeaderParser(){
System.out.println(applicationContext.getBean(HeaderParser.class));
}
}
@ConditionalOnMissingBean
判断环境中没有对应的 bean (类型或名称),才注册 bean 到 IOC 容器
@Configuration
public class HeaderConfig {
@Bean
@ConditionalOnMissingBean //不存在该类型的bean,才会将该bean加入IOC容器
public HeaderParser headerParser(){
return new HeaderParser();
}
}
上述当 IOC 中没有 HeaderConfig 类型的 bean 才会创建
也可以在注解中指定其他 bean 名字
@Configuration
public class HeaderConfig {
@Bean
//不存在指定名称的bean,才会将该bean加入IOC容器
@ConditionalOnMissingBean(name="deptController2")
public HeaderParser headerParser(){
return new HeaderParser();
}
}
上例在不存在名字为 deptController2 的 bean 对象才会创建 HeaderConfig 对象注册到 IOC
还可以指定类型
@Configuration
public class HeaderConfig {
@Bean
//不存在指定类型的bean,才会将bean加入IOC容器
@ConditionalOnMissingBean(HeaderConfig.class)
public HeaderParser headerParser(){
return new HeaderParser();
}
}
上例运行调用该 bean 会引发异常 NoSuchBeanDefinitionException,在 @Configuration 中有 @Component,所以会自动创建 HeaderConfig 的 bean,所以不会创建 HeaderParser 的 bean
@ConditionalOnProperty
判断配置文件中有对应属性和值,才注册 bean 到 IOC 容器
配置文件
name: header
配置类
@Configuration
public class HeaderConfig {
@Bean
//配置文件中存在指定属性名与值,才会将bean加入IOC容器
@ConditionalOnProperty(name ="name",havingValue = "header")
public HeaderParser headerParser(){
return new HeaderParser();
}
}
自定义起步依赖
例如自定义一个阿里云 OSS 的起步依赖
首先是命名,SpringBoot 官方 starter 命名为 spring-boot-starter-xxx
,而第三方组织提供的为 xxx-spring-boot-starter
然后是模块,需要按规范定义两个模块
- starter 模块,进行依赖管理,把程序开发所需要的依赖都定义在 starter 起步依赖中
- autoconfigure 模块,用于自动配置
定义好这俩模块后,其他项目只需要引入起步依赖即可,自动配置模块会依赖传递
模块 pom 文件
aliyun-oss-spring-boot-starter 模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!--引入autoconfigure模块-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
aliyun-oss-spring-boot-autoconfigure 模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--引入web起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--阿里云OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
</dependencies>
</project>
自动配置
AliOSSAutoConfiguration 类
@Configuration
//导入AliOSSProperties类,并交给SpringIOC管理
@EnableConfigurationProperties(AliOSSProperties.class)
public class AliOSSAutoConfiguration {
//创建AliOSSUtils对象,并交给SpringIOC容器
@Bean
public AliOSSUtils aliOSSUtils(AliOSSProperties aliOSSProperties){
AliOSSUtils aliOSSUtils = new AliOSSUtils();
aliOSSUtils.setAliOSSProperties(aliOSSProperties);
return aliOSSUtils;
}
}
AliOSSProperties 类
/*阿里云OSS相关配置*/
@Data
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {
//区域
private String endpoint;
//身份ID
private String accessKeyId ;
//身份密钥
private String accessKeySecret ;
//存储空间
private String bucketName;
}
AliOSSUtils 类
@Data
public class AliOSSUtils {
private AliOSSProperties aliOSSProperties;
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile multipartFile) throws IOException {
// 获取上传的文件的输入流
InputStream inputStream = multipartFile.getInputStream();
// 避免文件覆盖
String originalFilename = multipartFile.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(aliOSSProperties.getEndpoint(),
aliOSSProperties.getAccessKeyId(), aliOSSProperties.getAccessKeySecret());
ossClient.putObject(aliOSSProperties.getBucketName(), fileName, inputStream);
//文件访问路径
String url =aliOSSProperties.getEndpoint().split("//")[0] + "//" + aliOSSProperties.getBucketName() + "." + aliOSSProperties.getEndpoint().split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
新建自动配置文件 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.aliyun.oss.AliOSSAutoConfiguration
使用
引入依赖
<!--引入阿里云OSS起步依赖-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
上例阿里云OSS相关配置需要从配置文件读取
#配置阿里云OSS参数
aliyun:
oss:
endpoint: your_oss_region
accessKeyId: your_key_id
accessKeySecret: your_key_secret
bucketName: your_bucker_name
测试
@RestController
public class UploadController {
@Autowired
private AliOSSUtils aliOSSUtils;
@PostMapping("/upload")
public String upload(MultipartFile image) throws Exception {
//上传文件到阿里云 OSS
String url = aliOSSUtils.upload(image);
return url;
}
}