在上篇《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.tebozhan.com/showinfo-26-5154-0.htmlSpringBoot3進階用法,你學會了嗎?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: SpringBoot整合RabbitMQ延遲隊列&優先級隊列詳解
下一篇: VasDolly服務端打渠道包教程