# 实验2-springboot
**Repository Path**: chenbairui/experiment_2springboot
## Basic Information
- **Project Name**: 实验2-springboot
- **Description**: 利用Spring boot的自动装配特性实现动态注册组件
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-04-12
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
##
实验二 利用Spring boot的自动装配特性实现动态注册组件
课程名称:企业级开发框架专题 学期:2020春季
|
实验名称 | Spring boot的自动装配特性实现动态注册组件 | 实验序号 | 二 |
|
姓 名 | 陈伯瑞 | 学 号 | 201741412102 | 班 级 | 17软卓1班 |
|
实验地点 | 在家 | 实验日期 | 2020/4/7 | 指导老师 | 黎志雄 |
|
教师评语 | | 实验成绩 | 评阅教师 |
| 百分制 | |
|
同组同学 | 无 |
## 实验目的:
1.掌握Spring Boot的自动配置原理;
2.掌握Spring框架动态注册Bean的原理;
3.掌握自动生成元数据文件。
4.掌握spring框架的事件模型。
## 实验环境:
- jdk13
- maven3.6.1
- IntelliJ IDEA
- springboot 2.2.5
## 实验过程
1. 在IntelliJ IDEA创建Spring Boot项目,添加Spring Configuration Processor依赖

2. 创建一个自定义的CommandLineRunner接口的实现类,不在自定义类上加@Component注解
package com.springboot.experiment2;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.env.Environment;
import java.util.Objects;
/**
* author 陈伯瑞
* date 2020/4/9 0009 13:49
* version 1.0
**/
@SuppressWarnings("unused")
public class CustomerCommandLineRunner implements CommandLineRunner {
Environment env;
public CustomerCommandLineRunner(Environment env) {
this.env = env;
}
@Override
public void run(String... args) throws Exception {
System.out.println("利用springboot 自动装配的CommandLineRunner。");
System.out.println("生成一个随机字符串:".concat(Objects.requireNonNull(env.getProperty("random."))));
}
}
3. 创建一个自定义的自动配置类。
package com.springboot.experiment2;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
/**
* author 陈伯瑞
* date 2020/4/9 0009 14:01
* version 1.0
**/
public class AutoConfig {
@Bean
CommandLineRunner createCustomerLineRunner(Environment env){
return new CustomerCommandLineRunner(env);
}
}
4. 在META-INF目录,创建spring.factories文件,key是EnableAutoConfiguration的全限定类名,值为自动配置类的全限定类路径类名
>org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.springboot.experiment2.AutoConfig

运行结果:

5.给自动配置类添加有效条件
* 利用@ConditionalOnProperty注解,添加属性条件,当自定义的属性值为ture时,该bean才会实例化

* 在application.properties属性文件中添加一个自定义的属性,对应属性名为jon.auto.enable,值为ture

* jon.auto.enable=ture,则运行时自定义的配置类肯定生效:

* 把jon.auto.enable改为fasle;则自定义的配置类失效:

6.自定义的一个Bean,绑定属性值,并生成spring配置类的元数据文件
* 创建一个类,并在类上加@ConfigurationProperties注解,设置注解的prefix属性指定绑定的属性的前缀
package com.springboot.experiment2;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* author 陈伯瑞
* date 2020/4/11 0011 13:12
* version 1.0
**/
@SuppressWarnings("unused")
@ConfigurationProperties(prefix = "jon.auto")
public class CustomProperties {
/**
* 自动配置类是否生效
* */
private boolean enable=false;
public boolean isEnable(){
return enable;
}
public void setEnable(boolean enable){
this.enable=enable;
}
}
* 在自定义配置类上添加@EnableConfigurationProperties,并指定装配的属性Bean
package com.springboot.experiment2;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
/**
* author 陈伯瑞
* date 2020/4/9 0009 14:01
* version 1.0
**/
@EnableConfigurationProperties(CustomProperties.class)
public class AutoConfig {
@Bean
@ConditionalOnProperty(prefix = "jon.auto",name="enable",havingValue = "true")
CommandLineRunner createCustomerLineRunner(Environment env){
return new CustomerCommandLineRunner(env);
}
}
* 使用spring boot框架提供的注解处理器生成自定义属性的元数据文件
> mvn clean package后元数据文件位于META-INF目录下,名字spring-configuration-metadata.json,spring-boot-configuration-processor依赖里有spring boot提供的注解处理器,在项目编译时检查项目内所有@ConfigurationProperties的类,并生成元数据文件(target/classes/META-INF/spring-configuration-metadata.json):

7.自定义一个事件发布器,并设置线程池,实现异步发布事件
* 自定义事件发布器
public class MyApplicationEventMulticaster {
/**
* 自定义一个事件发布器
* */
@Bean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
ApplicationEventMulticaster customApplicationEventMulticaster(ThreadPoolTaskExecutor taskExecutor){
SimpleApplicationEventMulticaster eventMulticaster=new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(taskExecutor);
return eventMulticaster;
}
}
8.自定义事件类
package com.springboot.experiment2;
/**
* author 陈伯瑞
* date 2020/4/12 0012 13:31
* version 1.0
**/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
/**
自定义事件*/
public class NoticeEvent extends ApplicationEvent {
private static final Logger logger= LoggerFactory.getLogger(NoticeEvent.class);
/**
* 存储信息
*/
private final String message;
public NoticeEvent(String message){
super(message);
this.message=message;
logger.info("添加事件成功!message:{}",message);
}
public String getMessage(){return message;}
}
9.自定义事件监听器
@SuppressWarnings("unused")
@Component
public class NoticeListener implements ApplicationListener {
private static final Logger logger= LoggerFactory.getLogger(NoticeListener.class);
@Override
public void onApplicationEvent(NoticeEvent noticeEvent){
logger.info("/事件监听器获取NoticeEvent,睡眠当前线程 2秒..");
try{
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
logger.info("NoticeEvent 的 message属性是:{}",noticeEvent.getMessage());
}
}
10.编写一个测试用例,检查发布事件时,使用了多线程异步处理
@SpringBootTest
class AutoConfigTest {
@Autowired
private ApplicationEventMulticaster eventMulticaster;
@Test
void customApplicationEventMulticaster() throws InterruptedException{
eventMulticaster.multicastEvent(new NoticeEvent("测试one"));
eventMulticaster.multicastEvent(new NoticeEvent("测试two"));
Thread.sleep(3000);
}
}
测试结果:

11.深入思考(自定义一个线程池,然后给自定义的事件发布器使用)
* 实验过程中自定义事件发布器时,加入了框架定义的线程池ThreadPoolTaskExecutor,改线程池的构建过程在实例化spring.factory的自动配置bean时发生,阅读源码可以发现默认线程池在自动配置类TaskExecutionAutoConfiguration中定义,并规定了bean的name以及bean实例化的条件,即假如存在继承Executor.class的类,就不会加载默认的线程池。
* 源码如下:

* 所以我们只需构建一个ThreadPoolTaskExecutor类型的bean,该类型已继承了Executor.class,这个bean就是我们自定义的线程池
/**
* 自定义线程池
*/
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(200);
/**
* 线程池前缀
*/
executor.setThreadNamePrefix("MyExecutor");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(200);
return executor;
}
* 运行测试用例:

## 实验总结
1. 学会看源码的重要性