← Назад к вопросам

Какие знаешь способы создания контроллера, кроме @Controller?

1.2 Junior🔥 211 комментариев
#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы создания контроллеров в Spring: альтернативы @Controller

1. @RestController (самый распространённый)

Это комбинация @Controller + @ResponseBody — автоматически сериализует ответ в JSON.

@RestController
@RequestMapping("/api/v1/users")
public class UserRestController {
    private final UserService userService;
    
    @GetMapping("/{id}")
    public UserDTO getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    @PostMapping
    public UserDTO createUser(@RequestBody CreateUserRequest request) {
        return userService.createUser(request);
    }
}

// Spring автоматически возвращает JSON
// { "id": 1, "name": "John", ... }

2. Функциональные endpoints (WebFlux style)

Вместо аннотаций используем функции. Это более functional programming подход.

@Configuration
public class UserRouterConfig {
    private final UserService userService;
    
    public UserRouterConfig(UserService userService) {
        this.userService = userService;
    }
    
    @Bean
    public RouterFunction<ServerResponse> userRoutes() {
        return RouterFunctions.route()
            .GET("/api/v1/users/{id}", this::getUser)
            .POST("/api/v1/users", this::createUser)
            .PUT("/api/v1/users/{id}", this::updateUser)
            .DELETE("/api/v1/users/{id}", this::deleteUser)
            .build();
    }
    
    private ServerResponse getUser(ServerRequest request) {
        Long id = Long.valueOf(request.pathVariable("id"));
        UserDTO user = userService.getUserById(id);
        return ServerResponse.ok()
            .contentType(MediaType.APPLICATION_JSON)
            .body(user);
    }
    
    private ServerResponse createUser(ServerRequest request) {
        CreateUserRequest dto = request.body(CreateUserRequest.class);
        UserDTO user = userService.createUser(dto);
        return ServerResponse.created(URI.create("/api/v1/users/" + user.getId()))
            .contentType(MediaType.APPLICATION_JSON)
            .body(user);
    }
    
    private ServerResponse updateUser(ServerRequest request) {
        Long id = Long.valueOf(request.pathVariable("id"));
        UpdateUserRequest dto = request.body(UpdateUserRequest.class);
        UserDTO user = userService.updateUser(id, dto);
        return ServerResponse.ok().body(user);
    }
    
    private ServerResponse deleteUser(ServerRequest request) {
        Long id = Long.valueOf(request.pathVariable("id"));
        userService.deleteUser(id);
        return ServerResponse.noContent().build();
    }
}

3. WebFilter (низкоуровневый подход)

Полная контроль над HTTP запросом/ответом.

@Component
public class CustomWebFilter implements WebFilter {
    private final UserService userService;
    
    public CustomWebFilter(UserService userService) {
        this.userService = userService;
    }
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String path = exchange.getRequest().getPath().value();
        
        if (path.startsWith("/api/v1/users")) {
            return handleUserRequest(exchange, chain);
        }
        
        return chain.filter(exchange);
    }
    
    private Mono<Void> handleUserRequest(ServerWebExchange exchange, WebFilterChain chain) {
        ServerRequest serverRequest = ServerRequest.create(
            exchange, HandlerStrategies.withDefaults().messageReaders()
        );
        
        Long id = Long.valueOf(serverRequest.pathVariable("id"));
        UserDTO user = userService.getUserById(id);
        
        // Вручную пишем ответ
        ServerResponse response = ServerResponse.ok()
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(user);
        
        return response.writeTo(exchange, HandlerStrategies.withDefaults());
    }
}

4. Spring Data REST (очень минималистичный)

Спринг автоматически генерирует REST endpoints из Repository.

// Зависимость
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

// Entity
@Entity
@Table(name = "users")
public class User {
    @Id
    private Long id;
    private String name;
    private String email;
}

// Repository (это всё!)
@RepositoryRestResource(path = "users")
public interface UserRepository extends JpaRepository<User, Long> {
}

// Spring автоматически создаёт endpoints:
// GET    /users
// POST   /users
// GET    /users/{id}
// PUT    /users/{id}
// DELETE /users/{id}
// И много других...

5. GraphQL (для специфичных случаев)

Полностью отличный подход — вместо REST используем GraphQL запросы.

// Зависимость
<dependency>
    <groupId>org.springframework.graphql</groupId>
    <artifactId>spring-graphql-boot-starter</artifactId>
</dependency>

// Schema (schema.graphqls)
type Query {
    user(id: ID!): User
    users: [User]
}

type Mutation {
    createUser(input: CreateUserInput!): User
    updateUser(id: ID!, input: UpdateUserInput!): User
}

type User {
    id: ID!
    name: String!
    email: String!
}

// Resolver
@Controller
@RequiredArgsConstructor
public class UserGraphQLController {
    private final UserService userService;
    
    @QueryMapping
    public UserDTO user(@Argument Long id) {
        return userService.getUserById(id);
    }
    
    @QueryMapping
    public List<UserDTO> users() {
        return userService.getAllUsers();
    }
    
    @MutationMapping
    public UserDTO createUser(@Argument CreateUserInput input) {
        return userService.createUser(input);
    }
}

// Запрос клиента:
// query {
//   user(id: 1) {
//     id
//     name
//     email
//   }
// }

6. HandlerMapping и Handler (очень низкоуровневый)

Полный контроль над маршрутизацией.

@Configuration
public class CustomHandlerMappingConfig {
    private final UserService userService;
    
    public CustomHandlerMappingConfig(UserService userService) {
        this.userService = userService;
    }
    
    @Bean
    public HandlerMapping customHandlerMapping() {
        return new AbstractHandlerMapping() {
            @Override
            protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
                String path = request.getRequestURI();
                
                if (path.startsWith("/api/v1/users")) {
                    return new UserHandler(userService);
                }
                
                return null; // не обработано
            }
        };
    }
}

public class UserHandler implements HttpRequestHandler {
    private final UserService userService;
    
    public UserHandler(UserService userService) {
        this.userService = userService;
    }
    
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        String method = request.getMethod();
        String pathInfo = request.getPathInfo();
        
        try {
            if ("GET".equals(method) && pathInfo.matches("/\\d+")) {
                Long id = Long.parseLong(pathInfo.substring(1));
                UserDTO user = userService.getUserById(id);
                
                response.setContentType("application/json");
                response.getWriter().write(new ObjectMapper().writeValueAsString(user));
            } else if ("POST".equals(method)) {
                // create
            }
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
}

7. gRPC (для микросервисов)

Альтернатива REST для эффективной коммуникации между сервисами.

// users.proto
package users;

service UserService {
    rpc GetUser(GetUserRequest) returns (UserResponse);
    rpc CreateUser(CreateUserRequest) returns (UserResponse);
}

message GetUserRequest {
    int64 id = 1;
}

message CreateUserRequest {
    string name = 1;
    string email = 2;
}

message UserResponse {
    int64 id = 1;
    string name = 2;
    string email = 3;
}
// Java implementation
public class UserGrpcService extends UserServiceGrpc.UserServiceImplBase {
    private final UserService userService;
    
    public UserGrpcService(UserService userService) {
        this.userService = userService;
    }
    
    @Override
    public void getUser(GetUserRequest request, StreamObserver<UserResponse> responseObserver) {
        try {
            UserDTO user = userService.getUserById(request.getId());
            
            UserResponse response = UserResponse.newBuilder()
                .setId(user.getId())
                .setName(user.getName())
                .setEmail(user.getEmail())
                .build();
            
            responseObserver.onNext(response);
            responseObserver.onCompleted();
        } catch (Exception e) {
            responseObserver.onError(e);
        }
    }
}

8. Servlet (raw, низкоуровневый)

Фактически, можешь зарегистрировать обычный Servlet.

@Configuration
public class ServletConfig {
    
    @Bean
    public ServletRegistrationBean<UserServlet> userServlet(UserService userService) {
        ServletRegistrationBean<UserServlet> registration = new ServletRegistrationBean<>();
        registration.setServlet(new UserServlet(userService));
        registration.addUrlMappings("/api/v1/users/*");
        return registration;
    }
}

public class UserServlet extends HttpServlet {
    private final UserService userService;
    
    public UserServlet(UserService userService) {
        this.userService = userService;
    }
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        String id = request.getPathInfo().substring(1);
        UserDTO user = userService.getUserById(Long.parseLong(id));
        
        response.setContentType("application/json");
        response.getWriter().write(new ObjectMapper().writeValueAsString(user));
    }
}

9. Actuator endpoints (встроенные)

Spring Boot предоставляет встроенные endpoints для мониторинга.

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,metrics,info
// Автоматически доступны:
// GET  /actuator/health      — статус приложения
// GET  /actuator/metrics      — метрики
// GET  /actuator/info         — информация

Таблица сравнения

ПодходСложностьПроизводительностьUse Case
@RestControllerнизкаяхорошостандартный REST
Router FunctionsсредняялучшеWebFlux
Spring Data RESTочень низкаянормальноCRUD сервисы
GraphQLвысокаяотличносложные запросы
gRPCвысокаяотличномикросервисы
Servletочень высокаяотлично (но)старый код, специальные случаи
WebFilterвысокаяотличнотрансформация запроса

Best Practice

// Для большинства случаев — используй @RestController
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    // стандартный подход
}

// Если используешь WebFlux или нужна функциональность — RouterFunction
@Configuration
public class UserRouterConfig {
    @Bean
    public RouterFunction<ServerResponse> userRoutes(UserService userService) {
        return RouterFunctions.route()
            .GET("/api/v1/users/{id}", req -> ...)
            .build();
    }
}

// Если это просто CRUD — Spring Data REST
@RepositoryRestResource(path = "users")
public interface UserRepository extends JpaRepository<User, Long> {
}

// Для микросервисной архитектуры — gRPC

Главное правило: выбирай подход в зависимости от требований проекта. @RestController — это 90% всех случаев, остальные подходы используются для специфичных задач.