SpringBoot3進階用法,你學會了嗎?
一、簡介
在上篇《SpringBoot3基礎(chǔ)》中已經(jīng)完成入門案例的開發(fā)和測試,在這篇內(nèi)容中再來看看進階功能的用法;
主要涉及如下幾個功能點:
調(diào)度任務:在應用中提供一定的輕量級的調(diào)度能力,比如方法按指定的定時規(guī)則執(zhí)行,或者異步執(zhí)行,從而完成相應的代碼邏輯;
郵件發(fā)送:郵件作為消息體系中的渠道,是常用的功能;
應用監(jiān)控:實時或定期監(jiān)控應用的健康狀態(tài),以及各種關(guān)鍵的指標信息;
切面編程:通過預編譯方式和運行期動態(tài)代理實現(xiàn)程序中部分功能統(tǒng)一維護的技術(shù),可以將業(yè)務流程中的部分邏輯解耦處理,提升可復用性;
二、工程搭建
1、工程結(jié)構(gòu)
圖片
2、依賴管理
<!-- 基礎(chǔ)框架依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- 應用監(jiān)控組件 -->
<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>
<!-- 郵件發(fā)送組件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>${spring-boot.version}</version>
</dependency>
這里再細致的查看一下各個功能的組件依賴體系,SpringBoot只是提供了強大的集成能力;
圖片
3、啟動類
注意在啟動類中使用注解開啟了異步EnableAsync和調(diào)度EnableScheduling的能力;
@EnableAsync
@EnableScheduling
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
三、切面編程
1、定義注解
定義一個方法級的注解;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DefAop {
/**
* 模塊描述
*/
String modelDesc();
/**
* 其他信息
*/
String otherInfo();
}
2、注解切面
在切面中使用Around環(huán)繞通知類型,會攔截到DefAop注解標記的方法,然后解析獲取各種信息,進而嵌入自定義的流程邏輯;
@Component
@Aspect
public class LogicAop {
private static final Logger logger = LoggerFactory.getLogger(LogicAop.class) ;
/**
* 切入點
*/
@Pointcut("@annotation(com.boot.senior.aop.DefAop)")
public void defAopPointCut() {
}
/**
* 環(huán)繞切入
*/
@Around("defAopPointCut()")
public Object around (ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result = null ;
try{
// 執(zhí)行方法
result = proceedingJoinPoint.proceed();
} catch (Exception e){
e.printStackTrace();
} finally {
// 處理邏輯
buildLogicAop(proceedingJoinPoint) ;
}
return result ;
}
/**
* 構(gòu)建處理邏輯
*/
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);
}
}
四、調(diào)度任務
1、異步處理
1.1 方法定義
通過Async注解標識兩個方法,方法在執(zhí)行時會休眠10秒,其中一個注解指定異步執(zhí)行使用asyncPool線程池;
@Service
public 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...");
}
}
1.2 線程池
定義一個ThreadPoolTaskExecutor線程池對象;
@Configuration
public class PoolConfig {
@Bean("asyncPool")
public Executor asyncPool () {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 線程池命名前綴
executor.setThreadNamePrefix("async-pool-");
// 核心線程數(shù)5
executor.setCorePoolSize(5);
// 最大線程數(shù)10
executor.setMaxPoolSize(10);
// 緩沖執(zhí)行任務的隊列50
executor.setQueueCapacity(50);
// 線程的空閑時間60秒
executor.setKeepAliveSeconds(60);
// 線程池對拒絕任務的處理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 線程池關(guān)閉的時等待所有任務都完成再繼續(xù)銷毀其他的Bean
executor.setWaitForTasksToCompleteOnShutdown(true);
// 設置線程池中任務的等待時間
executor.setAwaitTerminationSeconds(300);
return executor;
}
}
1.3 輸出信息
從輸出的日志信息中可以發(fā)現(xiàn),兩個異步方法所使用的線程池不一樣,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...
2、調(diào)度任務
2.1 調(diào)度配置
通過實現(xiàn)SchedulingConfigurer接口,來修改調(diào)度任務的配置,這里重新定義任務執(zhí)行的線程池;
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
}
}
2.2 調(diào)度方法
通過Scheduled注解來標記方法,基于定時器的規(guī)則設定,來統(tǒng)一管理方法的執(zhí)行時間;
@Component
public class ScheduleJob {
private static final Logger log = LoggerFactory.getLogger(ScheduleJob.class);
private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") ;
/**
* 上一次開始執(zhí)行時間點之后10秒再執(zhí)行
*/
@Scheduled(fixedRate = 10000)
private void timerJob1(){
log.info("timer-job-1:{}",format.format(new Date()));
}
/**
* 上一次執(zhí)行完畢時間點之后10秒再執(zhí)行
*/
@Scheduled(fixedDelay = 10000)
private void timerJob2(){
log.info("timer-job-2:{}",format.format(new Date()));
}
/**
* Cron表達式:每30秒執(zhí)行一次
*/
@Scheduled(cron = "0/30 * * * * ?")
private void timerJob3(){
log.info("timer-job-3:{}",format.format(new Date()));
}
}
五、郵件發(fā)送
1、郵件配置
采用QQ郵箱來模擬郵件的發(fā)送方,需要先開啟smtp郵件傳輸協(xié)議,在QQ郵箱的設置/賬戶路徑下,并且獲取相應的授權(quán)碼,在項目的配置中使用;
spring:
application:
name: boot-senior
# 郵件配置
mail:
host: smtp.qq.com
port: 465
protocol: smtps
username: 郵箱賬號
password: 郵箱授權(quán)碼
properties:
mail.smtp.ssl.enable: true
2、方法封裝
定義一個簡單的郵件發(fā)送方法,并且可以添加附件,是常用的功能之一;另外也可以通過Html靜態(tài)頁渲染,再轉(zhuǎn)換為郵件內(nèi)容的方式;
@Service
public class SendMailService {
@Value("${spring.mail.username}")
private String userName ;
@Resource
private JavaMailSender sender;
/**
* 帶附件的郵件發(fā)送方法
* @param toUsers 接收人
* @param subject 主題
* @param content 內(nèi)容
* @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);
// 郵件發(fā)送方From和接收方To
helper.setFrom(userName);
helper.setTo(toUsers);
// 郵件主題和內(nèi)容
helper.setSubject(subject);
helper.setText(content);
// 郵件中的附件
File attachFile = ResourceUtils.getFile(attachPath);
helper.addAttachment(attachFile.getName(), attachFile);
// 執(zhí)行郵件發(fā)送命令
sender.send(mimeMessage);
return "send...mail...sus" ;
}
}
測試結(jié)果
圖片
六、應用監(jiān)控
1、監(jiān)控配置
在springboot的actuator組件中,可以通過提供的Rest接口,來獲取應用的監(jiān)控信息;
# 應用監(jiān)控配置
management:
endpoints:
web:
exposure:
# 打開所有的監(jiān)控點
include: "*"
base-path: /monitor
endpoint:
health:
enabled: true
show-details: always
beans:
enabled: true
shutdown:
enabled: true
2、相關(guān)接口
2.1 Get類型接口:主機:端口/monitor/health,查看應用的健康信息,三個核心指標:status狀態(tài),diskSpace磁盤空間,ping檢查;
{
/* 狀態(tài)值 */
"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,關(guān)閉應用程序;
{
"message": "Shutting down, bye..."
}
七、參考源碼
文檔倉庫:
https://gitee.com/cicadasmile/butte-java-note
源碼倉庫:
https://gitee.com/cicadasmile/butte-spring-parent