Spring & Spring Boot - robbiehume/CS-Notes GitHub Wiki

Links:

Codecademy Cheatsheets

General notes

  • Spring is an open-source Java framework that can be used to build RESTful web applications
  • It can be used for the MVC design pattern
  • Similar to Node.js, Django, etc., but for Java
  • Spring contains templates for many different kinds of applications (Spring Cloud, Spring Web, Spring Services, Spring Boot, etc.)
  • Spring Boot is an extension of the Spring framework, and it eliminates the boilerplate configurations required for setting up a Spring app and allows for quick development
  • Spring uses the IoC (inversion of control) container to handle the flow instead of the developer having to manage it
  • AOP (aspect-oriented programming) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns (aspects of a program that affect several modules, without the possibility of being encapsulated in any of them)

Annotations:

  • Annotations (used by the @ symbol), are built-in units of Spring code that make complex functionality readily available
  • For example, @GetMapping("/helloworld") annotation means that the annotated method will be executed every time the app receives a GET request to the /helloworld endpoint

Dependency injections (link)

Spring core concepts (DI, IoC, AOP)

  • IoC (inversion of control): the principle of OOP, in which objects of the program do not depend on concrete implementations of other objects, but may have knowledge about their abstractions (interfaces) for later interaction
  • DI (dependency injection): for each function of the application there is one independent object (service) that can have the need to use other objects (dependencies) known to it by interfaces
    • Java DI Notes (wiki)
    • Spring DI & Autowire (YouTube)
    • Dependencies are transferred (implemented) to the service at the time of its creation. This is a situation where we introduce an element of one class into another.
    • In practice, DI is implemented by passing parameters to the constructor or using setters
  • AOP (aspect oriented programming): a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns (aspects of a program that affect several modules, without the possibility of being encapsulated in any of them)
    • An aspect is a class that implements enterprise application concerns that cut across multiple classes

Java Web Apps

Setup and structure

  • start.spring.io can be used to quickly build a new Spring Boot project
  • Spring Boot projects are organized by a predefined Maven folder structure
  • Main files in every Spring Boot project:
    • pom.xml: specifies and loads important project data, such as configuration, build profiles, and dependencies
      • POM stands for Project Object Model
    • application.properties: a config file that specifies the properties of your Spring application
      • Examples of application properties can include JSON properties, data properties, server properties, etc.
      • A Spring application will read and load properties specified in application.properties during project build
      • Located in src/main/resources
    • <ProjectName>Application.java: contains the main() method
      • Located in src/main/java/com/<ProjectName>
  • You can put files in src/main/webapp/ and Spring will know to look there for them

Web app / page content

Controllers

  • Spring controllers define endpoints and sends data in response to requests sent to those endpoints
  • In Spring, a class is marked as a controller and each of its methods is associated with an endpoint

@RestController annotation

  • Used to declare a class as a controller that can provide application-specific types of data as part of an HTTP response
  • To use it, import org.springframework.web.bind.annotation.RestController;
  • It should be added to each class that will handle responses to HTTP REST API requests
  • Don't use it if not doing a REST API and instead want to return something else (HTML page, file, etc.). Just use @Controller
  • It combines the functionality of two separate annotations: @Controller and @ResponseBody
    • @Controller is used to make a class identifiable to Spring as a component of the web app
    • @ResponseBody tells Spring to convert the return values of a controller's methods to JSON and bind that JSON to the HTTP response body

@RequestMapping annotation

  • Used to wire request types and endpoints to specific class methods
  • To use it, import org.springframework.web.bind.annotation.RequestMapping;
  • It accepts several arguments: path, method, params, and consumes
    • path/value: is used to determine where requests are mapped: @RequestMapping(path = "/endpoint1")
    • method: specifies which HTTP method should be used when accessing the controller method
      • Ex: RequestMethod.POST, .PUT, .DELETE, etc.
      • RequestMethod.GET is used as default if nothing is provided
      • NOTE: when using the RequestMethod enum, you need to import org.springframework.web.bind.annotation.RequestMethod
    • params: filters requests based on given parameters
    • consumes: used to specify which media type can be consumed * Common types are "text/plain", "application/JSON", etc.
  • It can also be used at the class level:
    • When you use @RequestMapping at the class level, the specified path argument becomes the base path
    • For example: a class with @RequestMapping("/books") will map all appropriate requests to this class, and in this case, "/books" becomes the base path
    • Methods in the class can be further mapped by adding @RequestMapping annotation to them

HTTP method annotations

  • Instead of providing the method type in the @RequestMapping, we can use method-specific mapping annotations
  • Ex: GetMapping, PostMapping, PutMapping, DeleteMapping
    • Need to import org.springframework.web.bind.annotation.GetMapping;
  • For example: @GetMapping("/about") is equivalent to @RequestMapping("/about", method = RequestMethod.GET)

URL query parameters and Template path variables

URL query parameters (@RequestParam)

  • @RequestParam is an annotation that can be used at the method parameter level to parse query parameters and capture those parameters as method arguments
  • This is helpful because we can take values passed in from the HTTP request, parse them, and then bind the values to a controller method for further processing
  • Need to import org.springframework.web.bind.annotation.RequestParam
  • Ex for HTTP request libraryapp.com/book?author_count2&published_year=1995
    • @GetMapping("/book")
      public Book isBookAvailable(@RequestParam int author_count, @RequestParam int published_year) {
          return books.find(author_count, published_year);
      }
  • Can also have it fill a default value if parameter isn't provided:
    • @RequestParam(value="published_year", defaultValue="2023") int year

Template path variables (@PathVariable)

  • @PathVariable allows for the variable to passed as the path instead of as a URL parameter
  • To use it, import org.springframework.web.bind.annotation.RequestMapping;
  • Ex for HTTP request libraryapp.com/books/28937, use /books/{id} template path
    • @GetMapping("/{id}")
      public Book isBookAvailable(@PathVariable int id)  {
          return book.findByID(id);
      }

Deserializing into objects (using @RequestBody)

  • @RequestBody annotation allows Spring to automatically deserialize the HTTP request body into a Java object which can be bound to the method and further processed
  • This would be done for requests that provide data in its body (other than in URL query parameters)
    • Like a POST form request
  • Ex using a Book object
    • class Book {
          public int authorCount;
          public int publishedYear;
      }
      ...
      @GetMapping("/book")
      public Book isBookAvailable(@RequestBody Book book) {
          return book.find(book.authorCount, book.publishedYear);
      }

Handling HTTP response status codes

  • Both the ResponseStatusException class and @ResponseStatus annotation can be used to specify an HTTP status code. ResponseStatusException is used to create specific responses dynamically, while a @ResponseStatus determines the status code for any response returned by the method.

Sending response status code (using @ResponseStatus)

  • @ResponseStatus can be applied to methods to apply custom HTTP status codes and reasons to an HTTP response
    • It accepts parameters for the status code and reason
  • Need to import org.springframework.web.bind.annotation.ResponseStatus to use @ResponseStatus
  • Also need to import import org.springframework.http.HttpStatus to use HttpStatus.<status> enum
  • @PutMapping(path="/book")
    @ResponseStatus(code = HttpStatus.CREATED, reason = "Book was successfully added")
    public string addNewBook(@RequestParam string title) {
        ... 
    }

Response exception Handling (using ResponseStatusException)

  • ResponseStatusException allows us to handle errors and throw exceptions
  • It accepts up to 3 arguments: HttpStatus code, String reason, and Throwable cause
  • Need to import org.springframework.web.server.ResponseStatusException
  • Ex: if expecting a numerical book ISBN, then an exception can be thrown if a non-number character is received
    • @GetMapping("/{id}")
      public Book isBookAvailable(@PathVariable string id) 
      {
          if (id.isNumeric()) {
              int id_int = Integer.parseInt(id)
              return book.findByID(id_int)
          } 
          else {
              throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "The ID contained a non-numerical value.");
          }
      }

Spring Beans

  • Spring beans allow us to have code reusability
  • A Spring bean is an instance of a class managed by the Spring Inversion of Control (IoC) container
    • It will be instantiated for us
  • The Spring IoC container's management of beans includes bean instantiation / assembly and the management of dependency injections
  • Can mark a class as a Spring bean using the @Component annotation
    • Use @Bean for methods
  • Can instantiate these dependency class objects in another class with the @Autowired annotation
    • It tells the IoC that it needs to instantiate an instance of the dependency class (bean) for the dependent class to use
  • Ex:
    • @Component
      public class RaceTrack {
        private String location;
        private int miles;
        private String trackType;
      }
       
      @Component
      public class Driver {
        private String name;
        private String team;
        private int yearsExperience;
      }
      
      public class RaceRound {
        private String startTime;
        @Autowired
        private RaceTrack currentRaceTrack;
        @Autowired
        private Driver currentDriver;
      }
  • Can also inject dependencies via the constructor (which doesn't require an annotation):
    • public class CoffeeController {
          private final CoffeeRepository coffeeRepository;
      
          public CoffeeController(CoffeeRepository coffeeRepo) {
              this.coffeeRepository = coffeeRepo;
          }
      }

Automated beans

  • The fully-automatic annotations approach is facilitated using three annotations, which in combination together tell the framework where to start looking, how to search our code, and automatically instantiate beans from the found components
    • @Configuration: notifies the framework that beans may be created via the annotated class
    • @ComponentScan: tells the framework to scan our code for components such as classes, controllers, services, etc.
    • @EnableAutoConfiguration: tells the container to auto-create beans from the found components
  • @SpringBootApplication annotation combines all three together
    • Add the annotation above the class
  • Ex:
    • @SpringBootApplication
      public class RecipeApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(RecipeApplication.class, args);
          }
      }

Bean representation and dependencies

  • Spring represents beans as a BeanDefinition object that has several properties
    • Two specific ones are class and properties
  • When the container instantiates a bean from a class, it populates the class property of that bean with the fully qualified name we have provided (ex: com.package.RacingGame.RaceRound)
    • The class property is Spring's way of representing the bean's underlying Java type so it knows what to instantiate
  • The properties property is populated from the properties of our class
    • If we have used a built-in type such as int or string, the container converts the property of our class into the same type of property for the bean
    • If we have a custom type, such as RaceTrack or Driver, this is a dependency and the container needs to create a BeanDefinition object for each of these types as well
  • When a class encapsulates other objects, the referenced objects become a dependency for the outer class
    • In other words, these other objects must be created so the outer class can use it
    • The container looks at our classes and, depending on the method you choose, instantiates beans from the referenced objects (RaceTrack or Driver) before it instantiates a bean from the outer classes (RaceRound)
  • Spring implements a way for the objects needed by another object to be provided as beans for others to reference
  • This process is known as dependency injection:
    • Our classes no longer have to instantiate their own dependencies, Spring does it for us
    • Therefore, the control of dependencies has been inverted back to the container, and this is why we call it an Inversion of Control (IoC) container

Spring Boot

  • Spring Boot is a collection of tools that extends the Spring framework and makes it easier to build applications quickly
  • It reads from the pom.xml file to know what dependencies to use
  • Some common Spring Boot tools are "Starters", Auto-configuration, Custom configurations with application.properties, and Distribution of your application
  • Starters: each one represents a set of dependencies with which to run our application
    • Ex: spring-boot-starter-web dependency instructs Spring Boot to configure Tomcat for running a server and Spring MVC for routing HTTP requests
  • Auto-Configuration:
    • SpringApplication class: responsible for starting up the Spring IoC container
    • @SpringBootApplication annotation: enables Spring Boot to review our dependencies and assume the intended purpose of the application. It will configure and run the application to best fit the assumed purpose

Spring Data JPA (Java Persistence API)

  • Spring Data JPA "wraps" JPA to make it a seamless process to integrate a database into your Spring Boot Application
    • It is available in the spring-boot-starter-data-jpa dependency, which needs to be added to the pom.xml file
  • JPA is the underlying standard used to translate objects from Java into queries that can be sent to the underlying database
    • JPA does this by wrapping around JDBC (Java Database Connectivity), which is a standard for sending raw database queries to the underlying database
    • JPA is a standard that can be implemented by an ORM (object-relational mapping)
    • JPA and the ORM allow us to work with classes and methods instead of defining tables and queries in SQL code
    • Ex:
      • SELECT name
        FROM plants         --->     Plant roseName = plantRepository.findByName('rose').name;
        WHERE name='rose'
        
  • Hibernate is the most common ORM used with Spring Data JPA

Database Types

  • Spring Data JPA supports many different kinds of databases
    • It allows you to switch between database types without needing to change the application logic
    • Ex: if you wanted to switch fro MySQL to PostgreSQL, you would only need to update the configuration of the Spring Boot application to point to the new database
    • This is allow possible because of the JPA standard. As long as the appropriate JDBC drivers exist for a given database, implementations of the JPA will be able to translate Java objects to the right SQL queries, which may be different between databases
    • The database type is specified in application.properties
  • H2
    • H2 is a relational database written entirely in Java
    • One convenient feature of H2 is that it can run on the same kind of infrastructure as your application and can run entirely "in-memory"
    • This makes it was to use to test your Spring Boot application on your machine, without having to obtain a database server from elsewhere for your application to use
    • To connect to an embedded H2 database using Spring Data JPA, you need to add the dependency to pom.xml and add the url and driverClassName in application.properties
    • In pom.xml and application.properties add:
      • // pom.xml                                      // application.properties          
        <dependency>                                    spring.datasource.url=jdbc:h2:~/<db_name>.db
          <groupId>com.h2database</groupId>             spring.datasource.driverClassName=org.h2.Driver
          <artifactId>h2</artifactId>
          <scope>runtime</scope>
        </dependency>
        

JPA class models

  • We write Java classes and use annotations to identify them as models to Spring Data JPA, then Spring Data JPA sends instructions to the Hibernate ORM to execute the correct SQL statements depending on the methods that we've called on our model and the annotations we've put on its attributes
  • We need getter and setter methods for each attribute so that the ORM is able to interact with the model (class) and access / modify fields / attributes in the model as required
  • We add JPA annotations that allow us to specify class parameters in order to map the class object to a database table
  • Ex:
    • import javax.persistence.*;    // can import each one specifically: javax.persistence.Entity, "".Id, etc.
      @Entity    // tells the ORM this model will be used to represent a table or relation in our database
      @Table(name="PEOPLE")    // tells ORM what table name this model corresponds to;
      public class Person {    // in this ex. it says that the Person entity represents a single entry in the PEOPLE table
      
          @Id    // tells the ORM this field (id) will be used to uniquely identify a single entry in our PEOPLE relation
          @GeneratedValue   // tells the ORM this value won't be provided and should be "auto-generated" by the database
          private Integer id;
      
          @Column(name="NAME")    // tells the ORM what column in the underlying relation the annotated field corresponds to
          private String name;    // in this ex. the age field of our class corresponds to the AGE column in the PEOPLE relation
      
          @Column(name="AGE")
          private Integer age;
      }

JPA repositories

  • Spring Data JPA uses repositories to determine how to use a model to interact with the database
  • A repository is a data access and manipulation layer that wraps around a data model
  • It comes with methods to call on instances of the data model
  • CrudRepository is an interface that gives full CRUD functionality for your model
    • Extend your class model with it and parameterize it with the model and ID field
    • import org.springframework.data.repository.CrudRepository;
      import com.codecademy.people.entities.Person;
      // creating an extension of the CrudRepository that can manage our Person model
      public interface PersonRepository extends CrudRepository<Person, Integer> { }
  • Useful methods of CrudRepository interface:
    • .findById(Integer id): allows you to query the database to find an instance of your model by its ID field
    • .findAll(): allows you to retrieve ALL the entries in the database for a given model
    • .save(Person p): allows you to create AND modify instances of your model in the database
    • .delete(Person p): allows you to delete instances of your model from the database

Connecting a repository to a controller

  • A RestController is used as the layer of the application that takes requests from the user and accordingly sends commands to the repository or data access layer to accomplish what task the user requested in their REST call
  • The repository must be a dependency of the controller
    • You make the repository bean available to the controller class using dependency injection
    • Since the extension of the CrudRepository will be used in the "Spring context", it is implicitly a bean
  • To make a bean available to a class that depends on it, you can simply add it was a field of the class and include it in the constructor
    • Spring Boot will automatically "wire" in the dependency when it discovers the dependency in the Spring context
  • Ex:
    • @RestController
      public class PersonController {
        private final PersonRepository personRepository;
      
        public PersonController(final PersonRepository personRepository) {
          this.personRepository = personRepository;
        }
        // ... all REST endpoint mappings
      }
⚠️ **GitHub.com Fallback** ⚠️