在 Spring Boot 中的控制器中自动装配通用(T)服务

Posted

技术标签:

【中文标题】在 Spring Boot 中的控制器中自动装配通用(T)服务【英文标题】:Autowiring Generic(T) Service in Controller within Spring Boot 【发布时间】:2021-12-07 13:13:30 【问题描述】:

我对如何在 Spring Boot 2.5.0 中将通用服务自动装配到控制器中感到有些困惑。

代码如下: 主控制器:

@RestController
@RequestMapping("/classifiers")
public class ClassifierController<T> 
    protected static final Logger LOG = LoggerFactory.getLogger(UserController.class);
    public final T classifierService;
    protected final JwtTokenFilter jwtTokenFilter;
    protected final JwtTokenUtil jwtTokenUtil;
    
    public ClassifierController(final JwtTokenFilter jwtTokenFilter,
                                final JwtTokenUtil jwtTokenUtil,
                                final T classifierService) 
        this.jwtTokenFilter = jwtTokenFilter;
        this.jwtTokenUtil = jwtTokenUtil;
        this.classifierService = classifierService;
    

扩展控制器:


@RestController
@RequestMapping("/ss01dictionary")
@CrossOrigin(origins = "*", allowedHeaders = "*")
public class SS01WasteDictionaryController
      extends ClassifierController<DictionaryService> 
    
    @Autowired
    public SS01WasteDictionaryController(JwtTokenFilter jwtTokenFilter,
                                         JwtTokenUtil jwtTokenUtil,
                                         DictionaryService classifierService) 
        super(jwtTokenFilter, jwtTokenUtil, classifierService);
    
    
    @RequestMapping(value = "/list", method = GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ResponseEntity<?> getData(@RequestParam(value = "page", defaultValue = "0") int page,
                                                     @RequestParam(value = "size", defaultValue = "20") int pageSize) 
        PageRequest pageable = PageRequest.of(page, pageSize);
        Page<Dictionary> dictionaryDTOList = classifierService.getAllDictionaryValues(pageable);
        return ResponseEntity.status(HttpStatus.OK).body(dictionaryDTOList);
    
...

主要抽象服务:


@Service
public abstract class ClassifierService 
    public final JdbcTemplate jdbcTemplate;
    
    public ClassifierService(final JdbcTemplate jdbcTemplate) 
        this.jdbcTemplate = jdbcTemplate;
    
    
    protected abstract Integer count();

字典服务:

@Service
public class DictionaryService
      extends ClassifierService 
    public DictionaryService(JdbcTemplate jdbcTemplate) 
        super(jdbcTemplate);
    
    
    public Page<DictionaryDTO> getAllDictionaryValues(Pageable page) 
        List<DictionaryDTO> result = jdbcTemplate.query(GET_SQL + " LIMIT " + page.getPageSize() + " OFFSET " + page.getOffset(),
                                                                 new DictionaryRowMapper());
        return new PageImpl<>(result, page, count());
    

我做错了什么? 我想拥有一些抽象方法和通用控制器的通用服务。 如何自动连接 T 服务,以便从特定服务中获取特定控制器的方法?

【问题讨论】:

您遇到什么错误?您没有发布任何错误。首先,我建议不要用@Service@RestController 注释你的超级/抽象类 @pleft 你好!抱歉,错误是 ClassifierController 中的最终 T 分类器服务没有 Beans 【参考方案1】:

如果你想在超抽象类中抽象出控制器和服务的共同内容,那么你必须将类标记为抽象,并将这个条件移到层次结构的下层。系统正确地抱怨它找不到类型 T 的服务,因为没有任何东西可以按该名称搜索。如果你创建一个名为T 的类,这个问题很可能会得到解决。现在,如何在这里使用泛型定义。以下是我的尝试方法。

首先创建一个抽象的服务类比如AbstractService 如果需要,可以通过限定符单独创建此服务的具体实现者。 创建一个抽象控制器并告诉它需要一个类,该类是AbstractService 的子类。 在子控制器中注入所需的服务并将其传递给超类。

代码如下:

package in.silentsudo.autowiregenericservicecontroller.service;

public abstract class AbstractTransporterService 

    final public String supportedVersion() 
        return "1.0";
    

    public abstract String transport();

Impl1

package in.silentsudo.autowiregenericservicecontroller.service;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
@Qualifier("audioTransporterService")
public class AudioTransporterService extends AbstractTransporterService 
    @Override
    public String transport() 
        return "transporting audio details";
    

Impl2

package in.silentsudo.autowiregenericservicecontroller.service;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
@Qualifier("videoTransporterService")
public class VideoTransporterService extends AbstractTransporterService 
    @Override
    public String transport() 
        return "transporting video details";
    

控制器如下 抽象控制器

package in.silentsudo.autowiregenericservicecontroller.controller;

import in.silentsudo.autowiregenericservicecontroller.service.AbstractTransporterService;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

public abstract class AbstractController<S extends AbstractTransporterService> 
    private final S service;

    public AbstractController(S service) 
        this.service = service;
    

    public S getService() 
        return service;
    


    @RequestMapping(path = "/version")
    public Map<String, String> controllerVersion() 
        return Map.of("type", getClass().getName(), "v", service.supportedVersion());
    

Impl1

package in.silentsudo.autowiregenericservicecontroller.controller;

import in.silentsudo.autowiregenericservicecontroller.service.AudioTransporterService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(path = "/audio")
public class AudioController extends AbstractController<AudioTransporterService> 


    public AudioController(AudioTransporterService service) 
        super(service);
    

    @GetMapping
    public String get() 
        return getService().transport();
    

Impl2

package in.silentsudo.autowiregenericservicecontroller.controller;

import in.silentsudo.autowiregenericservicecontroller.service.VideoTransporterService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(path = "/video")
public class VideoController extends AbstractController<VideoTransporterService> 

    public VideoController(VideoTransporterService service) 
        super(service);
    

    @GetMapping
    public String get() 
        return getService().transport();
    


@RestController 注解信息在运行时不传递。所以最好不要用 Service 注释超类

也是路径

http://localhost:8080/audio/version http://localhost:8080/video/version

他们正在扩展具有/version 方法的超类

源码https://gitlab.com/spring-boot-cloud-samples/autowire-generic-service-controller

【讨论】:

以上是关于在 Spring Boot 中的控制器中自动装配通用(T)服务的主要内容,如果未能解决你的问题,请参考以下文章

从源码中理解Spring Boot自动装配原理

spring boot自动装配原理@EnableAutoConfiguration

Spring Boot - 自动装配中的不可忽视的@Import

Spring boot 自动装配

Spring boot 自动装配

Spring Boot核心特性之组件自动装配