Giter Site home page Giter Site logo

springboot3-spring6's Introduction

SpringBoot3-Spring6

Learn Spring Boot 3 with Spring 6 and Hibernate Course: https://www.udemy.com/course/spring-hibernate-tutorial/

Hibernate, Spring Data JPA, Spring Data REST, Spring Security, Thymeleaf, Spring MVC, JSP, Spring REST API

Spring Boot Initialzr

https://start.spring.io/

  • select the dependencies and download the zip file
  • unzip and import to IDE

Create a REST Controller

@RestController
public class FunRestController {
    @GetMapping("/")
    public String sayHello(){
        return "Hello World!";
    }
}

Spring Framework

Core Container contains: Beans -> Core -> SpEL -> Context Infrastructure contains: AOP -> Aspects -> Instrumentation -> Messaging Data Access Layer contains: JDBC -> ORM -> Transactions -> OXM and JMS Web Layer contains: Servlet -> WebSocket -> Web

What is Maven?

  • Maven is the most popular Project Management tool
  • Can download the jar files needed for your project
  • Structure: Standard Directory Structure
  • To find dependency: spring.io, hibernate.org, search.maven.org
  • Maven Wrapper files
    • mvnw allows you to run Maven project
    • to run on windows/linux: mvnw clean compile test / ./mvnw clean compile test
    • if you have maven installed in your pc run:
      • ./mvnw package
      • ./mvnw spring-boot:run mvnw
  • Warning: Do not use the src/main/weapp directory if your application is a packaged as a JAR. Although tihis is a standard Maven directory, it works only with WAR packaging.

Spring Boot

  • can use templates like FreeMarker, Thymeleaf or Mustache

Spring Boot Starters

  • for Spring MVC you normally need: spring-webmvc, hibernate-validator, thymeleaf
  • for Spring Boot Starter-Web you need: spring-boot-starter-web (contains all the bellow, json, tomcat etc)
  • include spring-boot-starter-security and spring-boot-starter-data-jpa

Spring Boot Parents for Starter

  • spring-boot-starter-parent is provided for Maven by defaults and contain default compiler lever and utf-8 encoding
  • if you wanna work with a specify java, not that is incluyed by default with Maven, change it:
<properties>
    <java.version> 17 </java.version>
</properties>

Spring Boot Starter

Spring Boot Dev Tools

  • for automatic restart, when add new dependencies to pom file for example, use spring-boot-devtools
  • then go to Preferences > Build, Execution, Deployment > Compiler > check box Build project automatically
  • additional settings > Preferences > Advanged settings > check box Allow auto-make to ...

Spring Boot Actuator

  • it use to expose to monitor and manage the endpoints of application, to check the health of application
  • add the REST endpoints automatically to application
  • endpoints: /health and /info
  • we need to update the application.properties

Actuator endpoints

  • add the endpoints and the artifact in pom file
management.endpoints.web.exposure.include=health,info
management.info.env.enabled=true
  • when run the project we get
LiveReload server is running on port 35729
Exposing 2 endpoint(s) beneath base path '/actuator'
Tomcat started on port(s): 8080 (http) with context path ''
  • go to the endpoint /actuator/health to check if is working
  • with /actuator/beans we get all the beans in project
  • to analyzing and profiling your application performance /actuator/threaddump

Spring Boot Actuator - securing endpoints

  • add dependency spring-boot-security

Actuator security

  • override default user name and generated a password

Actuator security pass

  • we also can exclude endopoints
management.endpoints.web.exposure.exclude=health,info

Spring Boot from command line

  • You don't need to have a server install or a IDE

Command line

  • Two options for running the app
    • java -jar
    • mvnw spring-boot:run

Annotations:

  • @Override - override the method from the principal class
  • @Component - mark a class like a spring bean and make the bean available for dependency injection
  • @Autowired - allow injecting as a bean after a constructor, method or attribute is created
  • @Qualifier - when there are one or more beans created and you want to use one of them, use this annotation together with @Autowired to specify which bean to use
  • @Primary - to identify the principal bean, it is an alternative to @Qualifier
  • @Lazy - bean is only initialized if needed for dependency injection
  • @Eager - is allwais initialized
  • @Scope - specifies the scope of the bean
  • @PostConstruct - to execute code during bean initialization
  • @PreDestroy - to execute code during bean destruction
  • @Configuration - define a class for bean creation
  • @ComponentScan - indicate in which package to start searching for components
  • @Bean - defines a bean
  • @Controller - inherits from @Component
  • @RequestMapping - used to map URLs to a class or method
  • @RequestParam - get a parameter from the request
  • @InitBinder - processes requests to the controller
  • @Entity - to map a table from the database
  • @Transactional - is an annotation to begin and end a transaction in in JPA code, is no need to implement because Spring do it behind the scenes
  • @Repository - is use for the DAOs
  • @RestController - is use in the REST application, is the sum of @Controller and @ResponseBody
  • @ControllerAdvice - is a filter used to controller all the global exception handlings
  • @Service - intermediate layer for custom business logic, can integrate multiple repositories/DAO and use Service Facade design pattern
  • @Table - to specify the database table
  • @Column - indicate the database column
  • @Id - pk of a table
  • @OneToOne
  • @OneToMany
  • @ManyToOne
  • @ManyToMany
  • @PostMapping
  • @GetMapping
  • @PutMapping
  • @DeleteMapping
  • @Pointcuts - to create pointcuts
  • @Before - use a pointcuts
  • @Order - order the pointcuts
  • @AfterReturning
  • @AfterThrowing
  • @After
  • @Around

Difference between @Controller and @RestController

  • @Controller is used to declare common web controllers which can return HTTP response but @RestController is used to create controllers for REST APIs which can return JSON.
  • @RestController = @Controller + @RequestBody
  • The @Controller is a common annotation which is used to mark a class as Spring MVC Controller while the @RestController is a special controller used in RESTFul web services

Injecting custom application properties

  • By default is located in src/main/resources/application.properties
  • With @Value annotation we can define a custom properties
coach.name=Micky Mouse
  • For injecting the properties
@RestController
public class FunRestController{
    @Value("${coach.name}")
    private String coachName;
}

Configure Spring Boot Server

  • Spring Boot properties have more than 1000. The properties are grouped into the following categories: core, web, security, data, acutator, integration, devtools, testing
  • Core properties: Core Properties
  • Create your own log file > logging.file=my-stuff.log
  • Web properties Web Properties
  • Data properties: Data Properties
  • Server properties:
server.port=7070
server.servlet.context-path=/home

Inversion Of Control

  • Design process and outsource, build and manage objects
  • Interfaces are used and the application must be as configurable as possible

Spring Container and his functions

  • create and manage objects (Inversion of control)
  • inject dependencies (dependency injections)
  • for spring container configuration use:
    • xml (deprecated)
    • java annotations
    • java code

Spring development process:

  • Configuration of a spring bean
<bean id="myCoach" class="inversionOfControl.TrackCoach"></bean>
  • Create the spring container (special implementation: ClassPathXmlApplicationContext, AnnotationConfigApplicationContext, GenericWebApplicationContext, etc)
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  • Recover the bean from container (a bean is just an object in java)
Coach theCoach = context.getBean("myCoach", Coach.class);

Spring Injection types

  • Constructor injection
  • Setter injection
  • Field injection (not recommended by spring.io)

Constructor injection development process:

  • Define the dependency class and the interface File: FortuneService.java
public interface FortuneService{
    puclic String getForune();
}

File: HappyFortuneService.java

@Component
public class HappyFortuneService implements FortuneService{
    @Override
    public String getFortune(){ 
        return "Today is my lucky day!"
    }
}
  • Create the REST Controller
@RestController
public class DemoController{}
  • Create a constructor in your class for injections
private FortuneService fortuneService;

@Autowired
public BaseballCoach(FortuneService theFortuneService){
    fortuneService = theFortuneService;
}
  • Configure the dependency injection in the spring config file, defined in the first step the dependency and in the second step inject the dependency applicationContext.xml
<bean id="myFortuneService" class="springdependency.HappyFortuneService"></bean>
<bean id="myCoach" class="springdependency.TrackCoach">
    <constructor-arg ref="myFortuneService"/>
</bean>

Spring behind the scene

Spring process

Component Scanner

  • Scan the java classes for this special annotations and automatically register the beans in the spring container
  • The @Component tag composed by: @EnableAutoConfiguration, @ComponentScan and @Configuration Spring  boot application

Component scanning

  • By default Spring Boot will not component scan the package with demo name
  • Explicit component scanning Explicitly list of packages

Setter injection

  • Create the setter method for injection
private FortuneService fortuneService;

@Autowired
public void setFortuneService(FortuneService fortuneService){
    this.fortuneService = fortuneService;
}
  • config the dependency for injection in the config file
<bean id="myCricketCoach" class="setterinjection.CricketCoach">
    <!-- set up the constructor injection -->
    <property name="fortuneServ" ref="myFortune" />
</bean>

Setter injection

Setter injection behind the scene

Spring process setter injection

Qualifiers

  • When have a lot a different classes that implement a injection class and use different @Components how Spring use Multiple implementation

Qualifier

	@Autowired
	@Qualifier("happyFortuneService")
	private FortuneService fService;
  • Or can do it directly in the constructor
    @Autowired
    public DemoController(@Qualifier("baseballCoach") Coach theCoach){
        myCoach = theCoach;
    }

@Primary

  • Other alternative to @Qualifier is @Primary

Lazy initialization

  • With @Lazy bean is only initialized if needed for dependency injection
  • For global configuration add lazy to application.properties '''spring.main.lazy-initialization=true'''
  • Advantages:
    • only create objects as needed
    • may help with faster startup time if you have large number of components
  • Disadvantages:
    • if you have web related components like @RestController, not created until requested
    • need enough memory for all beans once created
  • It is deseabled by default

Bean scopes

  • Default scop is singleton
  • all dependency injection for the bean will referenece the same bean
  • The scope of a bean refers to the life cycle of a bean, how long it will exist, how many instances will be created and how the bean will be shared
  • By default the bean will be singleton type. Singleton type creates a single instance per bean by default, it is stored in cache memory, all requests for this bean will give us a shared reference to the same bean.
Coach theCoach = context.getBean("myCoach", Coach.class);
Coach alphCoach = context.getBean("myCoach", Coach.class);

// check if the beans are the same		
boolean result = false;
result = (theCoach == alphCoach);

System.out.println("Pointing to the same object: " + result);
System.out.println(theCoach + " / " + alphCoach);
  • With scope prototype no longer point to the same bean object
    <bean id="myCoach" class="beanscopes.BaseballCoach" scope="prototype"></bean>

Bean Scopes

  • Type of scopes Additional Bean Scopes

New injection for each object

Bean Lifecycle Methods

  • Custom code can be added during bean initialization or bean destruction
  • init-method is the method to initialize an action during bean initialization
  • destroy-method is the method to initialize an action during bean destruction
<bean id="myCoach" class="beanlifecycle.TrackCoach" 
	init-method="doInitMethod" destroy-method="doDestroyMehtod">
</bean>
  • The scope prototype does not have the destroy method so it cannot be initialized. In order to do this, the destroy() method is defined.

Java Config Bean

  • In 3 steps:
    • Create @Configuration class @Configuarion class
    • Define @Bean method @Bean Method
    • Inject the bean Inject the bean

Hibernate & JPA

Development Process 1

  • Add hibernate configuration file, placed in the src folder
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <!-- JDBC Database connection settings -->
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/hb_student_tracker?useSSL=false&amp;serverTimezone=UTC</property>
        <property name="connection.username">hbstudent</property>
        <property name="connection.password">hbstudent</property>

        <!-- JDBC connection pool settings ... using built-in test pool -->
        <property name="connection.pool_size">1</property>

        <!-- Select our SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <!-- Echo the SQL to stdout -->
        <property name="show_sql">true</property>

		<!-- Set the current session context -->
		<property name="current_session_context_class">thread</property>
    </session-factory>
</hibernate-configuration>

Development Process 2

  • Modify the application.properties: Datasource

command-line-app

  • When run the application if the connect to BBDD es correctly we se the messages:
- Starting...
- Added connection com.mysql.cj.jdbc.ConnectionImpl@64b7225f
- Start completed.
  • Tricks
# Turn of the spring boot banner
spring.main.banner-mode=off
# reduce logging level, set to warn
logging.level.root=warn

JPA Dev Process

  • Annotate Java Class

  • Develop Java Code to perform database operations

  • If you don't declare any constructors in Java, Java will privide a no-argument constructor for you

  • Exist two options for mapping

    • xml config file
    • java annotations (is more modern)
  • Map class and fields to table and columns

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="student")
public class Student {
	@Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="id")
	private int id;
	@Column(name="first_name")
	private String firstName;
	@Column(name="last_name")
	private String lastName;
	@Column(name="email_name")
	private String email;

	..........
}
  • @Column is optional but recommandly if you update BBDD column

Primary Keys

  • Id generation strategies
    • GenerationType.AUTO - pick an appropiate strateg for the particular database
    • GenerationType.IDENTITY - asign primary keys using database identity columns
    • GenerationType.SEQUENCE - assing primary keys using a database secuence
    • GenerationType.TABLE - assign primary keys using an underlying database table to ensure uniqueness
  • Data Access Object (DAO):
    • save()
    • findById()
    • findAll()
    • findByLastName()
    • update()
    • delete()
    • deleteAll() JPA Entity Manager

Dev process for create:

  • Define DAO interface
public interface StudentDAO {
    void save(Student theStudent);
}
  • Define DAO implementation and inject entity manager
@Repository
public class StudentDAOImpl implements StudentDAO{
    // define field for entity manager
    private EntityManager entityManager;

    // inject entity manager using constructor injection
    @Autowired // Autowired is optional if you have only one constructor
    public StudentDAOImpl(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    // implement save method
    @Override
    @Transactional
    public void save(Student theStudent) {
        entityManager.persist(theStudent);
    }
}
  • Update app
@Bean
public CommandLineRunner commandLineRunner(StudentDAO studentDAO){
    return runner ->{
        //createStudent(studentDAO);
        createMultipleStudent(studentDAO);
    };
}

private void createStudent(StudentDAO studentDAO) {
    // create the object
    Student tempStudent = new Student("Paul", "Doe", "[email protected]");

    // save the object
    studentDAO.save(tempStudent);

    // display id of the object
    System.out.println("Student saved with id " + tempStudent.getId());
}

Dev process for read

  • Add DAO interface
public interface StudentDAO {
    Student findById(Integer id);
}
  • Add DAO implementation
@Repository
public class StudentDAOImpl implements StudentDAO{
    @Override
    public Student findById(Integer id) {
        return entityManager.find(Student.class, id);
    }
}
  • Update app
	private void readStudent(StudentDAO studentDAO) {
		// create a student object
		Student tempStudent = new Student("Daffy", "Duckovicy", "[email protected]");

		// save the student
		studentDAO.save(tempStudent);

		// display id of the save student
		int theId = tempStudent.getId();

		// retrive the student based in primary key
		Student myStudent = studentDAO.findById(theId);

		// display student
		System.out.println("Find the student " + myStudent);
	}

JPQL

  • JPQL is a query language for retrieving object and is similar in concept with SQL
  • Is based on entity name and entity fields JPQL

JPQL2

JPQL with parameters

Dev process JPQL

  • Add DAO interface
public interface StudentDAO {   
    List<Student> findAll();
}
  • Add DAO implementation
@Repository
public class StudentDAOImpl implements StudentDAO{
    @Override
    public List<Student> findAll() {
        // create query
        TypedQuery<Student> theQuery = entityManager.createQuery("FROM Student", Student.class);
        
        // return query result
        return theQuery.getResultList();
    }
}
  • Update app
@SpringBootApplication
public class Application {
	@Bean
	public CommandLineRunner commandLineRunner(StudentDAO studentDAO){
		return runner ->{
			readAllStudents(studentDAO);
		};
	}
}

private void readAllStudents(StudentDAO studentDAO) {
    // get a list of students
    List<Student> theStudents = studentDAO.findAll();

    // display the students
    for(Student tempStudent: theStudents){
        System.out.println(tempStudent);
    }
}

Dev process JPQL update

Update

  • Add DAO interface
public interface StudentDAO {   
    void update(Student theStudent);
}
  • Add DAO implementation
@Repository
public class StudentDAOImpl implements StudentDAO{
    // define field for entity manager
    private EntityManager entityManager;

    @Override
    @Transactional
    public void update(Student theStudent) {
        entityManager.merge(theStudent);
    }
}
  • Update app
private void updateStudent(StudentDAO studentDAO) {
    // retrive student bases on the id
    int studentId = 1;
    Student myStudent = studentDAO.findById(studentId);
    
    // change a column
    myStudent.setFirstName("Scooby");
    
    // update the student
    studentDAO.update(myStudent);
    
    // display the updated student
    System.out.println(myStudent);
}

Dev process JPQL delete

  • For multiple delete Multiple delete
  • Add DAO interface
void delete(Integer id);
  • Add DAO implementation
@Override
@Transactional
public void delete(Integer id) {
    // retrive the student
    Student theStudent = entityManager.find(Student.class, id);

    // delete the student
    entityManager.remove(theStudent);
}
  • Update app
@SpringBootApplication
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

	@Bean
	public CommandLineRunner commandLineRunner(StudentDAO studentDAO){
		return runner ->{
			deleteStudent(studentDAO);
		};
	}

	private void deleteStudent(StudentDAO studentDAO) {
		int studentId = 3;
		studentDAO.delete(studentId);
	}
}

Create table with JPA

  • provides an option to automagically create database table
  • It's need to add to application.properties spring.jpa.hibernate.ddl-auto=create
  • JPA drop and then create the table, all data lose it. Don't use this in production ddl-auto

Spring Boot - REST CRUD APIs

  • REST is a language independent, calls can be made over HTTP Rest

CRUD operations

  • HTTP Response HTTP Response

Spring REST controller

@RestController
@RequestMapping("/test")
public class DemoRestController {
    @GetMapping("/hello")
    public String sayHello(){
        return "Hello World";
    }
}

JSON Data Binding

  • Data binding is the process of converting JSON to a Java POJO
  • This process is also known as mapping, serialization / deserialization, mashalling / unmarshalling
  • Convert JSON to Java POJO, call setter methods on POJO Setter methods POJO

  • Convert Java POJO to JSON, call getter methods on POJO Getter methods POJO

Spring REST Service process

  • Create java POJO class, the Entity
  • Create Spring REST Service using @RestController
@RestController
@RequestMapping("/api")
public class StrudentController {
    @GetMapping("/students")
    public List<Student> getStudents(){
        List<Student> theStudents = new ArrayList<>();

        theStudents.add(new Student("Pepe", "Percival"));
        theStudents.add(new Student("Marรญa Luisa", "Nazaret"));
        theStudents.add(new Student("Jesolu", "Xaviter"));

        return theStudents;
    }
}

Path Variable

  • Retravie a single row
@GetMapping("/students/{id}")
public Student getStudent(@PathVariable int id){
    return theStudents.get(id);
}

Exception Handling

Dev process

  • Create custom error response class
public class StudentErrorResponse {
    private int status;
    private String message;
    private long timeStamp;

    public StudentErrorResponse() {    }

    public StudentErrorResponse(int status, String message, long timeStamp) {
        this.status = status;
        this.message = message;
        this.timeStamp = timeStamp;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public long getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(long timeStamp) {
        this.timeStamp = timeStamp;
    }
}
  • Create custom exception
public class StudentNotFoundException extends RuntimeException{
    public StudentNotFoundException(String message) {
        super(message);
    }

    public StudentNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public StudentNotFoundException(Throwable cause) {
        super(cause);
    }
}
  • Update REST service to throw exception
@GetMapping("/students/{id}")
public Student getStudent(@PathVariable int id){
    // check the object exist
    if(id >= theStudents.size() || id < 0){
        throw new StudentNotFoundException("Student id not found - " + id);
    }
    // index the list
    return theStudents.get(id);
}
  • Add exception handler method
@ExceptionHandler
public ResponseEntity<StudentErrorResponse> handleException(StudentErrorResponse exc){
    // create a error response
    StudentErrorResponse error = new StudentErrorResponse();
    error.setStatus(HttpStatus.NOT_FOUND.value());
    error.setMessage(exc.getMessage());
    error.setTimeStamp(System.currentTimeMillis());
    
    // return entity
    return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
@ExceptionHandler // to cover all the exception
public ResponseEntity<StudentErrorResponse> handleException(Exception exc){
    // create a error response
    StudentErrorResponse error = new StudentErrorResponse();

    error.setStatus(HttpStatus.BAD_REQUEST.value());
    error.setMessage(exc.getMessage());
    error.setTimeStamp(System.currentTimeMillis());

    // return entity
    return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}

Global exception handling

  • @ControllerAdvice is similar to an filter
    • pre-process requests to controllers
    • pre-process responses to handle exception

Dev process

  • Create new @ControllerAdvice
  • Add exception code to @ControllerAdvice
  • Refact the code
@ControllerAdvice
public class StudentRestExceptionHandler {
    // add an exception handler using @ExceptionHandler
    @ExceptionHandler
    public ResponseEntity<StudentErrorResponse> handleException(StudentNotFoundException exc){
        // create a error response
        StudentErrorResponse error = new StudentErrorResponse();

        error.setStatus(HttpStatus.NOT_FOUND.value());
        error.setMessage(exc.getMessage());
        error.setTimeStamp(System.currentTimeMillis());

        // return entity
        return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler
    public ResponseEntity<StudentErrorResponse> handleException(Exception exc){
        // create a error response
        StudentErrorResponse error = new StudentErrorResponse();

        error.setStatus(HttpStatus.BAD_REQUEST.value());
        error.setMessage(exc.getMessage()); // se puede cambiar con mensaje personalizado
        error.setTimeStamp(System.currentTimeMillis());

        // return entity
        return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
    }
}

Spring Boot REST API Design

  1. Review API requierements (example employee directory)
    • REST client should be able to get a list of employee, get a single employee, add a new employee, update or delete
  2. Indentify the entitys
    • use noun
  3. Use HTTP methods to assing action on resource (GET, POST, PUT, DELETE) Real Time Project
  • Don't use the anti-patters, is a bad practice, for endpoints like: /api/addEmployee, /api/deleteEmployee etc

Spring Boot REST CRUD API

  • REST API with Spring Boot connect to a database
  • Create the DAO and Entity

Service annotation

  • @Service, like @Repository and @RestController, is an annotation that provide Spring
  • Define Service interface
public interface EmployeeService {
    List<Employee> findAll();
}
  • Define Service implementation
@Service
public class EmployeeServiceImpl implements EmployeeService{
    private EmployeeDAO employeeDAO;

    public EmployeeServiceImpl(EmployeeDAO theEmployeeDAO) {
        this.employeeDAO = theEmployeeDAO;
    }

    @Override
    public List<Employee> findAll() {
        return employeeDAO.findAll();
    }
}

Add or update

add-or-update

  • When use the @Service the @Transactional annotations need put in the service class

Dev process CRUD

  • Add DAO interface and DAO implementation DAO
  • Add Service interface and Service implementation SERVICE
  • Update the app
@RestController
@RequestMapping("/api")
public class EmployeeController {
    private EmployeeService employeeService;

    // inject object dao
    public EmployeeController(EmployeeService theEmployeeService) {
        employeeService = theEmployeeService;
    }

    // expose the endpoint to return the list
    @GetMapping("/employees")
    public List<Employee> findAll(){
        return employeeService.findAll();
    }

    @GetMapping("/employees/{id}")
    public Employee getEmployee(@PathVariable int id) {
        Employee theEmployee = employeeService.findById(id);

        if(theEmployee == null){
            throw new EmployeeNotFoundException("Employee id not found: " + id);
        }

        return theEmployee;
    }

    @PostMapping("/employees")
    public Employee addEmployee(@RequestBody Employee employee){
        // if in json don't exist an id, id == 0, is a new insert else is a update
        employee.setId(0);
        Employee dbEmployee = employeeService.save(employee);

        return dbEmployee;
    }

    @PutMapping("/employees")
    public Employee updateEmployee(@RequestBody Employee employee){
        Employee dbEmployee = employeeService.save(employee);
        return dbEmployee;
    }

    @DeleteMapping("/employees/{id}")
    public String deleteEmployee(@PathVariable int id){
        Employee dbEmployee = employeeService.findById(id);

        if(dbEmployee == null){
            throw new EmployeeNotFoundException("Employee with this id not found " + id);
        }

        employeeService.deleteById(id);
        return "Employee deleted!";
    }
}

Spring Data JPA

  • Previously used JPA API, from now Spring Data JPA
  • If we need to create anothers DAO (customer, student, product etc) it's need to repeat the same code egain
  • Repet pattern DAO pattern
  • Create a DAO, plug in the entity type and primary key, and with Spring Data we have a CRUD implementation
  • Spring Data provide a JpaRepository to reduce the lines code to 70%

Dev process Spring Data

  • Extends JpaRepository and delete de EmployeeDAO interface and implementation
  • JpaRepository use @Transactional so no need to use it
public interface EmployeeRepository extends JpaRepository<Employee, Integer> { }
  • Use this repository in app
@RestController
@RequestMapping("/api")
public class EmployeeController {
    private EmployeeService employeeService;

    // inject object dao
    public EmployeeController(EmployeeService theEmployeeService) {
        employeeService = theEmployeeService;
    }

    // expose the endpoint to return the list
    @GetMapping("/employees")
    public List<Employee> findAll(){
        return employeeService.findAll();
    }

    @GetMapping("/employees/{id}")
    public Employee getEmployee(@PathVariable int id) {
        Employee theEmployee = employeeService.findById(id);

        if(theEmployee == null){
            throw new EmployeeNotFoundException("Employee id not found: " + id);
        }

        return theEmployee;
    }

    @PostMapping("/employees")
    public Employee addEmployee(@RequestBody Employee employee){
        // if in json don't exist an id, id == 0, is a new insert else is a update
        employee.setId(0);
        Employee dbEmployee = employeeService.save(employee);

        return dbEmployee;
    }

    @PutMapping("/employees")
    public Employee updateEmployee(@RequestBody Employee employee){
        Employee dbEmployee = employeeService.save(employee);
        return dbEmployee;
    }

    @DeleteMapping("/employees/{id}")
    public String deleteEmployee(@PathVariable int id){
        Employee dbEmployee = employeeService.findById(id);

        if(dbEmployee == null){
            throw new EmployeeNotFoundException("Employee with this id not found " + id);
        }

        employeeService.deleteById(id);
        return "Employee deleted!";
    }
}

Spring Data REST

  • If we need to create a REST API for another entity is necesary repet the code
  • Spring Data REST leverages the existing JpaRespository and give a REST CRUD implementation
  • Create automatically the endpoints for the entity Spring Data REST
  • HATEOS - provide information to access REST interface

Dev process

  • Add Spring Data REST to pom file
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-rest</artifactId>
			<scope>test</scope>
		</dependency>

Customize base path

# Spring Data REST
spring.data.rest.base-path=/magic-app

Spring Data REST: Configuration, Pagination and Sorting

  • Configuration: specify plural name/path with an annotation
@RepositoryRestResource(path = "members")
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {}
http://localhost:8080/employees?page=1
  • Sorting:
http://localhost:8080/employees?sort=lastName,desc

Spring Boot - REST API Security

  • Edit pom file to add spring-boot-starter-security
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • Change user and password
spring.security.user.name=admin
spring.security.user.password=test123

Basic Configuration

  • Password are stored in a specific format {id}encodePassword
  • {noop}test123 the password are stored as plain text
  • {bcrypt}test123 the password are stored as encrypted password Users and roles

Basic authentication

@Configuration
public class SecurityConfiguration {
    @Bean
    public InMemoryUserDetailsManager userDetailsManager(){
        UserDetails employee1 = User.builder()
                .username("John")
                .password("{noop}test123")
                .roles("EMPLOYEE")
                .build();
        UserDetails employee2 = User.builder()
                .username("Mary")
                .password("{noop}test123")
                .roles("EMPLOYEE", "MANAGER")
                .build();
        UserDetails employee3 = User.builder()
                .username("Susan")
                .password("{noop}test123")
                .roles("EMPLOYEE","MANAGER","ADMIN")
                .build();

        return new InMemoryUserDetailsManager(employee1, employee2, employee3);
    }
}

Restrict access to roles

Restrict access

Authorize Requests Roles

Cross-Site Request Forgery CSRF

  • Protect agains attacks
  • Embed additional authentication data/token into HTML forms
  • Verify token before processing
  • Disabled CSRF http.csrf().disable();
@Configuration
public class SecurityConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception{
        httpSecurity.authorizeHttpRequests(configurer ->
            configurer
                    .requestMatchers(HttpMethod.GET,"/api/employees").hasRole("EMPLOYEE")
                    .requestMatchers(HttpMethod.GET,"/api/employees/**").hasRole("EMPLOYEE")
                    .requestMatchers(HttpMethod.POST,"/api/employees").hasRole("MANAGER")
                    .requestMatchers(HttpMethod.PUT,"/api/employees").hasRole("MANAGER")
                    .requestMatchers(HttpMethod.DELETE,"/api/employees/**").hasRole("ADMIN")
        );

        // use http basic authentication
        httpSecurity.httpBasic();

        // disabled CSRF
        // in general is not required for post, put, delete or patch
        httpSecurity.csrf().disable();
        
        return httpSecurity.build();

    }
}
  • From postman use Basic Auth with the specified roles to do the actions

JDBC Authentication

Dev process

  • Add database support to pom file
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource){
    return new JdbcUserDetailsManager(dataSource);
}
  • Create JDBC properties
  • Update Spring Security configuration to use JDBC JDBC

Bcrypt

bcrypt

Login Process

Custom tables

  • Create our custom table with sql
  • Update Spring Security configuration
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource){
    JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource);

    // define query to retrieve a user by username
    jdbcUserDetailsManager.setUsersByUsernameQuery("select user_id, pw, active from members where user_id = ?");

    // define query to retrieve the roles by username
    jdbcUserDetailsManager.setAuthoritiesByUsernameQuery("select user_id, role from roles where user_id = ?");
    
    return jdbcUserDetailsManager;
}

Spring Boot MVC Thymeleaf

  • Thymeleaf is a Java templating engine
  • Used to generate HTML views for web apps
  • It is seperat progect separate to spring.io, you can create Java apps without Spring
  • Template files go to /src/main/resources/templates
  • More info in www.thymeleaf.org

Dev process

  • Add Thymeleaf to pom file
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  • Develop Spring MVC Controller
@Controller
public class AppController {
    @GetMapping("/hello")
    public String sayHello(Model theModel){
        theModel.addAttribute("theDate", new java.util.Date());

        return "hello-world";
    }
}
  • Add css and js file to the /resource/static directory
  • Create Thymeleaf template
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<!-- in the udemy course used -->
<!--<html xmlns:th="http://www.thymeleaf.org">-->
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
    <link rel="stylesheet" th:href="@{css/style.css}">
</head>
<body>
<p th:text="'Time on the server is ' + ${theDate}" class="green-color"/>
</body>
</html>

Spring Boot MVC CRUD

  • Add button to 'add'
<a th:href="@{/employees/showFormForAdd}" class="btn btn-primary btn-sm mb-3">Add Employee</a>
  • Add controller
@PostMapping("/save")
public String saveEmployee(@ModelAttribute("employee") Employee theEmployee){
    // save
    employeeService.save(theEmployee);
    
    // use a redirect to prevent duplicate submision
    return "redirect:/employees/list";
}
  • Create the form
<form action="#" th:action="@{/employees/save}" th:object="@{employee}" method="POST">
    <input type="text" th:field="*{firstName}" class="form-control mb-4 w-25" placeholder="First name">
    <input type="text" th:field="*{lastName}" class="form-control mb-4 w-25" placeholder="Last name">
    <input type="text" th:field="*{email}" class="form-control mb-4 w-25" placeholder="Email">
    
    <button type="submit" class="btn btn-info col-2">Save</button>
</form>

Dev process for update to prepopulate the form

  • Add button with the id
<td><a th:href="@{/employees/showFormForUpdate(employeeId=${tempEmployee.id})}"
    class="btn btn-info btn-sm">Update</a></td>
  • Populate the form
    @GetMapping("/showFormForUpdate")
    public String showFormForUpdate(@RequestParam("employeeId") int theId, Model theModel){
        // get employee from service
        Employee theEmployee = employeeService.findById(theId);

        // set employee in the model to populate the form
        theModel.addAttribute("employee", theEmployee);

        // send to form the data
        return "employees/employee-form";
    }

Dev process for delete

  • The button
<a th:href="@{/employees/delete(employeeId=${tempEmployee.id})}"
    class="btn btn-danger btn-sm">Update</a>
  • The Controller
@GetMapping("/delete")
public String deleteEmployee(@RequestParam("employeeId") int theId){
    // delete
    employeeService.deleteById(theId);

    // redirect to list
    return "redirect:/employees/list";
}

Spring MVC Form Validation


  • Hibernate have a validator very usefull http://hibernate.org/validator
  • All the packages are renamed from javax.* to jakarta.*
  • Hibernate Validator 7 is based in Jakarta EE 9, but Spring 5 is still based in Java EE (javax.*), so Spring 5 and Hibernate Validator 7 are not compatible. We need to use Hibernate Validator 6.2 https://hibernate.org/validator/releases/6.2/
  • When finished the comprimited file we copy the 3 files that are in the dist directory to the WEB-INF >> lib >> and from lib >> requiered subdirectory copy all the jar files

Form Tags

  • Form tags generate HTML for you:
    • form:form
    • form:input
    • form:textarea
    • form:checkbox
    • form:radiobutton
    • form:select
    • etc
  • Create the controller
@Controller
@RequestMapping("/student")
public class StudentController {
	
	@RequestMapping("/showForm")
	public String showForm(Model theModel) {
		// create a student object
		Student theStudent = new Student();
		
		// add student object to the model
		theModel.addAttribute("student", theStudent);
		
		return "student-form";
	}
}
  • To reference need to specify the spring namespace at beginning of JSP file
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
  • You must add a model attribute, this is a bean that will hold form data for the data binding
	@RequestMapping("/processForm")
	public String processForm(@ModelAttribute("student") Student theStudent) {
		return "student-confirmation";
	}
  • Setting the form
	<form:form action="processForm" modelAttribute="student">
		Fist name <form:input path="firstName"/>
		Second name <form:input path="lastName"/>
		<br><br>
		Country:
		<form:select path="country">
			<form:options items="${countryOptions}"/>
		</form:select>
		<br><br>
		Favorite language:
		<form:radiobuttons path="language" items="${student.languageOptions}"/>
		<br><br>
		OS:
		Linux<form:checkbox path="os" value="Linux"/>
		Mac<form:checkbox path="os" value="Mac"/>
		<br><br>
		<input type="submit" value="Submit" />
	</form:form>
  • A simple way to create a form
Java <form:radiobutton path="language" value="Java"/>
Python <form:radiobutton path="language" value="Python"/>
JavaScript <form:radiobutton path="language" value="JavaScript"/>
PHP <form:radiobutton path="language" value="PHP"/>

<form:select path="country">
	<form:option value="Romania">Romania</form:option>
	<form:option value="Spain">Spain</form:option>
	<form:option value="Germany">Germany</form:option>
	<form:option value="Austria">Austria</form:option>
</form:select>

Development process

  • Add validation rule to class
public class Professor {
	@NotNull(message="Name is required")
	@Size(min=4, message="Name most have more than 4 caracters")
	private String firstName;
	@Min(value=1, message="Must be gratear than or equal to 1")
	@Max(value=10, message="Must be less than or igual to 10")
	private int cualification;
	@Pattern(regexp="^[a-zA-Z0-9]{5}", message="Only 5 chars/digits")
	private String postalCode;
}
  • Display error messages on HTML form
	<form:form action="processForm" modelAttribute="professor" >
		First name(*):
		<form:input placeholder="What's your name?" path="firstName" />
		<form:errors path="firstName" cssClass="error"/>
		Last name(*):
		<form:input placeholder="What's your surname?" path="lastName"/>
		<form:errors path="lastName" cssClass="error"/>
		<br><br>
		Cualification:
		<form:input path="cualification"/>
		<form:errors path="cualification" cssClass="error"/>
		<br><br>
		Postal Code:
		<form:input path="postalCode"/>
		<form:errors path="postalCode" cssClass="error"/>
		<br><br>
		Course Code:
		<form:input path="courseCode"/>
		<form:errors path="courseCode" cssClass="error"/>
		<br><br>
		<input type="submit" value="Submit"/>
	</form:form>
  • Perform validation in controller
@Controller
@RequestMapping("/student")
public class StudentController {
	@RequestMapping("/showForm")
	public String showForm(Model theModel) {
		// create a student object
		Student theStudent = new Student();
		
		// add student object to the model
		theModel.addAttribute("student", theStudent);
		
		return "student-form";
	}
}
  • Update confirmation page
@RequestMapping("/processForm")
public String processForm(@Valid @ModelAttribute("student") Student theStudent, BindingResult theBindingRes) {
    if(!theBindingRes.hasErrors()) {
        return "student-confirmation";
    }else {
        return "student-form";
    }
    
}
  • Update confirmation page
  • Add @InitBinder to eliminate white space from inputs
	@InitBinder
	public void initBinder(WebDataBinder dataBinder) {
		StringTrimmerEditor stringTrimm = new StringTrimmerEditor(true);
		dataBinder.registerCustomEditor(String.class, stringTrimm);
	}
<h1>Student confirmation</h1>
<p>The student is ${student.firstName} ${student.lastName}</p>
<p>The student country is ${student.country}</p>
<p>The student like ${student.language} and operating with: </p>
<ul>
    <c:forEach var="temp" items="${student.os}">
        <li>${temp}</li>
    </c:forEach>
</ul>
  • To eliminate white spaces in the input forms
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
    StringTrimmerEditor stringTrimm = new StringTrimmerEditor(true);
    dataBinder.registerCustomEditor(String.class, stringTrimm);
}

Handle String input for integer fields

  • Create custom error message in src >> resources >> messages.properties
typeMismatch.professor.cualification=Invalid number
  • Load custom messages resource in Spring config file:
WebContent/WEB-INF/spring-mvc-demo-servlet.xml

	<bean id="messageSource" 
		class="org.springframework.context.support.ResourceBundleMessageSource" >
		<property name="basenames" value="resources/messages" />
	</bean>

Create custom Annotation

  • Create a new package with a new annotation, the custom validation rule
@Constraint(validatedBy = CourseCodeConstraintValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CourseCode {
	// define default course code
	public String value() default "BRIND";
	
	// define default error message
	public String message() default "Must start with a secret code";
	
	// defain default groups
	public Class<?>[] groups() default {};
	
	// defain default payloads
	public Class<? extends Payload>[] payload() default {};
}
  • Create the validator
public class CourseCodeConstraintValidator implements ConstraintValidator<CourseCode, String> {
	private String coursePrefix;
	
	@Override
	public void initialize(CourseCode constraintAnnotation) {
		coursePrefix = constraintAnnotation.value();
	}
	
	@Override
	public boolean isValid(String theCode, ConstraintValidatorContext theConstraint) {
		boolean result = false;
		if(theCode != null) {
			theCode.startsWith(coursePrefix);
		}else {
			result = true;
		}
		return result;
	}
}
  • Add the validation rules
	@CourseCode
	private String courseCode;
  • Display the errors in html

Hibernate advanced mapping

Entity lifecycle

Entity lifecycle

Session method calls

Cascade-Type

  • @OneToOne - use to relation a column from a table with the foreign key from another table
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="instructor_detail_id")
private InstructorDetail instructorDetail;
  • @OneToOne - bidirectional
@OneToOne(mappedBy="instructorDetail", cascade=CascadeType.ALL)
private Instructor instructor;

OneToOne-bidirectional

  • Delete details but not the instructor
@OneToOne(mappedBy="instructorDetail", cascade= {CascadeType.DETACH, CascadeType.MERGE,
                                        CascadeType.PERSIST, CascadeType.REFRESH})
  • @OneToMany - use to relation a column from a table with the many foreign key from another table
@OneToMany(fetch=FetchType.EAGER, mappedBy="instructor")
private List<Course> courses;

public void add(Course tempCourse) {
    if(courses == null) {
        courses = new ArrayList<>();
    }
    
    courses.add(tempCourse);
    tempCourse.setInstructor(this);
    
}

Eager vs Lazy loading

  • eager will retrive everything
  • lazy will retrive on request
  • only load data when absolutely needes, so prefer lazy loading instead of eager loading Eager Loading
  • The best practice is load data when absolutely needed, prefer use lazy loading
  • How resolve the Lazy loading
    • use hibernate query with HQL
    // start a transaction
    session.beginTransaction();
    
    // resolve lazy loading
    // option 2 hibernate query with HQL
    
    // get the instructor from db
    int theId = 1;
    Query<Instructor> query = session.createQuery("select i from Instructor i " +
            " JOIN FETCH i.courses " + 
            " where i.id=:theInstructorId",Instructor.class);
    
    query.setParameter("theInstructorId", theId);
    
    // execute query and get the instructor
    Instructor tempInstructor = query.getSingleResult();
    
    // get courses of the instructor
    System.out.println("Courses: " + tempInstructor);
    
    // commit transaction
    session.getTransaction().commit();
    
    session.close();
  • @OneToMany
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name="course_id")
private List<Review> reviews;

@ManyToMany

  • @ManyToMany use @JoinTable and @JoinColumn
@ManyToMany(fetch = FetchType.LAZY,
        cascade= {CascadeType.PERSIST, CascadeType.MERGE,
        CascadeType.DETACH, CascadeType.REFRESH})
@JoinTable(name="course_student",
        joinColumns = @JoinColumn(name="course_id"),
        inverseJoinColumns = @JoinColumn(name="student_id"))
private List<Student> students;

Create a Dynamic Web Project with Eclipse

Dev environment:

  • You should have installed: apache tomcat, eclipse java EE version, and connect eclipse to tomacat

Spring MVC configuration process:

  • create new Dynamic Web Project in Eclipse
    • remove the source folder (src/main/java) and add the folder src
    • remove the content directory (src/main/webapp) and add the folder WebContent and finish
    • add JDBC driver
    • Create new Servlet to test the connection
    • uncheck and let only 'Inherited abstract methods' and 'doGet'
  • To work propertily in right click to project > Properties
    • Targeted Runtime > select the server

    • Java Build Path > click to Classpath > add JARs > select all the jars from lib directory

    • Check if the web.xml and spring-mvc-servlet.xml are the same package web.xml

servlet

  • If project open other project lock for server.xml in server apache and modify 'Context'

Spring MVC & Hibernate

DAO implementation

  • Spring provide @Transactional annotation
  • @Transactional begin and end a transaction in Hibernate
  • Begin > session.beginTransaction() and End > session.getTransaction().commit() is replaced with @Transactional Transactional
  • Spring provide @Repository annotation Repository
  • Is applied to DAO implementation. Spring also provide translation of any JDBC related exceptions
@Repository
public class CustomerDAOImpl implements CustomerDAO {
	@Autowired
	private SessionFactory sessionFactory;
	
	@Override
	@Transactional
	public List<Customer> getCustomers() {
        ...
	}
}
  • When use @Transactional in DAO and when in Service?
Here is the general guidance (of course it may vary for specific use cases / app requirements)

If you are using the single database/datasource
- If you want to manage transaction rollback across multiple DAOs, place @Transactional at Service layer

If you are using multiple databases/datasources
- Place @Transactional on DAO layer
  • POST POST
  • UPDATE
  • Define a variable
<c:url var="updateLink" value="/customer/showFormForUpdate">
    <c:param name="customerId" value="${tempCustomer.id}" />
</c:url>	
...
<a href="${updateLink}">Update</a>

update

<c:url var="deleteLink" value="/customer/delete">
    <c:param name="customerId" value="${tempCustomer.id}" />
</c:url>
...
<a href="${deleteLink}" onclick="if (!(confirm('Are you sure you want to delete this customer?'))) return false">Delete</a>

delete

AOP (Aspect oriented programming)

  • AOP use cases:
  • most common: logging, security, transactions
  • audit logging: who, what, when, where
  • exeption handling: log exception and notify DevOps teams via SMS/EMAIL
  • API management: how many times has a method been called user, what are peak times?, what is avafege load? etc AOP
  • Terminology: Terminology AOP
  • Advice Type Advice Types
  • Exists two AOP frameworks for Java
  • Spring AOP:
    • support of Spring
    • key component: security, transactions, caching
  • AspectJ:
    • privide complete support for AOP
    • join points: method-level, constructor, field
    • code weaving: compile-time, post compile-time and load-time
  • Comparasion: Comparasion AOP

Comparasion AspectJ

@Before

Dev process

  • Create target object
@Component
public class AccountDAO { }
  • Create Spring java config
@Configuration						// Spring pure java configuration
@EnableAspectJAutoProxy				// Spring AOP proxy support
@ComponentScan("com.spring.aop")	// Component scan for components and aspects
public class DemoConfig { }
  • Main
public class MainApp {

	public static void main(String[] args) {
		// read spring config java class
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
		
		// get the bean from spring container
		AccountDAO theAccountDAO = context.getBean("accountDAO", AccountDAO.class);
		
		// call the business method
		theAccountDAO.addAccount();
		
		// call business method again
		System.out.println("Do it again!");
		theAccountDAO.addAccount();
		
		// close the context
		context.close();
	}		

}
  • Create Aspect with @Before
@Aspect
@Component
public class LogginAspect {
   // add advice for loggin
   // start with @Before advice
   
   @Before("execution(public void addAccount())")
   public void beforeAddAcountAdvice() {
   	System.out.println(">>>>>> @Before advice on addAcount()");
   }
}

Pointcut Expression

  • Pointcut: a predicate expression for where advice should be applied
execution(modifiers-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(param-pattern) throws-pattern?)
  • Modifiers: Spring AOP only supports public
  • Return type: void, Boolean, String
  • Declaring type: the class name
  • Method name pattern
  • Params
  • Exceptions Pointcut Expression
  • Match methods starting with add in any class
@Before("execution(public void add*())")
...
@Before("execution(public VerificationResult processCreditCard*())")
...
@Before("execution(public * processCreditCard*())")
  • Modifier is optional
@Before("execution(* processCreditCard*())")
  • For the full class name, if whe have more than one method with the same name
@Before("execution(public void com.spring.aop.dao.AccountDAO.addAccount())")
public void beforeAddAcountAdvice() { }
  • Parameter pattern wildcards
    • () - matches a method with no arguments
    • (*) - matches a method with one argument of any type
    • (..) - matches a method with zero or more arguments of any types
@Before("execution(* addAccount(com.spring.aop.dao.Account))")
...
@Before("execution(* addAccount(..))")
...
@Before("execution(* com.spring.aop.dao.*.*(..))")

Pointcut Expression 2

How can reuse a pointcut expression?

Not ideal pointcut expression

  • The ideal solution is create a pointcut declaration and apply it
    • Declaration Ideal pointcut expression
    • Apply Apply pointcut expression
// pointcut declaration
@Pointcut("execution(* com.spring.aop.*.*(..))")
private void personalPointcutDeclaration() {}

@Before("personalPointcutDeclaration()")
public void beforeAddAcountAdvice() {
    System.out.println(">>>>>> @Before advice on addAcount()");
}

Combining pointcuts

  • You cas use operators
@Before("express1ionOne() && expressionTwo()")
...
@Before("express1ionOne() || expressionTwo()")
...
@Before("express1ionOne() && !expressionTwo()")

Ordering Aspects

  • Refactor code in separate the aspects Separate Aspects
  • Use @Order annotations
@Aspect
@Component
@Order(-1)
public class LogginAspect { }
...
@Aspect
@Component
@Order(5)
public class ApiAnalyticsAspect { }

Read method arguments wiht JoinPoints

  • Access and display the methdo signature
@Aspect
@Component
@Order(-1)
public class LogginAspect {
	@Before("com.spring.aop.aspect.AspectExpression.forPointcutWithoutGettersAndSetters()")
	public void beforeAddAcountAdvice(JoinPoint theJoinPoint) {		
		// display the method signtature
		MethodSignature methodSignature = (MethodSignature) theJoinPoint.getSignature();
		System.out.println("Method >>> " + methodSignature);		
		
		// get the arguments ant loop through
		Object[] args = theJoinPoint.getArgs();
		
		for(Object tempArgObject: args) {			
			if(tempArgObject instanceof Account) {
				Account theAccount = (Account) tempArgObject;
			}
		}
	}
}

@AfterReturning

  • most common: logging, security, transactions
  • audit logging: who, what, when, where
  • post-processing data
    • post process data before returning to caller
    • format the data or enrich the data Return value AOP
@AfterReturning(
        pointcut="execution(* com.spring.aop.dao.AccountDAO.findAccounts(..))",
        returning="result")
public void afterReturningAdvice(JoinPoint theJoinPoint, List<Account> result) {
    // print the method
    String method = theJoinPoint.getSignature().toShortString();
    
    // print the result of the method calls
    System.out.println(">>>>>>>>>> result " + result);
}

@AfterThrowing

  • Use case:
    • Log the exceptions
    • Perform auditing on the expception
    • Notify DevOps team via email or SMS
    • Encapusate this functionality in AOP aspect for easy reuse
@AfterThrowing(
        pointcut="execution(* com.spring.aop.dao.AccountDAO.findAccounts(..))",
        throwing="theExc")
public void afterThrowingFindAccountsAdvice(JoinPoint theJoinPoint, Throwable theExc) {
    // print the method
    String method = theJoinPoint.getSignature().toShortString();
    
    // log the exception
    System.out.println("The exception is >> " + theExc);
}

@After

  • Uses case:
    • Log the exception and/or peforming auditing
    • Code to execute the method result independently
    • Encapusate this functionality in AOP aspect for easy reuse
@After("execution(* com.spring.aop.dao.AccountDAO.findAccounts(..))")
public void afterFinallyFindAccountsAdvice(JoinPoint theJoinPoint) {    
    System.out.println("After >>>>> " + theJoinPoint);
}

@Around

  • Uses:
    • most common: logging, auditing, security
    • pre-processing and post-processing data
    • instrumentation/profiling code: how long does it take for a section of code tu run?
    • managing exceptions: swallow/handle/stop exception
@Around("execution(* com.spring.aop.service.*.getTraffic(..))")
public Object aroundGetTraffic(ProceedingJoinPoint theProceedingJoinPoint) throws Throwable {    
    // get begin timestamp
    long begin = System.currentTimeMillis();
    
    // execute the method
    Object result = theProceedingJoinPoint.proceed();
    
    // end of timestamp
    long end = System.currentTimeMillis();
    
    // compute duration 
    long duration = end - begin;
    myLogger.info("Duration: " + duration / 1000.0 + " seconds");
    
    return result;
}

Rethrow Exception

Rethrow Exception

Spring Security

Spring security filter

  • Use java config (@Configuration) or xml config to configuration spring security

Dev process

  • Add maven dependencies for spring mvc web app Add maven dependency
  • Crate spring configuration Spring configuration
  • The old way was config the xml servlet Old way
  • The new way to config Spring Dispatcher Servlet Initializer
  • Develop the Spring Controller Spring Controlelr
  • Add spring-security-web and spring-security-config (spring framework and spring security are 2 different projects) Spring security
  • Create spring seurity initializer, create spring security configuration (@Configuration) and add users and roles
  • The login in Spirng security is a "web browser session"

Dev process for login

  • Modify spring security configuration to reference custom login form Spring security config
  • Develop a controller to custom login Develop a controller
  • Create custom login form (html, css, spring mvc form tag) Custom login

CSRF (Cross site request forgery)

  • Manually add CSRF token Manually CSRF
  • The best practice is use form:form tag because add automatically the CSRF

Configure access resources

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/").hasRole("EMPLOYEE")
        .antMatchers("/leaders/**").hasRole("MANAGER")
        .antMatchers("/systems/**").hasRole("ADMIN")
        .antMatchers("/resources/**").permitAll()
        .anyRequest().authenticated()
    .and()
    .formLogin()
        .loginPage("/showLoginPage")
        .loginProcessingUrl("/authTheUser")
        .permitAll()
    .and()
    .logout().permitAll()
    .and()
    .exceptionHandling().accessDeniedPage("/access-denied");
}

Dev process access BBDD

  • Add database support to maven pom file
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.9</version>
</dependency>	
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.5</version>
</dependency>	
  • Create JDBC properties file
# JDBC connection properties
#
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_security_demo_plaintext?useSSL=false
jdbc.user=springstudent
jdbc.password=springstudent

#
# Connection pool properties
#
connection.pool.initialPoolSize=5
connection.pool.minPoolSize=5
connection.pool.maxPoolSize=20
connection.pool.maxIdleTime=3000
  • Define DataSource in Spring configuration
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.spring.security")
@PropertySource("classpath:persitence-mysql.properties")
public class AppConfig implements WebMvcConfigurer {
	// set up variable to hold the properties
	@Autowired
	private Environment env;
	
	private Logger logger = Logger.getLogger(getClass().getName());
    ....
}
  • Define a bean
@Bean
public DataSource securityDataSource() {
    // create connection pool
    ComboPooledDataSource securityDataSource = new ComboPooledDataSource();
    
    // set the jdbc driver class
    try {
        securityDataSource.setDriverClass(env.getProperty("jdbc.driver"));
    } catch (PropertyVetoException e) {
        throw new RuntimeException(e);
    }
    
    // log the connection props
    logger.info(">>>> jdbc.url= " + env.getProperty("jdbc.url"));
    logger.info(">>>> jdbc.user= " + env.getProperty("jdbc.user"));
    
    // set dataase connection props
    securityDataSource.setJdbcUrl(env.getProperty("jdbc.url"));
    securityDataSource.setUser(env.getProperty("jdbc.user"));
    securityDataSource.setPassword(env.getProperty("jdbc.password"));
    
    // set connection pool props
    securityDataSource.setInitialPoolSize(getIntProperty("connection.pool.initialPoolSize"));
    securityDataSource.setMinPoolSize(getIntProperty("connection.pool.minPoolSize"));
    securityDataSource.setMaxPoolSize(getIntProperty("connection.pool.maxPoolSize"));
    securityDataSource.setMaxIdleTime(getIntProperty("connection.pool.maxIdleTime"));
    
    return securityDataSource;
}

// helper method
// read environment property and convert to int
private int getIntProperty(String propName) {
    String propVal = env.getProperty(propName);
    int intPropVal = Integer.parseInt(propVal);
    
    return intPropVal;
}
  • Update Spring security to use JDBC Use JDBC
  • Storage password in bcrypt Password encrypt

Spring REST API

Spring REST API

  • Spring use the Jackson Project behind the scenes, Jakson handles data binding between JSON and Java POJO Java POJO

Java POJO to JSON

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;

public class Driver {

	public static void main(String[] args) {
		try {
			// create object mapper
			ObjectMapper mapper = new ObjectMapper();
			
			// read JSON and convert to java pojo
			Student theStudent = mapper.readValue(new File("data/sample-full.json"), Student.class);
			
			// print the data
			System.out.println("First name: " + theStudent.getFirstName());
			System.out.println("Last name: " + theStudent.getLastName());
			System.out.println("Address: " + theStudent.getAddress().getCity());
			
			Address tempAddress = theStudent.getAddress();
			System.out.println("Country: " + tempAddress.getCountry());
			
			for(String tempLang: theStudent.getLanguages()) {
				System.out.print(tempLang + ",");
			}
		}catch(Exception exc) {
			exc.printStackTrace();
		}
	}

}
  • To ignore the unknown properties:
@JsonIgnoreProperties(ignoreUnknown = true)
public class Student {
    ...
}

HTTP overview

  • Stauts code
100 - 199 > Informational
200 - 299 > Successful
300 - 399 > Redirection
400 - 499 > Client error
500 - 599 > Server error

Handling Exception

  • Create a custom error response class
public class StudentErrorResponse {
	private int status;
	private String message;
	private int timeStamp;
	
	public StudentErrorResponse() { }

	public StudentErrorResponse(int status, String message, int timeStamp) {
		this.status = status;
		this.message = message;
		this.timeStamp = timeStamp;
	}

	public int getStatus() {
		return status;
	}

	public void setStatus(int status) {
		this.status = status;
	}
    ...
}
  • Create a custom exception class
public class StudentNotFoundException extends RuntimeException{
	
	public StudentNotFoundException(String message, Throwable cause) {
		super(message, cause);
	}

	public StudentNotFoundException(String message) {
		super(message);
	}

	public StudentNotFoundException(Throwable cause) {
		super(cause);
	}
	
}
  • Update Rest Service
@GetMapping("/students/{studentId}")
public Student getStudent(@PathVariable int studentId) {		
    if((studentId < 0) || (studentId >= theStudents.size())) {
        throw new StudentNotFoundException("Not found student with id: " + studentId);
    }
    
    return theStudents.get(studentId);		
}
  • Add an exception handler using @ExceptionHandler
@ExceptionHandler
public ResponseEntity<StudentErrorResponse> handleException(StudentNotFoundException exc){
    StudentErrorResponse error = new StudentErrorResponse();
    
    error.setStatus(HttpStatus.NOT_FOUND.value());
    error.setMessage(exc.getMessage());
    error.setTimeStamp(System.currentTimeMillis());
    
    return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}

API Design

  • Review API requirement
    • Create REST API for CRM system (get list customers, delete customer, update customer)
  • Identify main resources / entity
    • the entity with noun "customer" and the endpoints with plural "customers"
  • Use HTTP methods to asign a action on resource
    • POST, GET, PUT, DELETE Perfect endpoints

Arquitecture CRM Web APP REST Client + CRM REST API

CRM

springboot3-spring6's People

Contributors

brandconstantin avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.