В этой статье:
➤ Какие основные аннотации в Spring
➤ Чем @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 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
➤ Чем @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 для явного определения бинов в конфигурационных классах, особенно когда нужна дополнительная настройка или логика инициализации.