JavaWeb后端开发原理篇
配置优先级
1.SpringBoot中支持三种格式的配置文件,优先级为(高->低):application.properties、application.yml、application.yaml。
注意:虽然SpringBoot支持多种格式配置文件,但是在项目开发时,推荐统一使用一种格式的配置 (yml是主流)。
2.SpringBoot除了支持配置文件属性配置,还支持Java系统属性和命令行参数的方式进行属性配置。
Java系统属性:-Dserver.port=9000
命令行参数:--server.port=10010

测试优先级:
1.执行maven打包指令package。
注意:Springboot项目进行打包时,需要引入插件spring-boot-maven-plugin(基于官网骨架创建项目,会自动添加该插件)。
2.执行java指令,运行jar包。
1
| java -Dserver.port=9000 -jar springboot-web-tlias-0.0.1-SNAPSHOT.jar --server.port=10010
|
优先级排序(低->高):
1 2 3 4 5
| application.yaml(忽略) application.yml application.properties java系统属性(-Dxxx=xxx) 命令行参数(
|
Bean管理
获取Bean
默认情况下,Spring项目启动时,会把bean都创建好放在IOC容器中,如果想要主动获取这些bean,可以通过如下方式:
- 根据
name获取bean:Object getBean(String name)
- 根据类型获取
bean:<T> T getBean(Class<T> requiredType)
- 根据
name获取bean(带类型转换):<T> T getBean(String name, Class<T> requiredType)
注意:上述所说的 【Spring项目启动时,会把其中的bean都创建好】还会受到作用域及延迟初始化影响,这里主要针对于默认的单例非延迟加载的bean而言。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.itheima;
@SpringBootTest class SpringbootWebTliasApplicationTests { @Autowired private ApplicationContext applicationContext;
@Test public void testGetBean(){ DeptController bean1 = (DeptController) applicationContext.getBean("deptController"); System.out.println(bean1);
DeptController bean2 = applicationContext.getBean(DeptController.class); System.out.println(bean2);
DeptController bean3 = applicationContext.getBean("deptController", DeptController.class); System.out.println(bean3); } }
|
Bean作用域
Spring支持五种作用域,后三种在web环境才生效:
| 作用域 |
说明 |
| singleton |
容器内同名称的 bean 只有一个实例(单例)(默认) |
| prototype |
每次使用该 bean 时会创建新的实例(非单例) |
| request |
每个请求范围内会创建新的实例(web环境中,了解) |
| session |
每个会话范围内会创建新的实例(web环境中,了解) |
| application |
每个应用范围内会创建新的实例(web环境中,了解) |
可以通过@Scope注解来进行配置作用域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public DeptController(){ System.out.println("调用DeptController构造方法"); }
@Test public void testScope(){ for (int i = 0; i < 10; i++) { DeptController deptController = applicationContext.getBean(DeptController.class); System.out.println(deptController); } }
|
注意:
- 默认
singleton的bean,在容器启动时被创建(Bean对象在容器启动的时候就实例化了,并将实例化的Bean对象放到IOC容器当中),可以使用@Lazy注解来延迟初始化(延迟到第一次使用时)。在一个类上加上@Lazy注解可以让该Bean对象延迟初始化。
prototype的bean,每一次使用该bean的时候都会创建一个新的实例。
- 实际开发当中,绝大部分的
Bean是单例的,也就是说绝大部分Bean不需要配置scope属性。
第三方Bean
1.如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component及衍生注解声明bean的,就需要用到@Bean注解。
1 2 3 4 5 6
| <dependency> <groupId>org.dom4j</groupId> <artifactId>dom4j</artifactId> <version>2.1.3</version> </dependency>
|
1 2 3 4 5 6
| <?xml version="1.0" encoding="UTF-8"?> <emp> <name>Tom</name> <age>18</age> </emp>
|
1 2 3 4 5 6 7 8 9 10
| package com.itheima;
@SpringBootApplication public class SpringbootWebTliasApplication { @Bean public SAXReader saxReader(){ return new SAXReader(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.itheima;
@SpringBootTest class SpringbootWebTliasApplicationTests { @Autowired private SAXReader saxReader;
@Test public void testThirdBean() throws Exception {
Document document = saxReader.read(this.getClass().getClassLoader().getResource("1.xml")); Element rootElement = document.getRootElement(); String name = rootElement.element("name").getText(); String age = rootElement.element("age").getText();
System.out.println(name + " : " + age); } }
|
2.若要管理的第三方bean对象,建议对这些bean进行集中分类配置,可以通过@Configuration注解声明一个配置类。
1 2 3 4 5 6 7 8 9 10 11
| package com.itheima.config;
@Configuration public class CommonConfig { @Bean public SAXReader saxReader(){ return new SAXReader(); } }
|
注意:
1.通过@Bean注解的name或value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名。
1 2 3 4 5
| @Test public void testGetBean2(){ Object saxReader = applicationContext.getBean("saxReader"); System.out.println(saxReader); }
|
2.如果第三方bean需要依赖其它bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配。
1 2 3 4 5 6 7
| @Bean public SAXReader saxReader(DeptService deptService) { System.out.println(deptService); return new SAXReader(); }
|
@Component及衍生注解与@Bean注解使用场景:
- 项目中自定义的,使用
@Component及其衍生注解。
- 项目中引入第三方的,使用
@Bean注解。
SpringBoot原理
起步依赖
起步依赖的原理:Maven的依赖传递。

自动配置
SpringBoot的自动配置就是当Spring容器启动后,一些配置类、Bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。
【案例】
新建项目itheima-utils,声明了三个类型的Bean对象(这三个Bean对象所在的包为com.example)。
1 2 3 4 5 6 7 8
| package com.example;
@Component public class TokenParser { public void parse(){ System.out.println("TokenParser ... parse ..."); } }
|
1 2 3 4 5 6 7
| package com.example;
public class HeaderParser { public void parse(){ System.out.println("HeaderParser ... parse ..."); } }
|
1 2 3 4 5 6 7
| package com.example;
public class HeaderGenerator { public void generate(){ System.out.println("HeaderGenerator ... generate ..."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.example;
@Configuration public class HeaderConfig { @Bean public HeaderParser headerParser(){ return new HeaderParser(); }
@Bean public HeaderGenerator headerGenerator(){ return new HeaderGenerator(); } }
|
在项目itheima-utils同一目录下另一个项目springboot-web-test中,引入项目itheima-utils。
1 2 3 4 5 6
| <dependency> <groupId>com.example</groupId> <artifactId>itheima-utils</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
|
然后测试是否引入了项目itheima-utils的三个类型的Bean对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.itheima;
@SpringBootTest class SpringbootWebTestApplicationTests { @Test public void testTokenParser(){ System.out.println(applicationContext.getBean(TokenParser.class)); }
@Test public void testHeaderParser(){ System.out.println(applicationContext.getBean(HeaderParser.class)); }
@Test public void testHeaderGenerator(){ System.out.println(applicationContext.getBean(HeaderGenerator.class)); } }
|
【报错】org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.TokenParser' available。
上述三个测试方法都会出现同一个报错:org.springframework.beans.factory.NoSuchBeanDefinitionException。因为启动类的注解@SpringBootApplication进行包扫描时,扫描范围是当前包及其子包,即:com.itheima及其子包,所以无法扫描第三方依赖中的com.example这个包。
自动配置原理
方案一:@ComponentScan组件扫描(使用繁琐,性能低)。
1 2 3 4 5 6 7
| @ComponentScan({"com.itheima", "com.example"}) @SpringBootApplication public class SpringbootWebTestApplication { public static void main(String[] args) { SpringApplication.run(SpringbootWebTestApplication.class, args); } }
|
方案二:@Import导入。使用@Import导入的类会被Spring加载到IOC容器中,导入形式主要有以下几种:
1.导入普通类。
1 2 3 4 5
| @Import({TokenParser.class}) @SpringBootApplication public class SpringbootWebTestApplication { }
|
2.导入配置类。
1 2 3 4 5
| @Import({HeaderConfig.class}) @SpringBootApplication public class SpringbootWebTestApplication { }
|
3.导入ImportSelector接口实现类。
1 2 3 4 5 6 7 8
| package com.example;
public class MyImportSelector implements ImportSelector { public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.example.HeaderConfig"}; } }
|
1 2 3 4 5
| @Import({MyImportSelector.class}) @SpringBootApplication public class SpringbootWebTestApplication { }
|
4.@EnableXxxx注解,封装@Import注解。(让第三方提供要导入的类,更方便,是SpringBoot中采用的方式)
1 2 3 4 5 6 7
| package com.example;
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(MyImportSelector.class) public @interface EnableHeaderConfig { }
|
1 2 3 4 5
| @EnableHeaderConfig @SpringBootApplication public class SpringbootWebTestApplication { }
|
源码跟踪
@SpringBootApplication:该注解标识在SpringBoot工程引导类上,是SpringBoot中最最最重要的注解。该注解由三个部分组成:
@SpringBootConfiguration:该注解与@Configuration注解作用相同,用来声明当前也是一个配置类。所以可以在启动类中声明第三方Bean,因为启动类也是一个配置类。
@indexed用来加速应用启动。
@ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包。
@EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解。

spring.factories是SpringBoot早期自动配置加载的文件,在SpringBoot2.7.x版本之后,提供了全新的自动配置文件org.springframework.boot.autoconfigure.AutoConfiguration.imports,会兼容SpringBoot文件。

@Conditional
作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的Bean对象到Spring IOC容器中。
位置:方法、类。
@Conditional本身是一个父注解,派生出大量的子注解:

1.@ConditionalOnClass:判断环境中是否有对应字节码文件,才注册bean到IOC容器。
1 2 3 4 5 6
| @Bean @ConditionalOnClass(name = "io.jsonwebtoken.Jwts")
public HeaderParser headerParser(){ return new HeaderParser(); }
|
如果引入了jjwt依赖,则可以测试成功(上述@Test测试方法),否则测试失败。
1 2 3 4 5 6
| <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.12.3</version> </dependency>
|
2.@ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。
1 2 3 4 5 6
| @ConditionalOnMissingBean
public HeaderParser headerParser(){ return new HeaderParser(); }
|
3.@ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器。
1 2 3 4 5
| @ConditionalOnProperty(name = "name", havingValue = "itheima")
public HeaderParser headerParser(){ return new HeaderParser(); }
|
在application.properties文件中,加入如下属性值:
如果是application.yml文件,则为:
案例(自定义starter)
在实际开发中,经常会定义一些公共组件,提供给各个项目团队使用。而在SpringBoot的项目中,一般会将这些公共组件封装为SpringBoot的starter。

需求:自定义aliyun-oss-spring-boot-starter,完成阿里云OSS操作工具类AliyunOSSUtils的自动配置。
目标:引入起步依赖引入之后,要想使用阿里云OSS,注入AliyunOSSUtils直接使用即可。

步骤:
1.创建aliyun-oss-spring-boot-starter模块。该模块下只有aliyun-oss-spring-boot-starter.iml和pom.xml两个文件。

pom.xml文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?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>3.3.5</version> <relativePath/> </parent>
<groupId>com.aliyun.oss</groupId> <artifactId>aliyun-oss-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version>
<properties> <java.version>17</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies>
</project>
|
如果没有iml文件,点击两下Ctrl键,在如下页面运行mvn idea:module命令,即可生成aliyun-oss-spring-boot-starter.iml文件。

2.创建aliyun-oss-spring-boot-autoconfigure模块,在starter中引入该模块。
在aliyun-oss-spring-boot-autoconfigure模块中的定义自动配置功能,并定义自动配置文件META-INF/spring/xxxx.imports。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| <?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>3.3.5</version> <relativePath/> </parent>
<groupId>com.aliyun.oss</groupId> <artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId> <version>0.0.1-SNAPSHOT</version>
<properties> <java.version>17</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.3.4</version> </dependency>
<dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.17.4</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> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>2.3.3</version> </dependency>
</dependencies> </project>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package com.aliyun.oss;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "aliyun.oss") public class AliOSSProperties { private String endpoint; private String accessKeyId; private String accessKeySecret; private String bucketName;
public String getEndpoint() { return endpoint; }
public void setEndpoint(String endpoint) { this.endpoint = endpoint; }
public String getAccessKeyId() { return accessKeyId; }
public void setAccessKeyId(String accessKeyId) { this.accessKeyId = accessKeyId; }
public String getAccessKeySecret() { return accessKeySecret; }
public void setAccessKeySecret(String accessKeySecret) { this.accessKeySecret = accessKeySecret; }
public String getBucketName() { return bucketName; }
public void setBucketName(String bucketName) { this.bucketName = bucketName; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| package com.aliyun.oss;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.InputStream; import java.util.UUID;
public class AliOSSUtils {
private AliOSSProperties aliOSSProperties;
public AliOSSProperties getAliOSSProperties() { return aliOSSProperties; }
public void setAliOSSProperties(AliOSSProperties aliOSSProperties) { this.aliOSSProperties = aliOSSProperties; }
public String upload(MultipartFile file) throws IOException { String endpoint = aliOSSProperties.getEndpoint(); String accessKeyId = aliOSSProperties.getAccessKeyId(); String accessKeySecret = aliOSSProperties.getAccessKeySecret(); String bucketName = aliOSSProperties.getBucketName();
InputStream inputStream = file.getInputStream();
String originalFilename = file.getOriginalFilename(); String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); ossClient.putObject(bucketName, fileName, inputStream);
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName; ossClient.shutdown(); return url; }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.aliyun.oss;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration @EnableConfigurationProperties(AliOSSProperties.class)
public class AliOSSAutoConfiguration { @Bean public AliOSSUtils aliOSSUtils(AliOSSProperties aliOSSProperties) { AliOSSUtils aliOSSUtils = new AliOSSUtils(); aliOSSUtils.setAliOSSProperties(aliOSSProperties); return aliOSSUtils; } }
|
org.springframework.boot.autoconfigure.AutoConfiguration.imports文件内容:
1
| com.aliyun.oss.AliOSSAutoConfiguration
|
在测试项目springboot-web-test的pom.xml文件中引入依赖:
1 2 3 4 5 6
| <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-oss-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.itheima.controller;
@RestController public class UploadController { @Autowired private AliOSSUtils aliOSSUtils;
@PostMapping("upload") public String upload(MultipartFile image) throws IOException { String url = aliOSSUtils.upload(image); return url; } }
|
测试链接:使用Postman进行测试,接口(POST):http://localhost:8080/upload。
Web后端开发总结


