Spring Boot 实用核心技巧汇总:日期格式化、线程管控、MCP服务、AOP进阶等
在Spring Boot日常开发中,我们经常会遇到日期格式化、线程管控、第三方服务集成、AOP切面编程等常见需求。本文汇总了多个实用核心技巧,附带完整可运行代码,方便开发者直接参考使用。
一、Spring Boot 实体类日期时间格式统一配置
在前后端数据交互时,日期类型常常需要统一格式化,避免出现格式不一致或时区偏移问题,核心使用@JsonFormat注解实现。
1. 实体类日期字段配置
java
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
public class DemoEntity {
// 日期格式化:指定格式 + 东八区时区
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime; // 对应数据库datetime(0)类型
// 省略getter/setter
}
2. 依赖引入(fastjson)
xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
二、Spring Boot 动态启动与停止运行中的线程
在异步任务场景中,常常需要动态管控线程的启动与停止,核心通过Future对象的cancel()方法实现线程中断。
1. 核心POM依赖
xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.demo</groupId>
<artifactId>springbootdemo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- SSE 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- HTTP客户端 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.21</version>
</dependency>
<!-- 简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-sse</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.12.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. 异步任务服务类(AsyncService)
核心通过ConcurrentHashMap存储任务ID与Future对象的映射,实现任务的缓存与查找,future.cancel(true)发送中断信号停止线程。
java
import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
@Service
@Slf4j
public class AsyncService {
// 存储任务ID与异步任务的映射
private final Map<String, Future<?>> tasks = new ConcurrentHashMap<>();
// 执行异步任务
public Future<?> runTask(String taskId) {
return ThreadUtil.execAsync(() -> {
try {
log.info("Task started: {}", taskId);
// 模拟耗时任务
for (int i = 0; i < 1000000000; i++) {
log.info("Task running i: {}", i);
Thread.sleep(0);
}
log.info("Task completed: {}", taskId);
} catch (InterruptedException e) {
log.error("Task interrupted: {}", taskId, e);
}
});
}
// 启动任务并缓存Future
public void startTask(String taskId) {
tasks.put(taskId, runTask(taskId));
}
// 停止指定任务
public void stopTask(String taskId) {
Future<?> future = tasks.get(taskId);
if (future != null) {
future.cancel(true); // true:发送线程中断信号
tasks.remove(taskId); // 移除缓存任务
}
}
}
3. 任务控制接口(TaskController)
暴露HTTP接口,实现任务的启动与停止操作。
java
import com.example.demo.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TaskController {
@Autowired
private AsyncService asyncService;
// 启动任务:/start/{taskId}
@GetMapping("/start/{taskId}")
public String start(@PathVariable String taskId) {
asyncService.startTask(taskId);
return "Task started!";
}
// 停止任务:/stop/{taskId}
@GetMapping("/stop/{taskId}")
public String stop(@PathVariable String taskId) {
asyncService.stopTask(taskId);
return "Task stopped!";
}
}
三、Spring Boot 集成 MCP 服务器完整实践
MCP(Model Context Protocol)是模型上下文协议,Spring Boot可快速集成MCP服务器提供AI工具服务,前置要求JDK 17。
1. 前置条件
- JDK 版本:17
- Spring Boot 版本:3.4.5
2. POM 文件配置
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.5</version>
<relativePath />
</parent>
<groupId>com.example</groupId>
<artifactId>mcp-weather-starter-webmvc-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AI MCP Weather Sample</name>
<description>Sample Spring Boot application demonstrating MCP client and server usage</description>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- MCP 服务器依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- 仓库配置 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>central-portal-snapshots</id>
<name>Central Portal Snapshots</name>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>
3. 配置文件(application.properties)
properties
# 关闭banner和控制台日志,保证STDIO传输正常
spring.main.banner-mode=off
# MCP 服务器配置
spring.ai.mcp.server.name=my-weather-server
spring.ai.mcp.server.version=0.0.1
# 日志文件配置
logging.file.name=./model-context-protocol/weather/starter-webmvc-server/target/starter-webmvc-server.log
4. MCP 服务实现
(1)业务服务(BIService + MIService)
通过@Tool注解标记AI工具方法,@ToolParam注解标记方法参数说明。
java
// BIService:项目信息查询服务
package org.springframework.ai.mcp.sample.server;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
@Component
public class BIService {
@Tool(description = "根据客户名称查询项目信息,输入参数是客户名称")
public String getProjectInfoByName(@ToolParam(description = "客户名称") String name) {
return "系统查询用户,"+name+"项目信息";
}
@Tool(description = "根据合同编号查询项目信息,输入参数是合同编号")
public String getProjectByContactNumber(@ToolParam(description = "合同编号") String contactNumber) {
return "系统查询合同编号,"+contactNumber+"项目信息";
}
}
// MIService:天气信息查询服务
package org.springframework.ai.mcp.sample.server;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
@Component
public class MIService {
@Tool(description = "根据城市名称获取该城市的天气信息")
public String getWeather(@ToolParam(description = "城市") String city){
return city+":温度25度,大太阳,适合旅行";
}
}
(2)启动类配置
注册工具回调提供者,将业务服务注入MCP服务器。
java
package org.springframework.ai.mcp.sample.server;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
// 注册MCP工具回调
@Bean
public ToolCallbackProvider mcpTool(BIService biService,MIService miService) {
return MethodToolCallbackProvider.builder()
.toolObjects(miService,biService) // 注入业务服务
.build();
}
}
5. 客户端访问配置
(1)Cursor 访问 MCP 服务
json
{
"mcpServers": {
"bi-mcp-server": {
"url": "http://localhost:8080/sse"
}
}
}
(2)MaxKB 访问 MCP 服务
json
{
"bi-mcp-server": {
"url": "http://host.docker.internal:8080/sse",
"transport": "sse"
}
}
四、基于 BeanPostProcessor + ApplicationContextAware 获取注解类
通过Spring的扩展接口,实现自定义注解类的扫描、方法映射存储,以及所有@RestController注解Bean的获取。
java
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.example.common.annotation.Demo;
import org.example.common.annotation.DemoMethod;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class DemoBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
private static ApplicationContext applicationContext;
// 存储 注解组合key -> 方法 的映射
private static Map<String,Method> concurrentHashMap=new ConcurrentHashMap<>();
// 存储 注解组合key -> 类 的映射
private static Map<String,Class<?>> concurrentHashMapClass=new ConcurrentHashMap<>();
// Bean初始化前执行:扫描@Demo和@DemoMethod注解
@Override
@SneakyThrows
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class<?> clazz=bean.getClass();
Demo demo=clazz.getAnnotation(Demo.class);
if(demo!=null){
log.info("添加注解Demo类名称:"+beanName);
Method[] methods=clazz.getDeclaredMethods();
for(Method method:methods){
DemoMethod demoMethod=method.getAnnotation(DemoMethod.class);
log.info("添加注解DemoMethod类名称:"+method.getName());
if(demoMethod!=null){
String key=demo.value()+demoMethod.value();
// 防止key重复
if(concurrentHashMap.containsKey(key)){
throw new RuntimeException("路径重复");
}
concurrentHashMap.put(key,method);
concurrentHashMapClass.put(key,clazz);
}
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 获取缓存的方法
public static Method get(String url){
return concurrentHashMap.get(url);
}
// 获取缓存的类
public static Class<?> getClass(String url){
return concurrentHashMapClass.get(url);
}
// 注入ApplicationContext:获取所有@RestController Bean
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
DemoBeanPostProcessor.applicationContext = applicationContext;
Map<String,Object> map= applicationContext.getBeansWithAnnotation(RestController.class);
map.forEach((key,value)->{
System.out.println("拿到controller:"+key+",拿到value:"+value);
Class<?> aClass = AopUtils.getTargetClass(value); // 获取目标类(解决AOP代理问题)
System.out.println("类的包名称{}"+aClass.getPackage().getName());
System.out.println("拿到Class:"+aClass);
});
}
}
五、Spring Boot 批量获取所有 Controller 及内部方法
通过ApplicationContextAware获取Spring容器,CommandLineRunner在项目启动后执行,批量扫描所有@RestController注解的Controller,并反射获取其内部方法。
java
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@Component
@Slf4j
public class CommandLiner2 implements ApplicationContextAware, CommandLineRunner {
private ApplicationContext applicationContext;
// 项目启动后执行:扫描所有Controller
@Override
public void run(String... args) throws Exception {
// 获取所有@RestController注解的Bean
Map<String, Object> controllers = applicationContext.getBeansWithAnnotation(RestController.class);
// 遍历Controller
for (Map.Entry<String, Object> entry : controllers.entrySet()) {
Object value = entry.getValue();
System.out.println("拿到controller:"+entry.getKey()+",拿到value:"+value);
Class<?> aClass = AopUtils.getTargetClass(value); // 获取目标类
System.out.println("拿到Class:"+aClass);
// 反射获取所有方法并打印
log.info("所有方法:"+ JSONUtil.toJsonStr(aClass.getDeclaredMethods()));
}
}
// 注入ApplicationContext
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
六、Spring Boot AOP 注解高级用法(操作日志记录)
通过自定义注解 + AOP切面,实现方法执行的全链路日志记录,包括请求信息、注解参数、响应结果、异常信息等。
1. 自定义操作日志注解
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD}) // 仅作用于方法
@Retention(RetentionPolicy.RUNTIME) // 运行时可获取
public @interface OperationLogAnno {
/**
* 操作位置
*/
String operatePage() default "";
/**
* 操作类型
*/
String operateType() default "";
/**
* 业务域
*/
String bizType() default "";
}
2. 引入 AOP 依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3. 操作日志切面类
实现@Before、@After、@Around、@AfterReturning、@AfterThrowing五种通知,覆盖方法执行全生命周期。
java
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.example.annotation.OperationLogAnno;
import org.springframework.aop.support.AopUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@Aspect
@Component
@Slf4j
public class LogAspect {
// ThreadLocal 存储UUID,保证方法执行链路唯一标识
private static final ThreadLocal<String> threadLocal=new ThreadLocal<>();
// 切点:匹配所有带有OperationLogAnno注解的方法
@Pointcut("@annotation(org.example.annotation.OperationLogAnno)")
private void cutMethod() {}
// 方法执行前:记录请求信息、注解参数
@Before("cutMethod()")
public void before(JoinPoint joinPoint) throws Throwable {
String uuid = IdUtil.simpleUUID();
log.info("uuid:"+uuid);
threadLocal.set(uuid);
// 获取方法信息
String methodName = joinPoint.getSignature().getName();
Class<?> targetClass = joinPoint.getTarget().getClass();
Object[] params = joinPoint.getArgs();
log.info("methodName={}", methodName);
log.info("params={}", params);
// 获取注解参数
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
Method objMethod = targetClass.getMethod(methodName, parameterTypes);
OperationLogAnno anno = objMethod.getDeclaredAnnotation(OperationLogAnno.class);
// 兼容接口注解
if( anno== null ){
anno = (OperationLogAnno) ((Class) AopUtils.getTargetClass(joinPoint.getTarget()).getGenericInterfaces()[0])
.getDeclaredMethod(objMethod.getName(),parameterTypes)
.getAnnotation(OperationLogAnno.class);
}
info(anno);
// 获取HTTP请求信息
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String servletPath = request.getServletPath();
log.info("servletPath:" + servletPath);
log.info("路径匹配结果" + PatternMatchUtils.simpleMatch("/user/**", servletPath));
log.info("url={}", request.getRequestURL());
log.info("method={}", request.getMethod());
log.info("ip={}", request.getRemoteAddr());
log.info("class_method={}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
}
// 方法执行后:打印链路UUID
@After(value = "cutMethod()")
public void after() {
log.info("after.........");
log.info("uuid:"+threadLocal.get());
}
// 方法正常返回后:记录响应结果
@AfterReturning(returning = "object", pointcut = "cutMethod()")
public void doAfterReturning(Object object) {
log.info("response={}", JSONUtil.toJsonStr(object));
}
// 方法抛出异常后:记录异常信息
@AfterThrowing(value="cutMethod()",throwing = "exception")
public void afterThrowing(Exception exception) {
System.out.println("afterThrowing.........");
log.info("exception:",exception);
}
// 方法环绕通知:控制方法执行流程
@Around(value = "cutMethod()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("doAroundFront");
Object res = null;
try {
res = joinPoint.proceed(joinPoint.getArgs()); // 执行目标方法
} catch (Throwable e) {
log.error("切面异常",e);
throw e;
}
System.out.println("doAroundBehind");
return res;
}
// 解析注解参数并打印
@SneakyThrows
private <T extends Annotation>void info(T t) {
Method[] methods=t.getClass().getDeclaredMethods();
Map<String,String> map=new HashMap<>();
for(Method method:methods){
if(!method.getName().contains("hashCode")&&
!method.getName().contains("equals")&&
!method.getName().contains("toString")&&
!method.getName().contains("annotationType")){
map.put(method.getName(),(String)method.invoke(t));
}
}
log.info("annotation={}", map);
}
}
七、Spring Boot 路径匹配工具类(PatternMatchUtils)
Spring内置的路径匹配工具类,支持通配符(*匹配任意字符,**匹配任意层级路径),无需额外依赖,直接使用。
java
import org.springframework.util.PatternMatchUtils;
// 用法示例:判断servletPath是否匹配/user/**路径
boolean isMatch = PatternMatchUtils.simpleMatch("/user/**", servletPath);
八、Spring Boot 基于注解实现方法切面拦截
通过AspectJExpressionPointcut指定注解切点,MethodInterceptor实现拦截逻辑,灵活实现方法增强。
1. 自定义切面注解
java
package org.example.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DemoAnnotation {
String value() default "test";
}
2. AOP 配置类
java
package org.example.config;
import lombok.extern.slf4j.Slf4j;
import org.example.interceptor.DemoInterceptor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnProperty(name = "aop.exception.pointcut", matchIfMissing = true)
@Slf4j
public class AopConfig {
// 配置切面:拦截带有DemoAnnotation注解的方法
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor2() {
// 声明切点:指定自定义注解
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("@annotation(org.example.annotation.DemoAnnotation)");
// 声明拦截器
DemoInterceptor interceptor = new DemoInterceptor();
// 配置切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(interceptor);
return advisor;
}
}
3. 方法拦截器
java
package org.example.interceptor;
import cn.hutool.json.JSONUtil;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.example.annotation.DemoAnnotation;
import java.lang.reflect.Method;
public class DemoInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("--------------------------------------------------");
// 打印方法参数
Object[] object = methodInvocation.getArguments();
for (Object obj : object) {
System.out.println(JSONUtil.toJsonStr(obj));
}
// 打印目标对象信息
Object obj = methodInvocation.getThis();
System.out.println(obj.getClass().getName());
// 打印方法信息
Method method = methodInvocation.getMethod();
System.out.println(method.getName());
// 执行目标方法
obj = methodInvocation.proceed();
if (obj != null){
System.out.println(obj.getClass().getName());
}
// 获取注解信息
DemoAnnotation d = method.getAnnotation(DemoAnnotation.class);
if (d == null) {
System.out.println("当前类没有DemoAnnotation注解");
} else {
System.out.println(d.value());
}
System.out.println("--------------------------------------------------");
return obj;
}
}
4. 业务方法使用注解
java
package org.example.service.impl;
import org.example.annotation.DemoAnnotation;
import org.example.entity.User;
import org.example.mapper.UserMapper;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional
@DemoAnnotation("insert") // 使用自定义切面注解
public void insert(User user) {
userMapper.insert(user);
}
@Override
public User findById(Long id) {
return userMapper.selectById(id);
}
}
总结
本文汇总了Spring Boot开发中的8个核心实用技巧,覆盖日期格式化、线程管控、MCP服务集成、注解扫描、Controller解析、AOP进阶、路径匹配等常见场景,所有代码均可直接复制使用,能够帮助开发者提升开发效率,解决实际开发中的痛点问题。