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)
- 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 servlets vs Spring MVC
- Tomcat vs Apache HTTP server
- Tomcat allows us to have dynamic content from Java programs
- 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>
- Located in
-
- You can put files in
src/main/webapp/
and Spring will know to look there for them
- 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
- 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
-
- 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
, andconsumes
-
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
- When you use
- 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;
- Need to
- For example:
@GetMapping("/about")
is equivalent to@RequestMapping("/about", method = RequestMethod.GET)
-
@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
-
-
@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); }
-
-
@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); }
-
- 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.
-
@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
andreason
- It accepts parameters for the status
- Need to
import org.springframework.web.bind.annotation.ResponseStatus
to use@ResponseStatus
- Also need to import
import org.springframework.http.HttpStatus
to useHttpStatus.<status>
enum -
@PutMapping(path="/book") @ResponseStatus(code = HttpStatus.CREATED, reason = "Book was successfully added") public string addNewBook(@RequestParam string title) { ... }
-
ResponseStatusException
allows us to handle errors and throw exceptions - It accepts up to 3 arguments:
HttpStatus code
,String reason
, andThrowable 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 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
- Use
- 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; } }
-
- 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); } }
-
- Spring represents beans as a
BeanDefinition
object that has several properties- Two specific ones are
class
andproperties
- Two specific ones are
- 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
orDriver
, this is a dependency and the container needs to create aBeanDefinition
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
orDriver
) 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 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
- Ex:
- 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 "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 thepom.xml
file
- It is available in the
-
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
- 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 inapplication.properties
- In
pom.xml
andapplication.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>
-
- 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; }
-
- 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
-
- 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 }
-