Spring Training With Spring Boot
Spring Training With Spring Boot
==================================
requirment:
------------
core java
jdbc: way to connection to db
servlet jsp
some knowlege of design pattern
--------------------
SW: java 17
sts 4: ide eclipse
maven 3.6: build tool, it automcatically download the jar files
any framework in java ---> jar
mysql
postman
Day-1
---------
Spring Introduction
• Shortcomings of Java EE and the Need for Loose Coupling
• Managing Beans, The Spring Container, Inversion of Control
• The Factory Pattern
• Configuration Metadata - XML, @Component, Auto-Detecting Beans
• Dependencies and Dependency Injection (DI) with the BeanFactory
• Setter Injection
Dependency Injection
• Using the Application Context
• Constructor Injection
The Spring Container and API
• The Spring Managed Bean Lifecycle
• Autowiring Dependencies
oracle or mysql
mysql 8.x
Spring framework:
-------------------
java framework that make sw easy
Depdendency injection
loose coupling
pull vs push
spring framework act as a container and manage the life cycle of the beans
Car , Passanger, AccountService, AccountDao
@Component
|
--------------------------------------------------
| |
|
@Controller @Service @Repository
controller service
Repository
@Autowire
DI at 3 places:
1. field injection XX
2. setter injection (non mandatory dep)
3. ctr injection (mandatory dep)
Spring profile:
------------------
@Profile("test")
VM arg:
-Dspring.profiles.active=dev
jdbc
spring basics
jdbc basics
jpa basics
Day 2:
-----
Spring and Persistence
• Spring and JDBC
• Spring and ORM
Spring Boot
Spring Boot Framework – High Level Overview
• Spring Architecture
• Spring Containers
• Spring Bean Lifecycle
• Spring DI
• Spring Autowiring
Day 3:
---------
Spring Boot – Database Integration
• Spring Boot – JDBC
• Spring Boot – JPA
• Spring Boot – Data
Introduction to Actuator
@Component
@Autowire
replace lots of xml
@Bean vs @Component
these are 2 ways to create bean using annotation
are they same... not
spring profile:
it allow you to swap the bean impl at run time without changing the code :)
devops: dev, test, pre-prod , prod
NoSuchBeanDefinitionException
Spring DI bankapplication
3 tier app:
@Component
|
----------------------------------------------------
| |
|
@controller @Service @Repository
@RestController
@Repository
public class AccountDaoImplMap implements AccountDao{
@Override
public Account getById(int id) {
return map.get(id);
}
@Override
public void updateAccount(Account account) {
map.put(account.getId(), account);
}
}
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
public AccountServiceImpl(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public List<Account> getAll() {
return accountDao.getAll();
}
@Override
public Account getById(int id) {
return accountDao.getById(id).orElseThrow(()->new
BankAccountNotFoundException("bank account with id "+id +" is not found"));
}
@Override
public void transfer(int fromId, int toId, double amount) {
Account fromAcc=getById(fromId);//load both the acc in memory
Account toAcc=getById(toId);
fromAcc.setAmount(fromAcc.getAmount()-amount);
toAcc.setAmount(toAcc.getAmount()+amount);
accountDao.updateAccount(fromAcc);
accountDao.updateAccount(toAcc);
}
@Override
public void deposit(int fromId, double amount) {
Account acc=getById(fromId);
acc.setAmount(acc.getAmount()+amount);
accountDao.updateAccount(acc);
}
@Override
public void withdraw(int fromId, double amount) {
Account acc=getById(fromId);
acc.setAmount(acc.getAmount()-amount);
accountDao.updateAccount(acc);
}
}
@Controller
@ResponseBody
public class AccountCrudController {
@Autowired
public AccountCrudController(AccountService accountService) {
this.accountService = accountService;
}
//------------add account-------
@RestController
public class TransctionController {
accountService.transfer(transferDto.getFromAccId(),transferDto.getToAccId()
, transferDto.getAmount());
return "fund transfer successfully";
}
//deposit
@PostMapping(path="deposit")
public String deposit(@RequestBody DepositDto depositDto) {
accountService.deposit(depositDto.getAccId(), depositDto.getAmount());
return "fund deposit successfully";
}
//withdraw
@PostMapping(path="withdraw")
public String withdraw(@RequestBody WithdrawDto withdrawDto) {
accountService.withdraw(withdrawDto.getAccId(),
withdrawDto.getAmount());
return "fund withdra successfully";
}
{
"fromAccId": 1,
"toAccId": 2,
"amount": 10
}
{
"accId": 1,
"amount": 10
}
server.port=8090
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Custom H2 Console URL
spring.h2.console.path=/h2
spring.jpa.hibernate.ddl-auto=update
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
spring.jpa.show-sql=true
@Entity
@Table(name="account_table")
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private double balance;
}
@Repository
public interface AccountRepository extends JpaRepository<Account, Integer>{
}
step 5: change the implementation of service layer
-----------------------------------
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
public AccountServiceImpl(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
@Override
public List<Account> getAll() {
return accountRepository.findAll();
}
@Override
public Account getById(int id) {
Account account= accountRepository.findById(id).orElseThrow(()->new
BankAccountNotFoundException("bank account not found") );
return account;
}
@Override
public void transfer(int fromId, int toId, double amount) {
Account fromAcc=getById(fromId);
Account toAcc=getById(toId);
fromAcc.setBalance(fromAcc.getBalance()-amount);
toAcc.setBalance(toAcc.getBalance()+amount);
accountRepository.save(fromAcc);
accountRepository.save(toAcc);
@Override
public void deposit(int fromId, double amount) {
Account acc=getById(fromId);
acc.setBalance(acc.getBalance()+amount);
accountRepository.save(acc);
@Override
public void withdraw(int fromId, double amount) {
Account acc=getById(fromId);
acc.setBalance(acc.getBalance()-amount);
accountRepository.save(acc);
}
step 6: As soon as my application start i should have some records by default added
to the database:
-----------------------------------------------------------------------------------
----------------
h2-> as soon as i restart my application my data would be lost
@Component
public class DataInit implements CommandLineRunner {
@Autowired
private AccountRepository accountRepository;
@Override
public void run(String... args) throws Exception {
//As soon as app start spring boot will run this run method
//data init job
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DepositDto {
private Integer accId;
private BigDecimal amount;
@Data
public class TransferDto {
private Integer fromAccId;
private Integer toAccId;
private BigDecimal amount;
}
@Data
public class WithdrawDto {
private Integer accId;
private BigDecimal amount;
@Entity
@Table(name="account_table")
@Data
@NoArgsConstructor
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private BigDecimal balance;
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
public AccountServiceImpl(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
@Override
public List<Account> getAll() {
return accountRepository.findAll();
}
@Override
public Account getById(int id) {
Account account= accountRepository.findById(id).orElseThrow(()->new
BankAccountNotFoundException("bank account not found") );
return account;
}
@Override
public void transfer(int fromId, int toId, BigDecimal amount) {
Account fromAcc=getById(fromId);
Account toAcc=getById(toId);
fromAcc.setBalance(fromAcc.getBalance().subtract(amount));
toAcc.setBalance(toAcc.getBalance().add(amount));
accountRepository.save(fromAcc);
accountRepository.save(toAcc);
@Override
public void deposit(int fromId, BigDecimal amount) {
Account acc=getById(fromId);
acc.setBalance(acc.getBalance().add(amount));
accountRepository.save(acc);
@Override
public void withdraw(int fromId, BigDecimal amount) {
Account acc=getById(fromId);
acc.setBalance(acc.getBalance().subtract(amount));
accountRepository.save(acc);
}
@Data
public class AccountDetailDto {
private String phone;
private String email;
private String address;
}
Step 10.3: implement these method new method decleration to the service layer
------------------------------------------------------
@Service
@Transactional
public class AccountServiceImpl implements AccountService{
//.....
@Override
public Account deleteAccount(int id) {
Account accountToDelete= getById(id);
accountRepository.delete(accountToDelete);
return accountToDelete;
}
@Override
public void updateAccount(int id, AccountDetailDto accountDetailDto) {
Account accountToUpdated= getById(id);
accountToUpdated.setAddress(accountDetailDto.getAddress());
accountToUpdated.setPhone(accountDetailDto.getPhone());
accountToUpdated.setEmail(accountDetailDto.getEmail());
//.......
//------------add account-------
@PostMapping(path = "accounts")
public Account addAccount( @RequestBody Account account) {
return accountService.addAccount(account);
}
@Controller
@ResponseBody
public class AccountCrudController {
@Autowired
public AccountCrudController(AccountService accountService) {
this.accountService = accountService;
}
//@ResponseBody annotation ie contain inside @RestController automatically
covert java object to json
//-----------get all accounts-----
@GetMapping(path = "accounts")
public ResponseEntity<List<Account>> getAll(){
List<Account> accounts= accountService.getAll();
return ResponseEntity.status(HttpStatus.OK).body(accounts);
}
//------------add account-------
@PostMapping(path = "accounts")
public ResponseEntity<Account> addAccount( @RequestBody Account account) {
return
ResponseEntity.status(HttpStatus.CREATED).body(accountService.addAccount(account));
}
@RestController
public class TransctionController {
accountService.transfer(transferDto.getFromAccId(),transferDto.getToAccId()
, transferDto.getAmount());
String fundTransferMessage= "fund transfer successfully";
return ResponseEntity.ok(fundTransferMessage);
}
//deposit
@PostMapping(path="deposit")
public ResponseEntity<String> deposit(@RequestBody DepositDto depositDto) {
accountService.deposit(depositDto.getAccId(), depositDto.getAmount());
String fundDepositMessage= "fund deposit successfully";
return ResponseEntity.ok(fundDepositMessage);
}
//withdraw
@PostMapping(path="withdraw")
public ResponseEntity<String> withdraw(@RequestBody WithdrawDto withdrawDto)
{
accountService.withdraw(withdrawDto.getAccId(),
withdrawDto.getAmount());
String fundWithdrawMessage= "fund withdraw successfully";
return ResponseEntity.ok(fundWithdrawMessage);
}
try{
tx.begin();
tx.commit();
}catch(..........){
}
Pls refer
https://www.marcobehler.com/guides/spring-transaction-management-transactional-in-
depth
for more indepth understanding on tx mgt
step 13: exception handing
--------------------------
How to do excpetion handing in spring boot:
using AOP* (Aspect oriented programming)
it is a way to handle cross cutting concern?
{
"timestamp": "2024-05-30T05:41:30.232+00:00",
"status": 500,
"error": "Internal Server Error",
"toContact":"[email protected]"
}
@Data
public class ErrorInfo {
private String timestamp;
private String status;
private String error;
private String toContact;
}
@ExceptionHandler(BankAccountNotFoundException.class)
public ResponseEntity<ErrorInfo> handle404(BankAccountNotFoundException ex){
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorInfo> handle500(Exception ex){
Step 14.2: Apply the annotation on pojo so that validation can be done
----------------------------------
@Entity
@Table(name="account_table")
@Data
@NoArgsConstructor
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
Step 14.3: Force spring boot to apply these annotation before converting to java
objection -> @Valid
--------------------------------------------------------------------
@PostMapping(path = "accounts")
public ResponseEntity<Account> addAccount( @RequestBody @Valid Account
account) {
return
ResponseEntity.status(HttpStatus.CREATED).body(accountService.addAccount(account));
}
BAD_REQUEST
@RestControllerAdvice
public class BankAppExHandlerController {
//-----------------
//MethodArgumentNotValidException
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorInfo> handle400(MethodArgumentNotValidException
ex){
Step 14.5: Now we need to give cause why validation have failed?
--------------------------------------------------------------------
//MethodArgumentNotValidException
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorInfo> handle400(MethodArgumentNotValidException
ex){
Step 14.6: Hard coding of error message is bad thing, how to control it?
--------------------------------------------------------------------
spring boot alreay have message handler configured... u just need to use it
ValidationMessages.properties
-----------------------------
account.email.absent=Please provide email address
account.email.invalid=Please provide valid email address
@NotNull(message = "{account.name.absent}")
@Pattern(regexp = "[A-Za-z]+( [A-Za-z]+)*", message =
"{account.name.invalid}")
private String name;
@NotNull(message = "{account.balance.absent}")
@Range(min = 100, max = 100000, message = "{account.balance.invalid}")
private BigDecimal balance;
@Email(message = "{account.email.invalid}")
@NotNull(message = "{account.email.absent}")
private String email;
@NotNull(message = "{account.phone.absent}")
@Pattern(regexp = "[789][0-9]{9}", message = "{account.phone.invalid}")
private String phone;
Step 14.7: Hard coding of error message is bad thing, how to control it?
--------------------------------------------------------------------
step 14.7.1: put string for error message into application.properties file
--------------------------------------------------------------------------
UserInterface.TRANSFER_SUCCESS=transfer done successfully
UserInterface.DEPOSIT_SUCCESS=amount deposit successfully
UserInterface.WITHDRAW_SUCCESS=amount withdraw successfully
@RestController
public class TransctionController {
step 14.7.2:we need to get values using environment so that no hard coding must be
there in project
--------------------------------------------------------------------------
@RestController
public class TransctionController {
@Autowired
public TransctionController(AccountService accountService, Environment
environment) {
this.accountService = accountService;
this.environment = environment;
}
accountService.transfer(transferDto.getFromAccId(),transferDto.getToAccId()
, transferDto.getAmount());
String fundTransferMessage=
environment.getProperty("UserInterface.TRANSFER_SUCCESS");
return ResponseEntity.ok(fundTransferMessage);
}
//deposit
@PostMapping(path="deposit")
public ResponseEntity<String> deposit(@RequestBody DepositDto depositDto) {
accountService.deposit(depositDto.getAccId(), depositDto.getAmount());
String fundDepositMessage=
environment.getProperty("UserInterface.DEPOSIT_SUCCESS");
return ResponseEntity.ok(fundDepositMessage);
}
//withdraw
@PostMapping(path="withdraw")
public ResponseEntity<String> withdraw(@RequestBody WithdrawDto withdrawDto)
{
accountService.withdraw(withdrawDto.getAccId(),
withdrawDto.getAmount());
String fundWithdrawMessage=
environment.getProperty("UserInterface.WITHDRAW_SUCCESS");
return ResponseEntity.ok(fundWithdrawMessage);
}
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
Step 2:
@GetMapping(path = "products", produces = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE})
spring.banner.location=classpath:banner.txt
https://devops.datenkollektiv.de/banner.txt/index.html
spring.jpa.hibernate.ddl-auto=update
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
dev
test
pre-prod
prod
test: h2
prod: mysql
spring.datasource.url=jdbc:mysql://localhost:3306/edu123
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
application-test.properties
----------------------------
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.path=/h2
step 17.2 use profile while running the app as jar file:
---------------------------------------------------------
java -jar bankapp.jar --spring.profiles.active=prod
how to change the port number?
1. @Value annotation
2. Enviornment
3. @ConfigrationProperties
@EnableConfigurationProperties(Config.class)
@ConfigurationProperties(prefix="spring.datasource")
Disable logging :
---------------
logging.level.root=OFF
logging.level.org.springframework.boot=OFF
spring.main.banner-mode=OFF
Customizing logging :
---------------
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
logging.level.com.productapp=info
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
logging.file.name=/home/raj/Desktop/logs/server.log
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-spring-boot</artifactId>
</dependency>
Step 1:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
Step 2:
http://localhost:9090/v3/api-docs
http://localhost:9090/swagger-ui/index.html
http://localhost:9090/v3/api-docs.yaml
Step 3:
Customization location
springdoc.swagger-ui.path=/swagger-ui-bankapp.html
http or jmx
server.port=8080
spring.devtools.restart.enabled=true
#management.endpoints.web.exposure.exclude=*
management.endpoints.web.exposure.include=health, custom-endpoint
management.endpoint.health.show-details=always
management.health.disk.enabled=true
management.health.livenessstate.enabled=true
management.health.readinessstate.enabled=true
management.server.port=9090
management.info.env.enabled=true
info.app.encoding=UTF-8
info.app.java.source=11
info.app.java.target=11
info.app.name=spring booot actuator
@Configuration
@Endpoint(id = "custom-endpoint")
public class CustomEndpoints {
@ReadOperation
public String getCustomData(){
return "This is custom Data";
}
}
step 20: how to create war file of the project and deploy to tomcat / jboss
---------------------------------------------------------------------------
FROM openjdk:17-alpine
MAINTAINER email="[email protected]"
EXPOSE 8080
ADD target/empapp.jar empapp.jar
ENTRYPOINT ["java","-jar","empapp.jar"]
docker image ls
io, serilization
ex handling
logging: log4j
adv java:
-------------
Threads
Java reflection
java annotation
java 8 features
java 9 features
GOF patterns
JDBC
tx mgt
ACID
Spring framework
DI
AOP
Spring jdbc
Spring Hibernate
Spring mvc with jsp (basics)
Spring Boot
=> Understand Depdendency Injection
=> bean wiring :xml, annoation, java configuraton
=> Spring boot aop
=> spring mvc basics
=> REST basics
=> Spring boot REST application
=> Spring AOP
=> Spring JDBCTemplate
=> Spring data
=> Spring boot web application, Spring MVC arch
=>Spring boot Monotring and Logging
=> Spring boot security
=> spring boot profile
=> spring boot actuator
=> spring boot jdbcTemplate
=> Spring Boot microservice architecture
=> spring boot openfeign
server.port=8090
server.servlet.context-path=/bankapp
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto= update
spring.datasource.url=jdbc:mysql://localhost:3306/edu123?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.hibernate.ddl-auto=update
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
logging.level.com.productapp: INFO
logging.level.com.productapp.service: INFO
spring.jpa.show-sql=true
spring.banner.location=
spring.jmx.enabled=true
management.endpoints.web.exposure.include=*
management.endpoints.jmx.exposure.include=*
management.info.env.enabled=true
info.app.encoding=UTF-8
info.app.java.source=21
info.app.java.target=21
info.app.name=productapp
info.app.dev=amit ku
management.endpoint.health.show-details=always
management.endpoint.health.probes.enabled=true
# livenessstate readinessstate
#management.health.livenessstate.enabled=true
#management.health.readinessstate.enabled=true
info.key=default
spring.profiles.active=test
server.port=8090
server.servlet.context-path=/productapp
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=foo
spring.datasource.password=foo
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
Reference book:
-----------------
Beginning Spring Boot 3: Build Dynamic Cloud-Native Java Applications and
Microservices Paperback
interview questions:
https://github.com/altafjava/spring-interview-questions-answers
https://stackoverflow.com/questions/11316688/inversion-of-control-vs-dependency-
injection-with-selected-quotes-is-my-unders
https://stackoverflow.com/questions/6550700/inversion-of-control-vs-dependency-
injection