Roman Kryvolapov Engineering Blog

Java Spring — Аннотации Spring

Какие основные аннотации в Spring

Annotation
├── Component
│ ├── Repository
│ ├── Service
│ ├── Controller
│ │ └── RestController
├── Configuration
│ └── EnableAutoConfiguration
├── SpringBootApplication
│ └── Configuration
│ └── EnableAutoConfiguration
├── Conditional
│ ├── ConditionalOnBean
│ ├── ConditionalOnMissingBean
│ ├── ConditionalOnClass
│ ├── ConditionalOnMissingClass
│ ├── ConditionalOnProperty
│ ├── ConditionalOnResource
│ ├── ConditionalOnWebApplication
│ └── ConditionalOnNotWebApplication
├── Enable
│ ├── EnableAspectJAutoProxy
│ ├── EnableCaching
│ ├── EnableScheduling
│ └── EnableAsync
├── Transactional
├── Bean
├── Qualifier
├── Autowired
├── Value
├── Primary
├── Lazy
├── Scope

@Configuration
указывает, что класс содержит определения бинов и должен быть обработан контейнером Spring для генерации бинов Spring.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class AppConfig {
@Bean
fun myBean(): MyBean {
return MyBean()
}
}
class MyBean {
fun doSomething() {
println("Doing something...")
}
}

@Bean
указывает, что метод производит бин, управляемый Spring-контейнером.

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class AppConfig {
@Bean
fun myBean(): MyBean {
return MyBean()
}
}

@Component
указывает, что класс является компонентом Spring, что позволяет Spring автоматически определять его для управления в контейнере.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component
import org.springframework.stereotype.Component
@Component
class MyComponent {
fun doWork() {
println("Component is working...")
}
}

@Service
специализированная аннотация, используемая для классов сервисного уровня. Это также разновидность @Component.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service
import org.springframework.stereotype.Service
@Service
class MyService {
fun performService() {
println("Service is performing...")
}
}

@Repository
специализированная аннотация для классов доступа к данным (DAO). Это также разновидность @Component.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface UserRepository : JpaRepository<User, Long> {
fun findByUsername(
username: String
): User?
}

@Controller
указывает, что класс является контроллером Spring MVC. Эта аннотация используется для обработки веб-запросов.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
@Controller
@RequestMapping("/web")
class WebController {
@GetMapping("/hello")
fun hello(model: Model): String {
model.addAttribute(
"message",
"Hello, World!"
)
return "hello"
}
}

@RestController
является комбинацией @Controller и @ResponseBody. Она указывает, что класс является RESTful контроллером, и возвращаемые данные должны быть напрямую написаны в HTTP-ответе в формате JSON или XML.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class ApiController {
@GetMapping("/greeting")
fun greeting(): String {
return "Hello, API World!"
}
}

@RestControllerAdvice
используется для глобальной обработки исключений и предоставления совета контроллерам, работающим с REST API. Это расширение аннотации @ControllerAdvice, которое автоматически включает @ResponseBody, что делает ее подходящей для обработки исключений в контроллерах REST, возвращая ответы в формате JSON или XML.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
class UserNotFoundException(
message: String
) : RuntimeException(message)
data class ErrorResponse(
val message: String,
val details: String
)
@RestControllerAdvice
class RestExceptionHandler {
@ExceptionHandler(UserNotFoundException::class)
fun handleUserNotFoundException(
ex: UserNotFoundException
): ResponseEntity<ErrorResponse> {
val errorResponse = ErrorResponse(
message = ex.message ?: "User not found",
details = "The user you are looking for does not exist."
)
return ResponseEntity(errorResponse, HttpStatus.NOT_FOUND)
}
@ExceptionHandler(Exception::class)
fun handleGenericException(
ex: Exception
): ResponseEntity<ErrorResponse> {
val errorResponse = ErrorResponse(
message = ex.message ?: "An error occurred",
details = "Please contact support."
)
return ResponseEntity(
errorResponse,
HttpStatus.INTERNAL_SERVER_ERROR
)
}
}
// Контроллер, выбрасывающий исключение
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/users")
class UserController {
private val users = mapOf(
1L to "John Doe",
2L to "Jane Doe"
)
@GetMapping("/{id}")
fun getUser(@PathVariable id: Long): String {
return users[id]
?: throw UserNotFoundException(
"User with ID $id not found"
)
}
}

Пример обработки исключений с использованием @RestControllerAdvice:
При запросе пользователя с ID, который не существует, сервер вернет JSON-ответ с HTTP-статусом 404 (Not Found) и телом:

{
"message": "User with ID 3 not found",
"details": "The user you are looking for does not exist."
}

Если возникает общее исключение, сервер вернет JSON-ответ с HTTP-статусом 500 (Internal Server Error) и телом:

{
"message": "An error occurred",
"details": "Please contact support."
}

@ResponseBody
делает автоматическую сериализацию в JSON любого возвращаемого объекта

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody
@Controller
class PersonController {
@GetMapping("/persons/{id}")
@ResponseBody
fun getData(
@PathVariable id: Long
): String {
return ""
}
}
// то же самое что
@RestController
class PersonController {
@GetMapping("/persons/{id}")
fun getData(
@PathVariable id: Long
): String{
return ""
}
}

@Aspect
способ вынести повторяющуюся логику (например, логирование или проверку безопасности) из методов в отдельный класс. Он автоматически подключается к нужным методам и выполняет нужные действия до, после или вокруг них. Это удобно, потому что не нужно дублировать один и тот же код во многих местах.

@Before
указывает, что метод-совет должен выполняться до вызова целевого метода. Используется, например, для логирования входа в метод или проверки прав доступа.

@After
указывает, что метод-совет должен выполняться после завершения целевого метода — независимо от того, успешно он завершился или с исключением. Подходит для очистки ресурсов или логирования выхода.

@Around
позволяет полностью обернуть вызов целевого метода: выполнить код до, вызвать сам метод через proceed(), а затем выполнить код после. Даёт полный контроль над выполнением — можно изменить аргументы, результат, или даже не вызывать сам метод.

@Pointcut
описывает, к каким методам применять аспект (по сигнатурам, аннотациям и т.д.)

@Aspect
@Component
class LoggingAspect {
// Pointcut that matches all methods in the service package
@Pointcut("execution(* com.example.service.*.*(..))")
fun allServiceMethods() {
}
// Runs before the matched method
@Before("allServiceMethods()")
fun logBefore() {
println("Method is about to be called")
}
// Runs after the matched method finishes
@After("allServiceMethods()")
fun logAfter() {
println("Method has finished execution")
}
// Runs around the matched method: before and after
@Around("allServiceMethods()")
fun logAround(joinPoint: ProceedingJoinPoint): Any? {
println("Before method: ${joinPoint.signature.name}")
val result = joinPoint.proceed()
println("After method: ${joinPoint.signature.name}")
return result
}
}

@ModelAttribute
используется для привязки данных модели к параметрам метода или возвращаемым значениям методов контроллера. Она может применяться как к параметрам метода, так и к самим методам, предоставляя гибкий механизм для подготовки и связывания данных модели.

@MatrixVariable
в Spring используется для привязки значений переменных матрицы к параметрам метода в контроллере. Переменные матрицы — это параметры, которые включаются в части пути URL между точками с запятой (;). Эта аннотация позволяет извлечь значения переменных матрицы и использовать их в методах контроллера.

@InitBinder
используется для настройки средств привязки данных на уровне контроллера. Эти средства привязки данных обрабатывают данные, поступающие из HTTP-запросов, и преобразуют их в объекты модели. Аннотация @InitBinder позволяет вам регистрировать пользовательские редакторы свойств или валидаторы для обработки данных, перед их связыванием с объектами модели.

@CookieValue
используется для привязки значения из HTTP cookie к параметру метода в контроллере. Это позволяет вам получать значения cookie непосредственно в методах контроллера, что упрощает работу с ними.

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.CookieValue
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class UserController {
@GetMapping("/token")
fun getToken(
@CookieValue("userToken") token: String
): String {
return "User token: $token"
}
}

@Autowired
используется для автоматического внедрения зависимостей. Spring автоматически находит подходящий бин и внедряет его в поле, конструктор или метод.

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
@Service
class MainService @Autowired constructor(
private val myComponent: MyComponent
) {
fun execute() {
myComponent.doWork()
}
}

@Transactional
используется для управления транзакциями. Она может быть применена к методам или классам для автоматического начала, коммита или отката транзакций.
Механизм работы @Transactional в Spring основан на использовании прокси и аспектно-ориентированного программирования для управления транзакциями. Прокси перехватывает вызовы методов и взаимодействует с транзакционным менеджером для начала, подтверждения или отката транзакций. Это позволяет автоматически обрабатывать транзакции, упрощая разработку надежных и согласованных приложений.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Reflective
public @interface Transactional
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@Service
class TransactionalService(
private val userRepository: UserRepository
) {
@Transactional
fun processTransaction() {
val data = userRepository
.findByUsername("username")
println("Processing data: $data")
}
}

@EnableTransactionManagement
используется в Spring для включения поддержки транзакционного менеджмента, основанного на аннотациях. Она автоматически настраивает необходимые бины и прокси для обработки методов, аннотированных @Transactional. Рассмотрим, как это работает и как его использовать.

Основные функции @EnableTransactionManagement:

Активация поддержки транзакций:
Включает инфраструктуру, необходимую для работы с транзакциями, основанными на аннотациях, такими как @Transactional.

Прокси:
Создает прокси для бинов, которые имеют методы, аннотированные @Transactional. Прокси перехватывает вызовы этих методов и управляет транзакциями.

Конфигурация:
Аннотация автоматически настраивает TransactionManager, который управляет началом, подтверждением и откатом транзакций.

Основные атрибуты @EnableTransactionManagement:

mode:
Определяет, будет ли использоваться проксирование на основе интерфейсов (JDK) или классов (CGLIB). Значение по умолчанию — AdviceMode.PROXY.

proxyTargetClass:
Если true, то используется CGLIB проксирование (по умолчанию false).

order:
Устанавливает порядок выполнения транзакционных аспектов.

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.transaction.annotation.EnableTransactionManagement
import org.springframework.transaction.annotation.TransactionManagementConfigurer
import org.springframework.transaction.PlatformTransactionManager
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
import org.springframework.transaction.interceptor.TransactionInterceptor
import org.springframework.transaction.interceptor.TransactionProxyFactoryBean
import javax.sql.DataSource
@Configuration
@EnableTransactionManagement
class TransactionConfig(
private val dataSource: DataSource
) : TransactionManagementConfigurer {
@Bean
override fun annotationDrivenTransactionManager(): PlatformTransactionManager {
return DataSourceTransactionManager(dataSource)
}
@Bean
fun transactionInterceptor(): TransactionInterceptor {
val source = AnnotationTransactionAttributeSource()
val transactionManager = annotationDrivenTransactionManager()
return TransactionInterceptor(transactionManager, source)
}
@Bean
fun transactionProxyFactoryBean(): TransactionProxyFactoryBean {
val proxyFactoryBean = TransactionProxyFactoryBean()
proxyFactoryBean.transactionManager = annotationDrivenTransactionManager()
proxyFactoryBean.setTransactionAttributes(
mapOf(
"save*" to "PROPAGATION_REQUIRED",
"delete*" to "PROPAGATION_REQUIRED",
"update*" to "PROPAGATION_REQUIRED",
"*" to "PROPAGATION_REQUIRED,readOnly"
)
)
return proxyFactoryBean
}
}

DataSourceTransactionManager:
Используется как транзакционный менеджер для управления транзакциями на уровне JDBC.

TransactionInterceptor:
Перехватывает вызовы методов и обрабатывает транзакционную логику.

TransactionProxyFactoryBean:
Создает прокси для бинов, чтобы перехватывать вызовы методов и обрабатывать транзакционную логику.

@RequestPart
используется для привязки части многочастного (multipart) запроса к параметру метода контроллера. Она позволяет обрабатывать файлы и данные формы, отправленные с использованием формы с атрибутом enctype=”multipart/form-data”.

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestPart
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestPart
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile
import java.nio.file.Files
import java.nio.file.Paths
@RestController
@RequestMapping("/api")
class FileUploadController {
@PostMapping("/upload")
fun handleFileUpload(
@RequestPart("file") file: MultipartFile
): String {
val uploadDir = Paths.get("uploads")
Files.createDirectories(uploadDir)
val uploadFilePath = uploadDir.resolve(
file.originalFilename ?: "uploadedFile"
)
file.inputStream.use { input ->
Files.copy(input, uploadFilePath)
}
return "File uploaded successfully: ${file.originalFilename}"
}
}
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestPart
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile
data class User(
val name: String,
val email: String
)
@RestController
@RequestMapping("/api")
class UserUploadController {
@PostMapping("/upload-user")
fun handleUserUpload(
@RequestPart("user") user: User,
@RequestPart("file") file: MultipartFile
): String {
return "User: ${user.name}, File: ${file.originalFilename}"
}
}

@Value
используется для внедрения значений из файла свойств в поля.

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Configuration
@Configuration
class AppConfig {
@Value("\${my.property}")
lateinit var myProperty: String
fun showProperty() {
println("Property value: $myProperty")
}
}

@RequestMapping
добавляет перед qrl метода то, что в него передано

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
@Reflective({ControllerMappingReflectiveProcessor.class})
public @interface RequestMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class ApiController {
@RequestMapping("/all")
fun getAll(): String {
return "Get all data"
}
}

@GetMapping и @PostMapping
являются специализированными версиями @RequestMapping для обработки GET и POST запросов соответственно.
Есть также @DeleteMapping, @PutMapping

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = {RequestMethod.GET})
public @interface GetMapping
@RestController
@RequestMapping("/api")
class ApiController {
@GetMapping("/get")
fun getData(): String {
return "Get data"
}
@PostMapping("/post")
fun postData(
@RequestParam data: String
): String {
return "Posted data: $data"
}
}

@RequestParam
используется для извлечения параметров запроса, тогда как @PathVariable используется для извлечения переменных пути из URL

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class ParameterController {
@GetMapping("/request")
fun requestParam(
@RequestParam name: String
): String {
return "Request param: $name"
}
@GetMapping("/path/{id}")
fun pathVariable(
@PathVariable id: Int
): String {
return "Path variable: $id"
}
}

@RequestBody
сериализует класс в JSON

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
data class User(
val username: String,
val password: String
)
@RestController
@RequestMapping("/api")
class UserController {
@PostMapping("/user")
fun createUser(
@RequestBody user: User
): String {
return "User created: ${user.username}"
}
}

@SessionAttribute
используется для доступа к атрибутам HTTP-сессии в методах контроллера. Эта аннотация позволяет легко получить значение атрибута сессии и использовать его в методе контроллера, не прибегая к прямому взаимодействию с объектом HttpSession.

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SessionAttribute
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.SessionAttributes
import org.springframework.web.bind.support.SessionStatus
import javax.servlet.http.HttpSession
@Controller
@RequestMapping("/session")
@SessionAttributes("user")
class SessionController {
@GetMapping("/set")
fun setSessionAttribute(
@RequestParam name: String,
session: HttpSession
): String {
session.setAttribute("user", name)
return "redirect:/session/show"
}
@GetMapping("/show")
fun showSessionAttribute(
@SessionAttribute("user") user: String
): String {
println("User in session: $user")
return "session"
}
@GetMapping("/invalidate")
fun invalidateSession(
session: HttpSession,
status: SessionStatus
): String {
status.setComplete()
session.invalidate()
return "redirect:/session/show"
}
}
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.SessionAttribute
@Controller
class UserController {
@GetMapping("/current-user")
fun currentUser(
@SessionAttribute("user") user: String
): String {
println("Current user: $user")
return "currentUser"
}
}

@SessionAttributes
используется для указания, какие атрибуты модели должны быть сохранены в HTTP-сессии и доступны в течение нескольких запросов в рамках одной сессии пользователя. Это полезно, когда вы хотите сохранить данные между запросами без необходимости вручную управлять объектом HttpSession.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SessionAttributes
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.SessionAttributes
import org.springframework.web.bind.support.SessionStatus
data class User(
var firstName: String = "",
var lastName: String = "",
var email: String = ""
)
@Controller
@RequestMapping("/session")
@SessionAttributes("user")
class SessionController {
@ModelAttribute("user")
fun user(): User {
return User()
}
@GetMapping("/form")
fun showForm(
model: Model
): String {
return "userForm"
}
@PostMapping("/save")
fun saveUser(
@ModelAttribute("user") user: User
): String {
// Сохранение пользователя в сессии
println("User saved: $user")
return "redirect:/session/details"
}
@GetMapping("/details")
fun userDetails(
@ModelAttribute("user") user: User
): String {
println("User details: $user")
return "userDetails"
}
@GetMapping("/clear")
fun clearSession(
sessionStatus: SessionStatus
): String {
sessionStatus.setComplete()
return "redirect:/session/form"
}
}

userForm.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>User Form</title>
</head>
<body>
<form action="#" th:action="@{/session/save}" th:object="${user}" method="post">
<label for="firstName">First Name:</label>
<input type="text" id="firstName" name="firstName" th:field="*{firstName}" /><br/>
<label for="lastName">Last Name:</label>
<input type="text" id="lastName" name="lastName" th:field="*{lastName}" /><br/>
<label for="email">Email:</label>
<input type="email" id="email" name="email" th:field="*{email}" /><br/>
<button type="submit">Save</button>
</form>
</body>
</html>

userDetails.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>User Details</title>
</head>
<body>
<h1>User Details</h1>
<p>First Name: <span th:text="${user.firstName}">First Name</span></p>
<p>Last Name: <span th:text="${user.lastName}">Last Name</span></p>
<p>Email: <span th:text="${user.email}">Email</span></p>
<a th:href="@{/session/clear}">Clear Session</a>
</body>
</html>

@ResponseStatus
используется для указания статуса HTTP-ответа, который должен быть возвращен с аннотированного метода или исключения. Это позволяет вам управлять HTTP-статусами, возвращаемыми клиенту, без необходимости вручную устанавливать их в коде контроллера.

Основные параметры @ResponseStatus:

code (тип: HttpStatus):
Указывает код статуса HTTP, который должен быть возвращен. Это обязательный параметр.

reason (тип: String):
Указывает причину, которая будет возвращена вместе с кодом статуса. Этот параметр является необязательным.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseStatus
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class MyController {
@GetMapping("/success")
@ResponseStatus(HttpStatus.OK)
fun success(): String {
return "Request was successful"
}
@GetMapping("/notfound")
@ResponseStatus(HttpStatus.NOT_FOUND)
fun notFound(): String {
return "Resource not found"
}
}

@RequestHeader
используется для привязки значения заголовка HTTP-запроса к параметру метода в контроллере. Это позволяет вам получать значения заголовков HTTP-запроса непосредственно в методах контроллера, что упрощает работу с заголовками запросов.

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class HeaderController {
@GetMapping("/user-agent")
fun getUserAgent(
@RequestHeader("User-Agent") userAgent: String
): String {
return "User-Agent: $userAgent"
}
}
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class HeaderController {
@GetMapping("/accept-language")
fun getAcceptLanguage(
@RequestHeader(
"Accept-Language",
defaultValue = "en-US"
) acceptLanguage: String
): String {
return "Accept-Language: $acceptLanguage"
}
}
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class HeaderController {
@GetMapping("/required-header")
fun getRequiredHeader(
@RequestHeader(
"X-Custom-Header",
required = true
) customHeader: String
): String {
return "X-Custom-Header: $customHeader"
}
}

@RequestAttribute
используется для привязки значения атрибута HTTP-запроса к параметру метода в контроллере. Это позволяет вам получать значения атрибутов запроса непосредственно в методах контроллера, что упрощает работу с атрибутами, установленными в запросе.

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestAttribute
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestAttribute
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class MyController {
@GetMapping("/attribute")
fun getRequestAttribute(
@RequestAttribute("myAttribute") myAttribute: String
): String {
return "Request Attribute: $myAttribute"
}
}
import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.FilterConfig
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import org.springframework.stereotype.Component
@Component
class MyFilter : Filter {
override fun init(filterConfig: FilterConfig?) {
}
override fun doFilter(
request: ServletRequest,
response: ServletResponse,
chain: FilterChain
) {
request.setAttribute("myAttribute", "Some Value")
chain.doFilter(request, response)
}
override fun destroy() {
}
}

@ExceptionHandler
используется для определения метода, который будет обрабатывать определенные типы исключений

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Reflective({ExceptionHandlerReflectiveProcessor.class})
public @interface ExceptionHandler
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class ExceptionController {
@GetMapping("/exception")
fun throwException(): String {
throw RuntimeException("Exception occurred!")
}
@ExceptionHandler(RuntimeException::class)
fun handleRuntimeException(
ex: RuntimeException
): ResponseEntity<String> {
return ResponseEntity(
"Handled exception: ${ex.message}",
HttpStatus.INTERNAL_SERVER_ERROR
)
}
}

@CrossOrigin
позволяет кросс-доменные запросы к методу или классу.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin
import org.springframework.web.bind.annotation.CrossOrigin
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = ["http://example.com"])
class CrossOriginController {
@GetMapping("/data")
fun getData(): String {
return "Cross-origin data"
}
}

@Qualifier
используется в Spring для разрешения неоднозначностей при внедрении зависимостей. Когда у Spring-контейнера есть несколько бинов одного типа, @Qualifier позволяет указать, какой именно бин должен быть внедрен.

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier
interface Animal {
fun speak()
}
@Component
@Qualifier("dog")
class Dog : Animal {
override fun speak() {
println("Woof!")
}
}
@Component
@Qualifier("cat")
class Cat : Animal {
override fun speak() {
println("Meow!")
}
}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Component
@Component
class AnimalService @Autowired constructor(
@Qualifier("dog") private val animal: Animal
) {
fun makeAnimalSpeak() {
animal.speak()
}
}

@Primary
если у вас есть бин, который вы хотите использовать по умолчанию, вы можете аннотировать его с помощью @Primary. Тогда @Qualifier будет использоваться только для уточнения. В этом случае, если @Qualifier не используется, будет внедрен бин DefaultAnimal

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Primary
@Component
@Primary
class DefaultAnimal : Animal {
override fun speak() {
println("Default sound!")
}
}

@ImportResource
используется для загрузки XML-конфигурации в контекст приложения. Это полезно, если у вас есть существующие XML-файлы конфигурации Spring, которые вы хотите импортировать в конфигурацию, основанную на аннотациях или Java-коде.

Пример использования:
Предположим, у вас есть XML-конфигурация applicationContext.xml:

applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="exampleBean" class="com.example.ExampleBean">
<property name="property" value="value"/>
</bean>
</beans>

Теперь вы хотите импортировать эту конфигурацию в ваш Spring Boot или другой Java-базированный конфигурационный класс:

import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.ImportResource
@Configuration
@ImportResource("classpath:applicationContext.xml")
class AppConfig {
// Java-based configuration or bean definitions
}

@ComponentScan
используется для автоматического поиска и регистрации бинов (компонентов) в Spring-контейнере. Это включает классы, аннотированные такими аннотациями, как @Component, @Service, @Repository, @Controller, и другими, которые указывают Spring, что эти классы являются бинами, которые необходимо управлять.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.ComponentScan
@SpringBootApplication
@ComponentScan(basePackages = [
"com.example.demo.services",
"com.example.demo.controllers"
])
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}

@Entity
используется в JPA (Java Persistence API) для указания, что класс представляет собой сущность (entity) и сопоставляется с таблицей в реляционной базе данных. Это часть ORM (Object-Relational Mapping), которая позволяет разработчикам работать с базой данных через объектно-ориентированные модели.

Основные аспекты аннотации @Entity:

Класс как сущность:
Аннотация @Entity определяет, что данный класс является сущностью и будет сопоставлен с таблицей в базе данных.

Таблица по умолчанию:
Если имя таблицы не указано, оно будет совпадать с именем класса. Однако это можно переопределить с помощью аннотации @Table.

Идентификационные столбцы:
Каждая сущность должна иметь хотя бы одно поле, аннотированное как @Id, чтобы указать первичный ключ.

import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Table
import javax.persistence.Column
@Entity
@Table(name = "users")
data class User(
@Id
@Column(name = "user_id")
val userId: String,
@Column(name = "email")
val email: String,
@Column(name = "username")
val username: String
)

@Entity
Указывает, что класс User является сущностью.

@Table
Определяет имя таблицы, с которой будет связана сущность. В данном случае, таблица называется users.

@Id
Обозначает первичный ключ сущности.

@Column
Указывает имя столбца в таблице, который будет сопоставлен с полем userId.

@GeneratedValue
Используется для указания стратегии генерации значений для первичного ключа (например, автоинкремент).

@OneToMany, @ManyToOne, @OneToOne, @ManyToMany
Определяют отношения между сущностями.

Пример с отношениями:

import javax.persistence.*
@Entity
@Table(name = "users")
data class User(
@Id
@Column(name = "user_id")
val userId: String,
@Column(name = "email")
val email: String,
@Column(name = "username")
val username: String,
@OneToMany(mappedBy = "user")
val orders: List<Order>
)
@Entity
@Table(name = "orders")
data class Order(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "order_id")
val orderId: Long,
@ManyToOne
@JoinColumn(name = "user_id")
val user: User,
@Column(name = "product")
val product: String
)

@EntityScan
используется для указания Spring, где искать JPA-сущности (классы, аннотированные @Entity). Это необходимо, когда сущности расположены вне стандартного пути сканирования Spring Boot, или если вы хотите указать другой пакет для сканирования сущностей.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EntityScanPackages.Registrar.class})
public @interface EntityScan
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.domain.EntityScan
@SpringBootApplication
@EntityScan(basePackages = ["com.example.demo.entities"])
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}

@Document в MongoDB
используется в Spring Data MongoDB для указания того, что класс представляет собой документ в коллекции MongoDB. Это эквивалент аннотации @Entity в JPA для реляционных баз данных. Аннотация @Document определяет, что данный класс будет отображаться на коллекцию в базе данных MongoDB.

Основные атрибуты аннотации @Document:

collection:
Имя коллекции в MongoDB, с которой связан документ. Если не указано, имя коллекции будет совпадать с именем класса, но приведенным к нижнему регистру.

language:
Указывает язык для полнотекстового поиска (опционально).

import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.Document
@Document(collection = "users")
data class User(
@Id
val id: String? = null,
val email: String,
val username: String,
)

@Document(collection = “users”):
Указывает, что класс User сопоставляется с коллекцией users в базе данных MongoDB.

@Id
Обозначает идентификатор документа. В MongoDB это поле обычно называется _id.

Дополнительные аннотации и атрибуты:

@Field
Используется для указания имени поля в документе MongoDB, если оно отличается от имени поля в классе.

@Indexed
Создает индекс на поле.

@CompoundIndex
Создает составной индекс на несколько полей.

@GeoSpatialIndexed
Создает геопространственный индекс на поле.

import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.index.Indexed
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.core.mapping.Field
@Document(collection = "users")
data class User(
@Id
val id: String? = null,
@Field("email")
@Indexed(unique = true)
val email: String,
@Field("username")
val username: String
)

@Field(“email”):
Указывает, что поле email в классе User соответствует полю email в документе MongoDB.

@Indexed(unique = true):
Создает уникальный индекс на поле email, что предотвращает наличие дублирующихся значений.

@Document в Elasticsearch
используется в Spring Data Elasticsearch для указания, что класс представляет собой документ в индексе Elasticsearch. Эта аннотация позволяет настроить индекс и его свойства, такие как имя, настройки шардирования и репликации, и некоторые другие параметры.

Основные атрибуты аннотации @Document:

indexName:
Указывает имя индекса, в котором будет храниться документ.

shards:
Указывает количество шардов (по умолчанию 5).

replicas:
Указывает количество реплик (по умолчанию 1).

refreshInterval:
Указывает интервал обновления индекса.

indexStoreType:
Указывает тип хранилища индекса (например, “fs” или “memory”).

import org.springframework.data.annotation.Id
import org.springframework.data.elasticsearch.annotations.Document
@Document(
indexName = "users",
shards = 3,
replicas = 2
)
data class User(
@Id
val id: String? = null,
val email: String,
val username: String
)

indexName:
Имя индекса Elasticsearch, в котором будет храниться документ (users).

shards:
Количество шардов (3).

replicas:
Количество реплик (2).

@Table
используется в Spring Data для обозначения того, что класс представляет собой сущность базы данных и сопоставляется с таблицей в базе данных. В контексте Spring Data Cassandra эта аннотация используется для указания, что класс будет отображаться на таблицу в базе данных Cassandra.

Основные атрибуты аннотации @Table:

value:
Имя таблицы в базе данных. Если не указано, имя таблицы будет совпадать с именем класса.

forceQuote:
Указывает, должны ли имена таблиц и столбцов заключаться в кавычки.

import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn
import org.springframework.data.cassandra.core.mapping.Table
import org.springframework.data.cassandra.core.cql.PrimaryKeyType
import org.springframework.data.cassandra.core.cql.Ordering
@Table("users")
data class User(
@PrimaryKeyColumn(
name = "user_id",
type = PrimaryKeyType.PARTITIONED
)
val userId: String,
@PrimaryKeyColumn(
name = "region",
type = PrimaryKeyType.PARTITIONED
)
val region: String,
@PrimaryKeyColumn(
name = "email",
ordinal = 0,
type = PrimaryKeyType.CLUSTERED,
ordering = Ordering.ASCENDING
)
val email: String,
@PrimaryKeyColumn(
name = "username",
ordinal = 1,
type = PrimaryKeyType.CLUSTERED,
ordering = Ordering.ASCENDING
)
val username: String
)

@Table(“users”):
Указывает, что класс User сопоставляется с таблицей users в базе данных Cassandra.

@PrimaryKeyColumn:
Указывает, какие столбцы являются частью первичного ключа.

Полное описание:

Класс:
Класс User является сущностью, которая будет сопоставлена с таблицей users в базе данных Cassandra.

Аннотация @Table:
Указывает имя таблицы, с которой связан класс. Если имя таблицы не указано, используется имя класса.

Аннотация @PrimaryKeyColumn:
Определяет столбцы первичного ключа в таблице. В данном случае, userId и region являются составными разделяющими ключами, а email и username являются кластерными ключами с порядком сортировки по возрастанию.

@PrimaryKeyColumn
используется в Spring Data Cassandra для определения столбцов первичного ключа в таблицах Cassandra. Она является частью API, которое помогает разработчикам взаимодействовать с базой данных Cassandra более удобно и эффективно. Эта аннотация позволяет явно указать, какие столбцы должны быть частью первичного ключа и как они должны быть обработаны.

Основные атрибуты аннотации @PrimaryKeyColumn:

name:
Имя столбца в таблице.

type:
Тип столбца первичного ключа (partitioned, clustered).

ordinal:
Порядковый номер столбца первичного ключа.

ordering:
Порядок сортировки для кластерных столбцов (ASC или DESC).

import org.springframework.data.cassandra.core.cql.PrimaryKeyType
import org.springframework.data.cassandra.core.cql.Ordering
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn
import org.springframework.data.cassandra.core.mapping.Table
@Table
data class User(
@PrimaryKeyColumn(
name = "user_id",
type = PrimaryKeyType.PARTITIONED
)
val userId: String,
@PrimaryKeyColumn(
name = "region",
type = PrimaryKeyType.PARTITIONED
)
val region: String,
@PrimaryKeyColumn(
name = "email",
ordinal = 0,
type = PrimaryKeyType.CLUSTERED,
ordering = Ordering.ASCENDING
)
val email: String,
@PrimaryKeyColumn(
name = "username",
ordinal = 1,
type = PrimaryKeyType.CLUSTERED,
ordering = Ordering.ASCENDING
)
val username: String
)

name: name = “user_id”
задает имя столбца в таблице, которое соответствует полю userId.

type: type = PrimaryKeyType.PARTITIONED
указывает, что столбец userId является разделяющим ключом (partition key).

ordinal: ordinal = 0
задает порядок столбца в составе кластерного ключа.

ordering: ordering = Ordering.ASCENDING
указывает порядок сортировки для кластерного столбца.

Типы столбцов первичного ключа:

PARTITIONED:
Разделяющий ключ (partition key) определяет, на каком узле будет храниться строка. Разделяющий ключ может состоять из одного или нескольких столбцов.

CLUSTERED:
Кластерный ключ (clustering key) определяет порядок строк внутри одного раздела.

@Unwrapped
В контексте Spring Data MongoDB и других реализаций Spring Data, аннотация @Unwrapped используется для указания, что атрибуты вложенного объекта должны быть развернуты (или "раскрыты") и сохранены как отдельные поля в родительском документе вместо того, чтобы сохранять вложенный объект как поддокумент.
Это может быть полезно, когда вам нужно, чтобы атрибуты вложенного объекта хранились на том же уровне, что и атрибуты родительского объекта, что позволяет упростить структуру хранения данных.

import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.core.mapping.Unwrapped
@Document(collection = "users")
data class User(
@Id
val id: String? = null,
val name: String,
@Unwrapped(prefix = "address_")
val address: Address
)

User:
Основной документ, аннотированный как @Document для хранения в коллекции users.

Address:
Вложенный объект.

@Unwrapped(prefix = “address_”)
Атрибуты объекта Address будут развернуты и сохранены в документе User как отдельные поля с префиксом address_.

@QueryIndexed
используется в Spring Data для обозначения поля как индексируемого. Эта аннотация применяется в основном в контексте Spring Data для MongoDB и позволяет указывать, что определенное поле должно быть проиндексировано для улучшения производительности поиска.

Применение аннотации @QueryIndexed в Spring Data MongoDB:

Когда поле аннотировано как @QueryIndexed, это указывает MongoDB создать индекс для этого поля. Индексация помогает ускорить операции поиска по этому полю, что особенно полезно для полей, которые часто используются в запросах.

import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.index.QueryIndexed
import org.springframework.data.mongodb.core.mapping.Document
@Document(collection = "users")
data class User(
@Id
val id: String? = null,
@QueryIndexed
val email: String,
val username: String
)

@Document(collection = “users”):
Определяет, что класс User представляет собой документ в коллекции users MongoDB.

@Id:
Обозначает поле id как идентификатор документа.

@QueryIndexed
Указывает, что поле email должно быть проиндексировано для ускорения операций поиска по этому полю.

@PreAuthorize
в Spring Security используется для ограничения доступа к методам на основе выражений SpEL (Spring Expression Language). Она позволяет задать условия, при которых доступ к методу будет разрешен или запрещен.

Основные возможности @PreAuthorize:

Проверка ролей и привилегий:
Проверяет, имеет ли текущий пользователь необходимые роли или привилегии для выполнения метода.

Проверка атрибутов:
Позволяет проверять различные атрибуты текущего пользователя, такие как имя пользователя, email и т.д.

Использование SpEL:
Позволяет использовать выражения SpEL для создания сложных логических условий.

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuthorize

Пример использования @PreAuthorize:
Рассмотрим пример использования @PreAuthorize для ограничения доступа к методам на основе ролей.
Создание сервиса с ограничением доступа. В этом примере метод adminMethod доступен только пользователям с ролью ADMIN, метод userMethod доступен только пользователям с ролью USER, а метод userOrAdminMethod доступен как пользователям с ролью USER, так и пользователям с ролью ADMIN.

import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.stereotype.Service
@Service
class UserService {
@PreAuthorize("hasRole('ADMIN')")
fun adminMethod() {
println("Admin method accessed")
}
@PreAuthorize("hasRole('USER')")
fun userMethod() {
println("User method accessed")
}
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
fun userOrAdminMethod() {
println("User or Admin method accessed")
}
}

Включение аннотаций безопасности:
Чтобы @PreAuthorize работала, необходимо включить аннотации метода безопасности в конфигурации безопасности.

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfig : WebSecurityConfigurerAdapter() {}

@DataMongoTest
используется для тестирования MongoDB репозиториев. Она фокусируется на конфигурации компонентов Spring Data MongoDB и обеспечивает быстрый тестовый контекст, исключая из него все лишние зависимости, не относящиеся к тестированию работы с данными.

@DynamicPropertySource
используется для динамического добавления свойств в Environment контекста приложения во время выполнения тестов. Это особенно полезно в интеграционных тестах, где необходимо настроить свойства, такие как URL базы данных, учетные данные или другие параметры, которые могут зависеть от внешних ресурсов, таких как Docker-контейнеры, поднятые с помощью Testcontainers.

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
@ExtendWith(SpringExtension::class)
@SpringBootTest
@Testcontainers
class UserServiceTest {
@Autowired
private lateinit var userService: UserService
companion object {
@Container
val postgreSQLContainer = PostgreSQLContainer<Nothing>(
"postgres:latest"
).apply {
withDatabaseName("testdb")
withUsername("test")
withPassword("test")
}
@JvmStatic
@DynamicPropertySource
fun registerPgProperties(
registry: DynamicPropertyRegistry
) {
registry.add(
"spring.datasource.url",
postgreSQLContainer::getJdbcUrl
)
registry.add(
"spring.datasource.username",
postgreSQLContainer::getUsername
)
registry.add(
"spring.datasource.password",
postgreSQLContainer::getPassword
)
}
}
@Test
fun `test createUser`() {
val user = User(
email = "[email protected]",
username = "testuser"
)
val savedUser = userService.createUser(user)
assertNotNull(savedUser.id)
assertEquals(
savedUser.email
)
assertEquals(
"testuser",
savedUser.username
)
}
@Test
fun `test getAllUsers`() {
val user1 = User(
email = "[email protected]",
username = "testuser1"
)
val user2 = User(
email = "[email protected]",
username = "testuser2"
)
userService.createUser(user1)
userService.createUser(user2)
val users = userService.getAllUsers()
assertEquals(2, users.size)
}
}

@Testcontainers
Аннотация, обозначающая, что этот класс теста использует Testcontainers для управления жизненным циклом контейнеров.

@Container
Обозначает контейнер, который будет автоматически управляться Testcontainers.

@DynamicPropertySource
Статический метод, аннотированный @DynamicPropertySource, используется для регистрации динамических свойств в контексте теста.

В данном примере метод registerPgProperties добавляет свойства, такие как URL базы данных, имя пользователя и пароль, которые извлекаются из контейнера PostgreSQL.

@BeforeEach
аннотация из библиотеки JUnit 5, которая указывает, что аннотированный метод должен выполняться перед каждым тестовым методом в текущем классе тестов. Это полезно для настройки условий или инициализации данных, необходимых для каждого теста.

@Embedded и @Embeddable
в JPA (Java Persistence API) используется для указания того, что класс представляет собой встраиваемый компонент, который может быть включен в другие сущности. Встраиваемые компоненты позволяют повторно использовать поля и логику в нескольких сущностях без дублирования кода.

import javax.persistence.Embeddable
@Embeddable
data class Address(
val street: String,
val city: String,
val country: String
)
import javax.persistence.*
@Entity
data class User(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
val name: String,
@Embedded
val address: Address
)

@Embeddable
указывает, что класс Address может быть встраиваемым компонентом в других сущностях.

@Embedded
используется в сущности User для указания, что объект Address должен быть включен как составная часть User.

@Secured
в Spring Security используется для ограничения доступа к методам на основе ролей. В отличие от аннотации @PreAuthorize, которая использует выражения SpEL (Spring Expression Language) для проверки условий, @Secured ограничивается только проверкой ролей. Эта аннотация является более простой и удобной, если нужно просто проверять наличие определенных ролей у пользователя.

Основные возможности @Secured:

Проверка ролей:
Позволяет указать, какие роли необходимы для доступа к методу.

Простота использования:
Используется для простой проверки наличия одной или нескольких ролей у пользователя.

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Secured

Пример использования @Secured:

Рассмотрим пример использования @Secured для ограничения доступа к методам на основе ролей.
Включение поддержки аннотаций безопасност
Чтобы @Secured работала, необходимо включить поддержку аннотаций метода безопасности в конфигурации безопасности.

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
class SecurityConfig : WebSecurityConfigurerAdapter() {}

Создание сервиса с ограничением доступа:
В этом примере метод adminMethod доступен только пользователям с ролью ADMIN, метод userMethod доступен только пользователям с ролью USER, а метод userOrAdminMethod доступен как пользователям с ролью USER, так и пользователям с ролью ADMIN.

import org.springframework.security.access.annotation.Secured
import org.springframework.stereotype.Service
@Service
class UserService {
@Secured("ROLE_ADMIN")
fun adminMethod() {
println("Admin method accessed")
}
@Secured("ROLE_USER")
fun userMethod() {
println("User method accessed")
}
@Secured("ROLE_USER", "ROLE_ADMIN")
fun userOrAdminMethod() {
println("User or Admin method accessed")
}
}

Различия между @PreAuthorize и @Secured:
Аннотации @PreAuthorize и @Secured в Spring Security имеют схожую цель — ограничивать доступ к методам на основе условий безопасности. Однако они различаются по функциональности, гибкости и способу использования.

@Secured
Простота использования: @Secured используется для проверки наличия одной или нескольких ролей у текущего пользователя.
Поддержка ролей: Проверяет только роли и не поддерживает более сложные условия.
Меньше возможностей: Поддерживает только простые проверки ролей, что делает её менее гибкой.
Анотации: Работает с простыми списками строковых значений, представляющих роли.

@PreAuthorize
Гибкость: @PreAuthorize использует выражения SpEL (Spring Expression Language) для проверки условий безопасности, что делает её более гибкой.
Поддержка сложных условий: Может проверять не только роли, но и другие атрибуты, такие как имя пользователя, параметры метода и т.д.
Широкие возможности: Поддерживает сложные логические выражения и позволяет проверять различные атрибуты безопасности.
Способность к настройке: Может использоваться для настройки более сложных условий доступа.

@Column в JPA
используется в Java Persistence API (JPA) для указания отображения поля сущности на столбец таблицы в базе данных. Эта аннотация предоставляет гибкость в конфигурировании соответствия полей класса и столбцов базы данных, позволяя указать имя столбца, его тип, ограничение на уникальность и другие атрибуты.

Основные атрибуты аннотации @Column:

name:
Имя столбца в таблице. Если не указано, используется имя поля.

nullable:
Указывает, может ли столбец содержать NULL значения. По умолчанию true.

unique:
Указывает, должно ли значение столбца быть уникальным. По умолчанию false.

length:
Длина столбца. Применимо только к строковым типам данных. По умолчанию 255.

precision:
Точность для числовых столбцов (общее количество цифр). Применимо к типам BigDecimal и BigInteger.

scale:
Масштаб для числовых столбцов (количество цифр после запятой). Применимо к типам BigDecimal и BigInteger.

insertable:
Указывает, должен ли столбец участвовать в SQL-операциях вставки. По умолчанию true.

updatable:
Указывает, должен ли столбец участвовать в SQL-операциях обновления. По умолчанию true.

columnDefinition:
SQL выражение, которое определяет тип данных столбца.

table:
Имя таблицы, если оно отличается от таблицы по умолчанию.

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
@Entity
data class Person(
@Id @GeneratedValue(strategy = GenerationType.AUTO)
val id: Long? = null,
@Column(name = "person_name", nullable = false, length = 100)
val name: String,
@Column(name = "person_age", nullable = false)
val age: Int,
@Column(name = "email", unique = true, length = 150)
val email: String?
)

Поле name отображается на столбец person_name, который не может быть NULL и имеет максимальную длину 100 символов.
Поле age отображается на столбец person_age, который не может быть NULL.
Поле email отображается на столбец email, который должен быть уникальным и имеет максимальную длину 150 символов.

@PostConstruct
это аннотация, предоставляемая пакетом javax.annotation, которая используется для обозначения метода, который должен быть выполнен после завершения инициализации зависимостей бина и до того, как бин будет доступен для использования.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface PostConstruct
import org.springframework.stereotype.Service
import javax.annotation.PostConstruct
@Service
class ExampleService {
private lateinit var someData: String
@PostConstruct
fun init() {
// Инициализация данных после создания бина
someData = "Инициализированные данные"
println("ExampleService инициализирован с данными: $someData")
}
fun getData(): String {
return someData
}
}

@PreDestroy
аннотация из Java Specification Request (JSR) 250, которая используется в Java EE и Spring для указания метода, который должен быть выполнен перед уничтожением бина. Эта аннотация обычно применяется к методу, который выполняет операции очистки, такие как освобождение ресурсов, закрытие соединений и другие завершающие действия.

Пример использования @PreDestroy в Spring:

В Spring, бины могут управляться контейнером Spring, и иногда необходимо выполнить некоторые действия перед уничтожением бина. Это может быть полезно для завершения соединений с базой данных, закрытия файловых потоков и других операций очистки.

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class AppConfig {
@Bean
fun exampleBean(): ExampleBean {
return ExampleBean()
}
}
import javax.annotation.PreDestroy
class ExampleBean {
fun start() {
println("Bean is starting")
}
@PreDestroy
fun cleanup() {
println("Bean is being destroyed")
// Здесь можно освободить ресурсы
}
}
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.ConfigurableApplicationContext
@SpringBootApplication
class Application
fun main(args: Array<String>) {
val context: ConfigurableApplicationContext = SpringApplication.run(Application::class.java, *args)
val bean = context.getBean(ExampleBean::class.java)
bean.start()
// Приложение закрывается и Spring вызывает метод cleanup()
context.close()
}

Как это работает:

Инициализация:
Когда Spring контейнер создает бин ExampleBean, он вызывает метод start, если это предусмотрено в коде.

Уничтожение:
Когда приложение закрывается (например, при вызове context.close()), Spring контейнер вызывает метод cleanup, помеченный аннотацией @PreDestroy.

Важные замечания:
Метод, помеченный @PreDestroy, не должен принимать аргументов и не должен возвращать значение.
Он должен быть публичным или защищенным, чтобы быть доступным контейнеру Spring.
Аннотация @PreDestroy может использоваться как с компонентами Spring, так и с бинами, определенными в конфигурационных файлах.

@EnableJdbcRepositories
Используется в Spring Data для включения поддержки репозиториев JDBC. Она автоматически настраивает инфраструктуру Spring Data JDBC и сканирует пакет на наличие интерфейсов репозиториев, которые будут превращены в реализации на основе JDBC.

Основные функции @EnableJdbcRepositories:

Автоматическая настройка репозиториев JDBC:
Включает механизм репозиториев Spring Data JDBC. Автоматически настраивает необходимые бины для работы с JDBC.

Сканирование пакетов:
Сканирует указанные пакеты на наличие интерфейсов репозиториев и создает их реализации.

Настройка бинов инфраструктуры:
Настраивает JdbcTemplate, DataSource и другие необходимые компоненты для работы с базой данных.

@Transient
используется в контексте Java и некоторых фреймворков для указания, что определенное поле не должно быть сериализовано или сохранено в базу данных. Она может применяться в разных контекстах, таких как JPA (Java Persistence API) и стандартная сериализация Java. Рассмотрим подробнее использование этой аннотации в каждом из этих контекстов.

@Transient в JPA
В JPA аннотация @Transient указывает, что поле не должно быть персистентным, то есть не должно сохраняться в базу данных. Это полезно для временных или вычисляемых полей, которые не требуют хранения в базе данных.

@Transient в стандартной сериализации Java
В контексте стандартной сериализации Java аннотация @Transient указывает, что поле не должно быть сериализовано, когда объект сериализуется.

@MappedCollection
используется в Spring Data JDBC для обозначения коллекции связанных сущностей в основной сущности. Она позволяет указывать, что поле в основной сущности должно быть отображено на соответствующие записи в связанной таблице. Это полезно для моделирования отношений “один-ко-многим” и “многие-ко-многим” в базах данных с использованием Spring Data JDBC.

Основные особенности @MappedCollection:

Коллекции связанных сущностей:
Указывает, что поле является коллекцией связанных сущностей.

Поддержка сложных структур данных:
Позволяет легко работать с коллекциями внутри основной сущности.

Сопоставление с внешними ключами:
Автоматически управляет внешними ключами для связанных сущностей.

import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.MappedCollection
import org.springframework.data.relational.core.mapping.Table
@Table("users")
data class User(
@Id
val id: Long? = null,
val email: String,
val username: String,
@MappedCollection(idColumn = "user_id")
val addresses: List<Address> = listOf()
)
@Table("addresses")
data class Address(
val street: String,
val city: String,
val country: String
)

User:
Основная сущность, аннотированная @Table(“users”).

Address:
Связанная сущность, аннотированная @Table(“addresses”).

@MappedCollection
Указывает, что addresses является коллекцией связанных сущностей Address. Атрибут idColumn указывает на столбец внешнего ключа в таблице addresses

@PayloadRoot
используется в Spring Web Services для определения метода-обработчика в классе, который будет обрабатывать входящие SOAP-сообщения с определенным корневым элементом в теле (payload) сообщения

@Endpoint
используется для обозначения класса как Spring Web Service Endpoint (конечная точка веб-сервиса)

@RequestPayload
указывает, что параметр метода (request) должен быть привязан к телу входящего SOAP-запроса

@ResponsePayload
Аннотация указывает, что возвращаемое значение метода должно быть преобразовано в тело SOAP-ответа

@XmlRootElement
используется для указания, что класс представляет собой корневой элемент XML-документа. Эта аннотация применяется в контексте Java Architecture for XML Binding (JAXB) и позволяет объекту быть маршалированным (преобразованным в XML) и демаршалированным (преобразованным из XML) как корневой элемент
В контексте Spring Web Services @XmlRootElement используется для аннотирования классов, которые будут автоматически маршалироваться и демаршалироваться в качестве XML сообщений.

Основные аспекты @XmlRootElement:

Пространство имен (namespace):
Пространство имен XML (namespace) может быть указано с помощью атрибута namespace.

Локальное имя (name):
Локальное имя корневого элемента может быть указано с помощью атрибута name.

<HelloRequest xmlns="http://example.com/helloworld">
<name>John</name>
</HelloRequest>
import javax.xml.bind.annotation.XmlRootElement
@XmlRootElement(
namespace = "http://example.com/helloworld",
name = "HelloRequest"
)
data class HelloRequest(
var name: String = ""
)

@SpringBootApplication
является комбинацией трех аннотаций: @Configuration, @EnableAutoConfiguration и @ComponentScan. Она обычно используется для обозначения основного класса конфигурации приложения Spring Boot.
Рекомендуется иметь только один класс с аннотацией @SpringBootApplication, который будет главным классом конфигурации для всего приложения. Если необходимо разделить конфигурацию на несколько частей, лучше использовать другие аннотации, такие как @Configuration, для создания отдельных конфигурационных классов

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)

Также- application.properties

my.property=Hello, Spring!
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update

Чем @Component отличается от @Service

В Spring Framework, аннотации @Component и @Service используются для обозначения классов в качестве бинов Spring. Хотя технически они функционируют одинаково и имеют одинаковую роль в процессе создания и управления бинами, их использование связано с различными семантическими значениями, что помогает лучше организовать и документировать ваш код. Вот основные различия:

@Component:
Используется для общего назначения, когда класс не подходит под другие специализированные аннотации (@Service, @Repository, @Controller).

@Service:
Используется для сервисных классов, которые содержат бизнес-логику.

Чтение и поддержка кода:
Использование специализированных аннотаций, таких как @Service, помогает лучше организовать код и сделать его более понятным для других разработчиков. Они сразу понимают, что этот класс используется для бизнес-логики.

Применение аспектов (AOP):
В некоторых случаях специализированные аннотации, такие как @Service, могут быть использованы для применения аспектов (AOP) более специфичным образом.

Чем @Component отличается от @Bean

@Component и @Bean — это два различных способа определения бинов (компонентов) в Spring, но они используются в разных контекстах и имеют свои особенности.

@Component:
аннотация, которая используется для маркировки класса как компонента Spring.

Используется в сочетании с механизмом сканирования классов (component scanning). Spring автоматически обнаруживает классы, аннотированные как @Component, и регистрирует их как бины в контексте приложения.

Также существуют специализированные версии @Component, такие как @Service, @Repository, и @Controller, которые делают то же самое, но добавляют семантическое значение для разработчиков и инструментария.

import org.springframework.stereotype.Component
@Component
class MyComponent {
fun doSomething() {
println("Doing something")
}
}

Использование @Component и @Autowired:

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.stereotype.Service
@Component
class MyComponent {
fun doSomething() {
println("Doing something")
}
}
@Service
class MyService(@Autowired private val myComponent: MyComponent) {
fun serve() {
myComponent.doSomething()
}
}

@Bean:
аннотация, которая используется для определения метода, который возвращает объект, который должен быть зарегистрирован как бин в контексте Spring.

Обычно используется в конфигурационных классах, аннотированных как @Configuration.

Позволяет явным образом создать и настроить бин с определенными параметрами или логикой инициализации.

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class MyConfig {
@Bean
fun myBean(): MyBean {
return MyBean()
}
}
class MyBean {
fun doSomething() {
println("Doing something")
}
}

Использование @Bean в конфигурационном классе:

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
class MyBean {
fun doSomething() {
println("Doing something")
}
}
class MyService(
private val myBean: MyBean
) {
fun serve() {
myBean.doSomething()
}
}
@Configuration
class MyConfig {
@Bean
fun myBean(): MyBean {
return MyBean()
}
@Bean
fun myService(myBean: MyBean): MyService {
return MyService(myBean)
}
}

Сравнение @Component и @Bean:

Механизм:

@Component:
используется в сочетании с механизмом сканирования классов (component scanning). Это означает, что Spring автоматически обнаруживает классы, аннотированные @Component, и регистрирует их как бины в контексте приложения.

@Bean:
используется для определения метода, который возвращает объект, который должен быть зарегистрирован как бин в контексте Spring. Обычно эта аннотация применяется в конфигурационных классах, аннотированных @Configuration.

Использование:

@Component:
применяется к классу, чтобы автоматически обнаружить и зарегистрировать его как бин.

@Bean:
используется для создания и настройки бинов через методы, что позволяет более явным образом контролировать процесс создания бинов.

Контекст:

Класс, аннотированный @Component, автоматически становится бином в контексте Spring.

Метод в конфигурационном классе, аннотированный @Bean, возвращает объект, который становится бином в контексте Spring.

Дополнительные аннотации:

Для @Component существуют специализированные версии, такие как @Service, @Repository, и @Controller. Эти аннотации делают то же самое, что и @Component, но добавляют семантическое значение для разработчиков и инструментария.

Для @Bean нет дополнительных аннотаций, но он используется в контексте конфигурационных классов для явного определения бинов.

Используйте @Component (или его специализированные версии) для классов, которые должны быть автоматически обнаружены и зарегистрированы как бины.

Используйте @Bean для явного определения бинов в конфигурационных классах, особенно когда нужна дополнительная настройка или логика инициализации.

Copyright: Roman Kryvolapov