Открыть все вопросы по Spring
В этой статье:
➤ Какие основные аннотации в Spring
➤ @Configuration
➤ @Bean
➤ @Component
➤ @Service
➤ @Repository
➤ @Controller
➤ @RestController
➤ @RestControllerAdvice
➤ @ResponseBody
➤ @Aspect
➤ @Before
➤ @After
➤ @Around
➤ @Pointcut
➤ @ModelAttribute
➤ @MatrixVariable
➤ @InitBinder
➤ @CookieValue
➤ @Autowired
➤ @Transactional
➤ @EnableTransactionManagement
➤ @RequestPart
➤ @Value
➤ @RequestMapping
➤ @GetMapping-и-@PostMapping
➤ @RequestParam
➤ @RequestBody
➤ @SessionAttribute
➤ @SessionAttributes
➤ @ResponseStatus
➤ @RequestHeader
➤ @RequestAttribute
➤ @ExceptionHandler
➤ @CrossOrigin
➤ @Qualifier
➤ @Primary
➤ @ImportResource
➤ @ComponentScan
➤ @Entity
➤ @Table
➤ @Id
➤ @Column
➤ @OneToMany,-@ManyToOne,-@OneToOne,-@ManyToMany
➤ @EntityScan
➤ @Document-в-MongoDB
➤ @Document-в-Elasticsearch
➤ @PrimaryKeyColumn
➤ @Unwrapped
➤ @Unwrapped(prefix = “address_”)
➤ @QueryIndexed
➤ @PreAuthorize
➤ @DataMongoTest
➤ @DynamicPropertySource
➤ @BeforeEach
➤ @Embeddable
➤ @Embedded
➤ @Secured
➤ @Column-в-JPA
➤ @PostConstruct
➤ @Transient-в-JPA
➤ @Transient-в-стандартной-сериализации-Java
➤ @MappedCollection
➤ @PayloadRoot
➤ @Endpoint
➤ @RequestPayload
➤ @ResponsePayload
➤ @XmlRootElement
➤ @SpringBootApplication
➤ Чем @Component отличается от @Service
➤ Чем @Component отличается от @Bean
➤ Какие основные аннотации в 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 Beanimport 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 RestControllerimport 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 RestControllerAdviceimport 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 Autowiredimport 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 Transactionalimport 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 RequestPartimport 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 Valueimport 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 RequestMappingimport 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 RequestParamimport 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 RequestBodyimport 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 SessionAttributeimport 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 SessionAttributesimport 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 ResponseStatusimport 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 RequestHeaderimport 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 RequestAttributeimport 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 ExceptionHandlerimport 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 CrossOriginimport 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 Qualifierinterface 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 EntityScanimport 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 в 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 Columnimport 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 PostConstructimport 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 SpringBootApplicationimport 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 для явного определения бинов в конфигурационных классах, особенно когда нужна дополнительная настройка или логика инициализации.