# 实验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依赖 ![结果](/img/1.png) 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 ![结果](/img/4.png) 运行结果: ![结果](/img/4-2.png) 5.给自动配置类添加有效条件 * 利用@ConditionalOnProperty注解,添加属性条件,当自定义的属性值为ture时,该bean才会实例化 ![结果](/img/5-1.png) * 在application.properties属性文件中添加一个自定义的属性,对应属性名为jon.auto.enable,值为ture ![结果](/img/5-2.png) * jon.auto.enable=ture,则运行时自定义的配置类肯定生效: ![结果](/img/5-3.png) * 把jon.auto.enable改为fasle;则自定义的配置类失效: ![结果](/img/5-4.png) 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): ![结果](/img/6.png) 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); } } 测试结果: ![结果](/img/10.png) 11.深入思考(自定义一个线程池,然后给自定义的事件发布器使用) * 实验过程中自定义事件发布器时,加入了框架定义的线程池ThreadPoolTaskExecutor,改线程池的构建过程在实例化spring.factory的自动配置bean时发生,阅读源码可以发现默认线程池在自动配置类TaskExecutionAutoConfiguration中定义,并规定了bean的name以及bean实例化的条件,即假如存在继承Executor.class的类,就不会加载默认的线程池。 * 源码如下: ![结果](/img/11.png) * 所以我们只需构建一个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; } * 运行测试用例: ![结果](/img/12.png) ## 实验总结 1. 学会看源码的重要性