Unit Ii
Unit Ii
Unit Ii
UNIT II: Spring Boot: Creating a Spring Boot Application, Spring Boot Application
Annotation, What is Autowiring , Scope of a bean, Logger, Introduction to Spring
AOP, Implementing AOP advices, Best Practices : Spring Boot Application
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.
Spring Boot forms opinions. 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.
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.
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:
In this course, you will learn how to use Spring Initializr for creating Spring Boot
applications.
Step 1: Create your Spring Boot application launch Spring Initializr. You will get
the following screen:
Note: This screen keeps changing depending on updates from Pivotal and
changes in the Spring Boot version.
Step 3: Click on Generate Project. This would download a zip file to your local
machine.
You have created a Spring Boot Maven-based project. Now let us explore what
is contained in the generated project.
1. pom.xml
This file contains information about the project and configuration details used by
Maven to build the project.
34. <artifactId>spring-boot-maven-
plugin</artifactId>
35. </plugin>
36. </plugins>
37. </build>
38. </project>
39.
2. application.properties
3. DemoSpringBootApplication.java
1. @SpringBootApplication
2. public class DemoSpringBootApplication {
3. public static void main(String[] args) {
4. SpringApplication.run(DemoSpringBootApplication
.class, args);
5. }
6. }
7.
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 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:
1. <dependency>
2. <groupId>org.springframework.boot</groupId>
3. <artifactId>spring-boot-starter</artifactId>
4. </dependency>
5.
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.
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 as follows:
1. <parent>
2. <groupId>org.springframework.boot</groupId>
3. <artifactId>spring-boot-starter-
parent</artifactId>
4. <version>2.1.13.RELEASE</version>
5. <relativePath/>
6. </parent>
7.
It allows you to manage the following things for multiple child projects and
modules:
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
1. @SpringBootApplication
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.
application.properties
Then autowire the Environment class into a class where the property is required.
1. @Autowired
2. Environment env;
You can read the property from Environment using the getProperty() method.
1. env.getProperty("message")
You can use other files to keep the properties. For example,
InfyTelmessage.properties.
InfyTelmessage.properties
1. 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?
1.
2. import
org.springframework.context.annotation.PropertySource;
3.
4. @SpringBootApplication
5. @PropertySource("classpath:InfyTelmessage.properties")
12.
To read the properties you need to autowire the Environment class into a class
where the property is required
1. @Autowired
2. Environment env;
You can read the property from Environment using the getProperty() method.
1. env.getProperty("message")
We have already learned that the class which is used to bootstrap the Spring
Boot application is annotated with @SpringBootApplication annotation as follows:
1. @SpringBootApplication
2. public class DemoSpringBootApplication {
3. public static void main(String[] args) {
4. SpringApplication.run(DemoSpringBootApplication
.class, args);
5. }
6. }
7.
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.
1. package com.eta;
2. @SpringBootApplication(scanBasePackages={"com.in
fy.service","com.infy.repository"})
3.
4. public class DemoSpringBootApplication {
5. public static void main(String[] args) {
6. SpringApplication.run(DemoSpringBootAppl
ication.class, args);
7. }
8. }
Highlights:
Demo Steps:
Demo5Application .java
1. package com.infy;
2.
3. import org.springframework.boot.SpringApplication;
4. import
org.springframework.boot.autoconfigure.SpringBootApplicatio
n;
5. import
org.springframework.context.support.AbstractApplicationCont
ext;
6.
7. import com.infy.service.CustomerServiceImpl;
8.
9. @SpringBootApplication
10. public class Demo5Application{
11.
12. public static void main(String[] args) {
13.
23.
CustomerService.java
1. package com.infy.service;
2.
3. public interface CustomerService {
4. public String fetchCustomer();
5. public String createCustomer();
6. }
7.
CustomerServiceImpl.java
1. package com.infy.service;
2.
3. import org.springframework.beans.factory.annotation.Value;
4. import org.springframework.stereotype.Service;
5.
6.
7. @Service("customerService")
8. public class CustomerServiceImpl implements CustomerService
{
9.
10. @Value("10")
11. private int count;
12.
13. public String fetchCustomer() {
22.
OUTPUT:
1.
2. . ____ _ __ _ _
3. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
4. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
5. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
6. ' |____| .__|_| |_|_| |_\__, | / / / /
7. =========|_|==============|___/=/_/_/_/
8. :: Spring Boot :: (v2.1.13.RELEASE)
9.
10. 2020-04-07 17:21:31.908 INFO 119940 --- [ main]
com.infy.Demo5Application : Starting Demo5Application on
11. 2020-04-07 17:21:31.912 INFO 119940 --- [ main]
com.infy.Demo5Application : No active profile set, falling
back to default profiles:
12. 2020-04-07 17:21:34.211 INFO 119940 --- [ main]
com.infy.Demo5Application : Started Demo5Application in
3.998 seconds
13. The no of customers fetched are : 10
What is Autowiring ?
n 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
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.
1. package com.infy.service;
2. public class CustomerServiceImpl implements
CustomerService {
3.
4. private CustomerRepository customerRepository;
5.
6. @Autowired
7. public void setCustomerRepository(CustomerRepository
customerRepository) {
8. this.customerRepository = customerRepository;
9. }
10. --------
11. }
In the above code snippet, 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.
1. package com.infy.service;
2. public class CustomerServiceImpl implements CustomerService {
3.
4. private CustomerRepository customerRepository;
5.
6. @Autowired
7. public CustomerServiceImpl(CustomerRepository customerRepository) {
8. this.customerRepository = customerRepository;
9. }
10. --------------
11. }
@Autowired on Properties
1. package com.infy.service;
2. public class CustomerServiceImpl {
3. // CustomerService needs to contact CustomerRepository,
hence injecting the customerRepository dependency
4. @Autowired
5. private CustomerRepository customerRepository;
6. ------------
7. }
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:
1. package com.infy.service;
2. public class CustomerServiceImpl {
3.
4. @Autowired
5. @Qualifier("custRepo")
6. private CustomerRepository customerRepository;
7. ------------
8. }
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.
10. }
Note that it accepts only a String argument but the passed-in value gets
converted to an appropriate type during value-injection.
Highlights:
Demo Steps:
Demo6Application .java
1. package com.infy;
2.
3. import java.util.List;
4. import org.springframework.boot.SpringApplication;
5. import
org.springframework.boot.autoconfigure.SpringBootApplic
ation;
6. import
org.springframework.context.support.AbstractApplication
Context;
7. import com.infy.dto.CustomerDTO;
8. import com.infy.service.CustomerServiceImpl;
9.
10. @SpringBootApplication
29.
CustomerService.java
1. package com.infy.service;
2.
3. import java.util.List;
4.
5. import com.infy.dto.CustomerDTO;
6.
7. public interface CustomerService {
8. public String createCustomer(CustomerDTO customerDTO);
9. public List<CustomerDTO> fetchCustomer();
10.
11. }
12.
CustomerServiceImpl.java
1. package com.infy.service;
2.
3. import java.util.List;
4. import
org.springframework.beans.factory.annotation.Autowired;
5. import org.springframework.stereotype.Service;
6. import com.infy.dto.CustomerDTO;
7. import com.infy.repository.CustomerRepository;
8.
9. @Service("customerService")
10. public class CustomerServiceImpl implements
CustomerService{
11.
12. @Autowired
13. private CustomerRepository customerRepository;
14.
15. public String createCustomer(CustomerDTO
customerDTO) {
16.
customerRepository.createCustomer(customerDTO);
17.
18. return "Customer with " +
customerDTO.getPhoneNo() + " added successfully";
19. }
20.
21. public List<CustomerDTO> fetchCustomer() {
22. return customerRepository.fetchCustomer();
23. }
24. }
25.
CustomerRepository.java
1. package com.infy.repository;
2.
3.
4. import java.util.ArrayList;
5. import java.util.List;
6.
7. import javax.annotation.PostConstruct;
8.
9. import org.springframework.stereotype.Repository;
10. import com.infy.dto.CustomerDTO;
11.
12.
13. @Repository
14. public class CustomerRepository {
15. List<CustomerDTO> customers = null;
16.
17. //Equivalent/similar to constructor. Here, populates
the DTOs in a hard-coded way
18. @PostConstruct
19. public void initializer()
20. {
21. CustomerDTO customerDTO = new
CustomerDTO();
22. customerDTO.setAddress("Chennai");
23. customerDTO.setName("Jack");
24. customerDTO.setEmail("Jack@infy.com");
25. customerDTO.setPhoneNo(9951212222l);
26. customers = new ArrayList<>();
27. customers.add(customerDTO);
28. }
29.
30. //adds the received customer object to customers
list
31. public void createCustomer(CustomerDTO
customerDTO)
32. {
33. customers.add(customerDTO);
34. }
35.
36. //returns a list of customers
37. public List<CustomerDTO> fetchCustomer()
38. {
39. return customers;
40. }
41. }
42.
CustomerDTO.java
1. package com.infy.dto;
2.
3. public class CustomerDTO {
4. long phoneNo;
5. String name;
6. String email;
7. String address;
8.
9. public long getPhoneNo() {
10. return phoneNo;
11. }
12.
13. public void setPhoneNo(long phoneNo) {
14. this.phoneNo = phoneNo;
15. }
16.
17. public String getName() {
18. return name;
19. }
20.
21. public void setName(String name) {
22. this.name = name;
23. }
24.
25. public String getEmail() {
26. return email;
27. }
28.
29. public void setEmail(String email) {
30. this.email = email;
31. }
32.
33. public String getAddress() {
34. return address;
35. }
36.
37. public void setAddress(String address) {
38. this.address = address;
39. }
40.
41. public CustomerDTO(long phoneNo, String name,
String email, String address) {
42. this.phoneNo = phoneNo;
43. this.name = name;
44. this.email = email;
45. this.address = address;
46.
47. }
48. public CustomerDTO() {
49.
50. }
51. }
52.
OUTPUT:
1.
2. . ____ _ __ _ _
3. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
4. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
5. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
6. ' |____| .__|_| |_|_| |_\__, | / / / /
7. =========|_|==============|___/=/_/_/_/
8. :: Spring Boot :: (v2.1.13.RELEASE)
9.
10. 2020-04-07 12:11:56.259 INFO 78268 --- [
main] com.infy.Demo6Application :
11. 2020-04-07 12:11:56.263 INFO 78268 --- [
main] com.infy.Demo6Application :
12. 2020-04-07 12:11:57.137 INFO 78268 --- [
main] com.infy.Demo6Application :
13. PhoneNumer Name Email Address
14. 9951212222 Jack Jack@infy.com Chennai
Scope of a bean
The lifetime of a bean depends on its scope. Bean's scope can be defined while
declaring it in the configuration metadata file.
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
1. package com.infy.service;
2.
3. @Service("customerService")
4. @Scope("singleton")
5. public class CustomerServiceImpl implements CustomerService {
6. @Value("10")
7. private int count;
8.
9. public int getCount() {
10. return count;
11. }
12.
13. public void setCount(int count) {
14. this.count = count;
15. }
16.
17. public String fetchCustomer() {
18. return " The number of customers fetched are : "
+ count;
19. }
20.
21. }
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.
1. package com.infy.service;
2.
3. import
org.springframework.beans.factory.annotation.Value;
4. import
org.springframework.context.annotation.Scope;
5. import org.springframework.stereotype.Service;
6.
7. @Service("customerService")
8. @Scope("prototype")
9. public class CustomerServiceImpl implements
CustomerService {
10. @Value("10")
11. private int count;
12.
13. public int getCount() {
14. return count;
15. }
16.
17. public void setCount(int count) {
18. this.count = count;
19. }
20.
21. public String fetchCustomer() {
22. return " The number of customers
fetched are : " + count;
23. }
24.
25. }
Highlights:
Demosteps:
CustomerService.java
1. package com.infy.service;
2.
3. public interface CustomerService {
4.
5. public String fetchCustomer();
6.
7. }
CustomerServiceImpl.java
1. package com.infy.service;
2.
3. @Service("customerService")
4. @Scope("prototype")
5. public class CustomerServiceImpl implements CustomerService
{
6. @Value("10")
7. private int count;
8.
9. public int getCount() {
10. return count;
11. }
12.
13. public void setCount(int count) {
14. this.count = count;
15. }
16.
17. public String fetchCustomer() {
18. return " The number of customers fetched are
: " + count;
19. }
20.
21. }
22.
Demo7Application.java
1. package com.infy;
2.
3. import org.springframework.boot.SpringApplication;
4. import
org.springframework.boot.autoconfigure.SpringBootApplicatio
n;
5. import
org.springframework.context.support.AbstractApplicationCont
ext;
6. import com.infy.service.CustomerServiceImpl;
7.
8. @SpringBootApplication
9. public class Demo7Application {
10.
11. public static void main(String[] args) {
12. AbstractApplicationContext context =
(AbstractApplicationContext)
SpringApplication.run(Demo7Application.class,
13. args);
14. CustomerServiceImpl service1 =
(CustomerServiceImpl) context.getBean("customerService");
15. System.out.println("The customerservice1
output=" + service1.fetchCustomer());
16. service1.setCount(20);
17. System.out.println("The customerservice1
output after setmethod=" + service1.fetchCustomer());
18. CustomerServiceImpl service2 =
(CustomerServiceImpl) context.getBean("customerService");
19. System.out.println("The customerservice2
output =" + service2.fetchCustomer());
20. System.out.println(service1==service2);
21. context.close();
22. }
23.
24. }
25.
Output
What is Autowiring?
Different types of Autowiring
What are the scope of a bean and its different types?
SpringBoot Logging
Logger
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
There are multiple reasons why we may need to capture the application activity.
There are several logging APIs to make logging easier. Some of the popular
ones are:
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.
While executing your Spring Boot application, have you seen things like the
below getting printed on your console?
Yes, you are right. These are 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.
Have you realized that you have not done any of the below activities for logging
which you typically do in any Spring application?
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.
16. }
Apart from info(), the Logger class provides other methods for logging
information:
Log level
Process id
Thread name
Separator
Logger name
Log message
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.
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.
1. logging.level.root=WARN
2. logging.level.com.infosys.ars=ERROR
3.
Since Spring Boot chooses Logback implementation by default, you need to exclude it and then
include log4j 2 instead of in your pom.xml.
1. <dependency>
2. <groupId>org.springframework.boot</groupId
>
3. <artifactId>spring-boot-
starter</artifactId>
4. <exclusions>
5. <exclusion>
6.
<groupId>org.springframework.boot</groupId>
7. <artifactId>spring-boot-
starter-logging</artifactId>
8. </exclusion>
9. </exclusions>
10. </dependency>
11. <dependency>
12. <groupId>org.springframework.boot</groupId
>
13. <artifactId>spring-boot-starter-
log4j2</artifactId>
14. </dependency>
Demo: Logger
Highlights:
Demo Steps:
CustomerService.java
1. package com.infy.service;
2.
3. import com.infy.dto.CustomerDTO;
4.
5. public interface CustomerService {
6.
7. public String createCustomer(CustomerDTO dto);
8.
9. public String fetchCustomer();
10.
11. public void deleteCustomer(long phoneNumber)
throws Exception;
12. }
CustomerServiceImpl.java
1. package com.infy.service;
2.
3. import org.slf4j.Logger;
4. import org.slf4j.LoggerFactory;
5. import
org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.stereotype.Service;
7.
8. import com.infy.dto.CustomerDTO;
9. import com.infy.repository.CustomerRepository;
10.
11. @Service("customerService")
12.
13. public class CustomerServiceImpl implements
CustomerService {
14. private static Logger logger =
LoggerFactory.getLogger(CustomerServiceImpl.class);
15. @Autowired
16. private CustomerRepository customerRepository;
17.
18.
19.
20. @Override
21. public String createCustomer(CustomerDTO dto) {
22. return
customerRepository.createCustomer(dto);
23.
24. }
25.
26. @Override
27. public String fetchCustomer() {
28.
29. return customerRepository.fetchCustomer();
30. }
31.
32. @Override
33. public void deleteCustomer(long phoneNumber) {
34.
35. try {
36.
customerRepository.deleteCustomer(phoneNumber);
37. } catch (Exception e) {
38. logger.info("In log Exception ");
39. logger.error(e.getMessage(),e);
40. }
41.
42. }
43. }
44.
CustomerRepository.java
1. package com.infy.repository;
2.
3.
4. import java.util.ArrayList;
5. import java.util.List;
6.
7. import javax.annotation.PostConstruct;
8.
9. import org.springframework.stereotype.Repository;
10.
11. import com.infy.dto.CustomerDTO;
12.
13.
14. @Repository
50. }
51.
52. }
53. }
54.
CustomerDTO.java
1. package com.infy.dto;
2.
3. public class CustomerDTO {
4. long phoneNo;
5. String name;
6. String email;
7. String address;
8.
9. public long getPhoneNo() {
10. return phoneNo;
11. }
12.
13. public void setPhoneNo(long phoneNo) {
14. this.phoneNo = phoneNo;
15. }
16.
17. public String getName() {
18. return name;
19. }
20.
21. public void setName(String name) {
22. this.name = name;
23. }
24.
25. public String getEmail() {
26. return email;
27. }
28.
29. public void setEmail(String email) {
30. this.email = email;
31. }
32.
33. public String getAddress() {
34. return address;
35. }
36.
37. public void setAddress(String address) {
38. this.address = address;
39. }
40.
41. public CustomerDTO(long phoneNo, String name,
String email, String address) {
42. this.phoneNo = phoneNo;
43. this.name = name;
44. this.email = email;
45. this.address = address;
46.
47. }
48.
49. public CustomerDTO() {
50.
51. }
52. }
53.
Demo8Application.java
1. package com.infy;
2.
3. import org.springframework.boot.SpringApplication;
4. import
org.springframework.boot.autoconfigure.SpringBootApplic
ation;
5. import
org.springframework.context.support.AbstractApplication
Context;
6. import com.infy.service.CustomerServiceImpl;
7.
8. @SpringBootApplication
9. public class Demo8Application {
10.
11. public static void main(String[] args) {
12.
13. CustomerServiceImpl service = null;
24.
Output:
1.
2. . ____ _ __ _ _
3. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
4. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
5. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
6. ' |____| .__|_| |_|_| |_\__, | / / / /
7. =========|_|==============|___/=/_/_/_/
8. :: Spring Boot :: (v2.1.13.RELEASE)
9.
10. 2020-04-07 14:50:12.615 INFO 99756 --- [
main] com.infy.Demo8Application :
Starting Demo8Application
11. 2020-04-07 14:50:12.621 INFO 99756 --- [
main] com.infy.Demo8Application : No
active profile set,
12. 2020-04-07 14:50:14.183 INFO 99756 --- [
main] com.infy.Demo8Application :
Started Demo8Application in
13. 2020-04-07 14:50:14.187 INFO 99756 --- [
main] com.infy.service.CustomerServiceImpl : In log
Exception
14. 2020-04-07 14:50:14.192 ERROR 99756 --- [
main] com.infy.service.CustomerServiceImpl :
Customer does not exist
15.
What is Logging?
How to implement logging in Spring Boot?
Intrng AOP
Introduction to Spring AOP (See Appendix A )
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.
Type Of
Execution Point
Execution
Before Before advice is executed before the Joinpoint execution.
After advice will be executed after the execution of Joinpoint whether it
After
returns with or without exception. Similar to finally block in exception
Type Of
Execution Point
Execution
handling.
AfterReturning advice is executed after a Joinpoint executes and
AfterReturning
returns successfully without exceptions
AfterThrowing advice is executed only when a Joinpoint exits by
AfterThrowing
throwing an exception
Around advice is executed around the Joinpoints which means Around
advice has some logic which gets executed before Joinpoint invocation
Around
and some logic which gets executed after the Joinpoint returns
successfully
2.
where,
Pointcut Description
execution of any public
execution(public * *(..))
methods
execution of any method with a
execution(* service *(..))
name beginning with "service"
execution of any method
defined in
execution(*com.infy.service.CustomerServiceImpl.*(..))
CustomerServiceImpl of
com.infy.service package
execution of any method
execution(* com.infy.service.*.*(..)) defined in the com.infy.service
package
execution of any public method
execution(public *
of CustomerServiceImpl of
com.infy.service.CustomerServiceImpl.*(..))
com.infy.service package
execution of all public method
execution(public String of CustomerServiceImpl of
com.infy.service.CustomerserviceImpl.*(..)) com.infy.service package that
returns a String
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:
1. <dependency>
2. <groupId>org.springframework.boot</groupId>
3. <artifactId>spring-boot-starter-aop</artifactId>
4. </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. The
following is an example of this advice:
1. @Before("execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))
")
2. public void logBeforeAdvice(JoinPoint joinPoint) {
3. logger.info("In Before Advice, Joinpoint
signature :{}", joinPoint.getSignature());
4. long time = System.currentTimeMillis();
5. String date =
DateFormat.getDateTimeInstance().format(time);
6. logger.info("Report generated at time:{}",
date);
7. }
After Advice:
1. @After("execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))
")
2. public void logAfterAdvice(JoinPoint joinPoint) {
3. logger.info("In After Advice, Joinpoint
signature :{}", joinPoint.getSignature());
4. long time = System.currentTimeMillis();
5. String date =
DateFormat.getDateTimeInstance().format(time);
6. logger.info("Report generated at time {}",
date);
7. }
1. @AfterReturning(pointcut = "execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))
")
2. public void logDetails(JoinPoint joinPoint) {
3. logger.info("In AfterReturning Advice, Joinpoint
signature :{}", joinPoint.getSignature());
4. }
You can also access the value returned by the joinpoint by defining the returning
attribute of @AfterReturning annotation as follows:
1. @AfterReturning(pointcut = "execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))
", returning = "result")
2. public void logDetails(JoinPoint joinPoint, String
result) {
3. logger.info("In AfterReturning Advice with
return value, Joinpoint signature :{}",
joinPoint.getSignature());
4. logger.info(result.toString());
5. }
In the above code snippet, the value of the returning attribute is returnvalue
which matches the name of the advice method argument.
AfterThrowing Advice :
1. @AfterThrowing("execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))
")
2. public void logAfterThrowingAdvice(JoinPoint
joinPoint) {
5. }
You can also access the exception thrown from the target method inside
the advice method as follows:
1. @AfterThrowing(pointcut ="execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))
",throwing = "exception")
2. public void logAfterThrowingAdvice(JoinPoint
joinPoint,Exception exception) {
3. logger.info("In After throwing Advice, Joinpoint
signature :{}", joinPoint.getSignature());
4. logger.info(exception.getMessage());
5. }
Demo: AOP
Highlights:
Demo Steps:
LoggingAspect.java
1. package com.infy.util;
2.
3. import java.text.DateFormat;
4. import java.util.List;
5.
6. import org.aspectj.lang.JoinPoint;
7. import org.aspectj.lang.ProceedingJoinPoint;
8. import org.aspectj.lang.annotation.After;
9. import org.aspectj.lang.annotation.AfterReturning;
45.
46. @Before("execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
47. public void logBeforeAdvice(JoinPoint joinPoint) {
48. // Log Joinpoint signature details
49. logger.info("In Before Advice, Joinpoint
signature :{}", joinPoint.getSignature());
50. long time = System.currentTimeMillis();
51. String date =
DateFormat.getDateTimeInstance().format(time);
52. logger.info("Report generated at time:{}",
date);
53.
54. }
55.
56. @AfterReturning(pointcut = "execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
57. public void logAfterReturningAdvice(JoinPoint
joinPoint) {
58. logger.info("In AfterReturning Advice,
Joinpoint signature :{}", joinPoint.getSignature());
59. }
60.
61. @AfterReturning(pointcut = "execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))",
returning = "result")
62. public void logAfterReturningDetails(JoinPoint
joinPoint, List<CustomerDTO> result) {
63. logger.info("In AfterReturning Advice with
return value, Joinpoint signature :{}",
joinPoint.getSignature());
64. System.out.println(result);
65. long time = System.currentTimeMillis();
66. String date =
DateFormat.getDateTimeInstance().format(time);
67. logger.info("Report generated at time:{}",
date);
68. }
69.
70. @Around("execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
71. public Object aroundAdvice(ProceedingJoinPoint
joinPoint) throws Throwable {
72. System.out.println("Before proceeding part of
the Around advice.");
73. Object cust = joinPoint.proceed();
74. System.out.println("After proceeding part of
the Around advice.");
79.
CustomerService.java
1. package com.infy.service;
2.
3. import java.util.List;
4.
5. import com.infy.dto.CustomerDTO;
6.
7. public interface CustomerService {
8. public String createCustomer(CustomerDTO customerDTO);
9.
10. public List<CustomerDTO> fetchCustomer();
11.
12. public String updateCustomer(long phoneNumber,
CustomerDTO customerDTO);
13.
14. public String deleteCustomer(long phoneNumber);
15.
16. }
17.
CustomerServiceImpl.java
1. package com.infy.service;
2.
3. import java.util.List;
4.
5. import
org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.stereotype.Service;
7.
8. import com.infy.dto.CustomerDTO;
9. import com.infy.repository.CustomerRepository;
10.
11. @Service("customerService")
39.
CustomerRepository.java
1. package com.infy.repository;
2.
3. import java.util.ArrayList;
4. import java.util.List;
5.
6. import javax.annotation.PostConstruct;
7.
8. import org.springframework.stereotype.Repository;
9. import com.infy.dto.CustomerDTO;
10.
11. @Repository
12. public class CustomerRepository {
13. List<CustomerDTO> customers = null;
14.
15. @PostConstruct
16. public void initializer() {
17. CustomerDTO customerDTO = new CustomerDTO();
18. customerDTO.setAddress("Chennai");
19. customerDTO.setName("Jack");
20. customerDTO.setEmail("Jack@infy.com");
21. customerDTO.setPhoneNo(9951212222l);
22. customers = new ArrayList<>();
23. customers.add(customerDTO);
24.
25. }
26.
27. // adds the received customer object to customers
list
28. public void createCustomer(CustomerDTO customerDTO)
{
29. customers.add(customerDTO);
30. }
31.
32. // returns a list of customers
33. public List<CustomerDTO> fetchCustomer() {
34.
35. return customers;
36. }
37.
38. // deletes customer
39. public String deleteCustomer(long phoneNumber) {
40. String response = "Customer of:" +
phoneNumber + "\t does not exist";
41. for (CustomerDTO customer : customers) {
42. if (customer.getPhoneNo() ==
phoneNumber) {
43. customers.remove(customer);
44. response = customer.getName() +
"of phoneNumber" + customer.getPhoneNo()
45. + "\t got deleted
successfully";
46. break;
47. }
48. }
49. return response;
50. }
51.
52. // updates customer
53. public String updateCustomer(long phoneNumber,
CustomerDTO customerDTO) {
54. String response = "Customer of:" +
phoneNumber + "\t does not exist";
55. for (CustomerDTO customer : customers) {
56. if (customer.getPhoneNo() ==
phoneNumber) {
57.
58. if (customerDTO.getName() !=
null)
59.
customer.setName(customerDTO.getName());
60. if (customerDTO.getAddress() !=
null)
61.
customer.setAddress(customerDTO.getAddress());
62.
63.
customers.set(customers.indexOf(customer), customer);
64. response = "Customer of
phoneNumber" + customer.getPhoneNo() + "\t got updated
successfully";
65. break;
66. }
67. }
68. return response;
69. }
70.
71. }
72.
Demo9Application.java
1. package com.infy;
2.
3. import
org.springframework.boot.SpringApplication;
4. import
org.springframework.boot.autoconfigure.SpringBootApplic
ation;
5. import
org.springframework.context.support.AbstractApplication
Context;
6. import com.infy.service.CustomerServiceImpl;
7.
8. @SpringBootApplication
9. public class Demo9Application {
10.
11. public static void main(String[] args) {
12.
13. CustomerServiceImpl service = null;
14. AbstractApplicationContext context =
(AbstractApplicationContext)
SpringApplication.run(Demo9Application.class,
15. args);
16. service = (CustomerServiceImpl)
context.getBean("customerService");
17. service.fetchCustomer();
18. context.close();
19. }
20.
21. }
Summary :AOP
What is AOP?
Different types of Advices used in AOP
How to implement AOP in Spring?
Let us discuss 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:
2. While creating the Spring boot projects must follow the Standard Project
Structure
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:
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.
Note: The above screen keeps changing depending on updates from Pivotal and
changes in the Spring Boot version.
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.
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 "com.infy.service" package. Similar to that we can
see we grouped the repository, controller, and entity classes.
1. @Component
2.
3. public class Employee {
4.
5. // Methods and variables
6.
7. }
Avoid creating beans for the domain class like the above.
Suppose that if we need to discover beans declared inside the service package.
Let us write the code for that.
1. @Configuration
2.
3. @ComponentScan("com.infosys")
4.
5. public class AppConfig {
6.
7. }
1. @Configuration
2.
3. @ComponentScan("com.infosys.service")
4.
5. public class AppConfig {
6.
7. }
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.
8.
The above common definition can be used when defining pointcuts in other
aspects.
1. @Around("com.infy.service.CustomerServiceImpl.fetchCust
omer.aspect.CommonPointConfig.logBeforeAdvice()")
2.
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.
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.
Appendix A: AOP