Open all questions about Spring
In this article:
➤ What are the main annotations in Spring
➤ @Configuration
➤ @Bean
➤ @Component
➤ @Service
➤ @Repository
➤ @Controller
➤ @RestController
➤ @RestControllerAdvice
➤ @ResponseBody
➤ @Aspect
➤ @Before
➤ @After
➤ @Around
➤ @Pointcut
➤ @ModelAttribute
➤ @MatrixVariable
➤ @InitBinder
➤ @CookieValue
➤ @Autowired
➤ @Transactional
➤ @EnableTransactionManagement
➤ @RequestPart
➤ @Value
➤ @RequestMapping
➤ @GetMapping-and-@PostMapping
➤ @RequestParam
➤ @RequestBody
➤ @SessionAttribute
➤ @SessionAttributes
➤ @ResponseStatus
➤ @RequestHeader
➤ @RequestAttribute
➤ @ExceptionHandler
➤ @CrossOrigin
➤ @Qualifier
➤ @Primary
➤ @ImportResource
➤ @ComponentScan
➤ @Entity
➤ @Table
➤ @Id
➤ @Column
@OneToMany, @ManyToOne, @OneToOne, @ManyToMany
➤ @EntityScan
➤ @Document in MongoDB
➤ @Document in Elasticsearch
➤ @PrimaryKeyColumn
➤ @Unwrapped
➤ @Unwrapped(prefix = “address_”)
➤ @QueryIndexed
➤ @PreAuthorize
➤ @DataMongoTest
➤ @DynamicPropertySource
➤ @BeforeEach
➤ @Embeddable
➤ @Embedded
➤ @Secured
➤ @Column in JPA
➤ @PostConstruct
➤ @Transient in JPA
➤ @Transient in standard Java serialization
➤ @MappedCollection
➤ @PayloadRoot
➤ @Endpoint
➤ @RequestPayload
➤ @ResponsePayload
➤ @XmlRootElement
➤ @SpringBootApplication
➤ How @Component differs from @Service
➤ How is @Component different from @Bean
➤ What are the main annotations in 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
Specifies that the class contains bean definitions and should be processed by the Spring container to generate Spring beans.
@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
Specifies that the method produces a bean managed by the Spring container.
@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
Specifies that the class is a Spring bean, which allows Spring to automatically detect it for management in the container.
@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
a specialized annotation used for service-layer classes. It is also a variant of @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
a specialized annotation for data access object (DAO) classes. It is also a variant of @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
Specifies that the class is a Spring MVC controller. This annotation is used to handle web requests.
@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
is a combination of @Controller and @ResponseBody. It specifies that the class is a RESTful controller and the returned data should be directly written in the HTTP response in JSON or XML format.
@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
is used to globally handle exceptions and provide advice to controllers working with REST APIs. It is an extension of the @ControllerAdvice annotation, which automatically includes @ResponseBody, making it suitable for handling exceptions in REST controllers returning responses in JSON or XML format.
@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 ) } }
// Controller throwing an exception 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" ) } }
Example of exception handling using @RestControllerAdvice:
When requesting a user with an ID that does not exist, the server will return a JSON response with HTTP status 404 (Not Found) and a body:
{ "message": "User with ID 3 not found", "details": "The user you are looking for does not exist." }
If a general exception occurs, the server will return a JSON response with HTTP status 500 (Internal Server Error) and a body:
{ "message": "An error occurred", "details": "Please contact support." }
@ResponseBody
automatically serializes any returned object into 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 "" } } // the same as @RestController class PersonController { @GetMapping("/persons/{id}") fun getData( @PathVariable id: Long ): String{ return "" } }
@Aspect
a way to move repetitive logic (such as logging or security checking) out of methods and into a separate class. It automatically hooks into the right methods and performs the right actions before, after, or around them. This is convenient because you don't have to duplicate the same code in too many places.
@Before
specifies that the advice method should be executed before the target method is called. Used, for example, to log entry into a method or check access rights.
@After
Specifies that the advice method should be executed after the target method completes, regardless of whether it completed successfully or with an exception. Suitable for resource cleanup or output logging.
@Around
allows you to completely wrap the call to the target method: execute the code before, call the method itself via proceed(), and then execute the code after. Gives full control over execution – you can change the arguments, the result, or even not call the method itself.
@Pointcut
describes which methods to apply the aspect to (by signatures, annotations, etc.)
@Aspect @Component class LoggingAspect { // Pointcut that matches all methods in the service package @Pointcut("execution(* com.example.service.*.*(..))") fun allServiceMethods() { } // Runs before the matched method @Before("allServiceMethods()") fun logBefore() { println("Method is about to be called") } // Runs after the matched method finishes @After("allServiceMethods()") fun logAfter() { println("Method has finished execution") } // Runs around the matched method: before and after @Around("allServiceMethods()") fun logAround(joinPoint: ProceedingJoinPoint): Any? { println("Before method: ${joinPoint.signature.name}") val result = joinPoint.proceed() println("After method: ${joinPoint.signature.name}") return result } }
@ModelAttribute
is used to bind model data to method parameters or controller method return values. It can be applied to both method parameters and methods themselves, providing a flexible mechanism for preparing and binding model data.
@MatrixVariable
is used in Spring to bind matrix variable values to method parameters in a controller. Matrix variables are parameters that are included in the path portion of a URL between semicolons (;). This annotation allows you to extract the values of matrix variables and use them in controller methods.
@InitBinder
is used to configure data binders at the controller level. These data binders process data coming from HTTP requests and transform it into model objects. The @InitBinder annotation allows you to register custom property editors or validators to process data before binding it to model objects.
@CookieValue
is used to bind a value from an HTTP cookie to a method parameter in a controller. This allows you to get cookie values directly in controller methods, making them easier to work with.
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
is used for automatic dependency injection. Spring automatically finds the appropriate bean and injects it into a field, constructor or method.
@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
is used to manage transactions. It can be applied to methods or classes to automatically start, commit, or roll back transactions.
Spring's @Transactional mechanism is based on the use of proxies and aspect-oriented programming to manage transactions. The proxy intercepts method calls and interacts with the transaction manager to start, commit, or roll back transactions. This allows for automatic transaction handling, making it easier to develop robust and consistent applications.
@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
is used in Spring to enable support for annotation-based transaction management. It automatically configures the necessary beans and proxies to handle methods annotated with @Transactional. Let's look at how it works and how to use it.
The main functions of @EnableTransactionManagement are:
Activating transaction support:
Includes the infrastructure needed to work with annotation-based transactions such as @Transactional.
Proxy:
Creates a proxy for beans that have methods annotated with @Transactional. The proxy intercepts calls to these methods and manages transactions.
Configuration:
The annotation automatically configures a TransactionManager that manages the initiation, commit, and rollback of transactions.
The main attributes of @EnableTransactionManagement are:
mode:
Determines whether interface-based (JDK) or class-based (CGLIB) proxying will be used. The default value is AdviceMode.PROXY.
proxyTargetClass:
If true, CGLIB proxying is used (default is false).
order:
Sets the order in which transactional aspects are executed.
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:
Used as a transaction manager to manage transactions at the JDBC level.
TransactionInterceptor:
Intercepts method calls and handles transactional logic.
TransactionProxyFactoryBean:
Creates a proxy for beans to intercept method calls and handle transactional logic.
@RequestPart
is used to bind a part of a multipart request to a parameter of a controller method. It allows processing of files and form data submitted using a form with the enctype=”multipart/form-data” attribute.
@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
used to embed values from a properties file into fields.
@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
adds what is passed to the method before its 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 and @PostMapping
are specialized versions of @RequestMapping to handle GET and POST requests respectively.
There is also @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
is used to extract query parameters whereas @PathVariable is used to extract path variables from 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
serializes a class to 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
is used to access HTTP session attributes in controller methods. This annotation allows you to easily get the value of a session attribute and use it in a controller method without interacting directly with the HttpSession object.
@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
is used to specify which model attributes should be stored in the HTTP session and available across multiple requests within a single user session. This is useful when you want to persist data across requests without having to manually manage the HttpSession object.
@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 { // Saving a user in a session 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
is used to specify the HTTP response status that should be returned from an annotated method or exception. This allows you to control the HTTP statuses returned to the client without having to manually set them in your controller code.
@ResponseStatus Main Parameters:
code (type: HttpStatus):
Specifies the HTTP status code to be returned. This is a required parameter.
reason (type: String):
Specifies the reason that will be returned along with the status code. This parameter is optional.
@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
is used to bind an HTTP request header value to a method parameter in a controller. This allows you to retrieve HTTP request header values directly in controller methods, making it easier to work with request headers.
@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
is used to bind the value of an HTTP request attribute to a method parameter in a controller. This allows you to retrieve request attribute values directly in controller methods, making it easier to work with attributes set in a request.
@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
used to define a method that will handle certain types of exceptions
@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
Allows cross-domain requests to a method or class.
@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
is used in Spring to resolve ambiguities in dependency injection. When a Spring container has multiple beans of the same type, @Qualifier allows you to specify which bean should be injected.
@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
if you have a bean that you want to use as default, you can annotate it with @Primary. Then @Qualifier will be used only for qualification. In this case, if @Qualifier is not used, the DefaultAnimal bean will be injected
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Primary
@Component @Primary class DefaultAnimal : Animal { override fun speak() { println("Default sound!") } }
@ImportResource
Used to load XML configuration into the application context. This is useful if you have existing Spring configuration XML files that you want to import into annotation-based or Java-based configuration.
Example of use:
Let's assume you have an XML configuration 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>
Now you want to import this configuration into your Spring Boot or other Java-based configuration class:
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
is used to automatically discover and register beans (components) in the Spring container. This includes classes annotated with annotations such as @Component, @Service, @Repository, @Controller, and others that indicate to Spring that these classes are beans that need to be managed.
@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
is used in JPA (Java Persistence API) to indicate that a class represents an entity and is mapped to a table in a relational database. It is part of ORM (Object-Relational Mapping), which allows developers to work with the database through object-oriented models.
Key aspects of the @Entity annotation:
Class as an entity:
The @Entity annotation specifies that this class is an entity and will be mapped to a table in the database.
Default table:
If the table name is not specified, it will be the same as the class name. However, this can be overridden using the @Table annotation.
Identification columns:
Each entity must have at least one field annotated as @Id to indicate the primary key.
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
Specifies that the User class is an entity.
@Table
Specifies the name of the table the entity will be associated with. In this case, the table is called users.
@Id
Indicates the primary key of an entity.
@Column
Specifies the name of the column in the table that will be mapped to the userId field.
@GeneratedValue
Used to specify the value generation strategy for the primary key (e.g. auto-increment).
@OneToMany, @ManyToOne, @OneToOne, @ManyToMany
Define relationships between entities.
Example with relationships:
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
is used to tell Spring where to look for JPA entities (classes annotated with @Entity). This is necessary when entities are located outside the standard Spring Boot scanning path, or if you want to specify a different package to scan for entities.
@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 in MongoDB
is used in Spring Data MongoDB to indicate that a class represents a document in a MongoDB collection. It is the equivalent of the @Entity annotation in JPA for relational databases. The @Document annotation specifies that a given class will be mapped to a collection in a MongoDB database.
The main attributes of the @Document annotation are:
collection:
The name of the MongoDB collection the document is associated with. If not specified, the collection name will be the same as the class name, but converted to lowercase.
language:
Specifies the language for full-text search (optional).
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”):
Specifies that the User class is mapped to the users collection in the MongoDB database.
@Id
Indicates the document identifier. In MongoDB, this field is usually called _id.
Additional annotations and attributes:
@Field
Used to specify the name of a field in a MongoDB document if it is different from the name of the field in the class.
@Indexed
Creates an index on a field.
@CompoundIndex
Creates a composite index on multiple fields.
@GeoSpatialIndexed
Creates a geospatial index on a field.
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”):
Specifies that the email field in the User class corresponds to the email field in the MongoDB document.
@Indexed(unique = true):
Creates a unique index on the email field, preventing duplicate values.
@Document in Elasticsearch
is used in Spring Data Elasticsearch to indicate that a class represents a document in an Elasticsearch index. This annotation allows you to customize the index and its properties, such as name, sharding and replication settings, and some other options.
The main attributes of the @Document annotation are:
indexName:
Specifies the name of the index in which the document will be stored.
shards:
Specifies the number of shards (default 5).
replicas:
Specifies the number of replicas (default 1).
refreshInterval:
Specifies the index update interval.
indexStoreType:
Specifies the index storage type (for example, “fs” or “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:
The name of the Elasticsearch index where the document will be stored (users).
shards:
Number of shards (3).
replicas:
Number of replicas (2).
@Table
is used in Spring Data to indicate that a class represents a database entity and is mapped to a table in the database. In the context of Spring Data Cassandra, this annotation is used to indicate that a class will be mapped to a table in a Cassandra database.
The main attributes of the @Table annotation are:
value:
The name of the table in the database. If not specified, the table name will be the same as the class name.
forceQuote:
Specifies whether table and column names should be enclosed in quotation marks.
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”):
Specifies that the User class is mapped to the users table in the Cassandra database.
@PrimaryKeyColumn:
Specifies which columns are part of the primary key.
Full description:
Class:
The User class is an entity that will be mapped to the users table in the Cassandra database.
Abstract @Table:
Specifies the name of the table with which the class is associated. If the table name is not specified, the class name is used.
Annotation @PrimaryKeyColumn:
Defines the primary key columns in the table. In this case, userId and region are composite partitioning keys, and email and username are clustering keys with ascending sort order.
@PrimaryKeyColumn
is used in Spring Data Cassandra to define the primary key columns in Cassandra tables. It is part of the API that helps developers interact with the Cassandra database more conveniently and efficiently. This annotation allows you to explicitly specify which columns should be part of the primary key and how they should be processed.
The main attributes of the @PrimaryKeyColumn annotation are:
name:
The name of a column in a table.
type:
Primary key column type (partitioned, clustered).
ordinal:
The ordinal number of the primary key column.
ordering:
The sort order for clustered columns (ASC or 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”
Specifies the name of the column in the table that corresponds to the userId field.
type: type = PrimaryKeyType.PARTITIONED
specifies that the userId column is a partition key.
ordinal: ordinal = 0
specifies the order of a column within a cluster key.
ordering: ordering = Ordering.ASCENDING
Specifies the sort order for the clustered column.
Primary Key Column Types:
PARTITIONED:
The partition key determines which node the row will be stored on. The partition key can consist of one or more columns.
CLUSTERED:
A clustering key determines the order of rows within a single partition.
@Unwrapped
In the context of Spring Data MongoDB and other Spring Data implementations, the @Unwrapped annotation is used to indicate that the attributes of a nested object should be unwrapped (or "unwrapped") and stored as separate fields in the parent document, rather than storing the nested object as a subdocument.
This can be useful when you want the attributes of a nested object to be stored at the same level as the attributes of the parent object, allowing you to simplify the data storage structure.
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:
The main document annotated as @Document to be stored in the users collection.
Address:
Nested object.
@Unwrapped(prefix = “address_”)
The attributes of the Address object will be expanded and stored in the User document as separate fields with the address_ prefix.
@QueryIndexed
is used in Spring Data to mark a field as indexable. This annotation is used primarily in the context of Spring Data for MongoDB and allows you to specify that a particular field should be indexed to improve search performance.
Using @QueryIndexed annotation in Spring Data MongoDB:
When a field is annotated as @QueryIndexed, it tells MongoDB to create an index on that field. Indexing helps speed up searches on that field, which is especially useful for fields that are frequently used in queries.
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”):
Specifies that the User class represents a document in the MongoDB users collection.
@Id:
Designates the id field as the document identifier.
@QueryIndexed
Specifies that the email field should be indexed to speed up searches on this field.
@PreAuthorize
In Spring Security, it is used to restrict access to methods based on SpEL (Spring Expression Language) expressions. It allows you to specify conditions under which access to a method will be allowed or denied.
Key features of @PreAuthorize:
Checking roles and privileges:
Checks whether the current user has the required roles or privileges to execute the method.
Checking attributes:
Allows you to check various attributes of the current user, such as username, email, etc.
Using SpEL:
Allows you to use SpEL expressions to create complex logical conditions.
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface PreAuthorize
Example of using @PreAuthorize:
Let's look at an example of using @PreAuthorize to restrict access to methods based on roles.
Creating a service with restricted access. In this example, the adminMethod method is available only to users with the ADMIN role, the userMethod method is available only to users with the USER role, and the userOrAdminMethod method is available to both users with the USER role and users with the ADMIN role.
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") } }
Enabling security annotations:
For @PreAuthorize to work, you must enable security method annotations in your security configuration.
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) class SecurityConfig : WebSecurityConfigurerAdapter() {}
@DataMongoTest
is used to test MongoDB repositories. It focuses on the configuration of Spring Data MongoDB components and provides a fast test context, excluding all unnecessary dependencies that are not related to testing work with data.
@DynamicPropertySource
used to dynamically add properties to the Environment of the application context during test execution. This is especially useful in integration tests where you need to set properties such as a database URL, credentials, or other parameters that may depend on external resources such as Docker containers spawned with 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
An annotation indicating that this test class uses Testcontainers to manage the container lifecycle.
@Container
Specifies a container that will be automatically managed by Testcontainers.
@DynamicPropertySource
The static method annotated with @DynamicPropertySource is used to register dynamic properties in the test context.
In this example, the registerPgProperties method adds properties such as the database URL, username, and password, which are retrieved from the PostgreSQL container.
@BeforeEach
an annotation from the JUnit 5 library that specifies that the annotated method should be executed before each test method in the current test class. This is useful for setting up conditions or initializing data needed for each test.
@Embedded and @Embeddable
in JPA (Java Persistence API) is used to indicate that a class is an embeddable component that can be included in other entities. Embeddable components allow fields and logic to be reused across multiple entities without duplicating code.
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
indicates that the Address class can be an embedded component in other entities.
@Embedded
used in a User entity to indicate that the Address object should be included as part of the User.
@Secured
in Spring Security is used to restrict access to methods based on roles. Unlike the @PreAuthorize annotation, which uses SpEL (Spring Expression Language) expressions to check conditions, @Secured is limited to checking roles only. This annotation is simpler and more convenient if you just need to check whether a user has certain roles.
Key features of @Secured:
Checking roles:
Allows you to specify which roles are required to access the method.
Ease of use:
Used to simply check if a user has one or more roles.
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Secured
Example of using @Secured:
Let's look at an example of using @Secured to restrict access to methods based on roles.
Enabling Security Annotations Support
For @Secured to work, you must enable support for security method annotations in your security configuration.
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true) class SecurityConfig : WebSecurityConfigurerAdapter() {}
Creating a service with restricted access:
In this example, the adminMethod method is available only to users with the ADMIN role, the userMethod method is available only to users with the USER role, and the userOrAdminMethod method is available to both users with the USER role and users with the ADMIN role.
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") } }
Differences between @PreAuthorize and @Secured:
The @PreAuthorize and @Secured annotations in Spring Security have a similar purpose — to restrict access to methods based on security conditions. However, they differ in functionality, flexibility, and usage.
@Secured
Ease of use: @Secured is used to check if the current user has one or more roles.
Role Support: Only checks roles and does not support more complex conditions.
Less features: Only supports simple role checks, making it less flexible.
Annotations: Works with simple lists of string values representing roles.
@PreAuthorize
Flexibility: @PreAuthorize uses SpEL (Spring Expression Language) expressions to check security conditions, which makes it more flexible.
Support complex conditions: Can check not only roles but also other attributes such as user name, method parameters, etc.
Powerful: Supports complex logical expressions and allows you to check various security attributes.
Customizability: Can be used to configure more complex access conditions.
@Column in JPA
is used in the Java Persistence API (JPA) to specify a mapping between an entity field and a table column in a database. This annotation provides flexibility in configuring the mapping between class fields and database columns, allowing you to specify the column name, type, uniqueness constraint, and other attributes.
The main attributes of the @Column annotation are:
name:
The name of a column in the table. If not specified, the field name is used.
nullable:
Specifies whether the column can contain NULL values. Defaults to true.
unique:
Specifies whether the column value must be unique. Defaults to false.
length:
The length of the column. Applies only to string data types. Defaults to 255.
precision:
Precision for numeric columns (total number of digits). Applies to BigDecimal and BigInteger types.
scale:
Scale for numeric columns (number of digits after the decimal point). Applies to BigDecimal and BigInteger types.
insertable:
Specifies whether the column should participate in SQL insert operations. Defaults to true.
updatable:
Specifies whether the column should participate in SQL update operations. Defaults to true.
columnDefinition:
An SQL expression that defines the data type of a column.
table:
The name of the table, if different from the default 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? )
The name field maps to the person_name column, which cannot be NULL and has a maximum length of 100 characters.
The age field maps to the person_age column, which cannot be NULL.
The email field maps to an email column, which must be unique and has a maximum length of 150 characters.
@PostConstruct
is an annotation provided by the javax.annotation package that is used to denote a method that should be executed after the initialization of a bean's dependencies has completed and before the bean is available for use.
@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() { // Initializing data after bean creation someData = "Initialized data" println("ExampleService initialized with data: $someData") } fun getData(): String { return someData } }
@PreDestroy
an annotation from Java Specification Request (JSR) 250 that is used in Java EE and Spring to specify a method that should be executed before a bean is destroyed. This annotation is typically applied to a method that performs cleanup operations such as releasing resources, closing connections, and other cleanup actions.
Example of using @PreDestroy in Spring:
In Spring, beans can be managed by the Spring container, and sometimes it is necessary to perform some actions before the bean is destroyed. This can be useful for terminating database connections, closing file streams, and other cleanup operations.
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") // Here you can free up resources } }
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() // The application is closed and Spring calls the cleanup() method. context.close() }
How does this work:
Initialization:
When the Spring container creates the ExampleBean bean, it calls the start method if provided in the code.
Destruction:
When the application is closed (for example, by calling context.close()), the Spring container calls the cleanup method annotated with @PreDestroy.
Important notes:
A method marked with @PreDestroy must not take any arguments and must not return a value.
It must be public or protected to be accessible by the Spring container.
The @PreDestroy annotation can be used with both Spring components and beans defined in configuration files.
@EnableJdbcRepositories
Used by Spring Data to enable support for JDBC repositories. It automatically configures the Spring Data JDBC infrastructure and scans the package for repository interfaces that will be turned into JDBC-based implementations.
The main functions of @EnableJdbcRepositories are:
Automatic configuration of JDBC repositories:
Enables the Spring Data JDBC repository engine. Automatically configures the necessary beans to work with JDBC.
Scanning packages:
Scans the specified packages for repository interfaces and creates implementations of them.
Setting up infrastructure beans:
Configures JdbcTemplate, DataSource and other necessary components for working with the database.
@Transient
is used in the context of Java and some frameworks to indicate that a certain field should not be serialized or saved to the database. It can be used in different contexts such as JPA (Java Persistence API) and standard Java serialization. Let's take a closer look at the use of this annotation in each of these contexts.
@Transient in JPA
In JPA, the @Transient annotation specifies that a field should not be persistent, meaning it should not be saved to the database. This is useful for transient or computed fields that do not require storage in the database.
@Transient in standard Java serialization
In the context of standard Java serialization, the @Transient annotation specifies that a field should not be serialized when an object is serialized.
@MappedCollection
is used in Spring Data JDBC to denote a collection of related entities in a primary entity. It allows you to specify that a field in the primary entity should be mapped to corresponding records in a related table. This is useful for modeling one-to-many and many-to-many relationships in databases using Spring Data JDBC.
Key features of @MappedCollection:
Collections of related entities:
Indicates that the field is a collection of related entities.
Support for complex data structures:
Allows you to easily work with collections within the main entity.
Mapping with foreign keys:
Automatically manages foreign keys for related entities.
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:
The main entity annotated with @Table(“users”).
Address:
Related entity annotated with @Table(“addresses”).
@MappedCollection
Specifies that addresses is a collection of related Address entities. The idColumn attribute points to a foreign key column in the addresses table.
@PayloadRoot
used in Spring Web Services to define a handler method in a class that will handle incoming SOAP messages with a specific root element in the message body (payload)
@Endpoint
used to mark a class as a Spring Web Service Endpoint
@RequestPayload
specifies that the method parameter (request) should be bound to the body of the incoming SOAP request
@ResponsePayload
The annotation specifies that the return value of the method should be converted into the body of the SOAP response.
@XmlRootElement
is used to indicate that a class represents the root element of an XML document. This annotation is used in the context of the Java Architecture for XML Binding (JAXB) and allows an object to be marshaled (converted to XML) and unmarshaled (converted from XML) as a root element.
In the context of Spring Web Services, @XmlRootElement is used to annotate classes that will be automatically marshaled and unmarshaled as XML messages.
Key aspects of @XmlRootElement:
Namespace:
An XML namespace can be specified using the namespace attribute.
Local name:
The local name of the root element can be specified using the name attribute.
<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
is a combination of three annotations: @Configuration, @EnableAutoConfiguration, and @ComponentScan. It is typically used to denote the main configuration class of a Spring Boot application.
It is recommended to have only one class annotated with @SpringBootApplication, which will be the main configuration class for the entire application. If you need to split the configuration into several parts, it is better to use other annotations, such as @Configuration, to create separate configuration classes.
@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)
Also- 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
➤ How @Component differs from @Service
In the Spring Framework, the @Component and @Service annotations are used to mark classes as Spring beans. Although they technically function the same and have the same role in the process of creating and managing beans, their usage comes with different semantic meanings that help you better organize and document your code. Here are the main differences:
@Component:
Used for general purposes when the class does not fit other specialized annotations (@Service, @Repository, @Controller).
@Service:
Used for service classes that contain business logic.
Reading and maintaining code:
Using specialized annotations like @Service helps to organize your code better and make it more understandable for other developers. They immediately understand that this class is used for business logic.
Application of Aspects (AOP):
In some cases, specialized annotations such as @Service can be used to apply aspects (AOP) in a more specific way.
➤ How is @Component different from @Bean
@Component and @Bean are two different ways of defining beans in Spring, but they are used in different contexts and have their own specific features.
@Component:
annotation that is used to mark a class as a Spring bean.
Used in conjunction with component scanning, Spring automatically discovers classes annotated with @Component and registers them as beans in the application context.
There are also specialized versions of @Component, such as @Service, @Repository, and @Controller, that do the same thing but add semantic meaning for developers and tooling.
import org.springframework.stereotype.Component @Component class MyComponent { fun doSomething() { println("Doing something") } }
Using @Component and @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:
annotation that is used to define a method that returns an object that should be registered as a bean in the Spring context.
Typically used in configuration classes annotated with @Configuration.
Allows you to explicitly create and configure a bean with specific parameters or initialization logic.
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") } }
Using @Bean in a configuration class:
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 and @Bean comparison:
Mechanism:
@Component:
used in conjunction with component scanning, which means that Spring automatically discovers classes annotated with @Component and registers them as beans in the application context.
@Bean:
is used to define a method that returns an object that should be registered as a bean in the Spring context. Typically, this annotation is used in configuration classes annotated with @Configuration.
Usage:
@Component:
applied to a class to automatically detect and register it as a bean.
@Bean:
used to create and configure beans via methods, allowing more explicit control over the bean creation process.
Context:
A class annotated with @Component automatically becomes a bean in the Spring context.
A method in a configuration class annotated with @Bean returns an object that becomes a bean in the Spring context.
Additional annotations:
There are specialized versions of @Component such as @Service, @Repository, and @Controller. These annotations do the same thing as @Component but add semantic meaning for developers and tooling.
There are no additional annotations for @Bean, but it is used in the context of configuration classes to explicitly define beans.
Use @Component (or its specialized versions) for classes that should be automatically discovered and registered as beans.
Use @Bean to explicitly define beans in configuration classes, especially when additional configuration or initialization logic is needed.