Showing posts with label Clean Architecture. Show all posts
Showing posts with label Clean Architecture. Show all posts

Thursday, 8 November 2018

Aggregating @Configuration classes with @Import | Spring Tutorial

🚀 Master Modular Spring JavaConfig!

Subscribe to Ram N Java for clean architectural patterns, Spring framework tutorials, and code walkthroughs!

🔔 Subscribe to Ram N Java

What Is the @Import Annotation in Spring?

In large-scale enterprise Java applications, keeping all bean definitions inside a single configuration class quickly leads to bloated, unmaintainable code. Spring's @Import annotation provides an elegant solution by allowing developers to split bean configurations across focused, modular @Configuration classes and aggregate them cleanly into a central configuration hub.

How Aggregating Configuration Works

Instead of registering multiple configuration classes manually when initializing the container, you designate one primary or root configuration class and aggregate other specialized classes:

  • Modular Configuration Classes: Separate your beans logically into domain-specific modules (such as AppConfigA and AppConfigB).
  • Aggregation Hub: Annotate a root configuration class with @Import({AppConfigA.class, AppConfigB.class}) to consolidate the configurations.
  • Simplified Container Bootstrapping: Pass only the main configuration class to AnnotationConfigApplicationContext. Spring automatically discovers and loads all imported classes and their associated @Bean definitions.

Step-by-Step Implementation Flow

  1. Define Feature Configs: Create individual configuration classes annotated with @Configuration, defining beans specific to their feature.
  2. Create Main Config: Define a main aggregation class (e.g., AppConfig) and apply @Import passing an array of module configuration classes.
  3. Initialize ApplicationContext: Instantiate the container with new AnnotationConfigApplicationContext(AppConfig.class).
  4. Access Beans: Retrieve beans declared across any of the imported configuration classes seamlessly from the single context.

Major Architectural Benefits

  • Separation of Concerns: Keeps configuration files cohesive, lightweight, and focused on specific domains (e.g., database, security, or business logic).
  • Easier Unit & Integration Testing: Allows test suites to bootstrap only relevant configuration modules rather than the entire application graph.
  • Clean Maintenance: Adding, removing, or swapping modular features becomes as simple as updating an entry in the @Import array.

Recommended Next Videos

Continue your deep dive into Spring configuration and annotations with these tutorials from our channel:


Click here to watch on Youtube:
https://www.youtube.com/watch?v=GRitnworI4c&list=UUhwKlOVR041tngjerWxVccw

Click the below Image to Enlarge:

Aggregating @Configuration classes with @Import | Spring Tutorial

Aggregating @Configuration classes with @Import | Spring Tutorial
pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.ram.core</groupId>
    <artifactId>SpringDemo</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>SpringHelloWorld</name>
    <url>http://maven.apache.org</url>

    <properties>
        <spring.version>5.0.2.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

        <!-- Spring 5 dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>
</project>

Employee.java

package com.ram.core;

/**
 * Spring bean
 *
 */

public class Employee
{
    private String name;

    public Employee(String name)
    {
        System.out.println(this.getClass().getName()
                + ", Employee(String name) constructor is called, " + name);

        this.name = name;
    }


    public void employeeDetails()
    {
        System.out.println("Employee Name =  " + name);
    }
}

Customer.java

package com.ram.core;

/**
 * Spring bean
 *
 */

public class Customer
{
    private String name;

    public Customer(String name)
    {
        System.out.println(this.getClass().getName()
                + ", Customer(String name) constructor is called, " + name);

        this.name = name;
    }

    public void customerDetails()
    {
        System.out.println("Customer Name =  " + name);
    }
}

EmployeeConfig.java

package com.ram.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.ram.core.Employee;

@Configuration
public class EmployeeConfig
{

    @Bean(name = "employee")
    public Employee employee()
    {

        System.out.println(
                "In EmployeeConfig employee() is called by the Spring container");
        return new Employee("Peter");

    }
}

CustomerConfig.java

package com.ram.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.ram.core.Customer;

@Configuration
public class CustomerConfig
{

    @Bean(name = "customer")
    public Customer customer()
    {
        System.out.println(
                "In CustomerConfig employee() is called by the Spring container");
        return new Customer("John");
    }

}

AppConfig.java

package com.ram.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({ EmployeeConfig.class, CustomerConfig.class })
public class AppConfig
{

}

App.java

package com.ram.core;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.ram.config.AppConfig;

public class App
{
    public static void main(String[] args)
    {
        ApplicationContext context = new AnnotationConfigApplicationContext(
                AppConfig.class);

        System.out.println("---------------------------------------");
     
        Employee employee = (Employee) context.getBean("employee");
        System.out.println("Got employee object from the ApplicationContext");
        employee.employeeDetails();
     
        System.out.println("---------------------------------------");
     
        Customer customer = (Customer) context.getBean("customer");
        System.out.println("Got customer object from the ApplicationContext");
        customer.customerDetails();

    }
}

Output

Oct 07, 2018 7:32:15 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5f4da5c3: startup date [Sun Oct 07 07:32:15 IST 2018]; root of context hierarchy
In EmployeeConfig employee() is called by the Spring container
com.ram.core.Employee, Employee(String name) constructor is called, Peter
In CustomerConfig employee() is called by the Spring container
com.ram.core.Customer, Customer(String name) constructor is called, John
---------------------------------------
Got employee object from the ApplicationContext
Employee Name =  Peter
---------------------------------------
Got customer object from the ApplicationContext
Customer Name =  John

Click the below link to download the code:
https://sites.google.com/site/ramj2eev2/java_basics/SpringDemo_aggregate_import.zip?attredirects=0&d=1

Github Link:
https://github.com/ramram43210/javaee/tree/master/Spring_2018/SpringDemo_aggregate_import

Bitbucket Link:
https://bitbucket.org/ramram43210/spring/src/e16b8c6ef73a72464fa5812e24080179c4adc913/Spring_2018/SpringDemo_aggregate_import/?at=master

See also:

  • All JavaEE Videos Playlist
  • All JavaEE Videos
  • All JAVA EE Links
  • Spring Tutorial
  • Servlets Tutorial
  • All Design Patterns Links
  • JDBC Tutorial
  • Java Collection Framework Tutorial
  • JAVA Tutorial
  • Kids Tutorial
  • Cooking Tutorial
  • Saturday, 1 February 2014

    State Design pattern - Implementation [Project]

    Master Hands-On Java Architecture & Patterns!

    Subscribe to Ram N Java for clean code walkthroughs, design pattern implementations, and step-by-step project builds!

    🔔 Subscribe to Ram N Java

    Managing Complex State Lifecycles in Real Projects

    In software development projects, domain entities frequently transition through intricate multi-stage lifecycles. Whether you are building an order processing system, an approval workflow, or a document management engine, objects change their behavior dramatically based on their current stage.

    Relying on flag variables and nested switch-case statements leads to brittle code that is prone to regression bugs whenever a new lifecycle phase is introduced. The State Design Pattern provides an object-oriented solution by turning states into independent classes.

    Project Implementation Architecture

    In this end-to-end project walkthrough, the architecture is structured around clean object-oriented delegation:

    • State Interface: Outlines the contract and domain actions valid across various lifecycle phases.
    • Concrete State Classes: Each state class encapsulates the exact logic for handling incoming requests and controls transitions to subsequent states.
    • Context Class: Maintains a reference to the active state instance and delegates operational methods directly to it, decoupling the consumer from state transition logic.

    Architectural Benefits in Production Code

    Applying the State pattern to real-world applications delivers clear advantages:

    • Open/Closed Principle: Introduce new project states or lifecycle steps by creating new classes without altering existing state code.
    • Single Responsibility Principle: Isolate state-specific execution logic into dedicated files for simpler unit testing and debugging.
    • Cleaner Domain Models: Remove convoluted conditionals, replacing them with expressive, polymorphic method dispatch.

    Related Tutorials from Ram N Java


    Click here to watch in Youtube : https://www.youtube.com/watch?v=RDANwlKtN6E

    Click the below Image to Enlarge
    State Design pattern - Implementation [Project]


    State Design pattern - Implementation [Project] - Class Diagram












































    ProjectState.java

    public interface ProjectState
    {

    public void doRequirmentGathering();

    public void doDesignAndArchitecture();

    public void doDevelopement();

    public void doTesting();


    }

    RequirementGatheringState.java

    public class RequirementGatheringState implements ProjectState
    {

    @Override
    public void doRequirmentGathering()
    {
    System.out.println("'RequirmentGathering' has been started .............");

    System.out.println("'RequirmentGathering' has been finished by 7 days .............");

    }

    @Override
    public void doDesignAndArchitecture()
    {
    System.out.println("Cannot do 'DesignAndArchitecture' because Project is in 'RequirmentGathering' Phase...");

    }

    @Override
    public void doDevelopement()
    {
    System.out.println("Cannot do 'developement' because Project is in 'RequirmentGathering' Phase...");

    }

    @Override
    public void doTesting()
    {
    System.out.println("Cannot do 'Testing' because Project is in 'RequirmentGathering' Phase...");

    }

    }

    DesignAndArchitectureState.java

    public class DesignAndArchitectureState implements ProjectState
    {
    @Override
    public void doRequirmentGathering()
    {
    System.out.println("'RequirmentGathering'[Clarification of Requirement] you can do in ''DesignAndArchitecture'' Phase .......");
    }

    @Override
    public void doDesignAndArchitecture()
    {
    System.out.println("'DesignAndArchitecture' has been started .............");

    System.out.println("'DesignAndArchitecture' has been finished by 10 days .............");
    }

    @Override
    public void doDevelopement()
    {
    System.out.println("Cannot do 'developement' because Project is in 'DesignAndArchitecture' Phase...");

    }

    @Override
    public void doTesting()
    {
    System.out.println("Cannot do 'Testing' because Project is in 'DesignAndArchitecture' Phase...");

    }

    }

    DevelopementState.java

    public class DevelopementState implements ProjectState
    {
    @Override
    public void doRequirmentGathering()
    {
    System.out.println("Cannot do 'RequirmentGathering' because Project is in 'Developement' Phase...");
    }

    @Override
    public void doDesignAndArchitecture()
    {
    System.out.println("'DesignAndArchitecture'[Clarification of DesignAndArchitecture] you can do in ''Developement'' Phase .......");
    }

    @Override
    public void doDevelopement()
    {
    System.out.println("'Developement' has been started .............");

    System.out.println("'Developement' has been finished by 40 days .............");

    }

    @Override
    public void doTesting()
    {
    System.out.println("Cannot do 'Testing' because Project is in 'Developement' Phase...");

    }

    }

    TestingState.java

    public class TestingState implements ProjectState
    {
    @Override
    public void doRequirmentGathering()
    {
    System.out.println("Cannot do 'RequirmentGathering' because Project is in 'Testing' Phase...");
    }

    @Override
    public void doDesignAndArchitecture()
    {
    System.out.println("Cannot do 'DesignAndArchitecture' because Project is in 'Testing' Phase...");
    }

    @Override
    public void doDevelopement()
    {
    System.out.println("'Developement'[Bug fixes] you can do in ''Testing'' Phase .......");

    }

    @Override
    public void doTesting()
    {
    System.out.println("'Testing' has been started .............");

    System.out.println("'Testing' has been finished by 20 days .............");

    }

    }

    Project.java

    // This is like Context
    public class Project implements ProjectState
    {

    private ProjectState projectState;

    public Project()
    {
    projectState = new RequirementGatheringState();
    }

    public ProjectState getProjectState()
    {
    return projectState;
    }

    public void setProjectState( ProjectState projectState )
    {
    this.projectState = projectState;
    }

    @Override
    public void doRequirmentGathering()
    {
    projectState.doRequirmentGathering();
    /*
    * Once RequirmentGathering is finished project internal state
    * will be moved to DesignAndArchitectureState
    */
    if( projectState instanceof RequirementGatheringState )
    {
    setProjectState(new DesignAndArchitectureState());
    System.out.println("\nProject internal state moved to : " + projectState.getClass().getName() +"\n" );
    }

    }

    @Override
    public void doDesignAndArchitecture()
    {
    projectState.doDesignAndArchitecture();

    /*
    * Once DesignAndArchitecture is finished project internal state
    * will be moved to DevelopementState
    */
    if( projectState instanceof DesignAndArchitectureState )
    {
    setProjectState(new DevelopementState());
    System.out.println("\nProject internal state moved to : " + projectState.getClass().getName() +"\n" );
    }

    }

    @Override
    public void doDevelopement()
    {
    projectState.doDevelopement();

    /*
    * Once Developement is finished project internal state will be
    * moved to TestingState
    */

    if( projectState instanceof DevelopementState )
    {
    setProjectState(new TestingState());
    System.out.println("\nProject internal state moved to : " + projectState.getClass().getName() +"\n" );
    }

    }

    @Override
    public void doTesting()
    {
    projectState.doTesting();

    }

    }

    Client.java

    public class Client
    {

    public static void main( String[] args )
    {
    /*
    * Initially Project in 'RequirementGatheringState'
    */
    Project project = new Project();

    System.out.println("Project internal state is  : " + project.getProjectState().getClass().getName()+"\n");

    project.doDesignAndArchitecture();
    project.doDevelopement();
    project.doTesting();
    project.doRequirmentGathering();

    System.out.println();

    project.doDesignAndArchitecture();
    project.doTesting();
    project.doDevelopement();
    project.doRequirmentGathering();

    }

    }

    Output

    Project internal state is  : RequirementGatheringState

    Cannot do 'DesignAndArchitecture' because Project is in 'RequirmentGathering' Phase...
    Cannot do 'developement' because Project is in 'RequirmentGathering' Phase...
    Cannot do 'Testing' because Project is in 'RequirmentGathering' Phase...
    'RequirmentGathering' has been started .............
    'RequirmentGathering' has been finished by 7 days .............

    Project internal state moved to : DesignAndArchitectureState


    'DesignAndArchitecture' has been started .............
    'DesignAndArchitecture' has been finished by 10 days .............

    Project internal state moved to : DevelopementState

    Cannot do 'Testing' because Project is in 'Developement' Phase...
    'Developement' has been started .............
    'Developement' has been finished by 40 days .............

    Project internal state moved to : TestingState

    Cannot do 'RequirmentGathering' because Project is in 'Testing' Phase...

    See also:

  • State Design pattern - Introduction
  • State Design pattern - Real time example [ATM]
  • State Design pattern - Real time example [TV Remote]
  • State Design pattern - Real time example [Vending Machine]
  • State Design pattern - Real time example [Project]
  • State Design pattern - Class Diagram
  • State Design pattern - Implementation [ATM]
  • State Design pattern - Implementation [Vending Machine]
  • State Design pattern - Implementation [LED TV]
  • State Design pattern - Keypoints
  • All Design Patterns Links
  • Friday, 30 August 2013

    Intercepting Filter Design Pattern - Implementation

    🚀 Level Up Your Java Architecture Skills!

    Subscribe to Ram N Java for clean code walkthroughs, design pattern implementations, and step-by-step programming guides!

    🔔 Subscribe to Ram N Java

    Practical Implementation of Intercepting Filter

    The Intercepting Filter Design Pattern is widely applied in modern web frameworks and enterprise backends to handle cross-cutting concerns cleanly. Instead of embedding security checks, request encoding, or auditing directly into your business controllers, this pattern lets you execute specialized filters in an ordered chain before reaching the final handler.

    How the Components Work Together

    • Filter Interface: Defines a standard contract (such as an execute(String request) method) that every concrete filter implements.
    • Concrete Filters: Specific implementations that handle focused tasks (e.g., an AuthenticationFilter validating credentials or a DebugFilter logging request payloads).
    • Target: The ultimate business destination or resource (such as a core service or web handler) that processes the request once all filters approve.
    • FilterChain: Maintains an ordered list of filters, executes them sequentially, and finally triggers the target.
    • FilterManager: Coordinates the filter chain setup and manages which filters attach to incoming requests.
    • Client: The caller that sends requests through the FilterManager rather than directly to the target.

    Execution Flow in Code

    1. The Client issues an incoming request.
    2. The FilterManager receives the request and initializes the FilterChain.
    3. The FilterChain invokes each registered Filter in succession (e.g., checking tokens, validating headers, logging data).
    4. Once every filter executes successfully, the chain passes control directly to the Target.

    Key Benefits for Software Design

    • Single Responsibility: Each filter class has only one job, making your codebase modular and easy to debug.
    • Dynamic Extensibility: You can plug in new filters (like rate limiting or data compression) without modifying the target resource.
    • High Reusability: Common filters can be attached to multiple handlers across different services.

    Recommended Next Videos

    Deepen your understanding of filters and servlet architecture with these related tutorials from our channel:


    Click here to watch in Youtube : https://www.youtube.com/watch?v=sWtchigtgXk

    Click the below Image to Enlarge
    Intercepting Filter Design Pattern - Implementation






















    Filter.Java

    public interface Filter
    {
    public void processRequest(String request);
    }

    AuthenticationFilter.java

    public class AuthenticationFilter implements Filter
    {

    @Override
            public void processRequest( String request )
            {
    System.out.println("Authenticating the request by AuthenticationFilter : " + request);
           
            }

    }

    LoggingFilter.java

    public class LoggingFilter implements Filter
    {
    @Override
            public void processRequest( String request )
            {
    System.out.println("Request Tracking is done by LoggingFilter : " + request);        
            }
    }

    Target.java

    public class Target
    {
    public void processRequest( String request )
    {
    System.out.println("Process the Request by Target Class: " + request);
    }
    }

    FilterChain.java

    import java.util.ArrayList;
    import java.util.List;

    public class FilterChain
    {
    private List<Filter> filters = new ArrayList<Filter>();
    private Target       target;

    public void addFilter( Filter filter )
    {
    filters.add(filter);
    }

    public void execute( String request )
    {
    for( Filter filter : filters )
    {
    filter.processRequest(request);
    }
    target.processRequest(request);
    }

    public void setTarget( Target target )
    {
    this.target = target;
    }
    }

    FilterManager.java

    public class FilterManager
    {
    FilterChain filterChain;

    public FilterManager( Target target )
    {
    filterChain = new FilterChain();
    filterChain.setTarget(target);
    }

    public void setFilter( Filter filter )
    {
    filterChain.addFilter(filter);
    }

    public void filterRequest( String request )
    {
    filterChain.execute(request);
    }
    }

    Client.java

    public class Client
    {
    FilterManager filterManager;

    public void setFilterManager( FilterManager filterManager )
    {
    this.filterManager = filterManager;
    }

    public void sendRequest( String request )
    {
    filterManager.filterRequest(request);
    }

    public static void main( String[] args )
    {
    FilterManager filterManager = new FilterManager(new Target());
    filterManager.setFilter(new AuthenticationFilter());
    filterManager.setFilter(new LoggingFilter());
    Client client = new Client();
    client.setFilterManager(filterManager);
    client.sendRequest("HOME");

    }

    }

    Output

    Authenticating the request by AuthenticationFilter : HOME
    Request Tracking is done by LoggingFilter : HOME


    Tutorials