Java Spring – Анотации на Spring
Кои са основните анотации в Spring
Annotation ├── Component │ ├── Repository │ ├── Service │ ├── Controller │ │ └── RestController ├── Configuration │ └── EnableAutoConfiguration ├── SpringBootApplication │ └── Configuration │ └── EnableAutoConfiguration ├── Conditional │ ├── ConditionalOnBean │ ├── ConditionalOnMissingBean │ ├── ConditionalOnClass │ ├── ConditionalOnMissingClass │ ├── ConditionalOnProperty │ ├── ConditionalOnResource │ ├── ConditionalOnWebApplication │ └── ConditionalOnNotWebApplication ├── Enable │ ├── EnableAspectJAutoProxy │ ├── EnableCaching │ ├── EnableScheduling │ └── EnableAsync ├── Transactional ├── Bean ├── Qualifier ├── Autowired ├── Value ├── Primary ├── Lazy ├── Scope
@Configuration
показва, че класът съдържа дефиниции на бийнове и трябва да бъде обработен от контейнера на Spring, за да генерира бийновете на Spring.
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Configurationimport org.springframework.context.annotation.Beanimport org.springframework.context.annotation.Configuration
@Configurationclass 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)@Documentedpublic @interface Beanimport org.springframework.context.annotation.Beanimport org.springframework.context.annotation.Configuration
@Configurationclass AppConfig {
@Bean fun myBean(): MyBean { return MyBean() }
}
@Component
показва, че класът е компонент на Spring, което позволява на Spring автоматично да го открива, за да го управлява в контейнера.
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Indexedpublic @interface Componentimport org.springframework.stereotype.Component
@Componentclass MyComponent {
fun doWork() { println("Component is working...") }
}
@Service
специализирана анотация, която се използва за класове от сервизния слой. Тя също е разновидност на @Component.
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Serviceimport org.springframework.stereotype.Service
@Serviceclass MyService {
fun performService() { println("Service is performing...") }
}
@Repository
специализирана анотация за класове за достъп до данни (DAO). Тя също е разновидност на @Component.
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Repositoryimport org.springframework.data.jpa.repository.JpaRepositoryimport org.springframework.stereotype.Repository
@Repositoryinterface UserRepository : JpaRepository<User, Long> {
fun findByUsername( username: String ): User?
}
@Controller
показва, че класът е контролер на Spring MVC. Тази анотация се използва за обработка на уеб заявки.
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Controllerimport org.springframework.stereotype.Controllerimport org.springframework.ui.Modelimport org.springframework.web.bind.annotation.GetMappingimport 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@ResponseBodypublic @interface RestControllerimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.RequestMappingimport org.springframework.web.bind.annotation.RestController
@RestController@RequestMapping("/api")class ApiController {
@GetMapping("/greeting") fun greeting(): String { return "Hello, API World!" }
}
@RestControllerAdvice
се използва за глобална обработка на изключения и за предоставяне на съвети (advice) на контролерите, които работят с REST API. Това е разширение на анотацията @ControllerAdvice, което автоматично включва @ResponseBody, затова е подходяща за обработка на изключения в REST контролерите, като връща отговори във формат JSON или XML.
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@ControllerAdvice@ResponseBodypublic @interface RestControllerAdviceimport org.springframework.http.HttpStatusimport org.springframework.http.ResponseEntityimport org.springframework.web.bind.annotation.ExceptionHandlerimport org.springframework.web.bind.annotation.RestControllerAdvice
class UserNotFoundException( message: String) : RuntimeException(message)
data class ErrorResponse( val message: String, val details: String)
@RestControllerAdviceclass 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.GetMappingimport org.springframework.web.bind.annotation.PathVariableimport 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)@Documentedpublic @interface ResponseBody@Controllerclass PersonController {
@GetMapping("/persons/{id}") @ResponseBody fun getData( @PathVariable id: Long ): String { return "" }
}
// същото като
@RestControllerclass PersonController {
@GetMapping("/persons/{id}") fun getData( @PathVariable id: Long ): String{ return "" }
}
@Aspect
начин да се изнесе повтаряща се логика (например логване или проверка на сигурността) от методите в отделен клас. Тя автоматично се прикачва към нужните методи и изпълнява нужните действия преди, след или около тях. Това е удобно, защото не се налага един и същ код да се дублира на много места.
@Before
показва, че методът-съвет (advice) трябва да се изпълни преди извикването на целевия метод. Използва се например за логване на влизането в метода или за проверка на правата за достъп.
@After
показва, че методът-съвет трябва да се изпълни след приключването на целевия метод – независимо дали е завършил успешно, или с изключение. Подходящ е за освобождаване на ресурси или за логване на излизането.
@Around
позволява напълно да обвиете извикването на целевия метод: изпълнявате код преди него, извиквате самия метод чрез proceed(), а после изпълнявате код след него. Дава пълен контрол над изпълнението – може да промените аргументите, резултата или дори изобщо да не извиквате самия метод.
@Pointcut
описва към кои методи да се прилага аспектът (по сигнатури, анотации и т.н.)
@Aspect@Componentclass 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
се използва за настройка на свързващите средства за данни (data binders) на ниво контролер. Те обработват данните, които идват от HTTP заявките, и ги преобразуват в обекти на модела. Анотацията @InitBinder позволява да регистрирате собствени редактори на свойства или валидатори, които да обработват данните, преди да бъдат свързани с обектите на модела.
@CookieValue
се използва за обвързване на стойност от HTTP cookie с параметър на метод в контролера. Така получавате стойностите на cookie директно в методите на контролера, което опростява работата с тях.
import org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.CookieValueimport org.springframework.web.bind.annotation.RequestMappingimport 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)@Documentedpublic @interface Autowiredimport org.springframework.beans.factory.annotation.Autowiredimport org.springframework.stereotype.Service
@Serviceclass MainService @Autowired constructor( private val myComponent: MyComponent) {
fun execute() { myComponent.doWork() }
}
@Transactional
се използва за управление на транзакции. Може да се прилага към методи или класове, за да се започват, комитват или връщат автоматично транзакции.
Механизмът на работа на @Transactional в Spring се основава на прокси и на аспектно-ориентираното програмиране за управление на транзакциите. Проксито прихваща извикванията на методите и взаимодейства с мениджъра на транзакциите, за да започне, потвърди или върне транзакция. Така транзакциите се обработват автоматично, което опростява разработката на надеждни и съгласувани приложения.
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documented@Reflectivepublic @interface Transactionalimport org.springframework.stereotype.Serviceimport org.springframework.transaction.annotation.Transactional
@Serviceclass 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.Beanimport org.springframework.context.annotation.Configurationimport org.springframework.transaction.annotation.EnableTransactionManagementimport org.springframework.transaction.annotation.TransactionManagementConfigurerimport org.springframework.transaction.PlatformTransactionManagerimport org.springframework.transaction.annotation.AnnotationTransactionAttributeSourceimport org.springframework.transaction.interceptor.TransactionInterceptorimport org.springframework.transaction.interceptor.TransactionProxyFactoryBeanimport javax.sql.DataSource
@Configuration@EnableTransactionManagementclass 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)@Documentedpublic @interface RequestPartimport org.springframework.web.bind.annotation.PostMappingimport org.springframework.web.bind.annotation.RequestMappingimport org.springframework.web.bind.annotation.RequestPartimport org.springframework.web.bind.annotation.RestControllerimport org.springframework.web.multipart.MultipartFileimport java.nio.file.Filesimport 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.PostMappingimport org.springframework.web.bind.annotation.RequestMappingimport org.springframework.web.bind.annotation.RequestPartimport org.springframework.web.bind.annotation.RestControllerimport 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)@Documentedpublic @interface Valueimport org.springframework.beans.factory.annotation.Valueimport org.springframework.context.annotation.Configuration
@Configurationclass AppConfig {
@Value("\${my.property}") lateinit var myProperty: String
fun showProperty() { println("Property value: $myProperty") }
}
@RequestMapping
добавя пред url на метода това, което е подадено в нея
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@Mapping@Reflective({ControllerMappingReflectiveProcessor.class})public @interface RequestMappingimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.RequestMappingimport 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)@Documentedpublic @interface RequestParamimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.PathVariableimport org.springframework.web.bind.annotation.RequestParamimport 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)@Documentedpublic @interface RequestBodyimport org.springframework.web.bind.annotation.PostMappingimport org.springframework.web.bind.annotation.RequestBodyimport org.springframework.web.bind.annotation.RequestMappingimport 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)@Documentedpublic @interface SessionAttributeimport org.springframework.stereotype.Controllerimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.RequestMappingimport org.springframework.web.bind.annotation.RequestParamimport org.springframework.web.bind.annotation.SessionAttributesimport org.springframework.web.bind.support.SessionStatusimport 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.Controllerimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.SessionAttribute
@Controllerclass 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@Documentedpublic @interface SessionAttributesimport org.springframework.stereotype.Controllerimport org.springframework.ui.Modelimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.ModelAttributeimport org.springframework.web.bind.annotation.PostMappingimport org.springframework.web.bind.annotation.RequestMappingimport org.springframework.web.bind.annotation.SessionAttributesimport 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)@Documentedpublic @interface ResponseStatusimport org.springframework.http.HttpStatusimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.RequestMappingimport org.springframework.web.bind.annotation.ResponseStatusimport 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)@Documentedpublic @interface RequestHeaderimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.RequestHeaderimport org.springframework.web.bind.annotation.RequestMappingimport 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.GetMappingimport org.springframework.web.bind.annotation.RequestHeaderimport org.springframework.web.bind.annotation.RequestMappingimport 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.GetMappingimport org.springframework.web.bind.annotation.RequestHeaderimport org.springframework.web.bind.annotation.RequestMappingimport 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)@Documentedpublic @interface RequestAttributeimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.RequestAttributeimport org.springframework.web.bind.annotation.RequestMappingimport 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.Filterimport javax.servlet.FilterChainimport javax.servlet.FilterConfigimport javax.servlet.ServletRequestimport javax.servlet.ServletResponseimport org.springframework.stereotype.Component
@Componentclass 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.HttpStatusimport org.springframework.http.ResponseEntityimport org.springframework.web.bind.annotation.ExceptionHandlerimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.RequestMappingimport 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
разрешава междудомейнни (cross-origin) заявки към метод или клас.
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface CrossOriginimport org.springframework.web.bind.annotation.CrossOriginimport org.springframework.web.bind.annotation.GetMappingimport org.springframework.web.bind.annotation.RequestMappingimport 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@Documentedpublic @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.Autowiredimport org.springframework.beans.factory.annotation.Qualifierimport org.springframework.stereotype.Component
@Componentclass 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)@Documentedpublic @interface Primary@Component@Primaryclass DefaultAnimal : Animal {
override fun speak() { println("Default sound!") }
}
@ImportResource
се използва за зареждане на XML конфигурация в контекста на приложението. Това е полезно, ако имате съществуващи XML файлове с конфигурация на Spring, които искате да импортирате в конфигурация, основана на анотации или на Java код.
Пример за използване:
Да предположим, че имате 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.Configurationimport 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 ComponentScanimport org.springframework.boot.autoconfigure.SpringBootApplicationimport 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.Entityimport javax.persistence.Idimport javax.persistence.Tableimport 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.SpringBootApplicationimport 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.Idimport 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.Idimport org.springframework.data.mongodb.core.index.Indexedimport org.springframework.data.mongodb.core.mapping.Documentimport 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.Idimport 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.PrimaryKeyColumnimport org.springframework.data.cassandra.core.mapping.Tableimport org.springframework.data.cassandra.core.cql.PrimaryKeyTypeimport 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 са съставни ключове за разделяне (partition key), а 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.PrimaryKeyTypeimport org.springframework.data.cassandra.core.cql.Orderingimport org.springframework.data.cassandra.core.mapping.PrimaryKeyColumnimport org.springframework.data.cassandra.core.mapping.Table
@Tabledata 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.Idimport org.springframework.data.mongodb.core.mapping.Documentimport 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.Idimport org.springframework.data.mongodb.core.index.QueryIndexedimport 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@Documentedpublic @interface PreAuthorizeПример за използване на @PreAuthorize:
Нека разгледаме пример за използване на @PreAuthorize за ограничаване на достъпа до методи въз основа на роли.
Създаване на услуга с ограничен достъп. В този пример методът adminMethod е достъпен само за потребители с роля ADMIN, методът userMethod е достъпен само за потребители с роля USER, а методът userOrAdminMethod е достъпен както за потребители с роля USER, така и за потребители с роля ADMIN.
import org.springframework.security.access.prepost.PreAuthorizeimport org.springframework.stereotype.Service
@Serviceclass 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.assertEqualsimport org.junit.jupiter.api.Assertions.assertNotNullimport org.junit.jupiter.api.Testimport org.junit.jupiter.api.extension.ExtendWithimport org.springframework.beans.factory.annotation.Autowiredimport org.springframework.boot.test.context.SpringBootTestimport org.springframework.test.context.DynamicPropertyRegistryimport org.springframework.test.context.DynamicPropertySourceimport org.springframework.test.context.junit.jupiter.SpringExtensionimport org.testcontainers.containers.PostgreSQLContainerimport org.testcontainers.junit.jupiter.Containerimport org.testcontainers.junit.jupiter.Testcontainers
@ExtendWith(SpringExtension::class)@SpringBootTest@Testcontainersclass 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( username = "testuser" ) val savedUser = userService.createUser(user) assertNotNull(savedUser.id) assertEquals( savedUser.email ) assertEquals( "testuser", savedUser.username ) }
@Test fun `test getAllUsers`() { val user1 = User( username = "testuser1" ) val user2 = User( 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
@Embeddabledata class Address( val street: String, val city: String, val country: String)import javax.persistence.*
@Entitydata 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@Documentedpublic @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.Securedimport org.springframework.stereotype.Service
@Serviceclass 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.Columnimport javax.persistence.Entityimport javax.persistence.GeneratedValueimport javax.persistence.GenerationTypeimport javax.persistence.Id
@Entitydata 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.Serviceimport javax.annotation.PostConstruct
@Serviceclass 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.Beanimport org.springframework.context.annotation.Configuration
@Configurationclass 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.SpringApplicationimport org.springframework.boot.autoconfigure.SpringBootApplicationimport org.springframework.context.ConfigurableApplicationContext
@SpringBootApplicationclass 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, не трябва да приема аргументи и не трябва да връща стойност.
Той трябва да е публичен или защитен (protected), за да е достъпен за контейнера на 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.Idimport org.springframework.data.relational.core.mapping.MappedCollectionimport 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.SpringBootApplicationimport org.springframework.boot.runApplication
@SpringBootApplicationclass DemoApplication
fun main(args: Array<String>) { runApplication<DemoApplication>(*args)}Също така – application.properties
my.property=Hello, Spring!spring.datasource.url=jdbc:h2:mem:testdbspring.datasource.driverClassName=org.h2.Driverspring.datasource.username=saspring.datasource.password=passwordspring.jpa.database-platform=org.hibernate.dialect.H2Dialectspring.h2.console.enabled=truespring.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
@Componentclass MyComponent {
fun doSomething() { println("Doing something") }
}Използване на @Component и @Autowired:
import org.springframework.beans.factory.annotation.Autowiredimport org.springframework.stereotype.Componentimport org.springframework.stereotype.Service
@Componentclass MyComponent {
fun doSomething() { println("Doing something") }
}
@Serviceclass MyService(@Autowired private val myComponent: MyComponent) {
fun serve() { myComponent.doSomething() }
}@Bean:
анотация, която се използва за определяне на метод, който връща обект, който трябва да бъде регистриран като бийн в контекста на Spring.
Обикновено се използва в конфигурационни класове, анотирани като @Configuration.
Позволява изрично да се създаде и настрои бийн с определени параметри или логика за инициализация.
import org.springframework.context.annotation.Beanimport org.springframework.context.annotation.Configuration
@Configurationclass MyConfig {
@Bean fun myBean(): MyBean { return MyBean() }
}
class MyBean {
fun doSomething() { println("Doing something") }
}Използване на @Bean в конфигурационен клас:
import org.springframework.context.annotation.Beanimport org.springframework.context.annotation.Configuration
class MyBean {
fun doSomething() { println("Doing something") }
}
class MyService( private val myBean: MyBean) {
fun serve() { myBean.doSomething() }
}
@Configurationclass 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 за изрично определяне на бийнове в конфигурационни класове, особено когато е нужна допълнителна настройка или логика за инициализация.