SpringBoot 

Tel application is a Rest based Application that provides functionality for adding, updating, and deleting a customer.

                                  

we will build this application incrementally to make it for learning the Spring core concepts with Spring Boot.

Tel is built as a three-tier application which consists of 

  • Presentation Layer
  • Service/Business Layer
  • Persistence Layer

The service layer of an enterprise application is the layer in which the business logic is implemented. It interacts with the persistence layer and the presentation layer.

                                 

Spring Core with Boot, we will see how to develop the Service/Business layer for this application. Once you have completed this course you can then learn to build the Persistence layer using Spring JDBC with Spring Boot course and, to build the Rest presentation layer using Spring REST course.

Below are the classes used in the Tel application.

  • CustomerService.java -> Interface to define service methods
  • CustomerServiceImpl.java -> A service class which implements the CustomerService interface
  • Client.java-> A class for the main method.
  • CustomerRepository.java-> A class for the persistence layer where all CRUD operations are performed.
Why spring?

package com.infy.service;
public interface CustomerService {
public String fetchCustomer();
        public String createCustomer(CustomerDto dto)
}

The business logic of InfyTel application is implemented in this class. This class is used to perform operations like creating a customer, deleting a customer, etc...  CustomerServiceImpl class interacts with CustomerRepository to perform the database related operations.

public class CustomerServiceImpl implements CustomerService{
       CustomerRepository customerRepository= new CustomerRepositoryImpl();
       public String createCustomer(CustomerDto dto) {
return customerRepository.createCustomer(dto);
}
public String fetchCustomer() {
return customerRepository.fetchCustomer();
}
}

In this implementation, CustomeServiceImpl depends on CustomerRepository. It also instantiates CustomerRepositoryImpl class which makes it tightly coupled with CustomerRepositoryImpl class. This is a bad design because of the following reasons:

  • If you want to unit test for createCustomer() method then you need a mock object of CustomerRepository. But you cannot use a mock object because there is no way to substitute the CustomerRepositoryImpl object that CustomerServiceImpl has. So testing CustomerServiceImpl becomes difficult.
  • Also, you cannot use a different implementation of CustomerRepository other than CustomerRepositoryImpl because of tight coupling.

So we need a more flexible solution where dependencies can be provided externally rather than a dependent creating its own dependencies


modified implementation of CustomerServiceImpl class:

public class CustomerServiceImpl implements CustomerService {
    private CustomerRepository customerRepository;
    public CustomerServiceImpl(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }
     public String createCustomer(CustomerDto dto) {
return customerRepository.createCustomer(dto);
}
 }

In this implementation, CustomerServiceImpl doesn’t create an object of the CustomerRepository. Instead, it is giving an object of CustomerRepository at creation time as a constructor argument. You can pass the object of any class that implements CustomerRepository interface at the time of object creation of CustomerServiceImpl class as a constructor argument. This makes CustomerServiceImpl class loosely coupled with CustomerRepositoryImpl class. So you can now easily test CustomerServiceImpl class by substituting its CustomerRepositoryImpl object with a mock CustomerRepository. Also, you can use any implementations of CustomerRepository inside CustomerServiceImpl.

This solution is more flexible and easy to test. But at the same time, it is tedious to manually wire together with the dependencies. Also if you alter the dependencies you may have to change vast amounts of code. So what is the solution?

We can use a technique called Dependency Injection (DI). It is a technique in which the responsibility of creating and wiring the dependencies of a dependent class is externalized to the external framework or library called dependency injection (DI) frameworks. So now the control over the construction and wiring of an object graph is no longer resides with the dependent classes themselves. This reversal of responsibilities is known as Inversion of Control(IoC). Dependency injection framework also called  IoC containers. 

There are many third-party frameworks that are available for dependency injection such as Spring Framework, Google Guice, Play Framework, etc. In this course, you will study about Spring Framework. Besides dependency injection, there are many other advantages of using the Spring Framework which you will study later in this course.

Note: Inversion of Control (IoC) represents the inversion of application responsibility of the object's creation, initialization, dependency, and destruction from the application to the third party.

Spring Framework

Spring Framework is an open source Java application development framework that supports developing all types of Java applications such as enterprise applications, web applications, cloud based applications, and many more.

Java applications developed using Spring are simple, easily testable, reusable, and maintainable.

Spring modules do not have tight coupling on each other, the developer can pick and choose the modules as per the need for building an enterprise application.


Spring Feature

Description

Light Weight

Spring JARs are relatively small.

A basic Spring framework would be lesser than 10MB.

It can be deployed in Tomcat and they do not require heavy-weight application servers.

Non-Invasive

The application is developed using POJOs.

No need to extend/implement any pre-defined classes.

Loosely Coupled

Spring features like Dependency Injection and Aspect Oriented Programming help in loosely coupled code.

Inversion of Control(IoC)

IoC takes care of the application object's life cycle along with their dependencies.

Spring Container

Spring Container takes care of object creation, initialization, and managing object dependencies.

Aspect Oriented Programming(AOP)

Promotes separation of supporting functions(concerns) such as logging, transaction, and security from the core business logic of the application




 

Spring Framework 5.x has the following key module groups:

  • Core Container: These are core modules that provide key features of the Spring framework.
  • Data Access/IntegrationThese modules support JDBC and ORM data access approaches in Spring applications.
  • WebThese modules provide support to implement web applications.
  • Others: Spring also provides few other modules such as the Test for testing Spring applications.

Core container has the following modules:

  • Core: This is the key module of Spring Framework which provides fundamental support on which all other modules of the framework are dependent.
  • Bean: This module provides a basic Spring container called BeanFactory. 
  • Context: This module provides one more Spring container called ApplicationContext which inherits the basic features of the BeanFactory container and also provides additional features to support enterprise application development. 
  • Spring Expression Language (SpEL): This module is used for querying/manipulating object values.
  • AOP (Aspect Oriented Programming) and aspects: These modules help in isolating cross-cutting functionality from business logic

The following modules support Data Access/Integration:

  • Java Database Connectivity (JDBC): It provides an abstract layer to support JDBC calls to relational databases.
  • Object Relational Mapping (ORM): It provides integration support for popular ORM(Object-Relational Mapping) solutions such as Hibernate, JPA, etc.
  • Transactions: It provides a simple transaction API which abstracts the complexity of underlying repository specific transaction API's from the application.

Spring Framework provides the following modules to support web application development:

  • Web: This module has a container called web application context which inherits basic features from ApplicationContext container and adds features to develop web based applications.
  • Webmvc: It provides the implementation of the MVC(model-view-controller) pattern to implement the serverside presentation layer and also supports features to implement RESTful Web Services.
  • WebFlux: Spring 5.0 introduced a reactive stack with a web framework called Spring WebFlux to support Reactive programming in Spring's web layer and runs on containers such as Netty, Undertow, and Servlet 3.1+.
  • WebSocket: It is used for 2 way communication between client and server in WebSocket based web applications.

Inversion of Control (IoC)

Usually it is the developer's responsibility to create the dependent application object using the new operator in an application. Hence any change in the application dependency requires code change and this results in more complexity as the application grows bigger.

Inversion of Control (IoC) helps in creating a more loosely coupled application. IoC represents the inversion of the responsibility of the application object's creation, initialization, and destruction from the application to the third party such as the framework. Now the third party takes care of application object management and dependencies thereby making an application easy to maintain, test, and reuse.

There are many approaches to implement IoC, Spring Framework provides IoC implementation using Dependency Injection(DI).

Spring Container managed application objects are called beans in Spring.

We need not create objects in dependency injection instead describe how objects should be created through configuration.

DI is a software design pattern that provides better software design to facilitate loose coupling, reuse, and ease of testing.

Benefits of Dependency Injection(DI):

  • Helps to create loosely coupled application architecture facilitating re-usability and easy testing.
  • Separation of responsibility by keeping code and configuration separately. Hence dependencies can be easily modified using configuration without changing the code.
  • Allows to replace actual objects with mock objects for testing, this improves testability by writing simple JUnit tests that use mock objects.
The Spring container knows which objects to create and when to create through the additional details that we provide in our application called Configuration Metadata.

IOC container : 

BeanFactory:

  • It is the basic Spring container with features to instantiate, configure and manage the beans. 
  • org.springframework.beans.factory.BeanFactory is the main interface representing a BeanFactory container.

ApplicationContext:

  • ApplicationContext is another Spring container that is more commonly used in Spring applications. 
  • org.springframework.context.ApplicationContext is the main Interface representing an ApplicationContext container. 
  • It inherits the BeanFactory features and provides added features to support enterprise services such as internationalization, validation, etc.

ApplicationContext is the preferred container for Spring application development. Let us look at more details on the ApplicationContext container.

ApplicationContext and AbstractApplicationContext 

org.springframework.context.annotation.AnnotationConfigApplicationContext is one of the most commonly used implementation of ApplicationContext. 

ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
Object obj = context.getBean("customerService");
  1. ApplicationContext container is instantiated by loading the configuration from the SpringConfiguration.class which is available in the utility package of the application.
  2. Accessing the bean with id "customerService" from the container.

AbstractApplicationContext

You can see a warning message as Resource leak: 'context' is never closed while using the ApplicationContext type. This is for the reason that you don’t have a close method with BeanFactory or even ApplicationContext. AbstractApplicationContext is an abstract implementation of the ApplicationContext interface and it implements Closeable and AutoCloseable interfaces. To close the application context and destroy all beans in its abstractApplicationContext has a close method that closes this application context.

Access bean in String

  •  The traditional way of accessing bean based on bean id with explicit typecast
CustomerServiceImpl service = (CustomerServiceImpl) context.getBean("customerService");
  • Accessing bean based on class type to avoid typecast if there is a unique bean of type in the container
CustomerServiceImpl service =  context.getBean(CustomerServiceImpl.class);


org.springframework.context.annotation.AnnotationConfigApplicationContext is one of the most commonly used implementation of ApplicationContext. 

ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
Object obj = context.getBean("customerService");
  1. ApplicationContext container is instantiated by loading the configuration from the SpringConfiguration.class which is available in the utility package of the application.
  2. Accessing the bean with id "customerService" from the container.

AbstractApplicationContext

You can see a warning message as Resource leak: 'context' is never closed while using the ApplicationContext type. This is for the reason that you don’t have a close method with BeanFactory or even ApplicationContext. AbstractApplicationContext is an abstract implementation of the ApplicationContext interface and it implements Closeable and AutoCloseable interfaces. To close the application context and destroy all beans in its abstractApplicationContext has a close method that closes this application context.

Access bean in String

  •  The traditional way of accessing bean based on bean id with explicit typecast
CustomerServiceImpl service = (CustomerServiceImpl) context.getBean("customerService");
  • Accessing bean based on class type to avoid typecast if there is a unique bean of type in the container
CustomerServiceImpl service =  context.getBean(CustomerServiceImpl.class);
  •  Accessing bean through bean id and also type to avoid explicit typecast
CustomerServiceImpl service = context.getBean("customerService", CustomerServiceImpl.class);

The Spring configuration metadata consists of definitions of the beans that the container must manage.

Spring allows providing the configuration metadata using :

  • XML Configuration
  • Annotation Based configuration
  • Java Based configuration
Java-based configuration metadata

The Java-based configuration metadata is provided in the Java class using the following annotations:

@Configuration: The Java configuration class is marked with this annotation. This annotation identifies this as a configuration class, and it’s expected to contain details on beans that are to be created in the Spring application context.

@Bean: This annotation is used to declare a bean. The methods of configuration class that creates an instance of the desired bean are annotated with this annotation. These methods are called by the Spring containers during bootstrap and the values returned by these methods are treated as Spring beans. By default, only one bean instance is created for a bean definition by the Spring Container, and that instance is used by the container for the whole application lifetime.

@Configuration
public class SpringConfiguration {
@Bean 
public CustomerServiceImpl customerService() {
return new CustomerServiceImpl();
}
}

By default, the bean name is the same as the name of the method in which the bean is configured. So in the above code bean name is customerService.  If you want to change the bean name then you can either rename the method or provide a different name with the name attribute as follows:

@Configuration
public class SpringConfiguration {
@Bean(name="service")
public CustomerServiceImpl customerService() {
return new CustomerServiceImpl();
}
}

Highlights:

  • How to create Maven based Spring basic application
  • How to add required dependencies in POM.xml file
Goto File > new > Maven Project, you would get the below screen

<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/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.infosys</groupId>
        <artifactId>demo1-spring-ioc</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
        <name>demo1-spring-ioc</name>
        <url>http://maven.apache.org</url>
        <properties>
                <spring.version>5.0.5.RELEASE</spring.version>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
        <dependencies>
                <!-- Spring Dependency -->
                <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-context</artifactId>
                        <version>${spring.version}</version>
                </dependency>
                <dependency>
                        <groupId>junit</groupId>
                        <artifactId>junit</artifactId>
                        <version>3.8.1</version>
                        <scope>test</scope>
                </dependency>
        </dependencies>
</project>
 
package com.infy.service;
public interface CustomerService {
public String createCustomer();
}
package com.infy.service;
public class CustomerServiceImpl implements CustomerService {
public String createCustomer() {
return "Customer is successfully created";
}
}
package com.infy.util;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.infy.service.CustomerServiceImpl;
@Configuration
public class SpringConfiguration {
@Bean(name = "customerService")
public CustomerServiceImpl customerServiceImpl() {
return new CustomerServiceImpl();
}
}
package com.infy;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import com.infy.service.CustomerServiceImpl;
import com.infy.util.SpringConfiguration;
public class Client {
public static void main(String[] args) {
CustomerServiceImpl service = null;
                AbstractApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
service = (CustomerServiceImpl) context.getBean("customerService");
System.out.println(service.createCustomer());
context.close();
}
}

Dependency Injection

Tel Customer application, we defined CustomerService bean as 
@Bean 
public CustomerService customerService() {
return new CustomerService();
}
Java code wherein an instance is created and initialized with default values using the default constructor.

CustomerService customerService = new CustomerService();

How do we initialize beans with some specific values in Spring?

This can be achieved through Dependency Injection in Spring.

Inversion of Control pattern is achieved through Dependency Injection (DI) in Spring. In Dependency Injection, the developer need not create the objects but specify how they should be created through configuration.

Spring container uses one of these two ways to initialize the properties:

  • Constructor Injection: This is achieved when the container invokes a parameterized constructor to initialize the properties of a class
  • Setter Injection: This is achieved when the container invokes setter methods of a class to initialize the properties after invoking a default constructor.

Constructor injection - Primitive value

CustomerService class has a count property, let us now modify this class to initialize count property during bean instantiation using the constructor injection approach
package com.infy.service;
public class CustomerServiceImpl implements CustomerService {
private int count;
public CustomerServiceImpl(int count) {
this.count = count;
}
}

How do we define bean in the configuration to initialize values?
@Configuration
public class SpringConfiguration {
@Bean // CustomerService bean definition with bean dependencies through constructor injection
public CustomerServiceImpl customerService() {
return new CustomerServiceImpl(20);
}

What is mandatory for constructor injection?

A parameterized constructor is required in the CustomerService class as we are injecting the values through the constructor argument.

Can we use constructor injection to initialize multiple properties?

Yes, we can initialize more than one property.

Constructor injection - Non Primitive value

inject object dependencies using Constructor injection.
CustomerServiceImpl.java class which is dependent on CustomerRepository(class used to in persistence layer to perform CRUD operations ) object type to call fetchCustomer() method.
package com.infy.service;
public class CustomerServiceImpl implements CustomerService {
// CustomerServiceImpl needs to contact CustomerRepository, hence injecting the customerRepository dependency
private CustomerRepository customerRepository;
        private int count;
public CustomerServiceImpl() {
}
public CustomerServiceImpl(CustomerRepository customerRepository, int count) {
this.customerRepository = customerRepository;
                this.count=count;
}
        public String fetchCustomer() {
return customerRepository.fetchCustomer(count);
}
public String createCustomer() {
return customerRepository.createCustomer();
}
}

Observe in the above code, CustomerRepository property of CustomerSevice class has not been initialized with any value in the code. This is because Spring dependency is going to be taken care of in the configuration.

How do we inject object dependencies through configuration using constructor injection?

package com.infy.util;
@Configuration
public class SpringConfiguration {
@Bean// customerRepository bean definition 
public CustomerRepository customerRepository() {
return new CustomerRepository();
}
@Bean // CustomerService bean definition with bean dependencies through constructor injection
public CustomerServiceImpl customerService() {
return new CustomerServiceImpl(customerRepository(),20);
}
}

Demo :
package com.infy.util;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.infy.repository.CustomerRepository;
import com.infy.service.CustomerServiceImpl;
@Configuration
public class SpringConfiguration {
@Bean // Constructor Injection
public CustomerServiceImpl customerService() {
return new CustomerServiceImpl(customerRepository(), 20);
}
@Bean // Constructor Injection
public CustomerRepository customerRepository() {
return new CustomerRepository();
}
}
package com.infy.service;
public interface CustomerService {
public String fetchCustomer();
public String createCustomer();
}
package com.infy.service;
import com.infy.repository.CustomerRepository;
public class CustomerServiceImpl implements CustomerService {
private int count;
private CustomerRepository repository;
public CustomerServiceImpl(CustomerRepository repository, int count) {
this.count = count;
this.repository = repository;
}
public String fetchCustomer() {
return repository.fetchCustomer(count);
}
public String createCustomer() {
return repository.createCustomer();
}
}
package com.infy.repository;
public class CustomerRepository {
public String fetchCustomer(int count) {
return " The no of customers fetched are : " + count;
}
public String createCustomer() {
return "Customer is successfully created";
}
}
package com.infy;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import com.infy.service.CustomerServiceImpl;
import com.infy.util.SpringConfiguration;
public class Client {
public static void main(String[] args) {
CustomerServiceImpl service = null;
AbstractApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
service = (CustomerServiceImpl) context.getBean("customerService");
System.out.println(service.fetchCustomer());
context.close();
}
}

Setter Injection - Primitive

In Setter Injection, Spring invokes setter methods of a class to initialize the properties after invoking a default constructor.

How can we use setter injection to inject values for the primitive type of properties

Following the CustomerServiceImpl class has a count property,
package com.infy.service;
public class CustomerServiceImpl implements CustomerService {
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
         public CustomerServiceImpl(){
    }
}
How do we define bean in the configuration to initialize values?
package com.infy.util;
@Configuration
public class SpringConfiguration {
@Bean // CustomerService bean definition using Setter Injection
public CustomerServiceImpl customerService() {
CustomerServiceImpl customerService = new CustomerServiceImpl();
customerService.setCount(10);
return customerService;
}
}

What is mandatory to implement setter injection?

Default constructor and setter methods of respective dependent properties are required in the CustomerServiceImpl class. For setter injection, Spring internally uses the default constructor to create a bean and then invokes a setter method of the respective property based on the name attribute in order to initialize the values.

Setter Injection - Reference Datatype

inject primitive values using setter injection in Spring.

How do we inject object dependencies using setter injection?

package com.infy.service;
public class CustomerServiceImpl implements CustomerService {
private CustomerRepository customerRepository;
        private int count;
public CustomerRepository getCustomerRepository() {
return customerRepository;
}
public void setCustomerRepository(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
How do we inject object dependencies through configuration using setter injection?
package com.infy.util;
@Configuration
public class SpringConfiguration {
@Bean
public CustomerRepository customerRepository() {
return new CustomerRepository();
}
@Bean // Setter Injection
public CustomerServiceImpl customerService() {
CustomerServiceImpl customerService = new CustomerServiceImpl();
                customerService.setCount(10);
customerService.setCustomerRepository(customerRepository());
return customerService;
}
}

Demo :

package com.infy.util;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.infy.repository.CustomerRepository;
import com.infy.service.CustomerServiceImpl;
@Configuration
public class SpringConfiguration {
@Bean // Setter Injection
public CustomerRepository customerRepository() {
CustomerRepository customerRepository = new CustomerRepository();
return customerRepository;
}
@Bean // Setter Injection
public CustomerServiceImpl customerService() {
CustomerServiceImpl customerService = new CustomerServiceImpl();
customerService.setCount(10);
customerService.setRepository(customerRepository());
return customerService;
}
}
package com.infy.service;
public interface CustomerService {
public String fetchCustomer();
public String createCustomer();
}
package com.infy.service;
import com.infy.repository.CustomerRepository;
public class CustomerServiceImpl implements CustomerService {
private int count;
private CustomerRepository repository;
public CustomerServiceImpl() {
}
public void setCount(int count) {
this.count = count;
}
public void setRepository(CustomerRepository repository) {
this.repository = repository;
}
public String fetchCustomer() {
return repository.fetchCustomer(count);
}
public String createCustomer() {
return repository.createCustomer();
}
}
package com.infy.repository;
public class CustomerRepository {
public String fetchCustomer(int count) {
return " The no of customers fetched are : " + count;
}
public String createCustomer() {
return "Customer is successfully created";
}
}
package com.infy;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import com.infy.service.CustomerServiceImpl;
import com.infy.util.SpringConfiguration;
public class Client {
public static void main(String[] args) {
CustomerServiceImpl service = null;
AbstractApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
service = (CustomerServiceImpl) context.getBean("customerService");
System.out.println(service.fetchCustomer());
context.close();
}
}

Auto Scanning
as a developer you have to declare all the bean definition in SpringConfiguration class so that Spring container can detect and register your beans 
@Configuration
public class SpringConfiguration {
@Bean
public CustomerRepository customerRepository() {
return new CustomerRepository();
}
@Bean 
public CustomerServiceImpl customerService() {
return new CustomerServiceImpl();
}
}

Is any other way to eliminate this tedious beans declaration?

Yes, Spring provides a way to automatically detect the beans to be injected and avoid even the bean definitions within the Spring configuration file through Auto Scanning. In Auto Scanning, Spring Framework automatically scans, detects, and instantiates the beans from the specified base package, if there is no declaration for the beans in the SpringConfiguration class.

@Configuration
@ComponentScan(basePackages="com.infy")/@ComponentScan
public class SpringConfiguration {
}
Component scanning isn’t turned on by default, however. You have to annotate the configuration class with @ComponentScan annotation to enable component scanning 
Spring will scan the package that contains SpringConfig class and it subpackages for beans. But if you want to scan a different package or multiple packages then you can specify this with the basePackages attribute
@Configuration
@ComponentScan(basePackages = "com.infy.service,com.infy.repository")
public class SpringConfiguration {
}
Spring uses @ComponentScan annotation for the auto scan feature. It looks for classes with the stereotype annotations and creates beans for such classes automatically.

what are Stereotype annotations?

  • Stereotype annotations denote the roles of types or methods at the conceptual level.
  • Stereotype annotations are @Component, @Service, @Repository, and @Controller annotations.
  • These annotations are used for auto-detection of beans using @ComponentScan.
  • The Spring stereotype @Component is the parent stereotype.
  • The other stereotypes are the specialization of @Component annotation.

Annotation

Usage

@Component

It indicates the class(POJO class) as a Spring component.

@Service

It indicates the Service class(POJO class) in the business layer.

@Repository

It indicates the Repository class(POJO class in Spring DATA) in the persistence layer.

@Controller

It indicates the Controller class(POJO class in Spring MVC) in the presentation layer.


  • @Component is a generic stereotype for any Spring-managed component. 
  • @Repository, @Service, and @Controller are specializations of @Component for more specific use cases.
  • @Component should be used when your class does not fall into either of three categories i.e. Controllers, Services, and DAOs.

Configuring IOC container java Annotation based configuration

  • @Component: It is a general purpose annotation to mark a class as a Spring-managed bean. 
​@Component
public class CustomerLogging{
//rest of the code
}
  • @Service - It is used to define a service layer Spring bean. It is a specialization of the @Component annotation for the service layer.
@Service
public class CustomerSeviceImpl implements CustomerService {
//rest of the code
}
  • @Repository - It is used to define a persistence layer Spring bean. It is a specialization of the @Component annotation for the persistence layer.
@Repository
public class CustomerRepositoryImpl implements CustomerRepository {
//rest of the code
 }
  • @Controller - It is used to define a web component. It is a specialization of the @Component annotation for the presentation layer
@Controller
public class CustomerController {
//rest of the code
}
  • By default, the bean names are derived from class names with a lowercase initial character. Therefore, your above defined beans have the names customerController, customerServiceImpl, and customerRepositoryImpl. It is also possible to give a specific name with a value attribute in those annotations as follows:
@Repository(value="customerRepository")
public class CustomerRepositoryImpl implements CustomerRepository  {
//rest of the code
}
Note: As a best practice, use @Service for the service layer, @Controller for the Presentation layer, and @Repository for the Persistence layer. 

Demo :
package com.infy.util;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages="com.infy")
public class SpringConfiguration {
}
package com.infy.service;
public interface CustomerService {
public String fetchCustomer(int count);
public String createCustomer();
}
package com.infy.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service("customerService")
public class CustomerServiceImpl implements CustomerService {
@Value("10")
private int count;
public String fetchCustomer(int count) {
return " The no of customers fetched are : " + count;
}
public String createCustomer() {
return "Customer is successfully created";
}
}
package com.infy;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import com.infy.service.CustomerServiceImpl;
import com.infy.util.SpringConfiguration;
public class Client {
public static void main(String[] args) {
CustomerServiceImpl service = null;
AbstractApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
service = (CustomerServiceImpl) context.getBean("customerService");
System.out.println(service.createCustomer());
context.close();
}
}

Spring Boot

have learned that Spring is a lightweight framework for developing enterprise Java applications. But using Spring for application development is challenging for developer because of the following reason which reduces productivity and increases the development time:

1. Configuration

You have seen that the Spring application requires a lot of configuration. This configuration also needs to be overridden for different environments like production, development, testing, etc.  For example, the database used by the testing team may be different from the one used by the development team. So we have to spend a lot of time writing configuration instead of writing application logic for solving business problems. 

2. Project Dependency Management

When you develop a Spring application you have to search for all compatible dependencies for the Spring version that you are using and then manually configure them. If the wrong version of dependencies is selected then it will be an uphill task to solve this problem. Also for every new feature added to the application, the appropriate dependency needs to be identified and added. All this reduces productivity. 

So to handle all these kinds of challenges Spring Boot came into the market.

What is Spring Boot?

Spring Boot is a framework built on top of the Spring framework that helps the developers to build Spring-based applications very quickly and easily. The main goal of Spring Boot is to create Spring-based applications quickly without demanding developers to write the boilerplate configuration.

But how does it work? It works because of the following reasons,

1. Spring Boot is an opinionated framework

Spring Boot forms  .  It means that Spring Boot has some sensible defaults which you can use to quickly build your application.  For example, Spring Boot uses embedded Tomcat as the default web container.

2. Spring Boot is customizable

Though Spring Boot has its defaults, you can easily customize it at any time during your development based on your needs.  For example, if you prefer log4j for logging over Spring Boot built-in logging support then you can easily make a dependency change in your pom.xml file to replace the default logger with log4j dependencies.

The main Spring Boot features are as follows:

  • Starter Dependencies
  • Automatic Configuration
  • Spring Boot Actuator
  • Easy-to-use Embedded Servlet Container Support

There are multiple approaches to create a Spring Boot application. You can use any of the following approaches to create the application:

  • Using Spring Boot CLI
  • Using Spring Initializr
  • Using the Spring Tool Suite (STS)
Using Spring Initializr for creating Spring Boot applications.

It is an online tool provided by Spring for generating Spring Boot applications which is accessible at http://start.spring.io/. You can use it for creating a Spring Boot project using the following steps:

Step 1: Create your Spring Boot application launch Spring Initializr. You will get the following screen:

Step 2: Select Project as Maven, Language as Java, and Spring Boot as 2.1.13 and enter the project details as follows:

  • Choose com.infy as Group
  • Choose Demo_SpringBoot as Artifact

Click on More options and choose com.infy as Package Name 

Step 3: Click on Generate Project. This would download a zip file to your local machine.

Step 4: Unzip the zip file and extract it to a folder.

Step 5: In Eclipse, Click File → Import → Existing Maven Project. Navigate or type in the path of the folder where you extracted the zip file to the next screen. click finishing,

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.13.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.infy</groupId>
<artifactId>Demo_SpringBoot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Demo_SpringBoot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

2. application.properties

This file contains application-wide properties. To configure your application Spring reads the properties defined in this file. In this file, you can define a server’s default port, the server’s context path, database URLs, etc.

3. DemoSpringBootApplication.java

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

It is annotated with @SpringBootApplication annotation which triggers auto-configuration and component scanning and can be used to declare one or more @Bean methods also. It contains the main method which bootstraps the application by calling the run() method on the SpringApplication class. The run method accepts DemoSpringBootApplication.class as a parameter to tell Spring Boot that this is the primary component.

4. DemoSpringBootApplicationTest.java

In this file test cases are written. This class is by default generated by Spring Boot to bootstrap Spring application.

Spring Boot Starters

Spring Boot starters are pre-configured dependency descriptors with the most commonly used libraries that you can add to your application. So you don't need to search for compatible libraries and configure them manually. Spring Boot will ensure that the necessary libraries are added to the build. To use these starters, you have to add them to the pom.xml file. For example, to use spring-boot-starter following dependency needs to be added in pom.xml:

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

Spring Boot comes with many starters. Some popular starters which we are going to use in this course are as follows:

  • spring-boot-starter - This is the core starter that includes support for auto-configuration, logging, and YAML.
  • spring-boot-starter-aop - This starter is used for aspect-oriented programming with Spring AOP and AspectJ.
  • spring-boot-starter-data-jdbc - This starter is used for Spring Data JDBC.
  • spring-boot-starter-data-jpa - This starter is used for Spring Data JPA with Hibernate.
  • spring-boot-starter-web - This starter is used for building a web application using Spring MVC  and Spring REST. It also provides Tomcat as the default embedded container.
  • spring-boot-starter-test - This starter provides support for testing Spring Boot applications using libraries such as JUnit, Hamcrest, and Mockito.

Spring Boot Starter Parent

The Spring Boot Starter Parent defines key versions of dependencies and default plugins for quickly building Spring Boot applications. It is present in the pom.xml file of the application as a parent

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.13.RELEASE</version>
        <relativePath/> 
</parent>

Executing the Spring Boot application

To execute the Spring Boot application run the DemoSpringBootApplication as a standalone Java class which contains the main method.

Spring Boot Runners

So far you have learned how to create and start the Spring Boot application. Now suppose you want to perform some action immediately after the application has started then for this Spring Boot provides the following two interfaces:

  • CommandLineRunner
  • ApplicationRunner

CommandLineRunner is the Spring Boot interface with a run() method. Spring Boot automatically calls this method of all beans implementing this interface after the application context has been loaded. To use this interface, you can modify the DemoSpringBootApplication class as follows:

@SpringBootApplication
public class DemoSpringBootApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(DemoSpringBootApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println("Welcome to CommandLineRunner");
}
}

Configuring Spring Boot application.
Spring Boot application is configured using a file named application.properties. It is auto detected without any Spring based configurations and is placed inside the "src/main/resources" directory

In this file, various default properties are specified to support logging, AOP, JPA, etc. All the default properties need not be specified in all cases. We can specify them only on-demand. At startup, the Spring application loads all the properties and adds them to the Spring Environment class.

To use a custom property add it to the application.properties file.

  • application.properties
       message= Welcome Spring
  • Then autowire the Environment class into a class where the property is required.
        @Autowired
Environment env;
  • You can read the property from Environment using the getProperty() method.
        env.getProperty("message")


@PropertySource Annotation

You can use other files to keep the properties. For example, InfyTelmessage.properties.

InfyTelmessage.properties

message=Welcome To InfyTel

But by default Spring Boot will load only the application.properties file. So how you will load the InfyTelmessage.properties file?

In Spring @PropertySource annotation is used to read from properties file using Spring’s Environment interface. The location of the properties file is mentioned in the Spring configuration file using @PropertySource annotation. 

So InfyTelmessage.properties which are present in classpath can be loaded using @PropertySource 

import org.springframework.context.annotation.PropertySource;

@SpringBootApplication
@PropertySource("classpath:InfyTelmessage.properties")
public class DemoSpringBootApplication {
    public static void main(String[] args) throws Exception {
    //code
    }
}
To read the properties you need to autowire the Environment class into a class where the property is required
       @Autowired
Environment env;
You can read the property from Environment using the getProperty() method.
        env.getProperty("message")

@SpringBootApplication

the class which is used to bootstrap the Spring Boot application is annotated with @SpringBootApplication annotation

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

The @SpringBootApplication annotation indicates that it is a configuration class and also triggers auto-configuration and component scanning. It is a combination of the following annotations with their default attributes:

@EnableAutoConfiguration – This annotation enables auto-configuration for the Spring boot application which automatically configures our application based on the dependencies that you have added.

@ComponentScan – This enables the Spring bean dependency injection feature by using @Autowired annotation. All application components which are annotated with @Component, @Service, @Repository, or @Controller are automatically registered as Spring Beans. These beans can be injected by using @Autowired annotation.

@Configuration – This enables Java based configurations for Spring boot application.

The class that is annotated with @SpringBootApplication will be considered as the main class, is also a bootstrap class. It kicks starts the application by invoking the SpringApplication.run() method.  You need to pass the .class file name of your main class to the run() method.

SpringBootApplication- scanBasePackages

By default, SpringApplication scans the configuration class package and all it’s sub-packages. So if our SpringBootApplication class is in "com.eta" package, then it won’t scan com.infy.service or com.infy.repository package. We can fix this situation using the SpringBootApplication scanBasePackages property.

package com.eta;
@SpringBootApplication(scanBasePackages={"com.infy.service","com.infy.repository"})
public class DemoSpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoSpringBootApplication.class, args);
    }
}

Demo :
package com.infy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.support.AbstractApplicationContext;
import com.infy.service.CustomerServiceImpl;
@SpringBootApplication
public class Demo5Application{
public static void main(String[] args) {
CustomerServiceImpl service = null;
AbstractApplicationContext context = (AbstractApplicationContext) SpringApplication
.run(Demo5Application.class, args);
service = (CustomerServiceImpl) context.getBean("customerService");
System.out.println(service.fetchCustomer());
        context.close();
}
}
package com.infy.service;
public interface CustomerService {
public String fetchCustomer();
public String createCustomer();
}
package com.infy.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service("customerService")
public class CustomerServiceImpl implements CustomerService {
@Value("10")
private int count;
public String fetchCustomer() {
return " The no of customers fetched are : " + count;
}
public String createCustomer() {
return "Customer is successfully created";
}
}
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v2.1.13.RELEASE)
2020-04-07 17:21:31.908  INFO 119940 --- [           main] com.infy.Demo5Application                : Starting Demo5Application on 
2020-04-07 17:21:31.912  INFO 119940 --- [           main] com.infy.Demo5Application                : No active profile set, falling back to default profiles:
2020-04-07 17:21:34.211  INFO 119940 --- [           main] com.infy.Demo5Application                : Started Demo5Application in 3.998 seconds 
The no of customers fetched are : 10

AutoWiring 

In Spring if one bean class is dependent on another bean class then the bean dependencies need to be explicitly defined in your configuration class. But you can let the Spring IoC container to inject the dependencies into dependent bean classes without been defined in your configuration class. This is called as autowiring.

To do autowiring, you can use @Autowired annotation. This annotation allows the Spring IoC container to resolve and inject dependencies into your bean.@Autowired annotation performs byType Autowiring i.e. dependency is injected based on bean type. It can be applied to attributes, constructors, setter methods of a bean class. 

Autowiring is done only for dependencies to other beans. It doesn't work for properties such as primitive data types, String, Enum, etc. For such properties, you can use the @Value annotation.

@Autowired on Setter methods

The @Autowired annotation can be used on setter methods. This is called a Setter Injection.

package com.infy.service;
public class CustomerServiceImpl implements CustomerService {
private CustomerRepository customerRepository;
@Autowired
public void setCustomerRepository(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
      --------
}
The Spring IoC container will call the setter method for injecting the dependency of CustomerRepository. 

@Autowired on Constructor

The @Autowired annotation can also be used on the constructor. This is called a Constructor Injection.

package com.infy.service;
public class CustomerServiceImpl implements CustomerService {
private CustomerRepository customerRepository;
@Autowired
public CustomerServiceImpl(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
     --------------
}

@Autowired on Properties

Let us now understand the usage of @Autowired on a property in Spring.

We will use @Autowired in the below code to wire the dependency of CustomerService class for CustomerRepository bean dependency.

package com.infy.service;
public class CustomerServiceImpl {
// CustomerService needs to contact CustomerRepository, hence injecting the customerRepository dependency
@Autowired
private CustomerRepository customerRepository;
------------
}

@Autowired is by default wire the dependency based on the type of bean. 

In the above code, the Spring container will perform dependency injection using the Java Reflection API. It will search for the class which implements CustomerRepository and injects its object. The dependencies which are injected using @Autowired should be available to the Spring container when the dependent bean object is created. If the container does not find a bean for autowiring, it will throw the NoSuchBeanDefinitionException exception.

If more than one beans of the same type are available in the container, then the framework throws an exception indicating that more than one bean is available for autowiring. To handle this @Qualifier annotation is used as follows: 

package com.infy.service;
public class CustomerServiceImpl {
@Autowired
        @Qualifier("custRepo")
private CustomerRepository customerRepository;
         ------------
}

@Value annotation:

In Spring @Value annotation is used to insert values into variables and method arguments. Using @Value we can either read spring environment variables or system variables. 

We can assign a default value to a class property with @Value annotation:

public class CustomerDTO {
@Value("1234567891")
long phoneNo;
@Value("Jack")
String name;
@Value("Jack@xyz.com")
String email;
@Value("ANZ")
String address;
}

Note that it accepts only a String argument but the passed-in value gets converted to an appropriate type during value-injection.

To read a Spring environment variable we can use @Value annotation:

public class CustomerDTO {

@Value("${value.phone}")
long phoneNo;
@Value("${value.name}")
String name;
@Value("${value.email}")
String email;
@Value("${value.address}")
String address;

}

Demo 
package com.infy;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.support.AbstractApplicationContext;
import com.infy.dto.CustomerDTO;
import com.infy.service.CustomerServiceImpl;
@SpringBootApplication
public class Demo6Application {
public static void main(String[] args) {
CustomerServiceImpl service = null;
AbstractApplicationContext context = (AbstractApplicationContext) SpringApplication.run(Demo6Application.class,
args);
service = (CustomerServiceImpl) context.getBean("customerService");
List<CustomerDTO> listcust = service.fetchCustomer();
System.out.println("PhoneNumer" + "   " + "Name" + "   " + "Email" + "     " + "Address");
for (CustomerDTO customerDTO2 : listcust) {
System.out.format("%5d%10s%20s%10s", customerDTO2.getPhoneNo(), customerDTO2.getName(),
customerDTO2.getEmail(), customerDTO2.getAddress());
System.out.println();
}
}
}
package com.infy.service;
import java.util.List;
import com.infy.dto.CustomerDTO;
public interface CustomerService {
public String createCustomer(CustomerDTO customerDTO);
public List<CustomerDTO> fetchCustomer();
}
package com.infy.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.infy.dto.CustomerDTO;
import com.infy.repository.CustomerRepository;
@Service("customerService")
public class CustomerServiceImpl implements CustomerService{
@Autowired
private CustomerRepository customerRepository;
public String createCustomer(CustomerDTO customerDTO) {
customerRepository.createCustomer(customerDTO);
return "Customer with " + customerDTO.getPhoneNo() + " added successfully";
}
public List<CustomerDTO> fetchCustomer() {
return customerRepository.fetchCustomer();
}
}
package com.infy.repository;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Repository;
import com.infy.dto.CustomerDTO;
@Repository
public class CustomerRepository {
List<CustomerDTO> customers = null;
//Equivalent/similar to constructor. Here, populates the DTOs in a hard-coded way
@PostConstruct
public void initializer()
{
CustomerDTO customerDTO = new CustomerDTO();
customerDTO.setAddress("Chennai");
customerDTO.setName("Jack"); 
customerDTO.setEmail("Jack@infy.com");
customerDTO.setPhoneNo(9951212222l);
customers = new ArrayList<>();
customers.add(customerDTO);
}
//adds the received customer object to customers list
public void createCustomer(CustomerDTO customerDTO)
{
customers.add(customerDTO);
}
//returns a list of customers
public List<CustomerDTO> fetchCustomer()
{
return customers;
}
}
package com.infy.dto;
public class CustomerDTO {
long phoneNo;
String name;
String email;
String address;
public long getPhoneNo() {
return phoneNo;
}
public void setPhoneNo(long phoneNo) {
this.phoneNo = phoneNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public CustomerDTO(long phoneNo, String name, String email, String address) {
this.phoneNo = phoneNo;
this.name = name;
this.email = email;
this.address = address;
}
public CustomerDTO() {
}
}
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v2.1.13.RELEASE)
2020-04-07 12:11:56.259  INFO 78268 --- [           main] com.infy.Demo6Application                :
2020-04-07 12:11:56.263  INFO 78268 --- [           main] com.infy.Demo6Application                : 
2020-04-07 12:11:57.137  INFO 78268 --- [           main] com.infy.Demo6Application                : 
PhoneNumer    Name      Email       Address
9951212222   Jack   Jack@infy.com   Chennai

Bean Scope :

The lifetime of a bean depends on its scope. Bean's scope can be defined while declaring it in the configuration metadata file.

A bean can be in singleton or prototype scope. A bean with the "singleton" scope is initialized during the container starts up and the same bean instance is provided for every bean request from the application. However, in the case of the "prototype" scope, a new bean instance is created for every bean request from the application.

singleton

There will be a single instance of "singleton" scope bean in the container and the same bean is given for every request from the application.

The bean scope can be defined for a bean using @Scope annotation in Java class. By default, the scope of a bean is the singleton

package com.infy.service;
@Service("customerService")
@Scope("singleton")
public class CustomerServiceImpl implements CustomerService {
@Value("10")
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public String fetchCustomer() {
return " The number of customers fetched are : " + count;
}
}

prototype

For the "prototype" bean, there will be a new bean created for every request from the application.

In the below example, customerService bean is defined with prototype scope. There will be a new customerService bean created for every bean request from the application.

package com.infy.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service("customerService")
@Scope("prototype")
public class CustomerServiceImpl implements CustomerService {
@Value("10")
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public String fetchCustomer() {
return " The number of customers fetched are : " + count;
}
}

Demo :  Singleton
package com.infy.service;
public interface CustomerService {
public String fetchCustomer();
}
package com.infy.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service("customerService")
@Scope("singleton")
public class CustomerServiceImpl implements CustomerService {
@Value("10")
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public String fetchCustomer() {
return " The number of customers fetched are : " + count;
}
}
package com.infy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.support.AbstractApplicationContext;
import com.infy.service.CustomerServiceImpl;
@SpringBootApplication
public class Demo7Application {
public static void main(String[] args) {
 AbstractApplicationContext context = (AbstractApplicationContext) SpringApplication.run(Demo7Application.class,  args);
CustomerServiceImpl service1 = (CustomerServiceImpl) context.getBean("customerService");
System.out.println("The customerservice1 output=" + service1.fetchCustomer());
service1.setCount(20);
System.out.println("The customerservice1 output after setmethod=" + service1.fetchCustomer());
CustomerServiceImpl service2 = (CustomerServiceImpl) context.getBean("customerService");
System.out.println("The customerservice2 output =" + service2.fetchCustomer());
System.out.println(service1==service2);
context.close();
}
}
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v2.1.13.RELEASE)
2020-04-07 12:31:39.627  INFO 88468 --- [           main] com.infy.Demo7Application                : 
2020-04-07 12:31:39.631  INFO 88468 --- [           main] com.infy.Demo7Application                : 
2020-04-07 12:31:40.505  INFO 88468 --- [           main] com.infy.Demo7Application                : 
The customerservice1 output= The number of customers fetched are : 10
The customerservice1 output after setmethod= The number of customers fetched are : 20
The customerservice2 output = The number of customers fetched are : 20
true

------
Demo : Prototype
package com.infy.service;
@Service("customerService")
@Scope("prototype")
public class CustomerServiceImpl implements CustomerService {
@Value("10")
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public String fetchCustomer() {
return " The number of customers fetched are : " + count;
}
}

 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v2.1.13.RELEASE)
2020-04-07 17:20:51.414  INFO 18992 --- [           main] com.infy.Demo7Application                : 
2020-04-07 17:20:51.417  INFO 18992 --- [           main] com.infy.Demo7Application                : 2020-04-07 17:20:52.438  INFO 18992 --- [           main] com.infy.Demo7Application                : 
The customerservice1 output= The number of customers fetched are : 10
The customerservice1 output after setmethod= The number of customers fetched are : 20
The customerservice2 output = The number of customers fetched are : 10
false

LOGGING

What is logging?

Logging is the process of writing log messages to a central location during the execution of the program. That means Logging is the process of tracking the execution of a program, where

  • Any event can be logged based on the interest to the
  • When exception and error occurs we can record those relevant messages and those logs can be analyzed by the programmer later
  • There are multiple reasons why we may need to capture the application activity.
  • Recording unusual circumstances or errors that may be happening in the program
  • Getting the info about what's going in the application

There are several logging APIs to make logging easier. Some of the popular ones are:

  • JDK Logging API
  • Apache Log4j
  • Commons Logging API

The Logger is the object which performs the logging in applications.


Levels in the logger specify the severity of an event to be logged. The logging level is decided based on necessity. For example, TRACE can be used during development and ERROR during deployment.


You know that logging is one of the important activities in any application. It helps in quick problem diagnosis, debugging, and maintenance. Let us learn the logging configuration in Spring Boot


logging messaged logged on the  INFO level.  However, you haven't written any code for logging in to your application. Then who does this?

By default, Spring Boot configures logging via Logback to log the activities of libraries that your application uses.  

As a developer, you may want to log the information that helps in quick problem diagnosis, debugging, and maintenance. So, let us see how to customize the default logging configuration of Spring Boot so that your application can log the information that you are interested in and in your own format. 

Log error message : 

Have you realized that you have not done any of the below activities for logging which you typically do in any Spring application?

Adding dependent jars for logging
Configuring logging through Java configuration or XML configuration

Still, you are able to log your messages.  The reason is Spring Boot's default support for logging.  The spring-boot-starter dependency includes spring-boot-starter-logging dependency, which configures logging via Logback to log to the console at the INFO level.

Spring Boot uses Commons Logging API with default configurations for Java Util Logging, Log4j 2, and Logback implementation. Among these implementations, Logback configuration will be enabled by default.

You, as a developer, have just created an object for Logger and raise a request to log with your own message in LoggingAspect.java as shown below. 

public class CustomerServiceImpl implements CustomerService
{
private static Logger logger = LoggerFactory.getLogger(CustomerServiceImpl.class);
public void deleteCustomer(long phoneNumber) {
public void deleteCustomer(long phoneNumber) {
try {
customerRepository.deleteCustomer(phoneNumber);
} catch (Exception e) {
logger.info("In log Exception ");
logger.error(e.getMessage(),e);
}
}
}

 how to change this default configuration if you want to,

  • log the message in a file rather than console
  • log the message in your own pattern
  • log the messages of a specific level
  • use Log4j instead of Logback

Log into file 

By default Spring Boot logs the message on the console. To log into a file, you have to include either logging.file or logging. path property in your application.properties file.

Note: Please note that from Spring boot 2.3.X version onwards logging.file and logging.path has been deprecated we should use "logging.file.name" and " logging.file.path" for the same.

Custom log pattern

Include logging.pattern.* property in application.properties file to write the log message in your own format.

Logging property

Sample value

Description

logging.pattern.console

%d{yyyy-MM-dd HH:mm:ss,SSS}

Specifies the log pattern to use on the console

logging.pattern.file

%5p [%t] %c [%M] - %m%n

Specifies the log pattern to use in a file



Custom log level

By default, the logger is configured at the INFO level.  You can change this by configuring the logging.level.* property in application.properties file as shown below.

logging.level.root=WARN
logging.level.com.infosys.ars=ERROR

Log4j instead of Logback

Since Spring Boot chooses Logback implementation by default, you need to exclude it and then include log4j 2 instead of in your pom.xml.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
  <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

Demo : 
package com.infy.service;
import com.infy.dto.CustomerDTO;
public interface CustomerService {
public String createCustomer(CustomerDTO dto);
public String fetchCustomer();
public void deleteCustomer(long phoneNumber) throws Exception;
}
package com.infy.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.infy.dto.CustomerDTO;
import com.infy.repository.CustomerRepository;
@Service("customerService")
public class CustomerServiceImpl implements CustomerService {
private static Logger logger = LoggerFactory.getLogger(CustomerServiceImpl.class);
@Autowired
private CustomerRepository customerRepository;
@Override
public String createCustomer(CustomerDTO dto) {
return customerRepository.createCustomer(dto);
}
@Override
public String fetchCustomer() {
return customerRepository.fetchCustomer();
}
    @Override
public void deleteCustomer(long phoneNumber) {
try {
customerRepository.deleteCustomer(phoneNumber);
} catch (Exception e) {
logger.info("In log Exception ");
logger.error(e.getMessage(),e);
}
}
}
package com.infy.repository;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Repository;
import com.infy.dto.CustomerDTO;
@Repository
public class CustomerRepository {
@PostConstruct
public void initializer()
{
CustomerDTO customerDTO = new CustomerDTO();
customerDTO.setAddress("Chennai");
customerDTO.setName("Jack"); 
customerDTO.setEmail("Jack@infy.com");
customerDTO.setPhoneNo(9951212222l);
customers = new ArrayList<>();
customers.add(customerDTO);
}
List <CustomerDTO> customers=null;
public String createCustomer(CustomerDTO dto) {
customers = new ArrayList<>();
customers.add(dto);
return "Customer added successfully"+customers.indexOf(dto);
}
public String fetchCustomer()  {
return " The customer fetched  "+customers;
}
public void deleteCustomer(long phoneNumber) throws Exception
{
for(CustomerDTO customer : customers)
if(customer.getPhoneNo() == phoneNumber)
{
customers.remove(customer);
System.out.println(customer.getName()+"of phoneNumber"+customer.getPhoneNo()+"\t got deleted successfully");
break;
}
else 
throw new Exception("Customer does not exist");
}
}
}
package com.infy.dto;
public class CustomerDTO {
long phoneNo;
String name;
String email;
String address;
public long getPhoneNo() {
return phoneNo;
}
public void setPhoneNo(long phoneNo) {
this.phoneNo = phoneNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public CustomerDTO(long phoneNo, String name, String email, String address) {
this.phoneNo = phoneNo;
this.name = name;
this.email = email;
this.address = address;
}
public CustomerDTO() {
}
}
package com.infy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.support.AbstractApplicationContext;
import com.infy.service.CustomerServiceImpl;
@SpringBootApplication
public class Demo8Application {
public static void main(String[] args) {
CustomerServiceImpl service = null;
AbstractApplicationContext context = (AbstractApplicationContext) SpringApplication.run(Demo8Application.class,
args);
service = (CustomerServiceImpl) context.getBean("customerService");
service.deleteCustomer(1151212222l);
// service.deleteCustomer(9951212222l);
}
}
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v2.1.13.RELEASE)
2020-04-07 14:50:12.615  INFO 99756 --- [           main] com.infy.Demo8Application                : Starting Demo8Application
2020-04-07 14:50:12.621  INFO 99756 --- [           main] com.infy.Demo8Application                : No active profile set, 
2020-04-07 14:50:14.183  INFO 99756 --- [           main] com.infy.Demo8Application                : Started Demo8Application in 
2020-04-07 14:50:14.187  INFO 99756 --- [           main] com.infy.service.CustomerServiceImpl     : In log Exception 
2020-04-07 14:50:14.192 ERROR 99756 --- [           main] com.infy.service.CustomerServiceImpl     : Customer does not exist
java.lang.Exception: Customer does not exist
at com.infy.repository.CustomerRepository.deleteCustomer(CustomerRepository.java:49) ~[classes/:na]
at com.infy.service.CustomerServiceImpl.deleteCustomer(CustomerServiceImpl.java:38) ~[classes/:na]
at com.infy.Demo8Application.main(Demo8Application.java:19) [classes/:na]

AOP (Aspect Oriented Programming) 

AOP is used for applying common behaviors like transactions, security, logging, etc. to the application.

These common behaviors generally need to be called from multiple locations in an application. Hence, they are also called as cross cutting concerns in AOP.

Spring AOP provides the solution to cross cutting concerns in a modularized and loosely coupled way.

Advantages

  • AOP ensures that cross cutting concerns are kept separate from the core business logic.

  • Based on the configurations provided, the Spring applies cross cutting concerns appropriately during the program execution.

  • This allows creating a more loosely coupled application wherein you can change the cross cutting concerns code without affecting the business code.

  • In Object Oriented Programming(OOP), the key unit of modularity is class. But in AOP the key unit of modularity is an Aspect.

What is an Aspect?

Aspects are the cross-cutting concerns that cut across multiple classes.

Examples: Transaction Management, Logging, Security, etc.





in single class cross cutting functionality ( common functions) repeated twice 
In Spring AOP, we can add these cross-cutting concerns at run time by separating the cross-cutting concerns


Spring AOP
  • Aspect is a class that implements cross-cutting concerns. To declare a class as an Aspect it should be annotated with @Aspect annotation. It should be applied to the class which is annotated with @Component annotation or with derivatives of it.
  • Joinpoint is the specific point in the application such as method execution, exception handling, changing object variable values, etc. In Spring AOP a join point is always the execution of a method.
  • Advice is a method of the aspect class that provides the implementation for the cross-cutting concern. It gets executed at the selected join point(s). The following table shows the different types of advice along with the execution point they have
Type Of ExecutionExecution Point
BeforeBefore advice is executed before the Joinpoint execution.
After After advice will be executed after the execution of Joinpoint whether it returns with or without exception. Similar to finally block in exception handling.
AfterReturning AfterReturning advice is executed after a Joinpoint executes and returns successfully without exceptions
AfterThrowing AfterThrowing advice is executed only when a Joinpoint exits by throwing an exception
AroundAround advice is executed around the Joinpoints which means Around advice has some logic which gets executed before Joinpoint invocation and some logic which gets executed after the Joinpoint returns successfully

  • Pointcut represents an expression that evaluates the method name before or after which the advice needs to be executed.
  • In Spring AOP, we need to modularize and define each of the cross cutting concerns in a single class called Aspect.
  • Each method of the Aspect which provides the implementation for the cross cutting concern is called Advice.
  • The business methods of the program before or after which the advice can be called is known as a Joinpoint.
  • The advice does not get inserted at every Joinpoint in the program.
  • An Advice gets applied only to the Joinpoints that satisfy the Pointcut defined for the advice.
  •  Pointcut represents an expression that evaluates the business method name before or after which the advice needs to be called.
Consider an Aspect "LoggingAspect" as shown below to understand different Spring AOP terminologies. LoggingAspect is defined as Spring AOP Aspect by using @Aspect annotation.



PointCut decleration :

A pointcut is an important part of AOP. So let us look at pointcut in detail.

Pointcut expressions have the following syntax:

execution(<modifiers> <return-type> <fully qualified class name>.<method-name>(parameters))

where,

  • execution is called a pointcut designator. It tells Spring that joinpoint is the execution of the matching method.
  • <modifiers> determines the access specifier of the matching method. It is not mandatory and if not specified defaults to the public.
  • <return-type> determines the return type of the method in order for a join point to be matched. It is mandatory. If the return type doesn't matter wildcard * is used.
  • <fully qualified class name> specifies the fully qualified name of the class which has methods on the execution of which advice gets executed. It is optional. You can also use * wildcard as a name or part of a name.
  • <method-name> specifies the name of the method on the execution of which advice gets executed. It is mandatory. You can also use * wildcard as a name or part of a name.
  • parameters are used for matching parameters. To skip parameter filtering, two dots( ..) are used in place of parameters.

Pointcut

Description

execution(public * *(..))

execution of any public methods

execution(* service *(..))

execution of any method with a name beginning with "service"

execution(*com.infy.service.CustomerServiceImpl.*(..))

execution of any method defined in CustomerServiceImpl of com.infy.service package

execution(* com.infy.service.*.*(..))

execution of any method defined in the com.infy.service package

execution(public * com.infy.service.CustomerServiceImpl.*(..))

execution of any public method of CustomerServiceImpl of com.infy.service package

execution(public String com.infy.service.CustomerserviceImpl.*(..))

execution of all public method of CustomerServiceImpl of com.infy.service package that returns a String

assaf
Config AOP in pom
To use Spring AOP and AspectJ in the Spring Boot project you have to add the spring-boot-starter-aop starter in the pom.xml file as follows:

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

Before Advice:

This advice is declared using @Before annotation. It is invoked before the actual method call.  ie. This advice is executed before the execution of fetchCustomer()methods of classes present in com.infy.service package.

@Before("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public void logBeforeAdvice(JoinPoint joinPoint) {
logger.info("In Before Advice, Joinpoint signature :{}", joinPoint.getSignature());
long time = System.currentTimeMillis();
String date = DateFormat.getDateTimeInstance().format(time);
logger.info("Report generated at time:{}", date);
}

After Advice:

This advice is declared using @After annotation. It is executed after the execution of the actual method(fetchCustomer), even if it throws an exception during execution. It is commonly used for resource cleanup such as temporary files or closing database connections.

@After("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public void logAfterAdvice(JoinPoint joinPoint) {
logger.info("In After Advice, Joinpoint signature :{}", joinPoint.getSignature());
long time = System.currentTimeMillis();
String date = DateFormat.getDateTimeInstance().format(time);
logger.info("Report generated at time {}", date);
}

After Returning Advice

This advice is declared using @AfterReturning annotation. It gets executed after joinpoint finishes its execution. If the target method throws an exception the advice is not executed.  The following is an example of this advice that is executed after the method execution of fetchCustomer()method of classes present in com.infy.service package.

@AfterReturning(pointcut = "execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public void logDetails(JoinPoint joinPoint) {
logger.info("In AfterReturning Advice, Joinpoint signature :{}", joinPoint.getSignature());
}
You can also access the value returned by the joinpoint by defining the returning attribute of @AfterReturning annotation as follows:
@AfterReturning(pointcut = "execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))", returning = "result")
public void logDetails(JoinPoint joinPoint, String result) {
logger.info("In AfterReturning Advice with return value, Joinpoint signature :{}", joinPoint.getSignature());
logger.info(result.toString());
}

AfterThrowing Advice :

This advice is defined using @AfterThrowing annotation. It gets executed after an exception is thrown from the target method. The following is an example of this advice that gets executed when exceptions are thrown from the fetchCustomer() method of classes present in com.infy.service package

@AfterThrowing("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public void logAfterThrowingAdvice(JoinPoint joinPoint) {
logger.info("In After throwing Advice, Joinpoint signature :{}", joinPoint.getSignature());
}
You can also access the exception thrown from the target method inside the advice method

@AfterThrowing(pointcut ="execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))",throwing = "exception")
public void logAfterThrowingAdvice(JoinPoint joinPoint,Exception exception) {
logger.info("In After throwing Advice, Joinpoint signature :{}", joinPoint.getSignature());
logger.info(exception.getMessage());
}

Around advice:

This advice gets executed around the joinpoint i.e. before and after the execution of the target method. It is declared using @Around annotation

@Around("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before proceeding part of the Around advice.");
 Object cust =  joinPoint.proceed();
System.out.println("After proceeding part of the Around advice.");
return cust;
}

Demo : 
package com.infy.util;
import java.text.DateFormat;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.infy.dto.CustomerDTO;
@Component
@Aspect
public class LoggingAspect {
private static Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
@AfterThrowing("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public void logAfterThrowingAdvice(JoinPoint joinPoint) {
logger.info("In After throwing Advice, Joinpoint signature :{}", joinPoint.getSignature());
}
@AfterThrowing(pointcut = "execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))", throwing = "exception")
public void logAfterThrowingAdviceDetails(JoinPoint joinPoint, Exception exception) {
logger.info("In After throwing Advice, Joinpoint signature :{}", joinPoint.getSignature());
logger.error(exception.getMessage(),exception);
}
@After("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public void logAfterAdvice(JoinPoint joinPoint) {
logger.info("In After Advice, Joinpoint signature :{}", joinPoint.getSignature());
long time = System.currentTimeMillis();
String date = DateFormat.getDateTimeInstance().format(time);
logger.info("Report generated at time {}", date);
}
@Before("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public void logBeforeAdvice(JoinPoint joinPoint) {
// Log Joinpoint signature details
logger.info("In Before Advice, Joinpoint signature :{}", joinPoint.getSignature());
long time = System.currentTimeMillis();
String date = DateFormat.getDateTimeInstance().format(time);
logger.info("Report generated at time:{}", date);
}
 
@AfterReturning(pointcut = "execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public void logAfterReturningAdvice(JoinPoint joinPoint) {
logger.info("In AfterReturning Advice, Joinpoint signature :{}", joinPoint.getSignature());
}
@AfterReturning(pointcut = "execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))", returning = "result")
public void logAfterReturningDetails(JoinPoint joinPoint, List<CustomerDTO> result) {
logger.info("In AfterReturning Advice with return value, Joinpoint signature :{}", joinPoint.getSignature());
System.out.println(result);
long time = System.currentTimeMillis();
String date = DateFormat.getDateTimeInstance().format(time);
logger.info("Report generated at time:{}", date);
}
@Around("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before proceeding part of the Around advice.");
Object cust =  joinPoint.proceed();
System.out.println("After proceeding part of the Around advice.");
return cust;
}
}
package com.infy.service;
import java.util.List;
import com.infy.dto.CustomerDTO;
public interface CustomerService {
public String createCustomer(CustomerDTO customerDTO);
public List<CustomerDTO> fetchCustomer();
public String updateCustomer(long phoneNumber, CustomerDTO customerDTO);
public String deleteCustomer(long phoneNumber);
}
package com.infy.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.infy.dto.CustomerDTO;
import com.infy.repository.CustomerRepository;
@Service("customerService")
public class CustomerServiceImpl implements CustomerService {
@Autowired
private CustomerRepository customerRepository;
public String createCustomer(CustomerDTO customerDTO) {
customerRepository.createCustomer(customerDTO);
return "Customer with " + customerDTO.getPhoneNo() + " added successfully";
}
public List<CustomerDTO> fetchCustomer() {
// uncomment the below line to see the AfterThrowing advice
// int b=10/0;
return customerRepository.fetchCustomer();
}
public String updateCustomer(long phoneNumber, CustomerDTO customerDTO) {
return customerRepository.updateCustomer(phoneNumber, customerDTO);
}
public String deleteCustomer(long phoneNumber) {
return customerRepository.deleteCustomer(phoneNumber);
}
}
package com.infy.repository;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Repository;
import com.infy.dto.CustomerDTO;
@Repository
public class CustomerRepository {
List<CustomerDTO> customers = null;
@PostConstruct
public void initializer() {
CustomerDTO customerDTO = new CustomerDTO();
customerDTO.setAddress("Chennai");
customerDTO.setName("Jack");
customerDTO.setEmail("Jack@infy.com");
customerDTO.setPhoneNo(9951212222l);
customers = new ArrayList<>();
customers.add(customerDTO);
}
// adds the received customer object to customers list
public void createCustomer(CustomerDTO customerDTO) {
customers.add(customerDTO);
}
// returns a list of customers
public List<CustomerDTO> fetchCustomer() {
return customers;
}
// deletes customer
public String deleteCustomer(long phoneNumber) {
String response = "Customer of:" + phoneNumber + "\t does not exist";
for (CustomerDTO customer : customers) {
if (customer.getPhoneNo() == phoneNumber) {
customers.remove(customer);
response = customer.getName() + "of phoneNumber" + customer.getPhoneNo()
+ "\t got deleted successfully";
break;
}
}
return response;
}
// updates customer
public String updateCustomer(long phoneNumber, CustomerDTO customerDTO) {
String response = "Customer of:" + phoneNumber + "\t does not exist";
for (CustomerDTO customer : customers) {
if (customer.getPhoneNo() == phoneNumber) {
if (customerDTO.getName() != null)
customer.setName(customerDTO.getName());
if (customerDTO.getAddress() != null)
customer.setAddress(customerDTO.getAddress());
customers.set(customers.indexOf(customer), customer);
response = "Customer of phoneNumber" + customer.getPhoneNo() + "\t got updated successfully";
break;
}
}
return response;
}
}
package com.infy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.support.AbstractApplicationContext;
import com.infy.service.CustomerServiceImpl;
@SpringBootApplication
public class Demo9Application {
public static void main(String[] args) {
CustomerServiceImpl service = null;
AbstractApplicationContext context = (AbstractApplicationContext) SpringApplication.run(Demo9Application.class,
args);
service = (CustomerServiceImpl) context.getBean("customerService");
service.fetchCustomer();
context.close();
}
}

The best practices which need to be followed as part of the Quality and Security for the Spring application and Spring with Spring Boot applications.  These practices, when applied during designing and developing a Spring/Spring Boot application, yields better performance.

Best Practices:

  1. To create a new spring boot project prefer to use Spring Initializr
  2. While creating the Spring boot projects must follow the Standard Project Structure
  3. Use @Autowired annotation before a constructor.
  4. Use constructor injection for mandatory dependencies and Setter injection for optional dependencies in Spring /Spring boot applications
  5. Inside the domain class avoid using the stereotype annotations for the automatic creation of Spring bean.
  6. To create a stateless bean use the singleton scope and for a stateful bean choose the prototype scope.

There are three different ways to create a Spring Boot project. They are:

  • Using Spring Initializr
  • Using the Spring Tool Suite (STS)
  • Using Spring Boot CLI 

But the recommended and simplest way to create a Spring Boot application is the Spring Boot Initializr as it has a very good UI to download a production-ready project. And the same project can be directly imported into the STS/Eclipse.

There are two recommended ways to create a spring boot project structure for Spring boot applications, which will help developers to maintain a standard coding structure.

Don't use the “default” Package:

When a developer doesn't include a package declaration for a class, it will be in the “default package”. So the usage of the “default package” should be avoided as it may cause problems for Spring Boot applications that generally use the annotations like @ComponentScan, or @SpringBootApplication. So, during the creation of the classes, the developer should follow Java's package naming conventions. For example, com.infosys.client, com.infosys.service, com.infosys.controller etc.

There are 2 approaches that can be followed to create a standard project structure in Spring boot applications.

First approach: The first approach shows a layout which generally recommended by the Spring Boot team. In this approach we can see that all the related classes for the customer have grouped in the "net.guides.springboot2.cutomer" package and all the related classes for the order have grouped in the "net.guides.springboot2.order" package

Second approach: However the above structure works well but developers prefer to use the following structure.

In this approach, we can see that all the service classes related to customer and Order are grouped in the net.guide.springboot2.service package. Similar to that we can see we grouped the dao, controller, and model classes

Best Practice : 

  • Inside the domain class, try to avoid using the stereotype annotations for the automatic creation of Spring bean.

@Component
public class Employee { 
 // Methods and variables
}

Avoid creating beans for the domain class like the above.

  • Avoid scanning unnecessary classes to discover Beans. Specify only the required class for scanning
Suppose that if we need to discover beans declared inside the service package. Let us write the code for that

@Configuration
@ComponentScan("com.infosys")
public class AppConfig {
}

In the above configuration class, we mentioned scanning the entire package com.infosys.   But our actual requirement is only needed to scan the service packages. We can avoid this unnecessary scanning by replacing the code as below.

@Configuration
@ComponentScan("com.infosys.service")
public class AppConfig {
}
  • java doesn't allow annotations placed on interfaces to be inherited by the implemented class so make sure that we place the spring annotations only on class, fields, or methods.

  • As a good practice place @Autowired annotation before a constructor.

We know that there are three places we can place @Autowired annotation in Spring on fields, on setter method, and on a constructor. The classes using field injection are difficult to maintain and it is very difficult to test. The classes using setter injection will make testing easy, but this has some disadvantages like it will violate encapsulation. Spring recommends that use @Autowired on constructors that are easiest to test, and the dependencies can be immutable.

  • In the case of AOP as a best practice store all the Pointcuts in a common class which will help in maintaining the pointcuts in one place.
public class CommonPointConfig {
@Pointcut("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")      
        public void logAfterAdvice(JoinPoint joinPoint){}
    @Pointcut("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")      
        public void logBeforeAdvice(){}
}
The above common definition can be used when defining pointcuts in other aspects

@Around("com.infy.service.CustomerServiceImpl.fetchCustomer.aspect.CommonPointConfig.logBeforeAdvice()")
  • While defining the scope of the beans choose wisely.

If we want to create a stateless bean then singleton scope is the best choice. In case if you need a stateful bean then choose prototype scope.

  • When to choose  Constructor-based DI and setter-based DI in Spring /Spring boot applications

A Spring application developer can mix the Constructor injection and Setter injection in the same application but it's a good practice to use constructor injection for mandatory dependencies and Setter injection for optional dependencies. 

 Problem Statement 

Background: This problem statement provides the high level design of the project that has to be implemented as part of the hands-on assessment in order to complete the course Spring Basics.

InfyGo is an airline booking application that provides services to its customers to search for flight details. InfyGo wants a lightweight, loosely coupled application to be implemented using Spring.

Let us start with basic implementation using Spring core concepts for the following functionalities

  • Add Flight
  • Search Flight

As part of the Spring Basics course, let us develop the business tier of this application.

Implement the application using the following classes:

Domain class Flight with below details

public class Flight
{
        private String flightId;
        private String airlines;
        private String source;
        private String destination;
        private Double fare;
        private LocalDate journeyDate;
        private Integer seatCount;
        
        //Getter & Setter Methods
}
 

Implement the required repository using the collection

  • FlightRepository interface
  • FlightRepositoryImpl class

Implement the Service layer of the application

  • FlightService interface
  • FlightServiceImpl class

Implement client code to access bean of FlightServiceImpl class and perform below tasks in a menu based approach.

Add Flight: Add flight details by auto generating FlightId starting from 1001 and accepting other flight details from the user

Search Flight: User can Search flights based on source, destination, and journey date

  • Before displaying search details to the user, if the provided journey date is during the festival season(Dec to Jan) then increase the flight fare by 20% for all search results.

Note: Assume as of now the application is accessible to the user who can perform all the above functionalities.

Implement the  Spring Boot application with the following features:

  • Use Autowiring through annotations for bean dependencies
  • Implement LoggingAspect class for logging details whenever the new flight details are added

  • Advice should log Joinpoint signature, the current date and time stamp on console
  • Client code has to provide below menu based functionalities
  • Add Flight
  • Search Flight











Featured Post

HTML cheetsheet

List: Link tgs Dropdown

Popular Post

(C) Copyright 2018, All rights resrved InShortView. Template by colorlib