在上篇《SpringBoot3基础》中已经完成入门案例的开发和测试,在这篇内容中再来看看进阶功能的用法;
主要涉及如下几个功能点:
调度任务:在应用中提供一定的轻量级的调度能力,比如方法按指定的定时规则执行,或者异步执行,从而完成相应的代码逻辑;
邮件发送:邮件作为消息体系中的渠道,是常用的功能;
应用监控:实时或定期监控应用的健康状态,以及各种关键的指标信息;
切面编程:通过预编译方式和运行期动态代理实现程序中部分功能统一维护的技术,可以将业务流程中的部分逻辑解耦处理,提升可复用性;
图片
<!-- 基础框架依赖 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version></dependency><!-- 应用监控组件 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>${spring-boot.version}</version></dependency><!-- 切面编程组件 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>${spring-boot.version}</version></dependency><!-- 邮件发送组件 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> <version>${spring-boot.version}</version></dependency>
这里再细致的查看一下各个功能的组件依赖体系,SpringBoot只是提供了强大的集成能力;
图片
注意在启动类中使用注解开启了异步EnableAsync和调度EnableScheduling的能力;
@EnableAsync@EnableScheduling@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}
定义一个方法级的注解;
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface DefAop { /** * 模块描述 */ String modelDesc(); /** * 其他信息 */ String otherInfo();}
在切面中使用Around环绕通知类型,会拦截到DefAop注解标记的方法,然后解析获取各种信息,进而嵌入自定义的流程逻辑;
@Component@Aspectpublic class LogicAop { private static final Logger logger = LoggerFactory.getLogger(LogicAop.class) ; /** * 切入点 */ @Pointcut("@annotation(com.boot.senior.aop.DefAop)") public void defAopPointCut() { } /** * 环绕切入 */ @Around("defAopPointCut()") public Object around (ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object result = null ; try{ // 执行方法 result = proceedingJoinPoint.proceed(); } catch (Exception e){ e.printStackTrace(); } finally { // 处理逻辑 buildLogicAop(proceedingJoinPoint) ; } return result ; } /** * 构建处理逻辑 */ private void buildLogicAop (ProceedingJoinPoint point){ // 获取方法 MethodSignature signature = (MethodSignature) point.getSignature(); Method reqMethod = signature.getMethod(); // 获取注解 DefAop defAop = reqMethod.getAnnotation(DefAop.class); String modelDesc = defAop.modelDesc() ; String otherInfo = defAop.otherInfo(); logger.info("DefAop-modelDesc:{}",modelDesc); logger.info("DefAop-otherInfo:{}",otherInfo); }}
通过Async注解标识两个方法,方法在执行时会休眠10秒,其中一个注解指定异步执行使用asyncPool线程池;
@Servicepublic class AsyncService { private static final Logger log = LoggerFactory.getLogger(AsyncService.class); @Async public void asyncJob (){ try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } log.info("async-job-01-end..."); } @Async("asyncPool") public void asyncJobPool (){ try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } log.info("async-job-02-end..."); }}
定义一个ThreadPoolTaskExecutor线程池对象;
@Configurationpublic class PoolConfig { @Bean("asyncPool") public Executor asyncPool () { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 线程池命名前缀 executor.setThreadNamePrefix("async-pool-"); // 核心线程数5 executor.setCorePoolSize(5); // 最大线程数10 executor.setMaxPoolSize(10); // 缓冲执行任务的队列50 executor.setQueueCapacity(50); // 线程的空闲时间60秒 executor.setKeepAliveSeconds(60); // 线程池对拒绝任务的处理策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 线程池关闭的时等待所有任务都完成再继续销毁其他的Bean executor.setWaitForTasksToCompleteOnShutdown(true); // 设置线程池中任务的等待时间 executor.setAwaitTerminationSeconds(300); return executor; }}
从输出的日志信息中可以发现,两个异步方法所使用的线程池不一样,asyncJob采用默认的cTaskExecutor线程池,asyncJobPool方法采用的是async-pool线程池;
[schedule-pool-1] c.boot.senior.schedule.ScheduleService : async-job-02-end...[cTaskExecutor-1] c.boot.senior.schedule.ScheduleService : async-job-01-end...
通过实现SchedulingConfigurer接口,来修改调度任务的配置,这里重新定义任务执行的线程池;
@Configurationpublic class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(5)); }}
通过Scheduled注解来标记方法,基于定时器的规则设定,来统一管理方法的执行时间;
@Componentpublic class ScheduleJob { private static final Logger log = LoggerFactory.getLogger(ScheduleJob.class); private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") ; /** * 上一次开始执行时间点之后10秒再执行 */ @Scheduled(fixedRate = 10000) private void timerJob1(){ log.info("timer-job-1:{}",format.format(new Date())); } /** * 上一次执行完毕时间点之后10秒再执行 */ @Scheduled(fixedDelay = 10000) private void timerJob2(){ log.info("timer-job-2:{}",format.format(new Date())); } /** * Cron表达式:每30秒执行一次 */ @Scheduled(cron = "0/30 * * * * ?") private void timerJob3(){ log.info("timer-job-3:{}",format.format(new Date())); }}
采用QQ邮箱来模拟邮件的发送方,需要先开启smtp邮件传输协议,在QQ邮箱的设置/账户路径下,并且获取相应的授权码,在项目的配置中使用;
spring: application: name: boot-senior # 邮件配置 mail: host: smtp.qq.com port: 465 protocol: smtps username: 邮箱账号 password: 邮箱授权码 properties: mail.smtp.ssl.enable: true
定义一个简单的邮件发送方法,并且可以添加附件,是常用的功能之一;另外也可以通过Html静态页渲染,再转换为邮件内容的方式;
@Servicepublic class SendMailService { @Value("${spring.mail.username}") private String userName ; @Resource private JavaMailSender sender; /** * 带附件的邮件发送方法 * @param toUsers 接收人 * @param subject 主题 * @param content 内容 * @param attachPath 附件地址 * @return java.lang.String * @since 2023-07-10 17:03 */ public String sendMail (String[] toUsers,String subject, String content,String attachPath) throws Exception { // MIME邮件类 MimeMessage mimeMessage = sender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); // 邮件发送方From和接收方To helper.setFrom(userName); helper.setTo(toUsers); // 邮件主题和内容 helper.setSubject(subject); helper.setText(content); // 邮件中的附件 File attachFile = ResourceUtils.getFile(attachPath); helper.addAttachment(attachFile.getName(), attachFile); // 执行邮件发送命令 sender.send(mimeMessage); return "send...mail...sus" ; }}
测试结果
图片
在springboot的actuator组件中,可以通过提供的Rest接口,来获取应用的监控信息;
# 应用监控配置management: endpoints: web: exposure: # 打开所有的监控点 include: "*" base-path: /monitor endpoint: health: enabled: true show-details: always beans: enabled: true shutdown: enabled: true
2.1 Get类型接口:主机:端口/monitor/health,查看应用的健康信息,三个核心指标:status状态,diskSpace磁盘空间,ping检查;
{ /* 状态值 */ "status": "UP", "components": { /* 磁盘空间 */ "diskSpace": { "status": "UP", "details": { "total": 250685575168, "free": 112149811200, "threshold": 10485760, "path": "Path/butte-spring-parent/.", "exists": true } }, /* Ping检查 */ "ping": { "status": "UP" } }}
2.2 Get类型接口:主机:端口/monitor/beans,查看bean列表;
{ "contexts": { "boot-senior": { "beans": { "asyncPool": { "scope": "singleton", "type": "org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor", "resource": "class path resource [com/boot/senior/schedule/PoolConfig.class]" }, "asyncService": { "scope": "singleton", "type": "com.boot.senior.schedule.AsyncService$$SpringCGLIB$$0" } } } }}
2.3 Post类型接口:主机:端口/monitor/shutdown,关闭应用程序;
{ "message": "Shutting down, bye..."}
文档仓库:https://gitee.com/cicadasmile/butte-java-note源码仓库:https://gitee.com/cicadasmile/butte-spring-parent
本文链接:http://www.28at.com/showinfo-26-5154-0.htmlSpringBoot3进阶用法,你学会了吗?
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: SpringBoot整合RabbitMQ延迟队列&优先级队列详解
下一篇: VasDolly服务端打渠道包教程