# SpringBoot_Exp2 **Repository Path**: zimomo333/SpringBoot_Exp2 ## Basic Information - **Project Name**: SpringBoot_Exp2 - **Description**: 实验二 利用Spring boot的自动装配特性实现动态注册组件 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-04-15 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 实验二 利用Spring boot的自动装配特性实现动态注册组件 ### 2017级软卓1班 梁梓豪 201741404139 ### 一、实验目的 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项目。
2. ##### 创建一个自定义的CommandLineRunner接口的实现类。 ```jade import org.springframework.boot.CommandLineRunner; import org.springframework.core.env.Environment; import java.util.Objects; 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. ##### 创建一个自定义的自动配置类。 ```java import org.springframework.boot.CommandLineRunner; import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; public class myAutoConfig { @Bean CommandLineRunner createMyCommandLineRunner(Environment env){ return new myCommandLineRunner(env); } } ``` 4. ##### 创建spring.factories文件
注意:spring.factories必须放在META-INF目录下。
spring.factories编写以下内容,key必须是EnableAutoConfiguration的全限定类名,值为刚才我们自定义的自动配置类的全限定类路径的类名。 ```properties # 自动配置 org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zimomo.exp2.myAutoConfig ``` **mvn spring-boot:run 控制台输出中文乱码解决办法** 修改pom文件: ```xml org.springframework.boot spring-boot-maven-plugin true -Dfile.encoding=UTF-8 ``` 运行结果:
5. ##### 给自动配置类添加有效条件。 步骤如下: 1)利用@ConditionalOnProperty注解,添加属性条件。 ```java import org.springframework.boot.CommandLineRunner; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; public class myAutoConfig { @Bean @ConditionalOnProperty(prefix = "zimomo.auto", name="enable",havingValue = "true") CommandLineRunner createMyCommandLineRunner(Environment env){ return new myCommandLineRunner(env); } } ``` 2)在application.properties属性文件中添加一个自定义的属性。 ```properties zimomo.auto.enable=true ``` 3)切换属性值为false,观察终端的显示,自动装配的配置类不生效。
6. ##### 自定义的一个Bean,绑定属性值,并生成spring配置类的元数据文件。 步骤如下: 1)创建一个类,并在类上加@ConfigurationProperties注解,设置注解的prefix属性指定绑定的属性的前缀。 ```java import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "zimomo.auto") public class myProperties { private boolean enable; public boolean isEnable() { return enable; } public void setEnable(boolean enable) { this.enable = enable; } } ``` 2)在某个配置类上添加@EnableConfigurationProperties,并指定装配的属性Bean。 ```java 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; @EnableConfigurationProperties(myProperties.class) public class myAutoConfig { @Bean @ConditionalOnProperty(prefix = "zimomo.auto", name="enable",havingValue = "true") CommandLineRunner createMyCommandLineRunner(Environment env){ return new myCommandLineRunner(env); } } ``` 3)使用spring boot框架提供的注解处理器生成自定义属性的元数据文件。 **说明:** Spring Boot应程序打包后的jar文件中包含元数据文件,这些文件提供了所有受支持的配置属性的详细信息。这些文件旨在让IDE开发人员在编辑application.properties或application.yml文件时提供上下文帮助和“代码完成” 。Interllj IDEA默认支持。 元数据文件位于META-INF目录下,名字spring-configuration-metadata.json。 元数据文件可以自己写,或使用spring boot官方提供的依赖包自动生成。 在pom.xml文件中引入spring-boot-configuration-processor依赖。这个依赖包里有spring boot提供的注解处理器,在项目编译时检查项目内所有@ConfigurationProperties的类,并生成元数据文件。 a. 在pom.xml文件中引入spring-boot-configuration-processor依赖: ```xml org.springframework.boot spring-boot-configuration-processor true ``` b. 编译打包项目。观察target目录下的元数据文件:
c. 现在我们再回去编辑属性文件时,只要输入属性的部分关键字,idea会自动提示,如下图:
7. ##### 根据阅读框架源码,我们可以自定义一个事件发布器,并设置线程池,实现异步发布事件。但注意:这个自定义的事件发布器的Bean的名称必须是“applicationEventMulticaster”。 源码如下图:
自定义的事件发布器: ```java @Bean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME) static ApplicationEventMulticaster myApplicationEventMulticaster(ThreadPoolTaskExecutor threadPoolTaskExecutor){ SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster(); simpleApplicationEventMulticaster.setTaskExecutor(threadPoolTaskExecutor); return simpleApplicationEventMulticaster; } ``` 8. ##### 自定义事件类。 ```java static class myNoticeEvent extends ApplicationEvent{ private static final Logger logger = LoggerFactory.getLogger(myNoticeEvent.class); private final String message; public myNoticeEvent(String message) { super(message); this.message = message; logger.info("添加事件成功!message{}",message); } public String getMessage() { return message; } } ``` 9. ##### 自定义事件监听器。 ```java @Component static class myNoticeListener implements ApplicationListener{ private static final Logger logger = LoggerFactory.getLogger(myNoticeListener.class); @Override public void onApplicationEvent(myNoticeEvent myNoticeEvent) { logger.info("事件监听器获取到 myNoticeEvent,睡眠当前线程 2 秒..."); try { Thread.sleep(2000); } catch (InterruptedException e){ e.printStackTrace(); } logger.info("myNoticeEvent 的 message 属性是:{}",myNoticeEvent.getMessage()); } } ``` 10. ##### 编写一个测试用例,检查发布事件时,是否使用了多线程异步处理。 ```java import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class Tests { @Autowired ApplicationEventMulticaster applicationEventMulticaster; @Test public void testMyEventMulticaster() throws InterruptedException { applicationEventMulticaster.multicastEvent(new myAutoConfig.myNoticeEvent("东莞理工学院")); applicationEventMulticaster.multicastEvent(new myAutoConfig.myNoticeEvent("网安学院")); applicationEventMulticaster.multicastEvent(new myAutoConfig.myNoticeEvent("软卓1班")); Thread.sleep(2000); } } ``` 运行单元测试结果如下:
**深入思考**: 在上面的例子中,我们使用了Spring Boot框架提供的默认线程池。默认线程池在自动配置类TaskExecutionAutoConfiguration中定义。我们可以自定义一个自己的线程池,这样可以配置更多的属性。尝试一下自定义一个线程池,然后给自定义的事件发布器使用。
a)创建自定义线程池配置属性类 myThreadPoolConfig ```java import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "thread.pool") public class myThreadPoolConfig { private int corePoolSize; //核心线程池大小 private int maxPoolSize; //最大线程数 private int keepAliveSeconds; //活跃时间 private int queueCapacity; //队列容量 //get、set... } ``` b)修改application.properties ```properties thread.pool.core-pool-size=30 thread.pool.max-pool-size=70 thread.pool.keep-alive-seconds=300 thread.pool.queue-capacity=50 ``` c)自定义线程池ThreadPoolTaskExecutor 注意:自定义线程池的Bean的名称必须是“applicationTaskExecutor”
```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; @Configuration public class myThreadPool { @Autowired private myThreadPoolConfig config; @Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) public ThreadPoolTaskExecutor myTaskAsyncPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //核心线程池大小 executor.setCorePoolSize(config.getCorePoolSize()); //最大线程数 executor.setMaxPoolSize(config.getMaxPoolSize()); //队列容量 executor.setQueueCapacity(config.getQueueCapacity()); //活跃时间 executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); //线程名字前缀 executor.setThreadNamePrefix("myExecutor-"); executor.initialize(); return executor; } } ``` 测试结果: