# spring boot 实验 2
**Repository Path**: superlgc/spring_boot_experiment_2
## Basic Information
- **Project Name**: spring boot 实验 2
- **Description**: No description available
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-04-14
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
东莞理工学院网络空间安全学院
实验报告模板
| 课程名称:企业级开发框架专题 |
学期:2019年秋季 |
| 课程名称 |
利用Spring boot的自动装配特性实现动态注册组件 |
实验序号 |
二 |
| 姓 名 |
李贵超 |
学 号 |
201741412214 |
班 级 |
17软件工程卓越2班 |
| 实验地点 |
无 |
实验日期 |
2020/4/10 |
指导老师 |
黎志雄 |
| 教师评语 |
|
实验成绩 |
评阅教师 |
| 百分制 |
|
| 同组同学 |
|
### 一、实验目的
**1、 掌握Spring Boot的自动配置原理;**
**2、 掌握Spring框架动态注册Bean的原理;**
**3、 掌握自动生成元数据文件。**
**4、 掌握spring框架的事件模型。**
### 二、 实验环境
**1、 JDK 1.8或更高版本**
**2、 Maven 3.6+**
**3、 IntelliJ IDEA**
### 三、 实验任务
**1. 通过IntelliJ IDEA的Spring Initializr向导创建Spring Boot项目。**
添加Spring Configuration Processor依赖。

**2. 创建一个自定义的CommandLineRunner接口的实现类。**
若加上@Component注解,该CommandLineRunner会执行两次。
```java
@SuppressWarnings("unused")
public class MyCommandLineRunner implements CommandLineRunner {
Environment env;
public MyCommandLineRunner(Environment env) {
this.env = env;
}
@Override
public void run(String... args) throws Exception {
System.out.println("利用Spring Boot自动装配的CommandLineRunner.");
System.out.println("生成一个随机字符串".concat(Objects.requireNonNull(env.getProperty("random."))));
}
}
```
**3. 创建一个自定义的自动配置类。**
该配置类加上@Component注解,也可以把自定义bean注册到Spring容器中。
```java
public class MyAutoConfig {
@Bean
CommandLineRunner createCustomCommandLineRunner(Environment env) {
return new MyCommandLineRunner(env);
}
}
```
**4. 创建spring.factories文件**
spring.factories文件必须添加在META-INF目录下

添加以下内容,spring.factories文件记录了需要注册的bean的类名,然后由@EnableAutoConfiguration注解进行注册。
```properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=MyAutoConfig
```
运行结果

**5. 给自动配置类添加有效条件。**
@ConditionalOnProperty注解从application.properties中读取指定值("prefix"."name"),如果该值为空,则返回false;如果非空,则将该值与havingValue指定的值进行比较,如果相等则返回true;否则返回false。如果返回值为false,则该配置类不生效;为true则生效。
```java
public class MyAutoConfig {
@Bean
@ConditionalOnProperty(prefix = "test.auto", name = "enable", havingValue = "true")
CommandLineRunner createCustomCommandLineRunner(Environment env) {
return new MyCommandLineRunner(env);
}
}
```
在application.properties属性文件中添加一个自定义的属性。
```properties
test.auto.enable=true
```
若test.auto.enable=false,则配置类不生效。

**6. 自定义的一个Bean,绑定属性值,并生成spring配置类的元数据文件。**
创建一个类,并在类上加@ConfigurationProperties注解,设置注解的prefix属性指定绑定的属性的前缀。
```java
@ConfigurationProperties(prefix = "test.auto")
public class MyProperties {
private boolean enable;
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
}
```
在某个配置类上添加@EnableConfigurationProperties,并指定装配的属性Bean。
```java
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfig {
@Bean
@ConditionalOnProperty(prefix = "test.auto", name = "enable", havingValue = "true")
CommandLineRunner createCustomCommandLineRunner(Environment env) {
return new MyCommandLineRunner(env);
}
}
```
使用spring boot框架提供的注解处理器生成自定义属性的元数据文件。
pom文件导入依赖。
```pom
org.springframework.boot
spring-boot-configuration-processor
true
```
编译打包项目。target目录下会生成元数据文件。

编辑application.properties时,idea出现自动提示。

**7. 根据阅读框架源码,我们可以自定义一个事件发布器,并设置线程池,实现异步发布事件。但注意:这个自定义的事件发布器的Bean的名称必须是“applicationEventMulticaster”。**
自定义的事件发布器
```java
@Bean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
ApplicationEventMulticaster customApplicationEventMulticaster(ThreadPoolTaskExecutor taskExecutor) {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(taskExecutor);
return eventMulticaster;
}
```
自定义事件类
```java
static 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;
}
}
```
自定义事件监听器
```java
@Component
static 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());
}
}
```
测试类
```java
@SpringBootTest
class SpringBootExperiment02ApplicationTests {
@Autowired
ApplicationEventMulticaster eventMulticaster;
@Test
void testAsyncEventMulticaster() throws InterruptedException {
eventMulticaster.multicastEvent(new MyAutoConfig.NoticeEvent("hello"));
eventMulticaster.multicastEvent(new MyAutoConfig.NoticeEvent("hi"));
Thread.sleep(2100);
}
}
```
**8. 思考题,自定义线程池**
在application.properties中增加以下配置
```properties
thread.pool.corePoolSize=20
thread.pool.maxPoolSize=40
thread.pool.keepAliveSeconds=200
thread.pool.queueCapacity=20
```
绑定配置类
```java
@ConfigurationProperties(prefix = "thread.pool")
public class MyThreadPoolProperties {
private int corePoolSize;
private int maxPoolSize;
private int keepAliveSeconds;
private int queueCapacity;
}
```
自定义线程池
```java
@Bean
public ThreadPoolTaskExecutor myTaskPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(poolProperties.getCorePoolSize());
executor.setMaxPoolSize(poolProperties.getMaxPoolSize());
executor.setQueueCapacity(poolProperties.getQueueCapacity());
executor.setKeepAliveSeconds(poolProperties.getKeepAliveSeconds());
executor.setThreadNamePrefix("MyTask-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
```
使用自定义的线程池
```java
@Bean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
ApplicationEventMulticaster customApplicationEventMulticaster(@Qualifier("myTaskPool") ThreadPoolTaskExecutor taskExecutor) {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(taskExecutor);
return eventMulticaster;
}
```
### 四、实验总结
在老师上课的时候听完感觉很容易,可能是因为老师课讲得太好了,自己动手做的时候出现了各种问题,通过这次实验,我理解了springboot是如何进行自动配置和注册bean的,学会了如何将自己写的bean注册进spring容器。