You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot控制器路径复用:多URL共享Person类接口逻辑

可行实现方案

针对你提出的Spring Boot中复用Person相关接口逻辑的需求,以下是几种实用的实现方案:

方案1:基于控制器继承复用公共方法

通过定义抽象父控制器封装所有需要复用的接口方法,不同路径的控制器继承该父类并实现各自的Person获取逻辑,避免重复编写GetMapping注解和业务代码。

// 抽象父控制器:封装公共的Person属性接口
public abstract class AbstractPersonController {

    // 子类需实现该方法,根据当前请求路径获取对应的Person对象
    protected abstract Person getPerson(HttpServletRequest request);

    @GetMapping("/first-name")
    public String getFirstName(HttpServletRequest request) {
        return getPerson(request).getFirstName();
    }

    @GetMapping("/last-name")
    public String getLastName(HttpServletRequest request) {
        return getPerson(request).getLastName();
    }

    // 可添加更多需要复用的属性接口,如 /age、/email 等
}

// 处理 /person/{id} 路径的控制器
@RestController
@RequestMapping("/person/{id}")
public class PersonController extends AbstractPersonController {

    @Autowired
    private PersonService personService;

    @Override
    protected Person getPerson(HttpServletRequest request) {
        // 从路径变量中提取id,查询Person
        String personId = (String) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE).get("id");
        return personService.getById(personId);
    }
}

// 处理 /payment/{id}/payer 路径的控制器
@RestController
@RequestMapping("/payment/{id}/payer")
public class PaymentPayerController extends AbstractPersonController {

    @Autowired
    private PaymentService paymentService;

    @Override
    protected Person getPerson(HttpServletRequest request) {
        String paymentId = (String) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE).get("id");
        Payment payment = paymentService.getById(paymentId);
        return payment.getPayer();
    }
}

// 处理 /payment/{id}/receiver 路径的控制器
@RestController
@RequestMapping("/payment/{id}/receiver")
public class PaymentReceiverController extends AbstractPersonController {

    @Autowired
    private PaymentService paymentService;

    @Override
    protected Person getPerson(HttpServletRequest request) {
        String paymentId = (String) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE).get("id");
        Payment payment = paymentService.getById(paymentId);
        return payment.getReceiver();
    }
}

此方案下,三个路径/person/{id}/first-name、/payment/{id}/payer/first-name、/payment/{id}/receiver/first-name会自动生效,且所有属性接口逻辑完全复用。

方案2:自定义参数解析器+单控制器多路径映射

通过自定义HandlerMethodArgumentResolver自动根据请求路径解析Person对象,再在单个控制器中映射所有需要支持的路径,统一处理属性接口。

步骤1:自定义参数解析器和注解

// 自定义注解,标记需要注入当前请求对应的Person参数
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentPerson {
}

// 参数解析器:根据请求路径自动获取Person对象
@Component
public class PersonArgumentResolver implements HandlerMethodArgumentResolver {

    @Autowired
    private PersonService personService;
    @Autowired
    private PaymentService paymentService;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType() == Person.class && parameter.hasParameterAnnotation(CurrentPerson.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        Map<String, String> pathVars = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        String requestURI = request.getRequestURI();

        if (requestURI.startsWith("/person/")) {
            return personService.getById(pathVars.get("id"));
        } else if (requestURI.contains("/payment/")) {
            Payment payment = paymentService.getById(pathVars.get("id"));
            return requestURI.contains("/payer/") ? payment.getPayer() : payment.getReceiver();
        }
        throw new IllegalArgumentException("无法解析当前请求的Person对象");
    }
}

步骤2:注册参数解析器

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private PersonArgumentResolver personArgumentResolver;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(personArgumentResolver);
    }
}

步骤3:编写公共控制器

@RestController
@RequestMapping({"/person/{id}", "/payment/{id}/payer", "/payment/{id}/receiver"})
public class PersonCommonController {

    @GetMapping("/first-name")
    public String getFirstName(@CurrentPerson Person person) {
        return person.getFirstName();
    }

    @GetMapping("/last-name")
    public String getLastName(@CurrentPerson Person person) {
        return person.getLastName();
    }

    // 其他复用接口...
}

此方案无需创建多个控制器,所有路径的属性接口都由同一个控制器处理,参数解析器负责适配不同路径的Person获取逻辑。

方案3:服务层逻辑复用(极简版)

如果不想引入复杂的Spring MVC扩展,可将Person的属性获取逻辑和不同场景的Person查询逻辑抽离到服务层,控制器只需简单调用服务方法即可。

// 服务层:封装所有Person相关的复用逻辑
@Service
public class PersonServiceFacade {

    @Autowired
    private PersonService personService;
    @Autowired
    private PaymentService paymentService;

    // 从Person路径获取Person
    public Person getPerson(String personId) {
        return personService.getById(personId);
    }

    // 从Payment路径获取付款人
    public Person getPaymentPayer(String paymentId) {
        return paymentService.getById(paymentId).getPayer();
    }

    // 从Payment路径获取收款人
    public Person getPaymentReceiver(String paymentId) {
        return paymentService.getById(paymentId).getReceiver();
    }

    // 公共属性获取方法
    public String getPersonFirstName(Person person) {
        return person.getFirstName();
    }

    public String getPersonLastName(Person person) {
        return person.getLastName();
    }
}

然后在控制器中调用:

@RestController
@RequestMapping("/person/{id}")
public class PersonController {

    @Autowired
    private PersonServiceFacade facade;

    @GetMapping("/first-name")
    public String getFirstName(@PathVariable String id) {
        Person person = facade.getPerson(id);
        return facade.getPersonFirstName(person);
    }
}

@RestController
@RequestMapping("/payment/{id}")
public class PaymentController {

    @Autowired
    private PersonServiceFacade facade;

    @GetMapping("/payer/first-name")
    public String getPayerFirstName(@PathVariable String id) {
        Person payer = facade.getPaymentPayer(id);
        return facade.getPersonFirstName(payer);
    }

    @GetMapping("/receiver/first-name")
    public String getReceiverFirstName(@PathVariable String id) {
        Person receiver = facade.getPaymentReceiver(id);
        return facade.getPersonFirstName(receiver);
    }
}

此方案实现简单,适合逻辑相对简单的场景,核心逻辑都在服务层复用,控制器仅做路由转发。

内容的提问来源于stack exchange,提问作者Rens Pols

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 22:43:18