Automation QA Testing Course Content

SpringBoot interview Questions

 

  1. What is Spring and Spring Boot?
  2. Difference between Spring and Spring Boot?
1) Spring is a framework that requires some effort to develop enterprise level applications, whereas Spring Boot is an extension of Spring Framework that provides rapid application development (RAD) to develop an application faster.

2) Spring is popular for dependency injection, whereas Spring Boot is popular for auto-configuration.

3) Spring framework requires developers to setup the web server manually. Spring Boot has built-in servers like Tomcat, Jetty & Undertow.

4) Spring framework requires a deployment descriptor (web.xml) to run an application. Spring Boot doesn’t require a deployment descriptor to run an application.

5) Spring requires many manual configurations, whereas Spring Boot promotes auto-configuration.

6) Spring Framework requires a number of dependencies to create a web app. Spring Boot requires only one dependency to work on web app which is  ‘spring-boot-starter-web’.

7) Spring framework requires XML Configurations, whereas Spring Boot doesn’t require XML configuration.

8) Spring Boot provides CLI (Command Line Interface) tool to develop & test the application, whereas Spring framework doesn’t provide such kind of tool.

9) Spring framework requires manually defining dependencies for the Spring project in the pom.xml. Spring Boot offers the concept of starter in pom.xml file that internally takes care of downloading the dependent JARs automatically.

10) Spring Boot offers production-ready features such as metrics, health check and other reports. Spring does not offer such kind of features.

11) Spring framework doesn’t support in-memory databases. Spring Boot includes support for in-memory databases like H2


  1. What is Dependency Injection?

Dependency Injection (DI) is a design pattern and core principle in the Spring framework that deals with how components or beans in an application acquire their dependencies. Instead of the components themselves creating instances of their dependencies, the responsibility of injecting the dependencies is handed over to an external entity, typically the IoC (Inversion of Control) container, such as the Spring framework. This leads to more decoupled and maintainable code.

Key Concepts of Dependency Injection

  1. Inversion of Control (IoC):

    • IoC is the principle where the control of object creation and dependency management is inverted from the components to a container or framework.
    • DI is a specific form of IoC where dependencies are injected by the container.
  2. Types of Dependency Injection:

    • Constructor Injection: Dependencies are provided through a class constructor.
    • Setter Injection: Dependencies are provided through setter methods.
    • Field Injection: Dependencies are injected directly into fields using annotations.

Benefits of Dependency Injection

  • Decoupling: Components are decoupled from their dependencies, making the code more modular and easier to test.
  • Maintainability: Easier to change implementations or configurations without altering the dependent components.
  • Testability: Simplifies unit testing by allowing dependencies to be mocked or stubbed easily.

Types of Dependency Injection in Spring

1. Constructor Injection

Example:

java
public class MyService { private final MyRepository myRepository; @Autowired public MyService(MyRepository myRepository) { this.myRepository = myRepository; } public void performService() { myRepository.doSomething(); } }

In this example, MyService declares a constructor that requires a MyRepository instance. Spring automatically injects the appropriate MyRepository bean when MyService is instantiated.

2. Setter Injection

Example:

java
public class MyService { private MyRepository myRepository; @Autowired public void setMyRepository(MyRepository myRepository) { this.myRepository = myRepository; } public void performService() { myRepository.doSomething(); } }

Here, MyService declares a setter method for MyRepository. Spring injects the dependency by calling this setter method.

3. Field Injection

Example:

java
public class MyService { @Autowired private MyRepository myRepository; public void performService() { myRepository.doSomething(); } }

In this example, MyRepository is injected directly into the field myRepository of MyService.

Configuring Dependency Injection

Using Annotations

  • @Component: Indicates that a class is a Spring component.
  • @Autowired: Marks a constructor, field, or setter method to be autowired by Spring’s dependency injection facilities.
  • @Service, @Repository, @Controller: Specializations of @Component used for specific types of components.
  • @Qualifier: Used to resolve ambiguity when multiple beans of the same type are present.

Example with Annotations:

java
@Service public class MyService { private final MyRepository myRepository; @Autowired public MyService(@Qualifier("specificRepository") MyRepository myRepository) { this.myRepository = myRepository; } public void performService() { myRepository.doSomething(); } } @Repository @Qualifier("specificRepository") public class MyRepositoryImpl implements MyRepository { // Implementation details }

Using XML Configuration

Example:

xml
<beans> <bean id="myRepository" class="com.example.MyRepositoryImpl"/> <bean id="myService" class="com.example.MyService"> <constructor-arg ref="myRepository"/> </bean> </beans>

In this XML configuration, myService is configured to have myRepository injected via its constructor.

Conclusion

Dependency Injection is a fundamental concept in Spring that enables loose coupling and enhances the testability and maintainability of an application. By delegating the responsibility of dependency management to the Spring container, components remain focused on their primary responsibilities without worrying about the creation and management of their dependencies.

  1. What is Inversion of Control?

Inversion of Control (IoC) is a design principle in software engineering where the control flow of a program is inverted compared to traditional procedural programming. In IoC, the control over the flow of a program is shifted from the program itself to an external framework or container. This principle is closely related to, and often used synonymously with, the Dependency Injection (DI) pattern.

Key Concepts of Inversion of Control (IoC)

  1. Control Flow:

    • In traditional programming, a program controls the flow of execution.
    • In IoC, the control flow is inverted, and the framework or container controls the flow.
  2. Dependency Injection (DI):

    • DI is a specific form of IoC where dependencies are injected into a class by an external entity (typically a framework or container).
  3. Decoupling:

    • IoC helps in decoupling the components of an application, making the code more modular and easier to maintain.

Benefits of Inversion of Control (IoC)

  • Modularity: Promotes modularity by decoupling components, making them easier to test and maintain.
  • Extensibility: Allows for easier extension of functionality by adding or replacing components.
  • Testability: Enhances testability by facilitating the use of mock objects for testing.
  • Reusability: Encourages the reuse of components in different parts of an application or in different applications.

Implementing Inversion of Control (IoC) in Spring

In Spring, IoC is implemented through the use of the Spring IoC container. The container is responsible for managing the lifecycle of objects, including creating, configuring, and assembling them. It achieves this by:

  • Component Scanning: Automatically detecting classes annotated with @Component (or its specializations) and registering them as beans.
  • XML Configuration: Defining beans and their dependencies in an XML configuration file.
  • Java Configuration: Using Java classes annotated with @Configuration to define beans and their dependencies.
  • Annotation-based Configuration: Using annotations like @Autowired, @Component, @Service, @Repository, and @Controller for dependency injection and bean management.

Example of Dependency Injection (DI) in Spring

Consider a simple example where a UserService class depends on a UserRepository interface:

java
public interface UserRepository { void save(User user); } @Service public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public void saveUser(User user) { userRepository.save(user); } }

In this example, the UserService class declares a constructor that accepts a UserRepository parameter. When an instance of UserService is created, Spring automatically injects a bean that implements the UserRepository interface.

Conclusion

Inversion of Control (IoC) is a fundamental concept in modern software development and plays a crucial role in the Spring framework. By inverting the control of object creation and management to the framework, IoC promotes decoupling, modularity, and testability, making applications more maintainable and extensible.

  1. Inversion of Control vs Dependency Injection?

Inversion of Control (IoC) and Dependency Injection (DI) are related concepts in software engineering, often used together to achieve loose coupling and improve the testability and maintainability of code. While they are closely related, they refer to slightly different aspects of the same principle.

Inversion of Control (IoC)

  • Definition: IoC is a design principle in which the control flow of a program is inverted compared to traditional programming. In IoC, the control over the flow of a program is shifted from the program itself to an external framework or container.

  • Key Points:

    • IoC is a broad concept that encompasses various design patterns and principles, including DI.
    • It emphasizes decoupling components by delegating control over their dependencies to an external entity (e.g., a framework).
  • Example:

    • In a traditional application, a class may create instances of its dependencies directly. In an IoC scenario, these dependencies are provided to the class (injected) by an external entity.

Dependency Injection (DI)

  • Definition: DI is a specific form of IoC where dependencies are injected into a class by an external entity (typically a framework or container) rather than the class itself creating or managing its dependencies.

  • Key Points:

    • DI is a pattern or mechanism used to achieve IoC.
    • It helps in achieving loose coupling between classes, as the class does not need to know how to create its dependencies but relies on an external entity to provide them.
  • Example:

    • In the context of a Spring application, DI is achieved using annotations like @Autowired to inject dependencies into a class.

Relationship Between IoC and DI

  • IoC is a broader concept that encompasses the idea of inverting control, while DI is a specific technique used to implement IoC.
  • DI is a way to achieve IoC by externalizing the dependencies of a class and injecting them rather than having the class create or manage its dependencies.

Summary

  • Inversion of Control (IoC): A design principle where control over the flow of a program is inverted, with an external entity controlling the creation and management of objects.
  • Dependency Injection (DI): A specific implementation of IoC where dependencies of a class are injected into the class by an external entity, rather than the class creating or managing its dependencies.

In practice, the terms IoC and DI are often used interchangeably, as DI is the most common way to achieve IoC in modern software development.

  1. What happen when we start spring boot application?

When you start a Spring Boot application, several key steps are taken behind the scenes to initialize the application and make it ready to handle incoming requests. Here's an overview of what happens:

  1. Initialization:

    • Spring Boot initializes by creating an instance of the ApplicationContext. This context is the central interface within a Spring application for providing configuration information to the application.
  2. Component Scanning:

    • Spring Boot scans the application's components (classes annotated with @Component, @Service, @Repository, @Controller, etc.) to identify beans and register them in the application context.
  3. Auto-Configuration:

    • Spring Boot automatically configures the application based on the dependencies and classpath.
    • It looks for libraries in the classpath and configures beans and other settings accordingly.
  4. Application Context Setup:

    • Spring Boot sets up the application context with all the necessary configurations, including beans, property sources, and environment settings.
  5. Embedded Web Server Start:

    • If the application is a web application, Spring Boot starts an embedded web server (like Tomcat, Jetty, or Undertow) to handle incoming HTTP requests.
  6. Running the Application:

    • Once the setup is complete, Spring Boot starts the application, making it ready to handle requests.
    • Any @PostConstruct methods are invoked, and the application is in a fully initialized state.
  7. Handling Requests:

    • The application is now ready to handle incoming requests based on the defined mappings (e.g., @RequestMapping, @GetMapping, @PostMapping).
  8. Shutdown:

    • When the application is shut down, any @PreDestroy methods are invoked, and resources are released.

Overall, Spring Boot's auto-configuration and opinionated setup make it easy to create standalone, production-ready Spring applications with minimal manual configuration.

  1. What is Bean ? Explain about Bean LifeCycle? Explain about different types of Bean?

In Spring Framework, a bean is an object that is instantiated, assembled, and managed by the Spring IoC container. Beans are the basic building blocks of a Spring application and are typically Java objects that form the backbone of the application's business logic.

Bean Lifecycle in Spring

The lifecycle of a Spring bean consists of several phases, each with its corresponding callback methods that you can implement to perform custom initialization and destruction logic. The typical bean lifecycle phases are as follows:

  1. Instantiation:

    • The container instantiates a bean using either a constructor (for XML-based configuration) or a static factory method (for Java-based configuration).
  2. Populating properties:

    • The container populates the bean's properties, either by setting values directly on the bean's fields or by calling setter methods.
  3. BeanNameAware and BeanFactoryAware (Optional):

    • If the bean implements the BeanNameAware or BeanFactoryAware interfaces, the corresponding methods are called, passing the bean's name and the containing BeanFactory.
  4. BeanPostProcessor PreInitialization:

    • If any BeanPostProcessor implementations are registered with the container, their postProcessBeforeInitialization methods are called.
  5. Initialization:

    • If the bean implements the InitializingBean interface, the afterPropertiesSet method is called.
    • If the bean has an initialization method defined (e.g., init-method attribute in XML configuration or @PostConstruct annotation), that method is called.
  6. BeanPostProcessor PostInitialization:

    • If any BeanPostProcessor implementations are registered with the container, their postProcessAfterInitialization methods are called.
  7. Bean Ready for Use:

    • The bean is now fully initialized and ready to be used by the application.
  8. Destruction:

    • If the bean implements the DisposableBean interface, the destroy method is called when the bean is being removed from the container.
    • If the bean has a destruction method defined (e.g., destroy-method attribute in XML configuration or @PreDestroy annotation), that method is called.

Types of Beans in Spring

  1. Singleton:

    • By default, beans in Spring are singletons, meaning that only one instance of the bean is created for the entire application context.
    • Singleton beans are cached, and subsequent requests for the bean return the cached instance.
  2. Prototype:

    • Prototype beans are not cached, and a new instance is created every time the bean is requested from the container.
    • This can be useful for beans that are stateful or require a new instance for each use.
  3. Request:

    • Request-scoped beans are created once per HTTP request in a web application.
    • Each HTTP request gets its instance of the bean, which is destroyed once the request is completed.
  4. Session:

    • Session-scoped beans are created once per HTTP session in a web application.
    • Each HTTP session gets its instance of the bean, which is destroyed when the session expires.
  5. Global Session:

    • Global session-scoped beans are similar to session-scoped beans but are scoped to a global HTTP session in a portlet context.
  6. Application Context:

    • Application context-scoped beans are scoped to the lifecycle of the entire application context.
    • They are created once per application context and are typically used in a multi-context scenario.
  7. Custom Scope:

    • Spring allows you to define custom bean scopes by implementing the Scope interface and registering it with the application context.

Summary

  • A bean in Spring is an object managed by the Spring IoC container.
  • The bean lifecycle consists of instantiation, initialization, and destruction phases, with corresponding callback methods for customization.
  • Spring supports different types of bean scopes, including singleton, prototype, request, session, global session, and application context.
  1. How to change port number in spring boot application?

You can change the port number of a Spring Boot application by configuring it in the application.properties or application.yml file. Here's how you can do it:

Using application.properties

  1. Open the src/main/resources/application.properties file.
  2. Add or update the following line to specify the desired port number:
    properties
    server.port=8081
    Replace 8081 with the port number you want to use.

Using application.yml

  1. Open the src/main/resources/application.yml file.
  2. Add or update the following lines to specify the desired port number:
    yaml
    server: port: 8081
    Replace 8081 with the port number you want to use.

Run the Application

Once you've configured the port number, you can run your Spring Boot application. It will now listen on the specified port.

Command Line Argument

Alternatively, you can also specify the port number as a command-line argument when running your Spring Boot application. For example:

sh
java -jar -Dserver.port=8081 your-application.jar

This will override any port configuration in your application.properties or application.yml file and use port 8081.

  1. Which server spring boot uses internally? How to change that?

Spring Boot uses an embedded server by default to run your application. The embedded server is included in the application's dependencies, so you don't need to install or configure an external server like Tomcat or Jetty. Spring Boot supports several embedded servers, including Tomcat, Jetty, and Undertow.

Changing the Embedded Server

You can change the embedded server used by Spring Boot by excluding the default server dependency and including the dependency for the server you want to use. Here's how you can do it for each server:

1. Tomcat (Default Server)

To use Tomcat as the embedded server, no additional configuration is needed, as Tomcat is the default embedded server in Spring Boot.

2. Jetty

Add the Jetty dependency and exclude the default Tomcat dependency:

xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>

3. Undertow

Add the Undertow dependency and exclude the default Tomcat dependency:

xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>

Conclusion

Spring Boot provides great flexibility in choosing the embedded server for your application. By simply adding the appropriate dependencies and excluding the default server, you can switch between embedded servers seamlessly.

What is graceful shutdown?

Graceful shutdown refers to the process of shutting down a software application in a way that allows it to complete its current tasks or requests before terminating. This ensures that the application behaves in a predictable and controlled manner, minimizing the risk of data corruption or incomplete operations.

In the context of a web server, such as those used in Spring Boot applications, graceful shutdown typically involves the following steps:

  1. Stopping New Requests: The server stops accepting new incoming requests but continues to process existing requests.

  2. Completing Existing Requests: The server allows existing requests to complete their processing, ensuring that no requests are abruptly terminated.

  3. Shutdown Delay: Optionally, a delay may be configured to allow enough time for existing requests to complete before the server shuts down completely.

  4. Shutdown Hook: During the shutdown process, the server may execute shutdown hooks or callbacks to perform cleanup tasks, such as releasing resources or closing connections.

  5. Shutdown Confirmation: Once all existing requests are completed, the server shuts down, and a confirmation message may be logged or sent to indicate successful shutdown.

Graceful shutdown is important for maintaining the integrity of the application and ensuring that no data is lost or corrupted due to abrupt termination. It allows the application to handle shutdowns in a controlled manner, providing a better experience for users and minimizing the impact of downtime.

  1. How to implement paging and sorting in Spring Boot?

Paging and sorting in Spring Boot can be easily implemented using Spring Data JPA. Spring Data JPA provides built-in support for pagination and sorting, making it straightforward to handle these features in your application. Here’s how you can implement paging and sorting in a Spring Boot application:

Step-by-Step Implementation

1. Set Up Spring Data JPA

Ensure you have the necessary dependencies for Spring Data JPA in your pom.xml (for Maven) or build.gradle (for Gradle).

Maven:

xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>

Gradle:

groovy
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' runtimeOnly 'com.h2database:h2'

2. Define an Entity

Create an entity class that represents the data model.

java
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; // Getters and setters }

3. Create a Repository Interface

Create a repository interface that extends PagingAndSortingRepository or JpaRepository. These interfaces provide methods for pagination and sorting.

java
import org.springframework.data.repository.PagingAndSortingRepository; public interface UserRepository extends PagingAndSortingRepository<User, Long> { }

4. Use Paging and Sorting in a Service

Create a service class to use the repository methods for pagination and sorting.

java
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserRepository userRepository; public Page<User> getUsers(int page, int size, String sortBy) { Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy)); return userRepository.findAll(pageable); } }

5. Create a Controller

Create a controller to handle HTTP requests and return paginated and sorted results.

java
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/users") public Page<User> getUsers( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "id") String sortBy) { return userService.getUsers(page, size, sortBy); } }

Explanation

  • Entity: The User class is a JPA entity that maps to a database table.
  • Repository: The UserRepository interface extends PagingAndSortingRepository, which provides methods for pagination and sorting.
  • Service: The UserService class contains the business logic for fetching paginated and sorted users.
  • Controller: The UserController class handles HTTP GET requests to the /users endpoint and returns paginated and sorted user data.

Running the Application

Once you have implemented the above steps, you can run your Spring Boot application. You can access the paginated and sorted users by making HTTP GET requests to the /users endpoint with query parameters for page, size, and sortBy.

For example:

bash
GET http://localhost:8080/users?page=0&size=10&sortBy=name

This request will return the first page of users, with 10 users per page, sorted by the name field.

  1. What is @SpringBootApplication?
The @SpringBootApplication annotation is a convenience annotation that combines several other annotations commonly used in Spring Boot applications. It is typically placed on the main class of a Spring Boot application to enable various configurations and settings that are needed to run the application.

The @SpringBootApplication annotation is effectively a combination of the following three annotations:

  1. @EnableAutoConfiguration:

    • This annotation tells Spring Boot to start adding beans based on the classpath settings, other beans, and various property settings. For example, if spring-boot-starter-web is on the classpath, this annotation flags the application as a web application and activates key behaviors, such as setting up a DispatcherServlet.
  2. @ComponentScan:

    • This annotation tells Spring to scan the current package and its sub-packages for components, configurations, and services. This allows Spring to find and register beans annotated with @Component, @Service, @Repository, etc.
  3. @Configuration:

    • This annotation indicates that the class can be used by the Spring IoC container as a source of bean definitions. It allows the class to declare one or more @Bean methods that provide the beans to the Spring container.

Here is an example of how @SpringBootApplication is typically used in a Spring Boot application:

java
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class, args); } }

Key Benefits

  • Convenience: By using @SpringBootApplication, you can avoid specifying each annotation (@EnableAutoConfiguration, @ComponentScan, and @Configuration) separately. This makes your main class cleaner and more readable.
  • Auto-Configuration: It enables the auto-configuration feature of Spring Boot, which attempts to automatically configure your Spring application based on the dependencies you have added.
  • Component Scanning: It simplifies component scanning, ensuring that Spring can find your beans and configurations without needing additional setup.

Customizing the Behavior

If you need to customize the behavior of the auto-configuration or component scanning, you can still use the individual annotations. For example, if you want to specify a different base package for component scanning, you can explicitly add @ComponentScan with the desired package:

java
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ComponentScan(basePackages = "com.example.myapp") public class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class, args); } }

In summary, @SpringBootApplication is a powerful and convenient annotation that simplifies the setup and configuration of Spring Boot applications by combining three essential annotations into one.

  1. Explain about different annotations like @Service, @Controller, @RestController, @Entity, @Component, @Repository, @Autowired, @Required, @Primary, @Async, @Transient, @ComponentScan?

1. @Service

  • Purpose: Indicates that a class is a service component in the service layer.
  • Usage: Used to annotate service classes, which typically contain business logic.
  • Example:
    java
    @Service public class MyService { public void performService() { // business logic } }

2. @Controller

  • Purpose: Indicates that a class is a Spring MVC controller, handling HTTP requests.
  • Usage: Used to annotate controller classes that handle web requests.
  • Example:
    java
    @Controller public class MyController { @GetMapping("/greet") public String greet() { return "greeting"; } }

3. @RestController

  • Purpose: A convenience annotation that combines @Controller and @ResponseBody.
  • Usage: Used to create RESTful web services where methods return JSON/XML responses.
  • Example:
    java
    @RestController public class MyRestController { @GetMapping("/hello") public String hello() { return "Hello, World!"; } }

4. @Entity

  • Purpose: Specifies that a class is an entity and is mapped to a database table.
  • Usage: Used to annotate model classes in JPA (Java Persistence API).
  • Example:
    java
    @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // getters and setters }

5. @Component

  • Purpose: Indicates that a class is a Spring component.
  • Usage: Used to define Spring-managed beans.
  • Example:
    java
    @Component public class MyComponent { public void doWork() { // logic } }

6. @Repository

  • Purpose: Indicates that a class is a repository, which encapsulates database operations.
  • Usage: Used to annotate DAO (Data Access Object) classes.
  • Example:
    java
    @Repository public class UserRepository { // database access methods }

7. @Autowired

  • Purpose: Marks a field, constructor, or setter method for dependency injection.
  • Usage: Used to automatically wire beans.
  • Example:
    java
    @Service public class MyService { @Autowired private MyRepository myRepository; // service methods }

8. @Required

  • Purpose: Indicates that a bean property must be set.
  • Usage: Applied to setter methods to enforce that the property is required.
  • Example:
    java
    public class MyBean { private String name; @Required public void setName(String name) { this.name = name; } }

9. @Primary

  • Purpose: Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency.
  • Usage: Used to resolve autowiring conflicts.
  • Example:
    java
    @Primary @Bean public MyService primaryService() { return new PrimaryService(); }

10. @Async

  • Purpose: Marks a method for asynchronous execution.
  • Usage: Used to execute methods asynchronously.
  • Example:
    java
    @Service public class MyService { @Async public void asyncMethod() { // asynchronous logic } }

11. @Transient

  • Purpose: Indicates that a field is not persistent.
  • Usage: Used in JPA entities to specify that the field should not be stored in the database.
  • Example:
    java
    @Entity public class User { private String name; @Transient private int age; // not persisted // getters and setters }

12. @ComponentScan

  • Purpose: Configures the packages to be scanned for components.
  • Usage: Used in configuration classes to specify the base packages for scanning components.
  • Example:
    java
    @Configuration @ComponentScan(basePackages = "com.example") public class AppConfig { // configuration }
  1. Difference between @Controller vs @RestController?                            The primary difference between @Controller and @RestController lies in the way they handle HTTP responses in a Spring MVC application.
  2. @Controller

    • Purpose: @Controller is used to define a controller in a Spring MVC application.
    • Response Handling: When a method in a class annotated with @Controller returns a value, it is typically interpreted as the name of a view (like a JSP, Thymeleaf template, etc.) that should be rendered.
    • Usage: @Controller is generally used when you want to return HTML views in response to web requests.

    Example:

    java
    @Controller public class MyController { @GetMapping("/greet") public String greet(Model model) { model.addAttribute("message", "Hello, World!"); return "greeting"; // Returns the name of the view } }

    In the example above, the method greet adds an attribute to the model and returns the name of the view (e.g., a Thymeleaf template named greeting.html).

    @RestController

    • Purpose: @RestController is a specialized version of @Controller that is used to create RESTful web services.
    • Response Handling: When a method in a class annotated with @RestController returns a value, it is directly written to the HTTP response body as JSON or XML (depending on the configured message converters). @RestController is a convenience annotation that combines @Controller and @ResponseBody.
    • Usage: @RestController is used when you want to return JSON or XML data directly from your controller methods.

    Example:

    java
    @RestController public class MyRestController { @GetMapping("/hello") public String hello() { return "Hello, World!"; // Directly written to the HTTP response body } }

    In the example above, the method hello returns a string which is directly written to the HTTP response body as plain text (or JSON/XML if it were an object).

    Key Differences:

    1. Annotation Combination:

      • @Controller is often used with @ResponseBody to return data as a response body.
      • @RestController combines @Controller and @ResponseBody, making it more convenient for RESTful web services.
    2. Response Handling:

      • @Controller returns the name of a view which is resolved to an HTML page.
      • @RestController returns data directly, which is written to the HTTP response body.
    3. Use Case:

      • Use @Controller when you are developing a web application with views (e.g., HTML pages).
      • Use @RestController when you are developing a REST API that returns data (e.g., JSON or XML).

    Example of Using @Controller with @ResponseBody:

    java
    @Controller public class MyController { @GetMapping("/json") @ResponseBody public String json() { return "{\"message\": \"Hello, World!\"}"; // JSON response } }

    Here, @ResponseBody is used to indicate that the return value should be written directly to the response body, similar to how @RestController works.

  3. What is use of @Configuration?

The @Configuration annotation in Spring is used to indicate that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime. It is a key part of the Java-based configuration in Spring.

Key Uses of @Configuration

  1. Bean Definitions:

    • It allows you to define beans using Java code rather than XML.
    • Beans are methods annotated with @Bean within a class annotated with @Configuration.
  2. Dependency Injection:

    • Enables dependency injection by defining beans and their dependencies programmatically.
  3. Component Scanning and Bootstrapping:

    • Used in combination with @ComponentScan to specify the packages to be scanned for components.
    • Often combined with @EnableAutoConfiguration and @ComponentScan in Spring Boot applications.
  4. Modularity:

    • Allows for the configuration to be modularized into multiple classes, each with its own configuration.

Example Usage

Simple Bean Definition

java
@Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } @Bean public MyRepository myRepository() { return new MyRepositoryImpl(); } }

In the example above, AppConfig is a configuration class that defines two beans, myService and myRepository.

Using @Configuration with @ComponentScan

java
@Configuration @ComponentScan(basePackages = "com.example") public class AppConfig { // configuration and bean definitions }

Here, @ComponentScan is used to specify the base packages to scan for components, combining component scanning with explicit bean definitions.

Using @Configuration with @PropertySource

java
@Configuration @PropertySource("classpath:application.properties") public class AppConfig { @Autowired private Environment env; @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getProperty("db.driver")); dataSource.setUrl(env.getProperty("db.url")); dataSource.setUsername(env.getProperty("db.username")); dataSource.setPassword(env.getProperty("db.password")); return dataSource; } }

In this example, @PropertySource is used to load properties from an external file, and the Environment is used to access those properties to configure a DataSource bean.

Benefits of Using @Configuration

  1. Type-Safe Configuration:

    • Provides type-safe configuration compared to XML, which can lead to fewer errors.
  2. IDE Support:

    • Better support for refactoring and navigation in modern IDEs.
  3. Modularity:

    • Encourages modular configuration, allowing different configurations for different parts of the application.
  4. Testability:

    • Configuration classes can be easily tested with standard unit tests.
  5. Integration:

    • Easily integrates with other parts of Spring such as component scanning and Spring Boot’s auto-configuration.

In summary, @Configuration is a cornerstone of Java-based Spring configuration, offering a powerful and flexible way to define and manage beans within the Spring container.

  1. What does autowiring mean? Types of autowiring?

Autowiring in Spring refers to the process of automatically injecting dependencies into a Spring-managed bean. Instead of explicitly wiring the dependencies using XML configuration or Java code, Spring can automatically resolve and inject the collaborating beans.

Types of Autowiring

Spring supports several types of autowiring. Here’s a detailed look at each:

1. Autowiring by Type (@Autowired)

Field Injection:

java
public class MyService { @Autowired private MyRepository myRepository; }

Constructor Injection:

java
public class MyService { private final MyRepository myRepository; @Autowired public MyService(MyRepository myRepository) { this.myRepository = myRepository; } }

Setter Injection:

java
public class MyService { private MyRepository myRepository; @Autowired public void setMyRepository(MyRepository myRepository) { this.myRepository = myRepository; } }

Explanation: When @Autowired is used on a field, constructor, or setter method, Spring will automatically inject the appropriate bean by matching the type.

2. Autowiring by Name

java
public class MyService { @Autowired @Qualifier("myRepository") private MyRepository myRepository; }

Explanation: When @Qualifier is used in conjunction with @Autowired, Spring will resolve the bean by matching the bean name.

3. Autowiring by Qualifier (@Qualifier)

Field Injection:

java
public class MyService { @Autowired @Qualifier("specificRepository") private MyRepository myRepository; }

Constructor Injection:

java
public class MyService { private final MyRepository myRepository; @Autowired public MyService(@Qualifier("specificRepository") MyRepository myRepository) { this.myRepository = myRepository; } }

Setter Injection:

java
public class MyService { private MyRepository myRepository; @Autowired public void setMyRepository(@Qualifier("specificRepository") MyRepository myRepository) { this.myRepository = myRepository; } }

Explanation: @Qualifier is used to specify the exact bean to be injected when there are multiple beans of the same type.

Autowiring Modes in XML Configuration

  1. no (default) - No autowiring. Dependencies must be specified manually.

    xml
    <bean id="myService" class="com.example.MyService"> <property name="myRepository" ref="myRepository"/> </bean>
  2. byName - Autowires by property name. Spring looks for a bean with the same name as the property.

    xml
    <bean id="myService" class="com.example.MyService" autowire="byName"/>
  3. byType - Autowires by property type. Spring looks for a bean with a matching type.

    xml
    <bean id="myService" class="com.example.MyService" autowire="byType"/>
  4. constructor - Autowires by constructor. Similar to byType, but applies to constructor arguments.

    xml
    <bean id="myService" class="com.example.MyService" autowire="constructor"/>
  5. autodetect - Deprecated. Uses constructor or byType autowiring, depending on which is applicable.

    xml
    <bean id="myService" class="com.example.MyService" autowire="autodetect"/>

Advantages of Autowiring

  • Reduces Boilerplate Code: Simplifies the wiring of beans and reduces the amount of configuration needed.
  • Increases Flexibility: Makes it easier to swap implementations or change configurations without altering the code.

Disadvantages of Autowiring

  • Ambiguity: Can lead to ambiguous dependencies if there are multiple candidates for injection.
  • Hidden Dependencies: Makes it harder to understand the dependencies of a class at a glance, as they are not explicitly defined in the configuration.

Example: Complete Autowiring in a Spring Boot Application

Application Configuration:

java
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

Service Class:

java
@Service public class MyService { private final MyRepository myRepository; @Autowired public MyService(MyRepository myRepository) { this.myRepository = myRepository; } public void performService() { myRepository.doSomething(); } }

Repository Interface:

java
@Repository public interface MyRepository { void doSomething(); }

In this example, MyService is automatically injected with an instance of MyRepository by Spring.

In summary, autowiring in Spring simplifies dependency injection, making the code cleaner and easier to manage. However, careful consideration is needed to avoid issues with ambiguity and hidden dependencies.

  1. What is ApplicationContext?

ApplicationContext is a central interface in the Spring Framework that provides configuration information to the application. It represents the Spring IoC (Inversion of Control) container and is responsible for instantiating, configuring, and assembling the beans.

Key Features of ApplicationContext

  1. Bean Factory:

    • ApplicationContext extends the BeanFactory interface, providing all the functionality of a BeanFactory (which is responsible for managing the lifecycle of beans).
  2. Internationalization (i18n):

    • Supports internationalization by providing message resources for different locales.
  3. Event Propagation:

    • Supports event handling and propagation, allowing beans to publish and listen for events.
  4. Resource Loading:

    • Provides a generic way to load resources (like files, URLs, etc.).
  5. Environment Abstraction:

    • Provides access to environment variables and properties, enabling the application to adapt to different environments.
  6. Lifecycle Management:

    • Manages the lifecycle of beans, including initialization, destruction, and other lifecycle callbacks.

Example Usage

Creating an ApplicationContext

Using ClassPathXmlApplicationContext (for XML-based configuration):

java
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); MyService myService = context.getBean(MyService.class); myService.performService();

Using AnnotationConfigApplicationContext (for Java-based configuration):

java
@Configuration @ComponentScan(basePackages = "com.example") public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } @Bean public MyRepository myRepository() { return new MyRepositoryImpl(); } } public class Application { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = context.getBean(MyService.class); myService.performService(); } }

Important Methods in ApplicationContext

  1. getBean():

    • Retrieves a bean from the Spring container.
    java
    MyService myService = context.getBean(MyService.class);
  2. getBeansOfType():

    • Retrieves all beans of a specified type.
    java
    Map<String, MyService> services = context.getBeansOfType(MyService.class);
  3. getBeanNamesForType():

    • Retrieves the names of all beans of a specified type.
    java
    String[] beanNames = context.getBeanNamesForType(MyService.class);
  4. getMessage():

    • Retrieves a message from the message source for internationalization.
    java
    String message = context.getMessage("welcome.message", null, Locale.US);
  5. publishEvent():

    • Publishes an event to be handled by event listeners.
    java
    context.publishEvent(new MyCustomEvent(this, "event data"));

Types of ApplicationContext

  1. ClassPathXmlApplicationContext:

    • Loads context definition from an XML file located in the classpath.
    java
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  2. FileSystemXmlApplicationContext:

    • Loads context definition from an XML file located in the file system.
    java
    ApplicationContext context = new FileSystemXmlApplicationContext("path/to/applicationContext.xml");
  3. AnnotationConfigApplicationContext:

    • Loads context definition from Java-based configuration classes.
    java
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
  4. GenericWebApplicationContext:

    • Suitable for web applications, providing integration with Servlet context.

Benefits of ApplicationContext

  • Advanced Dependency Injection: Supports setter-based, constructor-based, and field-based dependency injection.
  • Bean Lifecycle Management: Manages the complete lifecycle of beans, including initialization and destruction callbacks.
  • Integration with Spring AOP: Supports Aspect-Oriented Programming, enabling the application of cross-cutting concerns.
  • Enterprise Services: Provides enterprise-level services such as transaction management, security, and more.

Conclusion

ApplicationContext is a powerful and flexible interface in the Spring Framework that provides comprehensive support for dependency injection, bean lifecycle management, and many other enterprise-level features. It is a fundamental part of any Spring application, ensuring that the application components are configured, managed, and assembled correctly.

  1. Difference between @Bean vs @Component?

@Bean and @Component are both used for defining beans in a Spring application, but they serve different purposes and are used in different contexts. Here’s a detailed comparison of the two:

@Bean

  • Purpose:

    • @Bean is used to explicitly declare a single bean, rather than letting Spring automatically detect it during component scanning.
  • Usage:

    • It is typically used in Java-based configuration classes, annotated with @Configuration.
    • Provides more control over bean creation and initialization.
  • Configuration Class:

    • Methods annotated with @Bean are located within a @Configuration class.
  • Example:

    java
    @Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } @Bean public MyRepository myRepository() { return new MyRepositoryImpl(); } }
  • Advantages:

    • Provides detailed control over the instantiation, configuration, and initialization of beans.
    • Useful for defining beans that need to be configured with specific parameters or for third-party library classes.

@Component

  • Purpose:

    • @Component is a generic stereotype for any Spring-managed component. It tells Spring that the annotated class is a bean.
  • Usage:

    • It is used for automatic component scanning. When Spring scans the classpath, it automatically registers all classes annotated with @Component (and other specialized annotations like @Service, @Repository, and @Controller).
  • Component Scanning:

    • Classes annotated with @Component are automatically detected and registered as beans when component scanning is enabled (using @ComponentScan).
  • Example:

    java
    @Component public class MyService { // class implementation } @Component public class MyRepository { // class implementation }
  • Specialized Annotations:

    • @Component is the base annotation, but there are specialized forms for specific types of components:
      • @Service for service layer beans.
      • @Repository for data access layer beans.
      • @Controller for web controllers.
  • Advantages:

    • Simplifies bean registration through classpath scanning.
    • Suitable for most application beans, making configuration less verbose.

Key Differences

  1. Declaration:

    • @Bean is used on methods in a configuration class (@Configuration).
    • @Component is used directly on classes.
  2. Bean Scope:

    • @Bean methods can be used to define the scope of the bean (@Scope can be applied to method level).
    • @Component beans are typically singleton by default, but scope can be defined with @Scope.
  3. Use Cases:

    • Use @Bean for complex bean initialization logic or for integrating third-party libraries.
    • Use @Component (and its specializations) for application-specific beans that can be automatically detected by classpath scanning.
  4. Control and Flexibility:

    • @Bean provides fine-grained control over bean instantiation and configuration.
    • @Component offers simplicity and convenience for most standard use cases.

Example of Combined Usage

You can combine both @Bean and @Component in a Spring application to take advantage of their respective strengths:

java
@Configuration @ComponentScan(basePackages = "com.example") public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(myRepository()); } @Bean public MyRepository myRepository() { return new MyRepositoryImpl(); } }

In this example, @ComponentScan is used to automatically discover @Component-annotated classes, while @Bean methods are used to define beans with specific initialization logic.

Conclusion

  • Use @Component (and its specialized annotations) for most application beans where automatic scanning is sufficient.
  • Use @Bean for explicit bean definitions, especially when detailed control over bean creation and configuration is needed, or when dealing with third-party libraries.
  1. What is Aspect Oriented Programming?

Aspect-Oriented Programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It enables you to add behavior to existing code without modifying the code itself, typically through the use of "aspects". These aspects enable you to define reusable functionality that can be applied to multiple points in an application.

Key Concepts in AOP

  1. Aspect:

    • An aspect is a module that encapsulates a concern that cuts across multiple classes. For example, logging, transaction management, and security are common cross-cutting concerns that can be modularized into aspects.
  2. Join Point:

    • A join point is a specific point in the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.
  3. Advice:

    • Advice is the action taken by an aspect at a particular join point. Different types of advice include:
      • Before Advice: Runs before the method execution.
      • After Advice: Runs after the method execution, regardless of its outcome.
      • After Returning Advice: Runs after the method execution, only if the method completes successfully.
      • After Throwing Advice: Runs after the method execution, only if the method exits by throwing an exception.
      • Around Advice: Runs before and after the method execution, allowing you to control whether the method is executed.
  4. Pointcut:

    • A pointcut is an expression that matches join points. It defines where advice should be applied. Pointcuts enable you to specify which methods the advice should be applied to, typically using expressions that match method signatures or annotations.
  5. Weaving:

    • Weaving is the process of applying aspects to a target object, creating an advised object. Weaving can occur at compile time, load time, or runtime. In Spring AOP, weaving is typically performed at runtime.

AOP in Spring

Spring AOP provides support for aspect-oriented programming within the Spring framework. Spring AOP is proxy-based and operates at the method level. Here is a brief overview of how AOP is implemented in Spring:

  1. Defining an Aspect:

    • Create a class annotated with @Aspect to define an aspect.
  2. Defining Advice:

    • Use annotations such as @Before, @After, @AfterReturning, @AfterThrowing, and @Around to define different types of advice.
  3. Defining Pointcuts:

    • Use the @Pointcut annotation to define reusable pointcut expressions.

Example

Here’s an example of using AOP in a Spring Boot application:

  1. Add Dependencies:

    • Ensure you have the necessary dependencies in your pom.xml or build.gradle.
    xml
    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
  2. Define an Aspect:

    java
    import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.myapp.service.*.*(..))") public void logBeforeMethodExecution() { System.out.println("A method in the service layer is about to be executed."); } }
  3. Enable AspectJ Auto Proxy:

    • In your main application class, enable AspectJ auto proxy.
    java
    import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @EnableAspectJAutoProxy public class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class, args); } }

Conclusion

Aspect-Oriented Programming (AOP) is a powerful paradigm that allows for the modularization of cross-cutting concerns, improving code maintainability and reducing duplication. Spring AOP provides a flexible and easy-to-use framework for implementing AOP in Spring-based applications, allowing you to define aspects, pointcuts, and advice to enhance the functionality of your code without modifying the core business logic.

  1. What is @Qualifier annotation?

The @Qualifier annotation in Spring is used to resolve the ambiguity when multiple beans of the same type are present in the Spring context and you want to inject a specific bean among them. It helps in explicitly specifying which bean should be injected when there are multiple candidates available.

When to Use @Qualifier

Consider a scenario where you have two beans of the same type, but you want to inject a specific one. Without @Qualifier, Spring would throw an exception due to ambiguity. The @Qualifier annotation helps in such cases by narrowing down the candidate beans to the one specified.

Example

Here is an example to illustrate the use of @Qualifier:

Step 1: Define Multiple Beans of the Same Type

java
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean public MyService myService1() { return new MyServiceImpl1(); } @Bean public MyService myService2() { return new MyServiceImpl2(); } }

In the above configuration, we have two beans of type MyService.

Step 2: Use @Qualifier to Inject a Specific Bean

java
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class MyComponent { private final MyService myService; @Autowired public MyComponent(@Qualifier("myService1") MyService myService) { this.myService = myService; } public void performService() { myService.execute(); } }

In this example, @Qualifier("myService1") is used to specify that the myService1 bean should be injected into MyComponent.

Explanation

  • @Configuration: Indicates that the class contains bean definitions.
  • @Bean: Defines a bean in the Spring context.
  • @Component: Marks the class as a Spring-managed component.
  • @Autowired: Indicates that Spring should perform dependency injection.
  • @Qualifier: Specifies which bean should be injected when there are multiple candidates.

Key Points

  • Explicit Injection: @Qualifier is used to explicitly specify which bean to inject when multiple beans of the same type exist.
  • Combination with @Autowired: @Qualifier is often used in conjunction with @Autowired to fine-tune the injection process.
  • Bean Names: The value of @Qualifier should match the name of one of the candidate beans. Bean names can be explicitly defined or default to the method name (for beans defined in configuration classes).

Conclusion

The @Qualifier annotation is a powerful feature in Spring that helps resolve ambiguity by specifying exactly which bean should be injected among multiple candidates. This is especially useful in larger applications where multiple beans of the same type might exist and precise control over dependency injection is required.

  1. What is @Primary annoatation?

The @Primary annotation in Spring is used to indicate that a specific bean should be given preference when multiple beans of the same type are present in the Spring context. When there are multiple candidates for autowiring, and one of them is annotated with @Primary, that bean will be injected by default unless another bean is explicitly specified with @Qualifier.

When to Use @Primary

Use the @Primary annotation when you want to set a default bean to be injected in situations where multiple beans of the same type exist. This is particularly useful when you have a general-purpose implementation and several specialized implementations of a bean.

Example

Step 1: Define Multiple Beans

java
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean public MyService myService1() { return new MyServiceImpl1(); } @Bean @Primary public MyService myService2() { return new MyServiceImpl2(); } }

In this configuration, myService2 is annotated with @Primary, indicating that it should be the default bean.

Step 2: Inject the Bean

java
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class MyComponent { private final MyService myService; @Autowired public MyComponent(MyService myService) { this.myService = myService; } public void performService() { myService.execute(); } }

In this example, the myService2 bean will be injected into MyComponent because it is marked as @Primary.

Explanation

  • @Configuration: Indicates that the class is a source of bean definitions.
  • @Bean: Defines a bean to be managed by the Spring container.
  • @Primary: Marks a bean as the primary candidate for injection when multiple beans of the same type are available.
  • @Autowired: Indicates that Spring should perform dependency injection.

Combining @Primary and @Qualifier

You can combine @Primary and @Qualifier annotations to provide both a default and specific beans.

java
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class MyComponent { private final MyService myPrimaryService; private final MyService mySpecificService; @Autowired public MyComponent(MyService myPrimaryService, @Qualifier("myService1") MyService mySpecificService) { this.myPrimaryService = myPrimaryService; this.mySpecificService = mySpecificService; } public void performPrimaryService() { myPrimaryService.execute(); } public void performSpecificService() { mySpecificService.execute(); } }

In this example:

  • myPrimaryService will be injected with myService2 because it is marked as @Primary.
  • mySpecificService will be injected with myService1 due to the @Qualifier annotation.

Key Points

  • Default Injection: @Primary is used to set a default bean for injection when multiple candidates are present.
  • Combining with @Qualifier: You can use @Primary for default behavior and @Qualifier for specific needs, providing flexibility in bean injection.
  • Bean Preferences: Only one bean of a given type should be marked as @Primary. If multiple beans are marked as @Primary, an exception will be thrown during application startup.

Conclusion

The @Primary annotation is a useful feature in Spring for resolving ambiguity when multiple beans of the same type exist. It allows you to designate a default bean to be injected, simplifying the configuration and reducing the need for explicit qualifiers in many cases. This is particularly helpful in large applications with multiple implementations of the same interface.

  1. What are uses of filter and ControllerAdvice?

In Spring, filters and @ControllerAdvice serve different purposes but are both essential in handling various cross-cutting concerns in web applications. Here's an overview of their uses:

Filters

Filters are used to perform filtering tasks on either the request to a resource or on the response from a resource, or both. They are part of the Servlet API and are used to preprocess or postprocess requests and responses.

Uses of Filters:

  1. Authentication and Authorization:

    • Filters can intercept incoming requests and check if the user is authenticated and authorized to access the requested resource.
  2. Logging and Auditing:

    • Filters can log request details, such as the request URL, parameters, and headers, as well as response details.
  3. Request and Response Modification:

    • Filters can modify request or response objects before they reach the controller or before they are sent back to the client.
  4. Cross-Origin Resource Sharing (CORS):

    • Filters can handle CORS settings by adding appropriate headers to responses.
  5. Compression:

    • Filters can compress response data before sending it to the client.

Example of a Filter:

java
import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter(urlPatterns = "/*") public class CustomFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // Initialization code } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // Pre-processing System.out.println("Request received at " + System.currentTimeMillis()); chain.doFilter(request, response); // Post-processing System.out.println("Response sent at " + System.currentTimeMillis()); } @Override public void destroy() { // Cleanup code } }

@ControllerAdvice

@ControllerAdvice is a specialization of the @Component annotation in Spring that allows you to handle exceptions, bind parameters, and apply model attributes to multiple controllers globally. It can be used to define shared logic for all or a subset of controllers.

Uses of @ControllerAdvice:

  1. Global Exception Handling:

    • @ControllerAdvice can be used to define methods that handle exceptions thrown by any controller. This centralizes exception handling logic.
  2. Model Attributes:

    • Methods annotated with @ModelAttribute within a @ControllerAdvice class can add attributes to the model, making them available to all controllers.
  3. Binding Initializers:

    • Methods annotated with @InitBinder within a @ControllerAdvice class can customize the data binding process for all controllers.

Example of @ControllerAdvice for Global Exception Handling:

java
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = { IllegalArgumentException.class, IllegalStateException.class }) protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) { String bodyOfResponse = "This should be application specific"; return new ResponseEntity<>(bodyOfResponse, HttpStatus.CONFLICT); } @ExceptionHandler(value = { Exception.class }) protected ResponseEntity<Object> handleGenericException(Exception ex, WebRequest request) { String bodyOfResponse = "An unexpected error occurred"; return new ResponseEntity<>(bodyOfResponse, HttpStatus.INTERNAL_SERVER_ERROR); } }

Key Differences

  • Filters:

    • Operate at a lower level than controllers, dealing with raw HTTP requests and responses.
    • Ideal for tasks that need to be performed on every request, such as security, logging, and compression.
    • Configured at the web application level, affecting all incoming requests.
  • @ControllerAdvice:

    • Operates at a higher level within the Spring MVC framework, specifically designed to work with controllers.
    • Ideal for handling exceptions, adding model attributes, and custom data binding across multiple controllers.
    • Provides a way to centralize controller-related concerns, making the codebase cleaner and easier to maintain.

Conclusion

Filters and @ControllerAdvice are powerful tools in Spring for managing cross-cutting concerns. Filters are best suited for low-level request and response processing, while @ControllerAdvice provides a higher-level mechanism for managing shared logic among controllers, such as exception handling and model attributes. Both can greatly enhance the modularity and maintainability of your Spring applications.

  1. Explain about different HTTP Methods?
  2. What is Idempotency? Explain about Idempotent methods?
  3. What is @Transactional annotation? Understand its uses?

he @Transactional annotation in Spring is used to manage transaction boundaries declaratively. It simplifies transaction management by allowing you to specify transaction properties directly in your code, without having to deal with low-level transaction APIs.

Uses of @Transactional

  1. Declarative Transaction Management:

    • Allows you to define the transactional behavior of methods directly within your code using annotations, instead of configuring transactions in XML or programmatically.
  2. Transaction Propagation:

    • Defines how transactions are propagated. You can specify if a method should run within an existing transaction or create a new one, among other behaviors.
  3. Isolation Levels:

    • Controls the degree of isolation for transactions, affecting how the transaction is visible to other transactions. This can help manage issues like dirty reads, non-repeatable reads, and phantom reads.
  4. Timeouts:

    • Specifies the maximum time a transaction should take before it is automatically rolled back. This can prevent long-running transactions from hanging indefinitely.
  5. Rollback Rules:

    • Determines which exceptions should cause a transaction to be rolled back. You can specify specific exceptions or even broad categories of exceptions.

Example

Step 1: Configure Transaction Management

Ensure that transaction management is enabled in your Spring configuration.

java
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; @Configuration @EnableTransactionManagement public class AppConfig { @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }

Step 2: Annotate Methods with @Transactional

Apply the @Transactional annotation to methods where you want to manage transactions.

java
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class MyService { @Transactional public void performTransaction() { // Business logic here // Any database operations performed within this method will be part of the transaction } }

Key Properties of @Transactional

  • Propagation:
    • Defines how the transaction should propagate. Common propagation types include:
      • REQUIRED: Supports the current transaction, or creates a new one if none exists.
      • REQUIRES_NEW: Suspends the current transaction and creates a new one.
      • MANDATORY: Supports the current transaction; throws an exception if none exists.
      • SUPPORTS: Executes within a transaction if one exists, otherwise, executes non-transactionally.
      • NOT_SUPPORTED: Executes non-transactionally, suspending any existing transaction.
      • NEVER: Executes non-transactionally; throws an exception if a transaction exists.
      • NESTED: Executes within a nested transaction if a current transaction exists, or creates a new one if none exists.
java
@Transactional(propagation = Propagation.REQUIRES_NEW) public void performAnotherTransaction() { // Business logic with a new transaction }
  • Isolation:
    • Defines the transaction isolation level. Common levels include:
      • DEFAULT: Uses the default isolation level of the underlying datastore.
      • READ_UNCOMMITTED: Allows dirty reads.
      • READ_COMMITTED: Prevents dirty reads.
      • REPEATABLE_READ: Prevents dirty reads and non-repeatable reads.
      • SERIALIZABLE: Ensures full serializability but may impact performance.
java
@Transactional(isolation = Isolation.SERIALIZABLE) public void performSerializableTransaction() { // Business logic with serializable isolation }
  • Timeout:
    • Specifies the timeout for a transaction in seconds.
java
@Transactional(timeout = 5) public void performTimedTransaction() { // Business logic that must complete within 5 seconds }
  • RollbackFor and NoRollbackFor:
    • Specifies which exceptions should trigger a rollback or which should not.
java
@Transactional(rollbackFor = {Exception.class}) public void performTransactionWithRollback() { // Business logic that will rollback on any exception } @Transactional(noRollbackFor = {IllegalArgumentException.class}) public void performTransactionWithoutRollback() { // Business logic that will not rollback on IllegalArgumentException }

Example Usage

Here is an example that combines multiple properties:

java
@Transactional( propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ, timeout = 10, rollbackFor = {RuntimeException.class} ) public void performComplexTransaction() { // Business logic with specific transaction properties }

Conclusion

The @Transactional annotation is a powerful tool in Spring for managing transactions declaratively. It allows developers to specify transaction management properties directly in the code, improving readability and maintainability. By using @Transactional, you can control transaction propagation, isolation levels, timeouts, and rollback rules effectively, ensuring your application's data integrity and consistency.

  1. What is actuator? Explain about few endpoints of actuator?

Spring Boot Actuator is a sub-project of Spring Boot that provides production-ready features to help you monitor and manage your application. It includes several built-in endpoints that expose various aspects of the application, such as health, metrics, environment properties, and more. These endpoints are primarily used for monitoring, diagnostics, and managing the application in production environments.

Key Features of Spring Boot Actuator

  1. Health Checks: Provides information about the health of the application.
  2. Metrics: Exposes metrics related to the application's performance and usage.
  3. Environment: Displays the environment properties and configurations.
  4. Thread Dump: Provides a thread dump for the application.
  5. Mappings: Shows the mappings of controllers and methods.
  6. Loggers: Allows you to view and configure logging levels at runtime.
  7. Custom Endpoints: Allows you to create your own custom actuator endpoints.

Common Actuator Endpoints

Here are some commonly used Actuator endpoints and their purposes:

  1. /actuator/health:

    • Provides the health status of the application.
    • Example response:
      json
      { "status": "UP" }
    • You can also add custom health indicators to include more detailed health checks.
  2. /actuator/info:

    • Displays arbitrary application information.
    • Example response:
      json
      { "app": { "name": "MyApp", "version": "1.0.0" } }
    • This can be configured to show application-specific information such as version, build details, etc.
  3. /actuator/metrics:

    • Provides various metrics related to the application, such as memory usage, garbage collection, CPU usage, etc.
    • Example response:
      json
      { "names": [ "jvm.memory.used", "jvm.memory.max", "system.cpu.usage" ] }
  4. /actuator/mappings:

    • Displays a list of all @RequestMapping paths and their associated handlers.
    • Example response:
      json
      { "contexts": { "application": { "mappings": { "dispatcherServlets": { "dispatcherServlet": [ { "handler": "public java.lang.String com.example.MyController.home()", "predicate": "{GET /}" } ] } } } } }
  5. /actuator/env:

    • Shows the current environment properties.
    • Example response:
      json
      { "propertySources": [ { "name": "systemProperties", "properties": { "java.version": { "value": "11.0.6" } } } ] }
  6. /actuator/loggers:

    • Allows you to view and change the logging level of the application at runtime.
    • Example response:
      json
      { "loggers": { "com.example.MyClass": { "configuredLevel": "DEBUG", "effectiveLevel": "DEBUG" } } }
  7. /actuator/threaddump:

    • Provides a thread dump of the application.
    • Example response:
      json
      [ { "threadName": "http-nio-8080-exec-1", "threadId": 28, "blockedTime": -1, "blockedCount": 3, "waitedTime": -1, "waitedCount": 13, "lockName": null, "lockOwnerId": -1, "lockOwnerName": null, "inNative": false, "suspended": false, "threadState": "RUNNABLE", "stackTrace": [ { "className": "java.net.SocketInputStream", "methodName": "socketRead0", "fileName": "SocketInputStream.java", "lineNumber": -2, "nativeMethod": true } ] } ]

Enabling Actuator in Spring Boot

To enable Actuator in your Spring Boot application, you need to add the spring-boot-starter-actuator dependency in your pom.xml (for Maven) or build.gradle (for Gradle) file.

Maven

xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

Gradle

groovy
implementation 'org.springframework.boot:spring-boot-starter-actuator'

Configuring Actuator

You can configure which endpoints are exposed and their access levels in the application.properties or application.yml file.

Example Configuration (application.properties)

properties
management.endpoints.web.exposure.include=health,info,metrics management.endpoint.health.show-details=always management.endpoint.metrics.enabled=true

Securing Actuator Endpoints

By default, all Actuator endpoints are unsecured. In a production environment, it's crucial to secure these endpoints. You can use Spring Security to secure them.

Example Security Configuration

java
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/actuator/**").authenticated() .and() .httpBasic(); } }

Conclusion

Spring Boot Actuator provides a comprehensive set of tools for monitoring and managing your application. By leveraging Actuator's built-in endpoints, you can gain insights into the application's health, performance, and behavior. Proper configuration and securing of these endpoints are crucial for maintaining a secure and efficient production environment.

  1. What is Profiling in Spring boot?

Profiling in Spring Boot refers to the mechanism of defining and managing different environments for an application, such as development, testing, and production. This is achieved using the @Profile annotation, which allows you to specify beans that should only be created in certain profiles. By using profiles, you can segregate configurations and beans specific to different environments, making your application more flexible and easier to manage.

Key Features of Profiling

  1. Environment-Specific Configuration:

    • Different configurations can be loaded for different environments, such as database connections, properties, or services.
  2. Conditional Bean Creation:

    • Beans can be created conditionally based on the active profile, ensuring that only the necessary beans are loaded in a particular environment.
  3. Simplified Management:

    • Profiles simplify the management of configurations by centralizing environment-specific properties and beans.

How to Use Profiles in Spring Boot

1. Defining Profiles in Configuration

You can define environment-specific properties in separate files, typically named application-{profile}.properties or application-{profile}.yml.

Example: application-dev.properties

properties
spring.datasource.url=jdbc:mysql://localhost:3306/devdb spring.datasource.username=devuser spring.datasource.password=devpassword

Example: application-prod.properties

properties
spring.datasource.url=jdbc:mysql://localhost:3306/proddb spring.datasource.username=produser spring.datasource.password=prodpassword

2. Activating Profiles

Profiles can be activated in several ways:

  • Command Line: Use the --spring.profiles.active argument when running the application.

    sh
    java -jar myapp.jar --spring.profiles.active=dev
  • Environment Variable: Set the SPRING_PROFILES_ACTIVE environment variable.

    sh
    export SPRING_PROFILES_ACTIVE=dev
  • Application Properties: Define the active profile in the application.properties file.

    properties
    spring.profiles.active=dev

3. Using @Profile Annotation

You can annotate beans or configuration classes with @Profile to specify the profiles in which they should be active.

Example: Conditional Bean Definition

java
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @Configuration public class AppConfig { @Bean @Profile("dev") public DataSource devDataSource() { return DataSourceBuilder.create() .url("jdbc:mysql://localhost:3306/devdb") .username("devuser") .password("devpassword") .build(); } @Bean @Profile("prod") public DataSource prodDataSource() { return DataSourceBuilder.create() .url("jdbc:mysql://localhost:3306/proddb") .username("produser") .password("prodpassword") .build(); } }

Example Usage

Consider an application with different configurations for development and production environments. You can use profiles to manage these configurations effectively.

Step 1: Define Configuration Files

Create application-dev.properties and application-prod.properties with respective configurations.

Step 2: Define Beans with @Profile

java
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @Configuration public class AppConfig { @Bean @Profile("dev") public DataSource devDataSource() { return DataSourceBuilder.create() .url("jdbc:mysql://localhost:3306/devdb") .username("devuser") .password("devpassword") .build(); } @Bean @Profile("prod") public DataSource prodDataSource() { return DataSourceBuilder.create() .url("jdbc:mysql://localhost:3306/proddb") .username("produser") .password("prodpassword") .build(); } }

Step 3: Activate Profiles

Activate the desired profile when running the application. For development:

sh
java -jar myapp.jar --spring.profiles.active=dev

For production:

sh
java -jar myapp.jar --spring.profiles.active=prod

Conclusion

Profiling in Spring Boot provides a powerful and flexible way to manage environment-specific configurations and beans. By using profiles, you can ensure that the appropriate configurations are loaded for each environment, simplifying development, testing, and deployment processes. The @Profile annotation and profile-specific property files make it easy to segregate and manage these configurations effectively.

  1. How Spring Knows Property file location? explain about different properties?
  2. @Async in Spring Boot?

In Spring Boot, the @Async annotation is used to execute methods asynchronously. This means that the method will be executed in a separate thread, and the caller will not block waiting for the method to complete. This can be useful for improving the performance of your application by offloading time-consuming tasks to background threads.

Key Features of @Async

  1. Asynchronous Execution:

    • Methods annotated with @Async will run in a separate thread, allowing the main thread to continue processing other tasks.
  2. Non-Blocking:

    • The caller can continue executing without waiting for the @Async method to complete, which can lead to improved responsiveness and performance.
  3. Return Types:

    • @Async methods can return void, java.util.concurrent.Future, org.springframework.util.concurrent.ListenableFuture, or java.util.concurrent.CompletableFuture.

Enabling Async Support

To use @Async in your Spring Boot application, you need to enable async processing by adding the @EnableAsync annotation to one of your configuration classes.

Step 1: Enable Async Support

Create a configuration class and annotate it with @EnableAsync.

java
import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; @Configuration @EnableAsync public class AsyncConfig { }

Step 2: Annotate Methods with @Async

Annotate the methods you want to run asynchronously with @Async.

java
import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.concurrent.CompletableFuture; @Service public class AsyncService { @Async public void asyncMethod() { System.out.println("Executing method asynchronously - " + Thread.currentThread().getName()); } @Async public CompletableFuture<String> asyncMethodWithReturn() { System.out.println("Executing method asynchronously with return - " + Thread.currentThread().getName()); return CompletableFuture.completedFuture("Hello from async method!"); } }

Example Usage

Here's an example that demonstrates how to use @Async in a Spring Boot application.

Step 1: Create a Service with Async Methods

java
import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.concurrent.CompletableFuture; @Service public class AsyncService { @Async public void performTask() { System.out.println("Performing task in background - " + Thread.currentThread().getName()); try { Thread.sleep(5000); // Simulate long running task } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Task completed"); } @Async public CompletableFuture<String> performTaskWithReturn() { System.out.println("Performing task in background with return - " + Thread.currentThread().getName()); try { Thread.sleep(5000); // Simulate long running task } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return CompletableFuture.completedFuture("Task completed with return"); } }

Step 2: Call Async Methods from a Controller

java
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.CompletableFuture; @RestController @RequestMapping("/api") public class AsyncController { @Autowired private AsyncService asyncService; @GetMapping("/async") public String callAsyncMethod() { asyncService.performTask(); return "Async method called"; } @GetMapping("/async-return") public CompletableFuture<String> callAsyncMethodWithReturn() { return asyncService.performTaskWithReturn(); } }

Example Output

When you call the /api/async endpoint, you will see something like this in the console:

arduino
Performing task in background - SimpleAsyncTaskExecutor-1

And the response will be:

sql
Async method called

For the /api/async-return endpoint, the response will be returned once the asynchronous method completes:

arduino
Performing task in background with return - SimpleAsyncTaskExecutor-2

And the response will be:

arduino
Task completed with return

Customizing the Executor

By default, Spring Boot uses a SimpleAsyncTaskExecutor, which is not suitable for production. You can customize the executor by defining a ThreadPoolTaskExecutor bean.

java
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; @Configuration @EnableAsync public class AsyncConfig { @Bean(name = "taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.setThreadNamePrefix("Async-"); executor.initialize(); return executor; } }

Then, specify this executor for your async methods:

java
@Async("taskExecutor") public void asyncMethod() { // ... }

Conclusion

The @Async annotation in Spring Boot allows you to execute methods asynchronously, improving the responsiveness and performance of your application by offloading time-consuming tasks to background threads. By enabling async support and customizing the executor, you can effectively manage asynchronous processing in your Spring Boot application.

  1. Difference between CrudRepository vs JPARespository?

In Spring Data, CrudRepository and JpaRepository are both interfaces that provide CRUD (Create, Read, Update, Delete) functionality for interacting with a database. However, they have some differences in terms of the features they offer and the way they are used.

CrudRepository

  • Basic CRUD Operations: CrudRepository provides basic CRUD operations such as save, findById, findAll, delete, and count.
  • Interface Definition: It is a generic interface that you can extend for a specific entity type. For example, CrudRepository<User, Long> for a User entity with a primary key of type Long.
  • Limited Query Methods: Provides only basic query methods. For complex queries, you need to define custom query methods in your repository interface.

JpaRepository

  • Extends CrudRepository: JpaRepository extends CrudRepository, so it inherits all the basic CRUD operations.
  • Additional Query Methods: Provides additional JPA-specific query methods such as findAllByOrderBy{Property}Asc, findFirstBy{Property}, deleteBy{Property}, etc. These methods are derived from the method name, and you don't need to write the query explicitly.
  • Pagination and Sorting: Supports pagination and sorting out of the box with methods like findAll(Pageable pageable) and findAll(Sort sort).
  • Entity Manager: JpaRepository extends PagingAndSortingRepository, which in turn extends CrudRepository, and also provides access to the EntityManager if you need it for custom queries.

Choosing Between CrudRepository and JpaRepository

  • If you need only basic CRUD operations and don't require complex query methods or pagination, CrudRepository is sufficient.
  • If you need additional query methods, pagination, and sorting features provided by JPA, JpaRepository is more suitable.
  • If you are using Spring Data JPA and want to leverage its query derivation features, JpaRepository is the better choice.

Example Usage

CrudRepository

java
import org.springframework.data.repository.CrudRepository; public interface UserRepository extends CrudRepository<User, Long> { }

JpaRepository

java
import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> { List<User> findByFirstName(String firstName); }

In this example, UserRepository extends JpaRepository and provides a custom query method findByFirstName, which is a JPA-specific query method that retrieves users by their first name.

  1. Explain about different starter dependencies of Spring boot?

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.