1、知道SpringBoot中配置文件的优先级
配置文件分3类:
.properties
.yaml
.yml
注意可以顺便复习一下yml文件的格式。
优先级顺序:
properties > yml > yaml
对于jar包的2种方式:
Java系统属性配置
命令行参数
优先级顺序:
命令行参数 > 系统属性参数
其他方法:
jar包同级下建立配置文件
总优先级顺序
jar包同级文件 > 命令行参数 > 系统属性参数 > properties参数 > yml参数 > yaml参数
2、能够说出bean的两种常见的作用域及如何设置作用域
两种常见作用域
单例 singleton
容器内同名称的bean只有一个实例
非单例 prototype
每次使用该bean时会创建新的实例
设置方法
在对应对象前加入@Scope参数。
单例:
@Scope // 默认为单例singleton模式。
// 同下
@Scope("singleton")
非单例:
@Scope("prototype") // 非单例模式
举例:
@Scope("prototype")
@Lazy
@Service
public class TestServiceImpl2 implements TestService {
public TestServiceImpl2() {
System.out.println("hahaha");
}
@Override
public void test() {
System.out.println("2 is testing~");
}
}
可以自己再做一些测试。
3、能通过@Bean注解来管理第三方bean
比如引入dom4j。
首先在pom.xml配置,略写了。
然后有两种方法管理第三方类:
方法一:在启动类上添加
在启动类中添加用@Bean注释过的第三方类:
@SpringBootApplication
public class MyWeb14Starter {
public static void main(String[] args) {
SpringApplication.run(MyWeb14Starter.class, args);
}
@Bean
public SAXReader saxReader() {
return new SAXReader();
}
}
然后可以用测试类测试。将这个第三方bean注入容器,即可使用:
@SpringBootTest
public class ThirdBeanTest {
@Autowired
private SAXReader saxReader;
@Test
public void test() throws DocumentException {
System.out.println(saxReader);
InputStream is = ClassLoader.getSystemResourceAsStream("haha\\a.xml");
Document doc = saxReader.read(is);
Element rootElement = doc.getRootElement();
String name = rootElement.element("name").getText();
String age = rootElement.element("age").getText();
System.out.println(name + ": " + age);
}
}
测试运行结果如下:
方法二:在配置类中添加
在能被启动类扫描的地方下建立一个配置类,比如config下的CommonConfig:
@Configuration
public class CommonConfig {
@Bean
public SAXReader reader(TestServiceImpl2 service){
System.out.println(service); // 注意,为了区分结果,打印了一下service
return new SAXReader();
}
}
测试类还是同上:
@SpringBootTest
public class ThirdBeanTest {
@Autowired
private SAXReader saxReader;
@Test
public void test() throws DocumentException {
System.out.println(saxReader);
InputStream is = ClassLoader.getSystemResourceAsStream("haha\\a.xml");
Document doc = saxReader.read(is);
Element rootElement = doc.getRootElement();
String name = rootElement.element("name").getText();
String age = rootElement.element("age").getText();
System.out.println(name + ": " + age);
}
}
结果如下:
打印了hahaha是因为我在TestServiceImpl2类的无参构造中定义了一下打印的。
@Scope("prototype")
@Lazy
@Service
public class TestServiceImpl2 implements TestService {
public TestServiceImpl2() {
System.out.println("hahaha"); // 看这里看这里~
}
@Override
public void test() {
System.out.println("2 is testing~");
}
}
4.、能说出@Conditional注解的作用
作条件判断,符合的bean对象才能注入到IOC容器中。
常见的有3类:
-
@ConditionalOnClass:判断环境中有对应字节码文件,才注册bean到IOC容器。
-
@ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。
-
@ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器。
最常用的是第三个。演示如下:
第三方bean:
@Configuration
public class CommonConfig {
@Bean
@ConditionalOnProperty(name = "hello", havingValue = "haha")
public SAXReader reader(TestServiceImpl2 service){
System.out.println(service);
return new SAXReader();
}
}
测试类同上:
@SpringBootTest
public class ThirdBeanTest {
@Autowired
private SAXReader saxReader;
@Test
public void test() throws DocumentException {
System.out.println(saxReader);
InputStream is = ClassLoader.getSystemResourceAsStream("haha\\a.xml");
Document doc = saxReader.read(is);
Element rootElement = doc.getRootElement();
String name = rootElement.element("name").getText();
String age = rootElement.element("age").getText();
System.out.println(name + ": " + age);
}
}
结果报错如下:
在application.properties文件中更改如下:
hello=haha
运行结果如下:
5、能说出@Import注解的作用
手动指定导入Bean的文件夹。
导入普通类
普通类如下:
@Component
public class Haha {
public Haha() {
System.out.println("happy happy happy~");
}
}
启动类上使用@Import注解:
@Import(Haha.class)
@SpringBootApplication
public class MyWeb14Starter {
public static void main(String[] args) {
SpringApplication.run(MyWeb14Starter.class, args);
}
// @Bean
// public SAXReader saxReader() {
// return new SAXReader();
// }
}
运行启动类,结果如下:
还有配置类、接口实现类都可以。
6、能简要的说出springboot自动配置的原理
启动类是配置类
ctrl + 鼠标左键点入@SpringBootApplication,可以看到如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 看这里看这里!
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
再点入@SpringBootConfiguration:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 看这里看这里
@Indexed
这是一个Configuration类,配置类。
启动类自带组件扫描
再回到@SpringBootApplication注解上:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan( // 看这里的组件扫描
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
可以看到它自带@ComponentScan组件扫描。
封装了Import注解
再看@SpringBootApplication里面的@EnableAutoConfiguration注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration // 看这个看这个
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
注解中如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class}) // 看这个看这个
看这个import的:
public class AutoConfigurationImportSelector implements DeferredImportSelector/*看这个*/, BeanClassLoaderAware, ...
再看这个DeferredImportSelector类:
public interface DeferredImportSelector extends ImportSelector
AutoConfigurationImportSelector类,实现了ImportSelector中的selectImports方法:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
进入getAutoConfigurationEntry方法:
protected AutoConfigurationImportSelector.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 AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
进入getCandidateConfigurations方法:
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;
}
看这个"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."
这个META-INF文件夹下的就是自动配置的文件。我们可以找到进去看看:
7、知道如何自定义一个简单的starter
新建项目
新建aliyun-oss-springboot-autoconfigure项目和aliyun-oss-springboot-starter项目。
配置pom文件
依赖关系是 主项目 -> 自定义starter -> 自定义autoconfigure。首先配置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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.heiha</groupId>
<artifactId>aliyun-oss-springboot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.heiha</groupId>
<artifactId>aliyun-oss-springboot-autoconfigure</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
然后配置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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.heiha</groupId>
<artifactId>aliyun-oss-springboot-autoconfigure</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
配置autoconfigure项目
配置Configuration类:
@Configuration
@EnableConfigurationProperties(AliOSSProperties.class)
public class OSSConfig {
@Bean
public AliOSSUtils aliOSSUtils(AliOSSProperties properties) {
return new AliOSSUtils(properties);
}
}
配置properties类:
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {
//区域
private String endpoint;
//身份ID
private String accessKeyId ;
//身份密钥
private String accessKeySecret ;
//存储空间
private String bucketName;
}
配置utils类:
/**
* 阿里云 OSS 工具类
*/
@Component
public class AliOSSUtils {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
@Value("${aliyun.oss.endpoint}")
private String endpoint;
// RAM用户的访问密钥(AccessKey ID和AccessKey Secret)。
@Value("${aliyun.oss.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.oss.accessKeySecret}")
private String accessKeySecret;
// 填写Bucket名称,例如examplebucket。
@Value("${aliyun.oss.bucketName}")
private String bucketName;
public AliOSSUtils(AliOSSProperties properties) {
}
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile file) throws IOException {
// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();
// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
配置主项目
在pom中引入自定义starter:
<dependency>
<groupId>com.heiha</groupId>
<artifactId>aliyun-oss-springboot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
配置中写好阿里云的参数:
aliyun:
oss:
endpoint: https://oss-cn-beijing.aliyuncs.com
accessKeyId: ****
accessKeySecret: ****
bucketName: heiha-huahua
编写controller:
@RestController
public class UploadController {
@Autowired
private AliOSSUtils ali;
// @PostMapping("/upload")
// public Result upload(String username, String age, MultipartFile image) throws IOException {
// UUID uuid = UUID.randomUUID();
// String originalFilename = image.getOriginalFilename();
// int i = originalFilename.lastIndexOf(".");
// String newFileName = uuid + originalFilename.substring(i);
// image.transferTo(new File("D:\\mmimg\\" + newFileName));
// return Result.success();
// }
@PostMapping("/upload")
public Result upload(MultipartFile image) throws IOException {
String url = ali.upload(image);
return Result.success(url);
}
}