Какие знаешь способы создания контроллера, кроме @Controller?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Способы создания контроллеров в 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% всех случаев, остальные подходы используются для специфичных задач.