This post will introduce the initial concepts used by Spring in every part of the framework. It is the base to have a good experience and a good understanding to make good decisions in your work.

If you don't know nothing about Spring you can see an overview about the Spring Ecosystem here.

Key Principles:

  • Don't Repeat Your Self (DRY)
  • Separation of Concerns
  • Convention Over configuration Testability

The Inversion of Control (IoC) container

The usual flow of control is done by programmer. In other words, the programmer is the responsible for, for example, the flow of creating or binding an object and execute the functions. What the IoC does is delegate this responsibility to other one, then, the container or framework will be responsible for all lifecycle of that object.

The IoC is a principle that allows classes to have low coupling (minimum interdependence). It can be implemented using different patterns as factory, service locator and dependency injection (by constructor, setter or interface).

The Spring Framework implements the IoC by Dependence Injection (DI) which can achieved through the constructor (best practice ) and Setters. The BeanFactory and ApplicationContext are part of Spring Framework's IoC container.

(1) The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. (2) ApplicationContext is a sub-interface of BeanFactory. It adds easier integration with Spring's AOP features; message resource handling (for use in internationalization), event publication; and application-layer specific contexts such as the WebApplicationContext for use in web applications. [1]

Benefits of IoC:

  • Less code
  • Low coupling
  • Make the tests and maintenance easier

Configuring the application

ApplicationContext

It is the heart of the SpringApplication. It incapsulate the BeanFactory, responsible for the injection of the beans and which handles all singleton beans. The ApplicationContext provides metadata for bean creation, as well, manage the order of creation. An application can have more then one context, as web containers.

The interface org.springframework.context.ApplicationContext represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the aforementioned beans. The ApplicationContext is the interface for an advanced factory capable of maintaining a registry of different beans and their dependencies. [2] [3]

Spring separates the application's configuration from the application's objects (beans). The context manages the beans.

// create and configure beans from XML
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// create and configure beans from the configuration
// Ex 1
ApplicationContext context = SpringApplication.run(InfrastructureConfig.class);
//Ex 2
ApplicationContext context = new AnnotationConfigApplicationContext(InfrastructureConfig.class);

// retrieve configured instance
MyService service = context.getBean("myService", MyService.class);

POM

The dependencies required to use these features are here:

<properties>
  <java.version>11</java.version>
  <spring.version>5.2.3.RELEASE</spring.version>
</properties>

<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>

Multiple Configurations

The definition or configuration of the beans is done by Spring in separate classes with @Configuration annotation. The framework allows create more than one configuration class to support the best practices to separates application and infrastructure configs. The SpringApplication will use the config class which import all the configs.

@Configuration
public class ApplicationConfig {
  @Bean
  public MyService myService(MyRepository repository){
    return new MyServiceImpl(repository);
  }
  @Bean
  public MyRepository myRepository(DataSource dataSource){
    return new JdbcMyRepository(dataSource);
  }
}

@Configuration
public class WebConfig {}

@Configuration
@import ({ApplicationConfig.class, WebConfig.class})
public class InfrastructureConfig {
  @Bean
  public DataSource dataSource(){
    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setDriverClassName("org.postgresql.Driver");
    dataSource.setUrl("jdbc:postgresql://localhost/myservicedb");
    dataSource.setUsername("user");
    dataSource.setPassword("pwd");
    return dataSource;
  }
}

Environment

Other import resource given by framework is the possibility to use properties files to the configuration. Additional properties can be declared using @PropertySource annotation.

// Ex 1
@Value {"${db.driver}"}
private String driver;
@Value {"${db.url}"}
private String url;
@Value {"${db.user}"}
private String user;
@Value {"${db.password}"}
private String password;

// Ex 2
@Bean
public DataSource dataSource(Environment env){
  BasicDataSource ds = new BasicDataSource();
  ds.setDriverClassName(env.getProperty("db.driver"));
  ds.setUrl(env.getProperty("db.url"));
  ds.setUser(env.getProperty("db.use"));
  ds.setPassword("db.password"));
}

//Ex 3
@Bean
public DataSource dataSource(
  @Value ("${db.driver}") String driver,
  @Value ("${db.url}") String url,
  @Value ("${db.user}") String user,
  @Value ("${db.password}") String password){
  BasicDataSource ds = new BasicDataSource();
  ds.setDriverClassName(driver);
  ds.setUrl(url);
  ds.setUser(user);
  ds.setPassword(password);
}

Profiles

The profiles are a resource used by containers to have different beans by environment.

#### 1. Example by Class ####
// CLASS 1
@Configuration
@Profile("development")
public class StandaloneDataConfig {
  @Bean
  public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
      .setType(EmbeddedDatabaseType.HSQL)
      .addScript("classpath:com/bank/config/sql/schema.sql")
      .build();
    }
}
// CLASS 2
@Configuration
@Profile("production")
public class JndiDataConfig {
  @Bean(destroyMethod="")
  public DataSource dataSource() throws Exception {
    Context ctx = new InitialContext();
    return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
  }
}

#### 2. Example by methods ####
@Configuration
public class AppConfig {
  // Using default name
  @Bean("dataSource")
  @Profile("default")
  public DataSource standaloneDataSource() {
    return new EmbeddedDatabaseBuilder()
      .setType(EmbeddedDatabaseType.HSQL)
      .addScript("classpath:com/bank/config/sql/schema.sql")
      .build();
    }

    // Using specific environment name
    @Bean("dataSource")
    @Profile("production")
    public DataSource jndiDataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }

    // Using exclusion condition
    @Bean
    @Profile("!development")
    public MyService myService(){
      return new MyService();
    }
}

// 3. Activing the profile
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();

PS: Those examples are in Spring documentation

Spring Expression Language (SpEL)

The SpEL is another resource to give support to the configuration. It uses expressions to evolve logic and to return a value.

@Value("#{ new Boolean(environment['spring.profiles.active'] != 'dev') }")
private String isDevelopment;

Component Scanning

The use of the root annotation @Component indicate the class should be loaded into the BeanFactory. The @Component is a generic stereotype. Its specialization are @Repository, @Service, @Controller, @RestController, @Configuration.

The process scan the package and loads configuration for each bean found. So, it goes through @Autowired, @Qualifier, @Value annotation. The scanning happing in the startup.

You can make your class to be autodetected using @ComponentScan(basePackages = "org.example") annotation.

Spring adding those new annotation and you can see the benefits to use them instead the standard annotation here

If you needs adding some behavior at startup or shutdown you can use @PostConstruct and @PreDestroy annotation, which are not specific of the Spring. The sequence of the call is:

  • Constructor Injection
  • Setter Injection
  • PostConstructor method.
  • PreDestroy method - called when ConfigurableApplicationContext is closed.
  • Bean

    BeanDefinition represents the bean's metadadata and contain a package-qualified class name, bean behavioral configuration elements (scope, lifecycle callbacks, etc.), references to other beans and other configuration settings.

    The instantiating of a bean is done by container through configuration metadata. It can be done using the constructor. By default, the instantiation of beans are eagerly.

    When you create a bean by the constructor approach, all normal classes are usable by and compatible with Spring.[4]
    @component
    public class MyClass {
    
      private FirstService firstService;
      private SecondService secondService;
    
      @Autowired
      public MyClass(MyService service){
        this.service = service;
      }
    
      @Autowired
      public void setSecondService(SecondService secondService){
        this.secondService = secondService;
      }
    
    }

    Constructor inject make the tests esier comparing to do by fields. It is the priority also comparing to the setter option because ensures required dependencies are not null. One example where setter injection should be used insted constructor injection is to avoid circular dependency.

    Bean Scopes

    The beans can be defined using one of the different bean scopes.

    • Singleton: "Scopes a single bean definition to a single object instance for each Spring IoC container."
    • Prototype: "Scopes a single bean definition to any number of object instances."
    • Request: "Scopes a single bean definition to the lifecycle of a single HTTP request. "
    • Session: "Scopes a single bean definition to the lifecycle of an HTTP Session"
    • Application: "Scopes a single bean definition to the lifecycle of a ServletContext"
    • WebSocket: "Scopes a single bean definition to the lifecycle of a WebSocket"
    The bean lifecycle

    Understanding the lifecycle helps to define your strategy to develop your application.

    Spring Bean container runs through three phases:

    • Initialization: creating beans and DI process. It beginnings with creation of ApplicationContext. After that happen the BeanFactory initialization phase (load and process bean definition). Then, it's time of the beans initialization and instantiation, where the beans are really created and managed by the framework.
    • Usage: beans are available
    • Destruction: beans ready for Garbage Collection.


    Spring Pattern

    Here is a list of some patterns you can find in the Spring framework.

    • Inversion Of Control(IoC) Pattern: It is an architectural pattern and it is the core pattern of the Spring wich one the runtime of the framework is based on. It makes Spring manages the dependencies. The objects are injected in runtime. ApplicationContext is the IoC Container.
    • Factory Pattern: IoC container is a factory. BeanFactory is a data factory.
    • Builder Patter: A creational pattern used, for example, in MockMvc.
    • Singleton and Prototype: Most of the configurations use them. Every bean is a singleton by default. The bean configuration is used as prototype.
    • Adapter: Used in Spring Integration to support communication between two different systems.
    • Decorator: the framework itself uses decorator. The @Qualifier is a way to use the decorator. It allows add behavior without code modification
    • Proxy Patter: involved in every object managed by Spring. Allows add new behaviors to improve the use of the framework. Every bean created gets a proxy. Creating proxies in Spring usually resolves around aspect-oriented programming. It is important when operations should be protected and called only when necessary, or to remove object access
    • Repository: It is a pattern introduced by DDD and used by Spring Data.
    • Template Pattern: Most used in remote calls. JDBC and REST are the most common scenario.
    • MVC: All the Web framework is based on this.
    • Observer: One example of use is by ApplicationListener interface to observe if ApplicationContext is changed.
    • Command: One example is in AbstractCommandController in MVC.
    • Mediator: One example is use this when you need set state of many components at once.
    • Interpreter: Used by Spring Expression Language.

    Aspect-Oriented Programming in Spring (AOP)

    Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns that cut across multiple types and objects.

    "(1) Provide declarative enterprise services. (2) Let users implement custom aspects, complementing their use of OOP with AOP"

    AOP allows reusable blocks of code inject in runtime.

    Common Applications of Aspects Parts of Spring Aspects
  • Logging
  • Transaction management
  • Caching
  • Security
  • Join point
  • Advice
  • Pointcut

  • Summary

    Here is a good summary about beans. It is in portugues, but I could not left it behind. They are very good.

    O que é um bean Spring Framework Quando Utilizar @Bean em Spring
    Profiles Cache

    References