Banu Prakash C
Full Stack Architect, Corporate Trainer
Co-founder & EX-CTO: Lucida Technologies Pvt Ltd.,
Email: banuprakashc@yahoo.co.in; banuprakash.cr@gmail.com;
https://www.linkedin.com/in/banu-prakash-50416019/
https://github.com/BanuPrakash/SPRING
===================================
Softwares Required:
1) openJDK 21
https://jdk.java.net/java-se-ri/21
For Mac machine USE SDKMAN to manage java
curl -s "https://get.sdkman.io" | bash
sdk install java 21.0.6-tem
sdk default java 21.0.6-tem
https://mydeveloperplanet.com/2022/04/05/how-to-manage-your-jdks-with-sdkman/#:~:text=Some%20time%20ago%2C%20a%20colleague%20of%20mine,maintain%20different%20versions%20of%20JDKs%2C%20Maven%2C%20etc.
2) IntelliJ Ultimate edition https://www.jetbrains.com/idea/download/?section=mac
3) MySQL [ Prefer on Docker]
Install Docker Desktop / PODMAN
Docker steps:
a) docker pull mysql
b) docker run --name local-mysql –p 3306:3306 -e MYSQL_ROOT_PASSWORD=Welcome123 -d mysql
container name given here is "local-mysql"
For Mac:
docker run -p 3306:3306 -d --name local-mysql -e MYSQL_ROOT_PASSWORD=Welcome123 mysql
c) CONNECT TO A MYSQL RUNNING CONTAINER:
$ docker exec -t -i local-mysql bash
d) Run MySQL client:
bash terminal> mysql -u "root" -p
mysql> exit
Spring Boot 3.5 version needs JDK 17 version
Introduction to Spring Framework and Spring Boot. RDBMS and JPA Building RESTful WebServices
- AOP
- Exception Handling
- Micrometer
- Cache
- HATEOAS
- Async operations Security
===================================================
SOLID Design Principles S - Single Responsibility O - Open close Principle L - Liskov Substitution I - Interface seggretgation D - Dependency Injection
======== Container is a layer/application on top of JRE. Servlet Container / EJB Container Java 1.2 - Bean is a resuable software component
Spring Framework provides container.
- Life Cycle management of beans [instantiate, destroy]
- Bean: any object managed by spring container
- Wiring dependencies
Spring needs metadata in the form of XML / Annotation
interface EmployeeDao {
void addEmployee(Employee employee);
}
public class EmployeeDaoDbImpl implements EmployeeDao {
// ..
public void addEmployee(Employee employee) {
..
}
}
public class EmployeeDaoMongoImpl implements EmployeeDao {
// ...
public void addEmployee(Employee employee) {
..
}
}
public class AppService {
private EmployeeDao employeeDao;
private void setEmpDao(EmployeeDao edao) {
this.employeeDao = edao;
}
public void doTask(Employee e) {
employeeDao.addEmployee(e);
}
}
XML as metadata for Spring beans.xml
<beans>
<bean id="rdbms" class="pkg.EmployeeDaoDbImpl" />
<bean id="mongo" class="pkg.EmployeeDaoMongoImpl" />
<bean id="service" class="pkg.AppService">
<property name="empDao" ref="mongo" />
</bean>
</beans>
// Internals
// SAX Parser - Runtime
Object rdbms = Class.forName("pkg.EmployeeDaoDbImpl").getConstructor().newInstance();
Object mongo = Class.forName("pkg.EmployeeDaoDbImpl").getConstructor().newInstance();
Object service = Class.forName("pkg.AppService").getConstructor().newInstance();
service.setEmpDao(mongo);
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", "");
AppService service = ctx.getBean("service", AppService.class);
Annotation as metadata: Preffered Spring looks for the following annotations at type level and instantiates the beans:
- @Component [util / helpers]
- @Repository [persistence store]
- @Service
- @Controller [ traditional web applications]
- @RestController [ RESTful Web services]
- @Configuration [ read config files, factory methods, ..]
- @ControllerAdvice [ Global exception handlers ]
- @ShellComponent [ spring boot 3.5 version onwards] [ Creating Shell Commands - REPL]
Spring uses @Autowired or Constructor Dependency Injection
Java 1.5 version onwards annotation is integral
interface EmployeeDao {
void addEmployee(Employee employee);
}
@Repository
public class EmployeeDaoDbImpl implements EmployeeDao {
// ..
public void addEmployee(Employee employee) {
..
}
}
@Service
public class AppService {
@Autowired
private EmployeeDao employeeDao;
public void doTask(Employee e) {
employeeDao.addEmployee(e);
}
}
ApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.cisco.prj"); // takes care of sub-packages
ctx.refresh();
A key benefit of using @Repository is that it enables Spring's exception translation mechanism.
try {
} catch(SQLException exception) {
if(exception.getErrorCode() == 1605) {
throw new DuplicateKeyException("Product with the ID : " + id + " already exists!!");
} else if(exception.getErrorCode() === ..) {
}
}
Spring Boot Framework: Framework on top of Spring Framework. Spring Boot 2 built on top of Spring Framework 5.x Spring Boot 3 is built ont top of Spring Framework 6.x
Why Spring Boot?
- Highly Opiniated Framework, lots of config comes out of the box
- assume we are building web based application
- Configures Embedded Tomcat Servlet Container / web server out of the box Alternatives: Jetty / Netty
- Provides JACKSON library for Java to JSON and JSON to Java conversion Alternates: Jettison / GSON / MOXY
- Provides DispatcherServlet as FrontController
- assume we are building JPA / ORM based application
- provides Database Connection Pooling using HikariCP library Alternate : C3p0/ DriverManagerDataSource ...
- Uses Hibernate as ORM provider Alternate: Toplink / KODO / JDO / OpenJPA ....
- Easy to Dockerize
=================================
SpringApplication.run(DemoApplication.class, args); is similar to new AnnotationConfigApplicationContext();
@SpringBootApplication is 3 in 1
- @Configuration
- @ComponentScan(basePackage="com.cisco.demo")
- @EnableAutoConfiguration
@Autowired
private UserRepo userRepo;
Could not autowire. There is more than one bean of 'UserRepo' type.
Beans:
userRepoDbImpl (UserRepoDbImpl.java)
userRepoMongoImpl (UserRepoMongoImpl.java)
Solution 1: using @Primary [ rarely used]
@Repository
@Primary
public class UserRepoDbImpl implements UserRepo {
@Repository
public class UserRepoMongoImpl implements UserRepo {
@Service
public class AppService {
@Autowired
private UserRepo userRepo; // UserRepoDbImpl will be wired
Solution 2: @Qualifier
@Repository
public class UserRepoDbImpl implements UserRepo{
@Repository
public class UserRepoMongoImpl implements UserRepo{
@Service
public class AppService {
@Autowired
@Qualifier("userRepoDbImpl")
private UserRepo userRepo;
@Service
public class AdminService {
@Autowired
@Qualifier("userRepoMongoImpl")
private UserRepo userRepo;
Solution 3: using @Profile
@Repository
@Profile("dev")
public class UserRepoDbImpl implements UserRepo{
@Repository
@Profile("prod")
public class UserRepoMongoImpl implements UserRepo{
@Service
public class AdminService {
@Autowired
private UserRepo userRepo;
Option 1: use application.properties
spring.profiles.active=dev
Option 2: Program arguments
More Run Configuration -> Modify Run Configuration -> Active profile = dev or prod
java -Dspring.profiles.active=prod com.cisco.demo.DemoApplication
Solution 4: using @ConditionalOnMissingBean
@Repository
public class UserRepoMongoImpl implements UserRepo{
@Repository
@ConditionalOnMissingBean(name="userRepoMongoImpl")
public class UserRepoDbImpl implements UserRepo{
@Service
public class AdminService {
@Autowired
private UserRepo userRepo;
==============
Factory Methods wrt Spring:
- 3rd party classes to be used in Spring Container
- Object instantiate and intialize is not straight forward.
============
Spring integration with JPA/ORM
ORM: Object Relational Mapping Object mapped to relational database tables
ORM frameworks for Java Hibernate -- JBOSS -- RedHat TopLink -- Oracle KODO -- BEA -- Oracle JDO -- SUN MS -- Oracle OpenJPA -- Apache ..
Example of using Spring Framework and JPA:
@Entity
@Table(name="EMP")
public class Employee {
@Column(name="EMP_NO")
private String empNo;
@Column(name="HDATE")
private Date hireDate;
}
@Configuration
public class AppConfig {
// @Bean informs Spring to invoke the method
// returned object should be managed within Spring container
@Bean
public DataSource getDataSource() throws Exception{
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "org.h2.Driver" ); //loads the jdbc driver
cpds.setJdbcUrl( "jdbc:h2:mem:testdb" );
cpds.setUser("sa");
cpds.setPassword("");
cpds.setMinPoolSize(5);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(20);
return cpds;
}
@Bean
public EntityManagerFactory emf(DataSource ds) {
LocalContainerEntityManagerDactoryBean emf = new LocalContainerEntityManagerDactoryBean();
emf.setDataSource(ds);
emf.setJpaVendor(new HibernateJpaVendor());
emf.setPackagestoScan("com.cisco.prj.entity"); // where are my entities
...
return emf;
}
}
@Repository
public class EmployeeRepo {
@PersistenceContext
EntityManager em;
public void addEmployee(Employee e) {
em.persist(e);
}
}
Spring Boot comes with Spring Data JPA module:
- we need to do entity mapping to database table
- No need to write any implementation classes. write an interface, implementation classes are generated by Spring Data JPA.
- no need to write DataSource, EntityManagerFactory ,,, Based on entries present in application.properties Spring Data JPA creates EntityManagerFactory, DataSource, ... [ No need for above AppConfig code]
=======================
docker run -p 3306:3306 -d --name local-mysql -e MYSQL_ROOT_PASSWORD=Welcome123 mysql
OR
podman run -p 3306:3306 -d --name local-mysql -e MYSQL_ROOT_PASSWORD=Welcome123 mysql
banuprakash@Banuprakashs-MacBook-Pro SPRING % docker exec -it local-mysql bash
bash-4.4# mysql -u root -p
Enter password:
mysql> create database SPRING;
mysql> use SPRING;
mysql>
Spring Boot application with following dependencies:
- lombok
- Spring Data JPA
- MySQL later we will add web [ for RESTful]
https://docs.spring.io/spring-boot/appendix/application-properties/index.html
-
spring.jpa.hibernate.ddl-auto=create create tables for entities when application starts, on application exist drop tables Good for Testing environment only
-
spring.jpa.hibernate.ddl-auto=update Top to Bottom apporach or Middle appraoch create tables if not exist if exists map table to entity if required alter table like adding columns, change size of column
-
spring.jpa.hibernate.ddl-auto=verify Bottom to Top Approach map entities to existing tables. Won't create tables if not present won't alter tables
=== CommandLineRunner is a interface in Spring Boot that provides a mechanism to execute run() after the Spring Boot application has started and the application context has been fully loaded.
Issues to resolve if any with lombok: 1) Settings --> Build, Execution, Deployment --> Compiler ->Annotation Processor --> shopapp -> Obtain processors from classpath.
- mvn clean
================== insert into customers values ('danny@cisco.com','Danny','Peter');
mysql> insert into customers values ('ria@cisco.com','Ria','Patel');
mysql> insert into customers values ('anne@cisco.com','Anne','Hathaway');
SQL uses table and column names
JP-QL uses class and field names; case sensitive; Polymorphic
@Entity
@Table(name="products")
class Product {
}
@Entity
@Table(name="tvs)
class Tv extends Product {
}
@Entity
@Table(name="mobiles")
class Mobile extends Product {
}
from Product; gets all records from products, "tvs" and "mobiles"
from Object; // records from all tables in database
By default built-in JpaRepository mutation code is Transactional.
Programmatic Transaction
JDBC:
public void doTask(...) {
Connection con = ...
try {
con.setAutoCommit(false);
// perform CRUD operations
con.commit();
} catch(SQLException ex) {
con.rollback();
}
}
Hibernate:
public void doTask(...) {
Session session = sessionFactory.getSession();
Transaction tx = session.beginTransaction();
try {
session.save(e1);
session.merge(e2);
tx.commit();
} catch(HibernateException ex) {
tx.rollback();
}
}
Declarative Transaction:
@Transactional
public void doTask(...) {
// JDBC / Hibernate / TopLink
}
Day 1 Recap:
Spring Framework vs Spring Boot
- Spring Core Module: Life cycle management of bean and Wiring dependencies.
- Spring Data JPA Module: HikariCP as database Connection pool and Hibernate as JPAVendor.
- @Entity, PersistenceContext, @Id, @Column, @Table ORM mapping
- JpaRepository interface - predefined Methods for CRUD operations
- JPA Projections using findByXXXX
- @Query for writing custom queries using SQL / JP-QL
- @Modifying
- @Transactional - Custom way of mutation / Insert
Built-in methods for INSERT / DELETE in JpaRepository have autocommit enabled.
productRepo.save(product); -- here in save() method they have turned on autocommit
By default for any other methods we are writing auto-commit is false
By placing @Transactional, this Aspect works like
- if there are no exceptions on the method which has @Transactional it commits else rollback
Day 2: update products set qty = 100 where 1 = 1;
Mapping associations:
- one to many
- many to one
- many to many
- one to one
https://www.database-answers.com/data_models/
Root aggregate of DDD: https://martinfowler.com/bliki/BoundedContext.html
@JoinColumn with @ManytoOne introduces FK in owning table/entity @JoinColumn with @OneToMany introduces FK in child table/entity
==========
Without Cascade Operations: One order has 4 line items
@OneToMany
@JoinColumn(name="order_fk")
private List<LineItem> items = new ArrayList<>();
Save operation:
orderRepo.save(order);
itemRepo.save(i1);
itemRepo.save(i2);
itemRepo.save(i3);
itemRepo.save(i4);
Delete operation:
orderRepo.delete(order);
itemRepo.delete(i1);
itemRepo.delete(i2);
itemRepo.delete(i3);
itemRepo.delete(i4);
With Cascade: One order has 4 line items
Composition and not Aggregation:
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name="order_fk")
private List<LineItem> items = new ArrayList<>();
Save operation:
orderRepo.save(order); // takes care of saving line items also
Delete operation:
orderRepo.delete(order); // takes care of deleting items also
Association: Composition or Aggregation
Fetching strategies:
- by default one to many is Lazy fetching and many to one is EAGER fetching
orderDao.findById(1); gets the customer data also but not line items
to get line items we need itemDao.getItemsOfOrder(orderId); // select * from line_items where order_fk = 1;
Make EAGER fetching:
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name="order_fk")
private List<LineItem> items = new ArrayList<>();
orderDao.findById(1);
gets the customer data also but not line items
gets line items also for the given order because of EAGER fetching
Dirty Checking: Withing @Transactional boundary if an entity becomes dirty [change], automatically JPA/ORM will trigger UPDATE SQL for that entity
=========
Building RESTful Web services using Spring MVC Module
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Provides:
- Tomcat as Embedded Servlet Container / Server
- Jackson for java to json and json to java conversion
- Provides DispatcherServlet as Front Controller and HandlerMapping
GET http://localhost:8080/api/products Accept: application/json
@RestController
@RequestMapping("api/products")
@RequiredArgsConstructor
public class ProductController {
private final OrderService service;
@GetMapping()
public List<Product> getProducts() {
return service.getProducts();
}
}
SSR: server side rendering
- Presentation pages are sent to client - HTML / PDF
- clients are thin [ like just a browser]
- Heavy payload [ whole pdf / html is sent between client and server]
- Client needs to understand how to handle HTML / PDF..
- Good for SEO
CSR: Client side Rendering
- different formats of representation is sent to clients: JSON / XML
- thin payload
- each client can consume data and generate views
- Heavy clients [ need an application ] in client machine
RESTful WS: REpresentational State Transfer.
- Resource: anything which can be named and present on server [ image / file / database / printer]
- Representation: state of resource at a given point [ printer is on / off / paused] data in a row of table
- ContentNegotiation: based on HttpHeaders - Accept and Content-type the representation is converted into various formats like JSON / XML / CSV ...
Characteristics of REST.
- Uniform Identifier: uses URL to identify a resource, HTTP methods GET / POST / PUT / PATCH / DELETE used to perform actions
- Client Server architecture: clients can evolve seperatly
- Stateless
- Layered
Content-type: application/json
PUT requests replace an entire resource with the data provided, while PATCH requests apply only partial updates to a resource, modifying only the fields sent in the request.
Task: CustomerController
- getAllCustomers
- getCustomer by Email
- register a new Customer [ add]
=================
PUT / PATCH / JSON-PATCH
A JSON Patch document is itself a JSON array containing a sequence of operations. Each operation specifies a particular type of change and uses JSON Pointer (RFC 6901) to identify the specific part of the document to be modified.
Key Operations in JSON Patch:
add: Adds a new value to an object or an element to an array.
remove: Removes a value from an object or an element from an array.
replace: Replaces an existing value.
move: Moves a value from one location to another within the document.
copy: Copies a value from one location to another within the document.
test: Tests if a value at a specified path matches a given value. If the test fails, the entire patch operation should abort.
Advantages of JSON Patch:
Reduced Bandwidth: Only the changes are sent, not the entire document.
Improved Performance: Efficient for applying partial updates.
Standardized Format: Ensures interoperability between different systems.
Fine-grained Control: Allows for precise manipulation of individual elements and properties, including within arrays.
AOP: Aspect Oriented Programming
Aspects: These are modules that encapsulate cross-cutting concerns. They are not a part of main business logic, but can be used along with business logic. Examples: log, profile, security, transaction
cross-cutting concerns leads to Code tangling and code scattering
public void transferFunds(Account fromAcc, Account toAcc, double amt) {
long startTime = System.currentTimeInMillis(); // profile
if(ctx.getPrinciple().hasPermission()) { // security
Transaction tx = ctx.getTransaction(); // transaction
log.info("transaction started!!!");
fromAcc.debit(amt);
log.info("amount debit done..");
toAcc.credit(amt);
log.info("amount credit done..");
insertIntoTxTable(data);
log.info("update TX history");
tx.commit(); // transaction
}
long endTime = System.currentTimeInMillis(); // profile
log.info("Tx compleed in : " ( endTime - startTime) + " ms");
}
Join Points: Specific points within the execution of a program where an aspect can be applied, such as method execution and exception.
Pointcuts: These are expressions that define precisely where an advice should be executed, for example, "before every method call on a class starting with 'UserService'".
Advice: how we weave an aspect to JoinPoint: Before, After, Around, AfterReturning, AfterThrowing
Weaving: The process of integrating the aspects into the core application code, either at compile-time, load-time, or run-time.
https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/pointcuts.html
@ControllerAdvice is an AfterThrowing Advice which is meant for Global Exception handler; Any exceptions thrown from @Controller or @RestController classes are progated to this class
Validation of Payload:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
public Product addProduct(@RequestBody @Valid Product product) {
@Valid makes of validation constraints...
@NotBlank(message = "Name is required!!!")
private String name;
Task : ticket Tracker application
employees
email | first_name | last_name | hire_date
a@cisco.com
b@cisco.com
c@cisco.com
tickets
ticket_id | raised_by_fk | raised_date | issue | resolved_by | resolved_date | fix
Use case 1: Raise a Ticket
tickets
ticket_id | raised_by_fk | raised_date | issue | resolved_by | resolved_date | fix
134 b@cisco.com 22-SEP-2025 3:44 IntelliJ Hangs. NULL. NULL. NULL
Use case 2: Resolve ticket
Pick a ticket to resolve -> input will be ticket ID
Make use of Dirty Checking capability
* pull the ticket based on Ticket ID
* set resolvedBy, resolvedDate [ system date], fix
tickets
ticket_id | raised_by_fk | raised_date | issue | resolved_by | resolved_date | fix
134 b@cisco.com 22-SEP-2025 3:44 IntelliJ Hangs. c@cisco 23-sep-2025 4:11 Applied patch
Hint:
Ticket class
@ManyToOne
@Joincolumn(name="raised_by_fk")
Employee raisedBy;
@ManyToOne
@JoinColumn(name="resolved_by")
Employee resolvedBy
Recap Day 2:
- Building RESTful WS
- @RestController instead of @Controller [SSR]
- @RequestMapping : Map a REQUEST URL to a Resource [ class]
- @GetMapping(), @PostMapping(), @PutMapping(), @PatchMapping(), JSON-PATCH [ array of json operations] , @DeleteMapping()
- @RequestBody to read payload from client
- @RequestParam to read Query Parameters [?] http://server.com/products?page=1&size=20
- @PathVariable to read Path parameters [/] http://server.com/products/3
- @ResponseBody - optional to specify that the return value has to be sent as response body
- @ResponseStatus
- AOP
- Aspect
- JoinPoint [eligibility where aspect can be weaved; with Spring methods and exceptions]
- PointCut [ selected JoinPoint]
- Advice [ Before, After, Around, AfterThrowing, AfterReturning]
- @ControllerAdvice [ built in advice works like AfterThrowing; any exceptions thrown from @Controller or @RestContoller is delegated to @ControllerAdvice]
- Validation - jakarta.validation.constraints like @NotBlank, @Pattern, @Min, @Max, @Future, @Past ...
- @Valid - MethodArgumentNotValidException
=======================
Day 3: Vehicle Rental Application
bookings
id | customer_fk | vehicle_fk | date_from | date_to | amount
select
v1_0.reg_no,
v1_0.fuel_type,
v1_0.hire_rate,
b1_0.date_from,
b1_0.date_to,
c1_0.fname,
c1_0.lname
from
bookings b1_0
join
vehicles v1_0
on v1_0.reg_no=b1_0.vehicle_fk
join
customers c1_0
on c1_0.email=b1_0.customer_fk
API Documentation:
- RAML
- OpenAPI - Swagger springdoc-openapi https://springdoc.org/
http://localhost:8080/v3/api-docs
http://localhost:8080/swagger-ui/index.html
====================================
Caching:
- Client-Side Caching Cache-Control -- Applicable only for Browser ETag - Entity Tag
- API level Caching - Spring Boot
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
By Default we get ConcurentMapCache - In Memory and CacheManager - Default
@Configuration
@EnableCaching
public class AppConfig {
}
@Cacheable(value = "productCache", key = "#id")
@CachePut(value = "productCache", key = "#id")
Redis is an in-memory key–value database, used as a distributed cache
Cache data survies system crash
docker run -p 6379:6379 -d --name some-redis -d redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring.data.redis.host=localhost
spring.data.redis.port=6379
Serialization: process of writing state of object to a stream [ like file, Other address space.. Outside of JVM]
public class Product implements Serializable
If NodeJS is installed: Redis Client
npx redis-commander
ConcurentMapCache is a ConditionalOnMissingBean; if no other CacheManager are available this will be used.
- Backend Caching: EhCache, JBossSwarmCache .... [database level] ==================
HATEOAS: Hypermedia As The Extension Of Application State https://martinfowler.com/articles/richardsonMaturityModel.html
WebMvcLinkBuilder - Builder to easily build link instances RepresentationModel
- EntityModel [Entity + links] Product --> EntityModel
- CollectionModel [ collection + links] List -> CollectionModel<List>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
Affordance : Characteristics about how an object can be used.
@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL_FORMS)
Spring Data REST: unmbrella project on top of Spring Data Repository and MVC module. Spring DATA REST builds endpoints based on repository interface and add Links [ HATEOAS]
- No need to write and @RestController or @Service classes, just JpaRepository will do.
- Spring Data RESt doesn't support @RestController
Spring boot application:
- Lombok
- MySQL
- JPA
- Rest Repositories
- web
http://localhost:8080/products http://localhost:8080/products/search/findByPriceBetween?low=10000&high=50000 http://localhost:8080/products/search/findByPriceGreaterThan?price=10000
How to do Customization? @BasePathAwareController instead of @RestController
=======================================
Spring Application Event and Async Operations.
class BillingService {}
class HouseKeepingService{}
class NotificationService{}
class MedicalRecordService {}
POST http://localhost:8080/api/discharge
{
patientId: 1235,
pateientName: "George"
}
// Synchronous Blocking Code and tightly coupled
public String dischargePatient(String id, String name) {
billingProcess.processBill(); // blocked
medicalRecordsService.updatePatientHistory(); // blocked
// service
houseKeepingService.cleanAndAssign(); // blocked
//notificationService.sendNotifcation(); // blocked
}
@EnableAsync --> Use Thread pool [default Spring Container provides a Thread Pool, we can have our own thread pool This is not Tomcat Thread pool - meant to handle HTTP request] @EventListener
With @Async Bill Processed for 555 by Thread Thread[#58,task-1,5,main] Notify Patient 555 by Thread : Thread[#59,task-2,5,main]
Without @Async Bill Processed for 555 by Thread Thread[#37,http-nio-8080-exec-1,5,main] Notify Patient 555 by Thread : Thread[#37,http-nio-8080-exec-1,5,main]
https://jsonplaceholder.typicode.com/users https://jsonplaceholder.typicode.com/posts
Spring Consuming Endpoints:
- RestTemplate
- WebClient
- RestClient [Spring Boot 3.0]
===================
Observability: Ability to measure the internal state of system Tells Why a System is at fault
Monitoring: System is at fault Focus on collection of Data
Status at the current time
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management.endpoint.health.show-details=always management.endpoints.web.exposure.include=*
http://localhost:8080/actuator
http://localhost:8080/actuator/health
http://localhost:8080/actuator/metrics
ab -c 100 -n 200 http://localhost:8080/api/products/1
http://localhost:8080/actuator/metrics/http.server.requests
Observability:
- Counter: incrementating and decrementing metric that represents a count
http://localhost:8080/actuator/metrics/my.counter
- timers: duration of events
- Gauges: Measure a value --> such as size of colleciton
============
Recap of Day 3:
- Vehicle Rental application: How to handle Date
- Caching: ETag, cache module --> ConcurrentMapCache, RedisCacheManager @EnableCaching, @Cacheable, @CachePut, @CacheEvit
- @EnableScheduling @Scheduled(cron = "* * * * * *")
- HATEOAS [ WebMvcLinkBuilder --> RepresentionModel [ data + links]] Spring Data REST based on HATEOAS and Spring Data Repository
- @EnableAsync, @Async, Spring ApplicationEvent, ApplicationEventPublisher, @EventListener
- Monitoring and Observability ; Actuator module --> Endpoints
Day 4: Prometheus collects and stores its metrics as time series data
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
Endpoint:
http://localhost:8080/actuator/prometheus
compose.yml
docker compose up
docker compose down
ab -c 100 -n 800 http://localhost:8080/api/products
http://localhost:9090/
jvm_threads_live_threads
http_server_requests_seconds_count
Security module: Authentication and Authorization
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
By including above dependency:
- All resources become protected
- creates a login and logout pages http://localhost:8080/login http://localhost:8080/logout
- creates a single user username: user password: <> Using generated security password: 569f2528-69f9-4255-8a44-0bb9f8fb1c59
===============
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
https://docs.spring.io/spring-security/reference/servlet/appendix/database-schema.html https://bcrypt-generator.com/
================
Token based authorization: JWT JSON Web Token (JWT)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30
HEADER:
{
"alg": "HS256",
"typ": "JWT"
}
PAYLOAD: CLAIMS
{
"sub": "harry@cisco.com",
"authorities": "ADMIN", "MANAGER", "GUEST",
"iat": 353253
"exp": 457323
"iss": "https://secure.cisco.com"
}
SIGNATURE:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
topsecretsaltvalueof256characters)
Scenario 1: use the same salt value to generate and validate Token
Scenario 2: use Private key to generate Token, public key to validate token [keytool ..]
http://server.com/api/products
Accept: application/json
Authorization: Bearer <<token>>
VehicleRentalApplication --> secure using JWT
- pom.xml add dependencies
- application.properites --> salt value
- added -> security package a) entities User and ROLE [ Many To Many relationship]
movies
mid | name
1 Pulp Fiction
2 Broken Arrow
actors
aid | name
1 John Travolta
2 Bruce Willis
movies_actors
mid_fk | aid_fk
1 1
1 2
2 1
b) Repo c) DTO SignUpRequest for Registration SignInRequest for Login d) UserDetailsServiceImpl e) AuthenticationService - register - signup() f) AuthController auth/register auth/login
h) JwtService getSigningKey() takes String Salt from application.properties --> java.security.Key
Login: AuthController [auth/login] AuthenticationService - JWTService Send Token To Client
Access Protected Resource: GET http://localhost:8080/api/vehicles Accept: application/json Authorization: Bearer <>
JwtAuthenticationFilter Extract email from Token using JwtService isTokenValid JwtService
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbm5lQGNpc2NvLmNvbSIsImlhdCI6MTc1ODc4MjI1MSwiZXhwIjoxNzU4NzgzNjkxLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiLCJST0xFX0FETUlOIl0sInJvbGVzIjpbIlJPTEVfVVNFUiIsIlJPTEVfQURNSU4iXSwiaXNzIjoiaHR0cHM6Ly9hdXRoc2VydmVyLmNpc2NvLmNvbSJ9.bBy6YdozL-HeD1GmDp2nnD2YFc2UeunhcCEtoIhdI-I
==========
Unit Testing: Testing in Isolation by mocking dependecies RestController - Service - Repository - Database
We will test RestControllers by mocking Service tier
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
- Mockito library to mock [ alternates are JMock , EasyMock]
- JUnit [ Unit testing framework - TestNG/JUnit]
- json-path [ https://jsonpath.com/ ]
- Hamcrest [ https://hamcrest.org/JavaHamcrest/tutorial ]
@WebMvcTest - loads a minimalistic Spring Container
-
MockMvc using which we can make HTTP requests in testing env
-
Testing version of DispatcherServlet
-
HandlerMapping
-
HttpMessageConvertors
-
Won't load Service / Repo / database ...