Sunday 24 October 2021

Spring, Spring-Boot, Microservices Interview Questions and Answers

» What is a Spring Bean?

A "Spring Bean" is simply a Java object.

When Java objects are created by the Spring Container, then Spring refers to them as "Spring Beans".

The Spring container (application context) is responsible for the creation and lifecycle management of beans.

Spring Beans are created from normal Java classes .... just like Java objects.

In the early days, there was a term called "Java Beans". Spring Beans have a similar concept but Spring Beans do not follow all of the rigorous requirements of Java Beans.

» What's the use-case of IOC (Inversion of Control)?

IoC is for externalizing the construction and management of objects.

Create and manage objects

Inversion of control basically means objects are not responsible for ( or in control of ) creating their own dependencies. Control is 'inverted'. So instead of your code handling bean creation with the 'new' operator -- the framework ( the Spring container ) does the work. You create the components required by your application and the framework manages/calls them into play.

Normally in your code, you create objects like:

Employee emp = new Employee();

But using Spring it will handle the creation of objects for you. It is like outsourcing so instead of you doing the work, outsource it and someone will do the work. This is handled by spring's object factory.

Why is this configuration necessary - To make your application flexible. When using Spring's XML based configuration, you can swap in new implementations without having to recompile your source code.

» Spring container configuration

3 ways to configure a spring container:

1) XML configuration file (Most legacy apps use this)

2) Java Annotation

3) Java Source Code

Java Annotation and Java Source Code are the modern ways to configure spring container

Example: XML configuration file

Create Java Interface and Class

package com.springbasic.ioc;

public interface Sports {
	public String getInfo();
}
package com.springbasic.ioc;

public class Cricket implements Sports {

	@Override
	public String getInfo() {
		String info = "Cricket is a bat-and-ball game played between two teams of eleven players on a field at the centre of which is a 22-yard (20-metre) pitch with a wicket at each end, each comprising two bails balanced on three stumps";
		return info;
	}
}

Create Spring container i.e. applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
    
       
	<!-- Define your beans here -->
	<bean id="mySports" class="com.springbasic.ioc.Cricket">
	</bean>
		

</beans>

Here we have created a bean i.e. mySports and class attribute defines the implementation class name i.e. com.springbasic.ioc.Cricket

Lets create a main class now

package com.springbasic.ioc;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SportsMain {

	public static void main(String[] args) {
		// load spring configuration file
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

		// retrieve bean from spring container
		Sports theSport = context.getBean("mySports", Sports.class);

		// call methods on the bean
		String result = theSport.getInfo();
		System.out.println(result);
	}
}

» Spring Dependency Injection

Dependency injection is a pattern used to create instances of objects that other objects rely upon without knowing at compile time which class will be used to provide that functionality or simply the way of injecting properties to an object is called dependency injection. We have 2 ways to inject Dependency injection:

1. Constructor Injection

2. Setter/Getter Injection

More on this: Dependency Injection in Spring

» Constructor Injection:

package com.springbasic.ioc;

public interface FortuneService {
	public String getFortune();
}
package com.springbasic.ioc;

public class HappyFortuneService implements FortuneService {

	@Override
	public String getFortune() {
		return "Its you lucky day today";
	}

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">


	<!-- Define your beans here -->

	<!-- Define dependency -->
	<bean id="myFortune" class="com.springbasic.ioc.HappyFortuneService"></bean>

	<bean id="mySports" class="com.springbasic.ioc.Cricket">	
		<!-- Setup constructor injection -->
		<constructor-arg ref="myFortune"></constructor-arg>
	</bean>


</beans>
package com.springbasic.ioc;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SportsMain {

	public static void main(String[] args) {
		// load spring configuration file
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

		// retrieve bean from spring container
		Sports theSport = context.getBean("mySports", Sports.class);

		// call methods on the bean
		String result = theSport.getDailyFortune();
		System.out.println(result);
		
		context.close();
	}
}
package com.springbasic.ioc;

public interface Sports {
	public String getInfo();
	public String getDailyFortune();
}
package com.springbasic.ioc;

public class Cricket implements Sports {

	// define a private field for dependency
	private FortuneService fortuneService;
	
	// define constructor for dependency injection
	public Cricket(FortuneService theFortuneService) {
		fortuneService = theFortuneService;
	}
	
	@Override
	public String getDailyFortune() {
		// Use fortuneService to get a fortune
		return fortuneService.getFortune();
	}
	
	@Override
	public String getInfo() {
		String info = "Cricket is a bat-and-ball game played between two teams of eleven players on a field at the centre of which is a 22-yard (20-metre) pitch with a wicket at each end, each comprising two bails balanced on three stumps";
		return info;
	}	
}

So Spring will actually construct our object (private FortuneService fortuneService;), they'll pass in a dependency (FortuneService theFortuneService), and then we accept it, and then we simply assign it (fortuneService = theFortuneService;). And that's basically a Dependency Injection.

» Bean Scopes

Scope refers to the lifecycle of a bean. It tells you how long does the bean live, how many instances are created and how is the bean shared.

Default scope for Bean is Singleton.

What is Singleton?

The Spring Container creates only one instance of the bean, it's cached in memory and then all requests for that bean will return a shared reference to the same bean, so the end result is that there is only one bean and everyone will share it.

» Additional Spring Bean Scopes

Singleton: Create a single shared instance of the bean. Default scope.

Prototype: Creates a new bean instance for each container request.

Request: Scoped to an HTTP web request. Only used for web apps.

Session: Scoped to an HTTP web session. Only used for web apps.

Global-session: Scoped to a global HTTP web session. Only used for web apps.

» Prototype Scope Example:

Prototype Scope: new object for each request

Example:

<beans ...>
	<bean id="myCoach" class="com.luv2code.springdemo.TrackCoach" scope="prototype">
		....
	</bean>
</beans>

» Defining init and destroy methods - Method Signatures. Special Note about init and destroy Method Signatures:

When using XML configuration, I want to provide additional details regarding the method signatures of the init-method and destroy-method.

Access modifier: The method can have any access modifier (public, protected, private)

Return type: The method can have any return type. However, "void' is most commonly used. If you give a return type just note that you will not be able to capture the return value. As a result, "void" is commonly used.

Method name: The method can have any method name.

Arguments: The method can not accept any arguments. The method should be no-arg.

» Destroy Lifecycle and Prototype Scope:

For "prototype" scoped beans, Spring does not call the destroy method.

In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean: the container instantiates, configures, and otherwise assembles a prototype object, and hands it to the client, with no further record of that prototype instance.

Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype bean(s) are holding.

This also applies to both XML configuration and Annotation-based configuration.

» When does bean actually get destroyed.

The bean is destroyed when it loses scope.

» How to create code to call the destroy method on prototype scope beans?

Spring won't manage the lifecycle of the prototype bean, so @PreDestroy (etc.) methods need to be called by your own code. Spring won't retain a reference, that means You can destroy prototype beans but custom coding is required.

» What are Java Annotations?

Java annotations are special labels/markers added to Java classes.

It provides the metadata about the class

It processed at compile time or run-time for special processing

Example:

public class Employee implements Department{
	@override	// Here @override is a Java Annotation
	public String getEmpInfo(){
		return "info";
	}
}

» Why to use Spring configuration with Annotations?

If you are working on a large project then XML configuration can be verbose.

Annotations minimizes the XML configuration.

Once you add an annotation to a class, then Spring will actually scan your Java classes for those annotations. When it finds a class that has a special Spring annotation on it, it'll automatically register that bean with the Spring container.

So instead of doing everything long hand via XML config file, Spring will just scan a bean and will register it with the Spring container.

Basically Spring will help you out here and do a lot of work for you in the background by scanning your classes.

Example:

1) Enable component scanning in spring config file (applicationContext.xml)

<beans ..>
   <context:component-scan base-package="com.demo.mypackage">
</beans>

here spring will scan com.demo.mypackage recursively

2) Add the @Component Annotation to you Java Class

@Component("mycomp") // here is the component annotation with spring bean
public class Employee implements Department{
	@override	// Here @override is a Java Annotation
	public String getEmpInfo(){
		return "info";
	}
}

It will register mycomp spring bean automatically

3) Retrieve bean from Spring Container

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext('applicationContext.xml');

Emp e = context.getBean("mycom", Emp.class);

context.close();

» What are Microservices?

Microservices is a way to split your application into set of smaller, interconnected services instead of building a single monolithic application.

Each microservices has its own architecture consisting of its own business logic.

Its beneficial for you as you can test these services as their own and different developer teams on their own or all of them can be released individually to production at a time.

» What is Spring Cloud Config Server?

Spring cloud Config Server is a common microservice that will be talking with all other microservices. i.e. It provides server-side and client-side support for externalized configuration.

All the common configuration will be placed in Spring Cloud Config Server microservice.

Maven users can add the below dependency into the pom.xml file.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-config-server</artifactId>
</dependency>

More on this: https://www.tutorialspoint.com/spring_boot/spring_boot_cloud_configuration_server.htm

» What is Load Balancing?

Load balancing improves the distribution of workloads across multiple Microservices or you can say Computing Resources

Consider you have a microservice i.e. ProductService running on multiple instances and you are getting thousands of request for ProductService. In this case you can divide the request load among these services by using multiple running instances.

There are two types of load balancer: 1) Client Side and 2) Server Side

Client Side Load Balancer: The load balancing decision resides with the client itself. The client can take the help of a naming server (eg. Netflix Eureka) to get the list of registered backend server instances, and then route the request to one of these backend instances using client-side load balancing libraries like Netflix Ribbon.

Advantages of client-side load balancing:

- No more single point of failure as in the case of the traditional load balancer approach.

- Reduced cost as the need for server-side load balancer goes away.

- Less network latency as the client can directly invoke the backend servers removing an extra hop for the load balancer.

Server Side Load Balancer: All backend server instances are registered with a central load balancer. A client requests this load balancer which then routes the request to one of the server instances using various algorithms like round-robin. AWS ELB is a prime example of server-side load-balancing that registers multiple EC2 instances launched in its auto-scaling group and then routes the client requests to one of the EC2 instances.

Advantages of server-side load balancing:

- Simple client configuration: only need to know the load-balancer address.

- Clients can be untrusted: all traffic goes through the load-balancer where it can be looked at.

- Clients are not aware of the backend servers.

» What Ribbon Load Balancer?

It is a client side load balancer that gives you a lot of control over the behaviour of HTTP and TCP client.

Dependency: spring-cloud-starter-netflix-ribbon

When we use Feign, the Ribbon also applies. Feign is a client for calling other microservices. You need to add dependency of it in pom.xml

» What Eureka Naming Server?

Eureka Naming Server is an application that holds information about all client service applications.

Each microservice registers itself with Eureka Naming Server.

The naming server registers the client services with their port numbers and ip addresses.

It is also known as Discovery Server.

It is REST-based server that is used in AWS cloud services for load balancing and fallover for middle tier services

» What is API Gateway?

An API gateway is an API management tool that sits between a client and a collection of backend services.

It takes care of invites or requests, matching them to the suitable stations or services for request/call processing, and sends them back to the target resource.

It implements some of the common features like:

Authentication and Authorization

Rate Limit: If you want to execute certain no. of request (Ex. 100 per hour) for that we can use Rate Limit.

Fault Tolerance: If your microservice is dependent on another microservice and that microservice is not up and running so in this kind of situation we need to provide a default output to the calling microservice that is what Fault Tolerance does.

Service Aggregation: Suppose a client application wants to use your multiple APIs i.e. 10 or 100. In this case you can't provide an endpoint of all the APIs to client application instead you will just provide one endpoint and internally you can connect with you APIs and provide only single implementation to client side application.

» How can you optimize the startup time of app?

Optimizing the startup time of a Spring Boot application is essential for ensuring a smooth user experience. Here are some simple ways to optimize startup time:

  • Reduce Dependencies:

    - Minimize the number of dependencies your application relies on. Each dependency adds to the application's startup time, as Spring Boot needs to initialize and configure them. - Review your pom.xml or build.gradle file and remove any unnecessary dependencies.

  • Lazy Initialization:

    - Utilize lazy initialization to defer the loading of beans until they are actually needed. This can significantly reduce the startup time by preventing unnecessary bean creation during application startup. - Use @Lazy annotation on beans that don't need to be initialized eagerly.

  • Profile Specific Configuration:

    - Utilize Spring profiles to load only the necessary configuration based on the environment (e.g., development, production). - Avoid loading unnecessary configurations and beans for each environment, which can help reduce startup time.

  • Optimize Auto-Configuration:

    - Spring Boot's auto-configuration feature automatically configures beans based on the classpath and the presence of certain dependencies. - Review and optimize auto-configuration to only include necessary configurations for your application. Exclude auto-configurations that are not required.

  • Use Spring Initializr wisely:

    - When creating a Spring Boot project using Spring Initializr, choose only the dependencies you need. Avoid selecting unnecessary dependencies, as they can increase the startup time of your application.

  • Enable Spring Boot DevTools (for development):

    - Spring Boot DevTools provide features like automatic application restarts and live reload during development. - While DevTools may slightly increase startup time in development mode, they can significantly improve developer productivity and overall development speed.

Example: Suppose you have a Spring Boot application that has multiple dependencies and beans that are initialized eagerly. To optimize its startup time:

  • Review the pom.xml file and remove any unnecessary dependencies that are not being used in the application.
  • Identify beans that can be lazily initialized and annotate them with @Lazy.
  • Use Spring profiles to load only the necessary configurations based on the environment.
  • Review auto-configuration classes and exclude any unnecessary auto-configurations.
  • When using Spring Initializr to create a new project, only select the dependencies that your application needs.
  • During development, enable Spring Boot DevTools to take advantage of its features for faster development iterations.

By implementing these optimizations, you can significantly reduce the startup time of your Spring Boot application, providing a better user experience and faster application responsiveness.

» What is role of Actuator in monitoring startup time?

Spring Boot Actuator is a set of features that can track and monitor startup information for Spring Boot applications. It can help you understand the application's startup sequence, context lifecycle, and time spent during startup phases.

Actuator exposes operational information about the running application, such as:

Health Provides an endpoint to monitor the application's health status

Metrics Monitors key metrics such as the response time and number of requests

Logging Retrieves log files, which is useful in debugging

Auditing Tracks users' actions

Actuator uses HTTP endpoints or JMX beans to enable interaction.

To enable Spring Boot Actuator, you can:

1. Add the spring-boot-starter-actuator dependency to your POM

2. Add the spring-boot-starter-web dependency

3. Set the configuration property in your application.properties file

Actuator allows you to monitor your application's performance and resource usage with helpful metrics, such as memory usage, request rates, and response times. This information can help you diagnose issues and optimize your application's performance.

» Explain how classpath scanning mechanism affects startup time

Classpath scanning is a mechanism that Spring uses to detect classes that need to be managed by the Spring container. This is done by scanning the classpath for classes that are annotated with the @Component, @Service, @Repository, or @Controller annotations.

The classpath scanning mechanism can have a significant impact on startup time, especially for large applications with a large number of classes. This is because Spring needs to scan the entire classpath for each class that it needs to manage.

There are a few things that you can do to reduce the impact of classpath scanning on startup time:

Use the @ComponentScan annotation to specify the packages that Spring should scan. This will prevent Spring from scanning the entire classpath, which can save a significant amount of time.

Use custom filters to exclude classes from being scanned. This can be useful for excluding classes that are not needed by Spring, such as test classes or classes that are only used in certain environments.

Use a Spring Boot starter project: Spring Boot starter projects include a number of features that can help to reduce startup time, including classpath scanning optimization.

» What are some tools/techniques to analyze startup time?

1. Spring Boot Actuator:

- Utilize Actuator's /metrics and /health endpoints to gather metrics and health status during startup. - Actuator provides insights into bean initialization, application context creation, and other startup-related metrics.

2. Application Profilers:

- Use profiling tools like YourKit, VisualVM, or JProfiler to profile the application during startup. - Profilers help identify hotspots, memory usage, and thread activity that contribute to startup time.

3. Logging:

- Enable debug logging during startup to capture detailed logs of bean initialization, component scanning, and other startup processes. - Analyze log messages to identify bottlenecks, errors, or delays during startup.

4. Application Monitoring Tools:

- Use application monitoring tools like New Relic, Datadog, or AppDynamics to monitor application performance in real-time. - These tools provide insights into CPU usage, memory consumption, and response times during startup.

5. Spring Boot DevTools:

- Enable DevTools to automatically restart the application when changes are detected during development. - DevTools provide quick feedback on startup time improvements by reducing the need for manual restarts.

6. Custom Timing Metrics:/

- Instrument the application code with custom timing metrics to measure the duration of specific startup tasks. - Use libraries like Micrometer or Spring Boot's MeterRegistry to record and report custom metrics.

7. Benchmarking Tools:

- Use benchmarking tools like JMH (Java Microbenchmark Harness) to benchmark specific startup tasks or components. - Benchmarking helps identify performance bottlenecks and evaluate the impact of optimizations.

8. Static Code Analysis:

- Perform static code analysis using tools like SonarQube or Checkstyle to identify potential code smells or anti-patterns that may impact startup time. - Addressing code quality issues can improve overall application performance, including startup time.

» What are Fault Tolerance and Circuit Breaker?

Fault Tolerance:

Think of a power generator at a hospital. If there's a sudden power outage, the generator kicks in to supply electricity, ensuring that critical operations like surgeries can continue without interruption. Here, the generator acts as a backup to ensure the hospital remains operational even if the primary power source fails.

Fault tolerance is like having a backup plan. If one part of a system fails or has a problem, another part can step in to keep things running smoothly.

Circuit Breaker:

Imagine you're using multiple appliances in your kitchen, such as a toaster, microwave, and blender. If all these appliances are running simultaneously and draw too much power, it could overload the circuit. A circuit breaker in your home's electrical panel detects this overload and "trips," cutting off the power to prevent overheating or electrical fires. Once the issue is resolved, you can reset the circuit breaker to restore power safely.

A circuit breaker is like a safety switch in your home's electrical panel. If there's a sudden surge of electricity or a fault in the wiring, the circuit breaker "trips" or "breaks" the circuit, cutting off the power to prevent damage or fires.

So, fault tolerance ensures that if one component fails, other components can continue to operate, maintaining system availability and reliability. On the other hand, a circuit breaker mechanism helps in preventing system overload or failures by "breaking" or "tripping" when certain thresholds are exceeded, thereby protecting the system and allowing it to recover gracefully without causing widespread failures or outages.

» How do you create microservices in in Spring boot?

In today's digital era, microservices architecture offers scalability and flexibility. Let's explore how to create a simple e-commerce app using Spring Boot with three microservices: Product, Order, and User.

Setup Spring Boot Projects

Generate three Spring Boot projects using Spring Initializr: product-service, order-service, and user-service.

Define Dependencies

Add Spring Web, Spring Data JPA, and H2 Database dependencies to each project's pom.xml.

Create Microservice Components

Product Service

Create a Product entity, repository, and controller for CRUD operations.

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String description;
    private BigDecimal price;
    
    // Getters and Setters
}

Repository: Create a ProductRepository interface extending JpaRepository.

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
}

Controller: Implement a RESTful API to fetch products.

@RestController
@RequestMapping("/api/products")
public class ProductController {
    @Autowired
    private ProductRepository productRepository;

    @GetMapping
    public List<Product> getAllProducts() {
        return productRepository.findAll();
    }
}

Order Service: Similar to the Product Service, create entities (Order, OrderItem) and corresponding repositories and controllers.

User Service: Define a User entity and implement CRUD operations using repositories and controllers.

Configure Service Communication:

Use Spring Cloud Eureka for service registration and discovery. Each microservice registers itself with the Eureka server and looks up dependencies via service names.

Implement Service Interactions:

In the Order Service, when creating an order, call the Product Service to verify product availability and User Service to validate user details.

Dockerize Microservices

Create Dockerfile for each microservice to containerize them. Use docker-compose to manage multi-container Docker applications, defining services, networks, and volumes.

Implement Circuit Breakers

Use Spring Cloud Circuit Breaker (Hystrix) to handle failures gracefully between service interactions.

Build and Deploy

Build Docker images for each microservice and deploy them to a container orchestration platform like Kubernetes or cloud platforms (AWS, Azure, Google Cloud).

» Explain the components (entities, repositories, controllers) for each microservice. Explain how service communication is implemented using Spring Cloud Eureka and service interactions between microservices.

Components of Microservices:

Entities:

Think of entities as the blueprint for your data. In our e-commerce example, each microservice has its data structure:

Product Service: Defines what a product looks like (e.g., name, price).

Order Service: Defines what an order looks like (e.g., items, customer details).

User Service: Defines what a user looks like (e.g., username, email).

Repositories:

Repositories are like specialized libraries where you can save, retrieve, and manage your data.

Product Repository: This is where you'd save, fetch, or delete product information.

Order Repository: This is where you'd handle saving and fetching order details.

User Repository: This manages user-related tasks like saving user details.

Controllers:

Controllers act as traffic controllers for your application, deciding what to do when you receive a request.

Product Controller: When someone wants product info, this controller handles it.

Order Controller: This manages requests related to orders, like placing a new order.

User Controller: When someone needs user details or wants to create a new user, this controller takes care of it.

Service Communication using Spring Cloud Eureka:

Imagine you have three friends, and instead of calling their names whenever you want something, you use a directory or a phonebook to find them easily. Spring Cloud Eureka acts like that phonebook for your microservices.

Registering Services: Each microservice registers itself with Eureka when it starts up. It's like adding your friend's contact details to the phonebook.

Discovering Services: If one microservice needs to talk to another, instead of hardcoding the location, it asks Eureka where the other service is. This way, it can find and communicate with other services without guessing their locations.

Service Interactions Between Microservices:

Imagine you're buying a product online. Multiple things need to happen behind the scenes:

You browse products (Product Service).

You add products to the cart and place an order (Order Service).

You might need to log in or create an account (User Service).

These services don't work in isolation; they talk to each other:

Order Service might ask the Product Service if a product is available when you try to buy it.

When creating an order, the Order Service might check with the User Service to ensure you're a valid user.

This interaction is crucial because each microservice handles its specialized task, but they work together to provide a seamless experience for you, the user. And Spring Cloud Eureka ensures they find and communicate with each other easily.

No comments:

Post a Comment