Spring Boot 实用核心技巧汇总:日期格式化、线程管控、MCP服务、AOP进阶等

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进阶、路径匹配等常见场景,所有代码均可直接复制使用,能够帮助开发者提升开发效率,解决实际开发中的痛点问题。

相关推荐
一线大码2 小时前
Java 8-25 各个版本新特性总结
java·后端
2501_906150562 小时前
私有部署问卷系统操作实战记录-DWSurvey
java·运维·服务器·spring·开源
better_liang2 小时前
每日Java面试场景题知识点之-TCP/IP协议栈与Socket编程
java·tcp/ip·计算机网络·网络编程·socket·面试题
VX:Fegn08953 小时前
计算机毕业设计|基于springboot + vue校园社团管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
niucloud-admin3 小时前
java服务端——controller控制器
java·开发语言
To Be Clean Coder3 小时前
【Spring源码】通过 Bean 工厂获取 Bean 的过程
java·后端·spring
Fortunate Chen3 小时前
类与对象(下)
java·javascript·jvm
程序员水自流3 小时前
【AI大模型第9集】Function Calling,让AI大模型连接外部世界
java·人工智能·llm
‿hhh3 小时前
综合交通运行协调与应急指挥平台项目说明
java·ajax·npm·json·需求分析·个人开发·规格说明书