Перейти к содержимому

Вопросы и ответы — Java / Spring Developer — Основные аннотации Spring

➤ Какие основные аннотации в Spring
➤ Чем @Component отличается от @Service
➤ Чем @Component отличается от @Bean

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 ""
    }
    
}

@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(name = “users”):
Определяет имя таблицы, с которой будет связана сущность. В данном случае, таблица называется users.

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

@Column(name = “user_id”):
Указывает имя столбца в таблице, который будет сопоставлен с полем userId.

@Column:
Настройки для столбца таблицы, такие как имя, уникальность, nullable и т.д.

@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(
          "[email protected]", 
          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
используется в 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

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

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

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

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

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

@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