Sunday, 8 December 2024

Creating Beans in Spring Framework: Detailed Explanation with Project and Reasons

    Spring Framework offers several methods to create and manage beans. Each method is designed for specific use cases and advantages, making Spring flexible and adaptable to different scenarios.

1. Using @Component and Stereotype Annotations

Why Use It

  • Simplifies bean declaration by annotating the class directly.
  • Enables Spring to auto-scan and register beans, reducing boilerplate code.

Implementation

//ComponentBean.java

package com.example.springbeans.model;

import org.springframework.stereotype.Component;

@Component

public class ComponentBean {

    public String getMessage() {

        return "Hello from @Component Bean!";

    }

}

  • Reason: Ideal for lightweight classes in service, DAO, or controller layers.

2. Using @Bean in Configuration Class

Why Use It

  • Gives explicit control over bean creation.
  • Allows detailed bean configuration (e.g., constructor arguments, custom methods).

Implementation

  • AppConfig.java

package com.example.springbeans.config;

 

import com.example.springbeans.model.BeanAnnotationBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

 

@Configuration

public class AppConfig {

 

    @Bean

    public BeanAnnotationBean beanAnnotationBean() {

        return new BeanAnnotationBean();

    }

}

  • Reason: Preferred for fine-grained control and scenarios where you need to customize bean initialization.

3. Using XML Configuration

Why Use It

  • Used in legacy projects or when external configuration files are needed.
  • Useful for modularized configurations in non-code setups.

If we want to explicitly use ApplicationContext to manage both your Spring Boot configuration and the XML-based configuration in the same context, we can integrate them seamlessly applicationContext.xml

Using ApplicationContext in BeanController

The ApplicationContext will load both the Spring Boot beans and the XML-based beans. Here's how to implement it:

1. Place applicationContext.xml in src/main/resources

Ensure your applicationContext.xml file is located in the src/main/resources directory so that Spring can find it.

applicationContext.xml:

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="

           http://www.springframework.org/schema/beans

           https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="xmlBean" class="com.example.springbeans.model.XmlBean" />

</beans>

Modify SpringBeansApplication.java

Ensure that applicationContext.xml is loaded into the ApplicationContext using Spring Boot's @ImportResource annotation.

package com.example.springbeans;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.ImportResource;

@SpringBootApplication

@ImportResource("classpath:applicationContext.xml") // Import XML configuration

public class SpringBeansApplication {

    public static void main(String[] args) {

        SpringApplication.run(SpringBeansApplication.class, args);

    }

}

 BeanController (Main file)

In the controller, use the ApplicationContext provided by Spring to access all beans, including those defined in applicationContext.xml.

package com.example.springbeans.controller;

import com.example.springbeans.model.*;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.ApplicationContext;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

 

@RestController

public class BeanController {

    @Autowired

    private ApplicationContext context; // Autowire ApplicationContext

    @Autowired

    private ComponentBean componentBean;

    @Autowired

    private BeanAnnotationBean beanAnnotationBean;

    @Autowired

    private LazyBean lazyBean;

    @Autowired

    private PrototypeBean prototypeBean;

    @GetMapping("/test")

    public String testBeans() {

        // Retrieve XML bean

        XmlBean xmlBean = (XmlBean) context.getBean("xmlBean");

 

        // Force lazy bean initialization

        String lazyBeanMessage = lazyBean.getMessage();

 

        // Retrieve another instance of the PrototypeBean

        PrototypeBean newPrototypeBean = context.getBean(PrototypeBean.class);


        return String.join("\n",

                componentBean.getMessage(),

                beanAnnotationBean.getMessage(),

                xmlBean.getMessage(),

                lazyBeanMessage,

                prototypeBean.getMessage(),

                newPrototypeBean.getMessage()

        );

    }

}


4. Using Factory Methods

Why Use It

  • Creates complex beans where the creation logic cannot be easily handled by the constructor or setter.

Implementation

  • Factory Method Example:

package com.example.springbeans.model;

public class FactoryMethodBean {

    private FactoryMethodBean() {}

    public static FactoryMethodBean createInstance() {

        return new FactoryMethodBean();

    }

    public String getMessage() {

        return "Hello from Factory Method Bean!";

    }

}

Reason: Essential for reusable, shared logic that creates multiple bean instances like creating database connections or proxies.


5. Programmatic Bean Registration

Why Use It

  • Dynamically register beans based on runtime conditions.

Implementation

  • Dynamic Bean Example in Main Class:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

context.registerBean("dynamicBean", XmlBean.class, XmlBean::new);

  • Reason: Useful for cases like dynamically loading plugins or runtime-specific configurations.

6. Using Lazy Initialization

Why Use It

  • Avoids initializing beans that are not immediately required.
  • Improves startup performance.

Implementation

  • Lazy Bean Example:

@Component

@Lazy

public class LazyBean {

    public LazyBean() {

        System.out.println("Lazy Bean Created!");

    }

}

  • Reason: Reduces resource consumption for beans used in rare scenarios.

7. Using Scoped Beans

Why Use It

  • Controls the lifecycle of beans based on their scope (singleton, prototype, request, etc.).

Implementation

  • Prototype Bean Example:

@Component

@Scope("prototype")

public class PrototypeBean {

    public PrototypeBean() {

        System.out.println("Prototype Bean Created!");

    }

}

  • Reason: Important for managing beans with specific lifecycles, like session-specific data.

8. Using Conditional Beans

Why Use It

  • Dynamically create beans based on conditions like properties or environment variables.

Implementation

  • Conditional Bean Example:

package com.example.springbeans.model;

public class ConditionalBean {

    public String getMessage() {

        return "Hello from Conditional Bean!";

    }

}

Reason: Makes the application more flexible and configurable without changing the code.

import com.example.springbeans.model.ConditionalBean;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import org.springframework.context.annotation.Bean;

 

@Bean

@ConditionalOnProperty(name = "feature.enabled", havingValue = "true", matchIfMissing = false)

public ConditionalBean conditionalBean() {

    return new ConditionalBean();

}


Project to Demonstrate All Methods

Directory Structure

src/main/java/com/example/springbeans/

    - SpringBeansApplication.java

    - config/

        - AppConfig.java

    - model/

        - ComponentBean.java

        - BeanAnnotationBean.java

        - XmlBean.java

        - FactoryMethodBean.java

        - LazyBean.java

        - PrototypeBean.java


When to Use Each Method

Method

Use Case

@Component

Lightweight, auto-detected components.

@Bean

Explicit, programmatic bean definitions.

XML Configuration

Legacy or modularized, external configurations.

Factory Methods

Complex creation logic or shared factory logic.

Lazy Initialization

Beans that are rarely used.

Scoped Beans

Beans with specific lifecycles like per-request or session.

Conditional Beans

Environment or property-driven configurations.

FactoryBean

Special objects like proxies or external libraries requiring custom logic.

 

Why Use This ApplicationContext  Approach?

  1. Unified Context:
    • By integrating applicationContext.xml into the Spring Boot context, you avoid creating separate contexts.
    • This simplifies configuration and avoids potential issues with duplicate or conflicting beans.
  2. Flexibility:
    • You can seamlessly use both XML-based and annotation-based configurations.
  3. Reusability:
    • The ApplicationContext is shared across the application, improving performance and consistency.

This implementation ensures that all beans, whether defined via XML or annotations, are accessible in a single ApplicationContext.

Sunday, 14 July 2024

Java 10 to 17 most features to optimizing the code in the application

 


Java 10

  1. Local-Variable Type Inference (var)

    • Description: Allows the type of local variables to be inferred by the compiler.
    • Example:
      var list = new ArrayList<String>();
    • Benefit: Reduces boilerplate code and makes code more readable.
  2. Application Class-Data Sharing (AppCDS)

    • Description: Extends CDS to allow application classes to be archived and shared.
    • Benefit: Reduces startup time and memory footprint.

Java 11

  1. New String Methods

    • Description: Adds methods like isBlank(), lines(), strip(), and repeat().
    • Example:
      String str = " ";
      System.out.println(str.isBlank()); // true
    • Benefit: Simplifies common string manipulations.
  2. Local-Variable Syntax for Lambda Parameters

    • Description: Allows var to be used in lambda parameters.
    • Example:
      (var s1, var s2) -> s1 + s2;
    • Benefit: Enables more consistent syntax and supports type inference in lambdas.
  3. HTTP Client (Standard)

    • Description: Introduces a new HTTP client API for handling HTTP requests.
    • Example:

      HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder().uri(URI.create("http://example.com")).build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    • Benefit: Provides a more efficient and feature-rich way to perform HTTP operations.

Java 12

  1. Switch Expressions (Preview)

    • Description: Extends the switch statement to be used as an expression.
    • Example:

      int num = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; };
    • Benefit: Simplifies switch statements and makes them more expressive.
  2. JVM Constants API

    • Description: Introduces an API to model nominal descriptions of key class-file and run-time artifacts.
    • Benefit: Enhances code maintenance and analysis tools.

Java 13

  1. Text Blocks (Preview)
    • Description: Introduces multiline string literals.
    • Example:
      String json = """ { "name": "John", "age": 30 } """;
    • Benefit: Simplifies writing and reading multiline strings.

Java 14

  1. Switch Expressions (Standard)

    • Description: Makes switch expressions a standard feature.
    • Benefit: Improves readability and reduces boilerplate code in switch statements.
  2. Pattern Matching for instanceof (Preview)

    • Description: Simplifies the use of instanceof by introducing pattern matching.
    • Example:

      if (obj instanceof String s) { System.out.println(s.toLowerCase()); }
    • Benefit: Reduces boilerplate code and makes type checks more readable.

Java 15

  1. Text Blocks (Standard)
    • Description: Makes text blocks a standard feature.
    • Benefit: Further simplifies handling of multiline strings.

Java 16

  1. Pattern Matching for instanceof (Standard)

    • Description: Makes pattern matching for instanceof a standard feature.
    • Benefit: Improves readability and reduces boilerplate in type checks.
  2. Records

    • Description: Introduces a new kind of class for immutable data carriers.
    • Example:

      public record Point(int x, int y) {}
    • Benefit: Reduces boilerplate code for data classes by automatically generating constructors, accessors, equals, hashCode, and toString methods.

Java 17

  1. Sealed Classes

    • Description: Allows a class or interface to restrict which other classes or interfaces may extend or implement it.
    • Example:

      public abstract sealed class Shape permits Circle, Square, Rectangle {}
    • Benefit: Provides more control over the class hierarchy and improves security and maintainability.
  2. Pattern Matching for switch (Preview)

    • Description: Extends switch expressions and statements with pattern matching.
    • Example:

      switch (obj) { case String s -> System.out.println(s.toLowerCase()); case Integer i -> System.out.println(i * 2); default -> throw new IllegalStateException("Unexpected value: " + obj); }
    • Benefit: Makes switch statements more powerful and expressive by enabling pattern matching.

Optimizing Code Using These Features

  1. Conciseness: Use lambda expressions, the Stream API, and text blocks to reduce boilerplate and make your code more concise and readable.
  2. Efficiency: Stream API, pattern matching, and records improve performance and reduce code complexity.
  3. Maintainability: The module system, sealed classes, and pattern matching improve code maintainability and readability.
  4. Safety: Optional, records, and sealed classes enhance type safety and reduce common errors.

By leveraging these features, you can write more efficient, maintainable, and readable Java code, significantly improving your applications.