Enterprise software is computer software used to support the operational and strategic needs of an organisation rather than individual users.
Java EE (currently known as Jakarta EE) provides a platform that makes enterprise applications easy to write.
The Java EE server provides several services in the form of a container, that manage transaction handling, database connection, state management, and multithreading, without us having to explicitly handwrite every single line of code.
Spring framework is divided into modules, which makes it faster and easier to choose the parts to use in any application.
The central part of the Spring Framework. Its core technologies are:
IoC is a principle in software development which transfers the control of objects to a container or framework.
The framework takes control of the flow of a program and make calls to our custom code.
Dependency Injection is an example of how we can achieve IoC.
Any Java application is composed of a number of objects that collaborate with each other. This collaboration is possible due to objects having dependencies on each other.
Dependency injection is a pattern used to implement IoC.
A given class or system is no longer responsible for instantiating their own dependencies; this is done by an assembler/DI tool.
An IoC container is a common characteristic of frameworks that implement IoC.
In Spring, it is represented by the interface ApplicationContext.
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
The Spring framework provides several concrete implementations of said interface.
In order to assemble beans, the container uses configuration metadata, which can be in the form of XML configuration or annotations.
But what are Spring Beans?
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans.
<!-- In the XML file-->
<bean id="myBean" class="com.mindera.mindswap.MyClass"/>
// In Main class
public class Main {
public static void main(String[] args){
(...)
MyClass myClass = context.getBean("myBean", MyClass.class);
myClass.doSomething();
}
}
<!--Constructor-based DI-->
<bean id="myBean" class="com.mindera.mindswap.MyClass"/>
<bean id="myBeanWithDependencies" class="com.mindera.mindswap.MyOtherClass">
<constructor-arg name="myclass" ref="myBean"/>
</bean>
<!--Setter-based DI-->
<bean id="myBean" class="com.mindera.mindswap.MyClass"/>
<bean id="myBeanWithDependencies" class="com.mindera.mindswap.MyOtherClass">
<property name="myclass" ref="myBean"/>
</bean>
There are two major ways of injecting dependencies with Spring: constructor-based DI and setter-based DI.
Spring Application
Spring Boot is an opinionated, convention-over-configuration-focused addition to the Spring framework.
It eliminates the boilerplate configurations required for setting up a Spring application, making it easy to create them.
start.spring.io allows us to generate JVM-based projects quickly.
We can customise the project to generate:
Spring Initializr
A software program that runs on a web server. It is accessible through a browser.
Java provides support for web application through Servlets and JSPs.
The web application and its components are managed and executed inside a web container (aka servlet container).
A servlet is a core class in web applications. They either respond to HTTP requests directly, or delegate that work to some other part of the application.
Servlets run in a servlet container.
Tomcat is a Web Application Container used to deploy applications built using the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies.
It handles the networking side, i.e., parsing requests, handling connections, etc.
By default, Spring Boot provides an embedded Apache Tomcat build.
JSP is a server-side programming technology that allows Java code to be embedded in HTML pages.
The Java code is executed on the server and compiled with the JSP to produce a plain, browser-readable HTML page.
Spring MVC is a higher level abstraction built on top of Java Servlets, and based on the Model-View-Controller pattern.
Servlets are replaced with controllers.
Front Controller receives the request and delegates it to the appropriate Controller.
When our application receives a GET request with the URL <app_domain>/hello, the FrontController will forward this request to the MyController servlet class.
// SPRING SCANS YOUR CODE FOR @Controller ANNOTATIONS
@Controller
public class MyController {
// @GetMapping MAPS HTTP GET REQUESTS ONTO SPECIFIC HANDLER METHODS
@GetMapping("/hello")
public String homePage(Model model) {
model.addAttribute("hello", "This is me, saying hello.");
// RETURNING THE VIEW
return "home";
}
}
The Spring Model can supply attributes that will be later used for rendering views. To provide a view with usable data, we simply add it to its Model object.
@Controller
public class MyController {
@GetMapping("/user")
public String userDetails(Model model) {
// CREATE USER OBJECT
User user = new User("Dave Bayley", "davebayley89@gmail.com");
// ADD USER OBJECT AS ATTRIBUTE; DATA WILL BE ACCESSIBLE IN VIEW
model.addAttribute("user", user);
// RETURN USER VIEW
return "user";
}
}
Data can be added to the Spring model inside the controller using the Model or ModelAndView classes.
The ViewResolver interface provides a mapping between view names and the actual view objects.
// IN THE application.properties FILE
spring.mvc.view.prefix=/templates
spring.mvc.view.suffix=.jsp
The view resolver surrounds the view name with a prefix and a suffix, providing the front controller with the file path to the template.
<!-- IN THE *.jsp FILE -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<head>
<title>Home Page</title>
</head>
<body>
<h1>${hello}</h1>
</body>
</html>
Model data is accessed using the JSP Expression Language.
Modern Java Template engine which can process HTML, text, JavaScript or CSS files.
Contrary to JSP, Thymeleaf supports Natural Templating, allowing the template to be correctly displayed in the browser when opened directly, working as a static prototype.
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<title>Home Page</title>
</head>
<body>
<h1>This is your user:</h1>
<p th:text="${user.name}"></p>
<p th:text="${user.email}"></p>
</body>
</html>
Hello World with Spring Boot MVC
Spring auto-wiring enables object dependencies to be injected without the need for explicit configuration.
A Spring-managed bean can be injected into a property by using the @Autowired annotation.
An URL template is an URL-like string that contains one or more variable names.
@Controller
public class UserController {
@GetMapping("/users/{id}")
public String getUser(@PathVariable Integer id) {
// (...)
}
}
The @PathVariable annotation can be to bind a method parameter to the value of an URL template variable.
User Details Pages with Spring Boot MVC
/users → Shows a list of users names
/users/{id} → Shows the details of that particular user
Spring Data is a Spring module whose mission is to provide a consistent programming model for data access.
It makes using data access technologies, and both relational and non-relational databases as easy as pie.
Spring Data JPA is the Spring Data's model responsible for implementing JPA based repositories.
Remember this?
The Data Access layer provides simplified access to data stored in persistent storage.
The DAO pattern allows us to isolate the application/business layer from the persistence layer.
It hides the complexity involved in performing CRUD operations in the underlying storage mechanism from our application.
public interface Dao<T> {
Optional<T> get(Integer id);
List<T> getAll();
void saveOrUpdate(T t);
void delete(T t);
}
public class UserDao implements Dao<User> {
(...)
@Override
public Optional<User> get(Integer id) {
return Optional.ofNullable(em.find(User.class, id));
}
@Override
public List<User> getAll() {
return entityManager.createQuery( "SELECT * FROM User user").getResultList();
}
@Override
public void saveOrUpdate(User user) {
(...)
}
@Override
public void delete(User user) {
(...)
}
}
The Repository pattern is similar to the DAO pattern in a sense that both deal with data and hide query complexity from the rest of our application.
However, the Repository sits at a higher level, closer to the business logic of an app. The Repository will use a DAO to get the data from the storage and use that data to restore a business object (and vice-versa).
Repositories are useful when we're dealing with non-anaemic models. In this case, our user is a fairly complex domain model.
public class UserRepository implements Repository<User> {
private UserDao userDao;
private TweetDao tweetDao;
@Override
public User get(Integer id) {
User user = userDao.read(id);
List<Tweet> tweets = tweetDao.fetchTweets(user.getEmail());
user.setTweets(tweets);
return user;
}
}
The goal of the Spring Data repository abstraction is to reduce the amount of boilerplate code required to implement data access layers.
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
(1)
<S extends T> S save(S entity);
(2)
T findOne(ID primaryKey);
(3)
Iterable<T> findAll();
Long count();
(4)
void delete(T entity);
(5)
boolean exists(ID primaryKey);
(6)
(...)
}
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
Customer findById(long id);
}
We don't need to write an implementation of the repository interface. Spring Data JPA creates an implementation when we run the application!
Spring Boot JPA
Next we will start creating our very own Rent-A-Car Application.
We'll start by creating an API that will allow a web application to consume our Customers and Cars data.
But first, learn what an API truly is.
@ResponseBody - Tells a controller that the object returned is to be automatically serialised into JSON format and sent back into the HttpResponse object.
@RestController - Combines the annotations @Controller and @ResponseBody. Annotating a class with @RestController means that every method of the controller class automatically serialises return objects into an HttpResponse.
@RestController
public class HelloRestController {
@GetMapping("/")
public String hello(){
return "Hello!";
}
}
@RequestParam - Used to extract query parameters from the request. Example request:
http://localhost:8080/message?name=Ronald
The application uses the Jackson library to automatically serialise Java instances into JSON.
@RestController
public class HelloRestController {
@GetMapping("/message")
public Message greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
return new Message("Hello, " + name);
}
}
Jackson is the standard JSON library for Java. It provides a framework to serialise Java objects to a JSON string, and vice-versa.
Jackson is included automatically, and by default, by the Spring Boot framework.
You can learn more about it here.
@RequestBody - Tells a controller that the value of the parameter annotated is to be automatically deserialised into a Java object.
@RestController
public class HelloRestController {
@PostMapping("/newMessage")
public void newMessage(@RequestBody Message message) {
messages.add(message);
}
}
Postman is a collaboration tool for API development. With Postman, we can send REST, SOAP, and GraphQL requests directly.
Postman Overview
ResponseEntity - represents an HTTP response, consisting of headers, body, and response code.
@RestController
public class HelloRestController {
@PostMapping("/newMessage")
public ResponseEntity<Message> newMessage(@RequestBody Message message) {
if(message.getText() == null){
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
messages.add(message);
return new ResponseEntity<>(message, HttpStatus.CREATED);
}
}
Spring Boot - REST API
DTO is an object that carries data between processes in order to reduce the number of method calls.
Martin Fowler in Patterns of Enterprise Architecture
In addition, using DTOs to transfer data to/from our application also allows us to separate our presentation logic from the persistence logic.
A DTO is a collection of properties with getters and setters, the represents the data we want to transfer.
public class UserDto {
private String firstName;
private String lastName;
private String email;
private String phoneNumber;
}
A converter is required to transfer data from DTOs to domain objects and vice-versa.
public class UserConverter {
public static UserDto fromUserEntityToUserDto(User user) {
(...)
}
public static User fromUserDtoToUserEntity(UserDto userDto) {
(...)
}
}
@RestController
public class UserRestController {
@PostMapping("/newUser")
public void addUser(@RequestBody UserDto userDto) {
(...)
}
}
JSR 380 is a specification of the Java API for bean validation. It ensures that the properties of a bean meet specific criteria, using annotations such as @NotNull, @Min, and @Max.
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
public class UserDto {
@NotBlank(message = "Must have first name")
private String firstName;
@NotBlank(message = "Must have last name")
private String lastName;
@Email(message = "Email must be valid")
private String email;
@Pattern(regexp = "^\+?[0-9]*$", message = "Phone has invalid characters")
@Size(min=9, max=16)
private String phoneNumber;
}
We can validate the deserialised JSON object by adding the @Valid annotation to method parameters.
@RestController
public class UserController {
@PostMapping("/newUser")
public ResponseEntity addUser(@Valid @RequestBody UserDto userDto, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
(...)
}
}
The BindingResult object contains the results of the evaluation. This object must follow the object to be validated.
We are going to create an application to be used by a car rental company, starting with a REST API that will later be consumed by a JavaScript frontend application.
We will need to store data regarding:
USERS:
CARS:
RENTALS:
Car Rental - Rest API
Some concerns are implemented across the several layers of our application. Theses concerns are called cross-cutting concerns.
AOP is a programming pattern that increases modularity by allowing the separation of cross-cutting concerns.
With AOP, we define common functionality in one place.
The functionality is applied without modifying the class to which we are applying the new feature.
The cross-cutting concern can now be modularised into special classes, called aspect.
JOIN POINT - A point in the application where we apply an AOP aspect
ADVICE - An action that we take either before or after the method execution. Advices are taken for a specific joint point.
POINTCUT - A pointcut is an expression that selects one or more join points where advice is executed
WEAVING - The process of applying aspects to a target object, creating a new, proxy object
TARGET - The object in which the aspects are applied
PROXY - The object created after applying the advices to the target object.
AOP is actually an extension of the Object-Oriented Paradigm.
\ | OOP | AOP |
---|---|---|
Basic Unit | Object (encapsulates methods and attributes) | Aspect (encapsulates pointcuts and advices) |
Entry Point | Pointcut | Method |
Implementation | Advice | Method Body |
Code Construction | Waver | Compiler |
There are 5 types of AOP advices:
AOP Example
A domain error occurs when a specific input data causes the program to execute a wrong, undesired path.
Example: someone trying to access a user that does not exist in our Rent-A-Car application.
Creating dedicated exceptions for signalling business logic errors allows specialised error handling for each situation, but how can we achieve this?
AOP comes to the rescue:
@ControllerAdvice - applies this controller methods to all controllers in the application.
@ExceptionHandler - when the exception is thrown, the method annotated will be called
@ControllerAdvice
public class RentacarExceptionHandler {
@ExceptionHandler(value = {
UserNotFoundException.class,
CarNotFoundException.class})
public ResponseEntity<Error> handleNotFoundException(Exception ex, HttpServletRequest request) {
return Error.builder()
.timestamp(new Date())
.message(ex.getMessage())
.method(request.getMethod())
.path(request.getRequestURI())
.build();
}
}
Car Rental - Rest API with Exception Handling
HTTPS is an internet communication protocol that protects the integrity and confidentiality of data between the user's computer and the site.
Data sent using HTTPS is secured via Transport Layer Security protocol (TLS), providing three key layers of protection:
We can enable HTTPS in our application using Spring Boot. Here's how.
The process of developing, adding, and testing security features (hardware and software) within applications to prevent security vulnerabilities against threats such as unauthorised access and/or data modification.
Spring Security is the standard framework for securing Spring-based applications.
It is a powerful, highly customisable authentication and access-control framework.
HTTP Basic Authentication requires that the server request a username and password from the web client and verify that they are valid by comparing them against a database of authorised users.
HTTP basic authentication sends user names and passwords over the Internet as text, so it's not a secure authentication mechanism.
bcrypt is one of the most famous password-hashing functions.
SALT - Generating random bytes (the salt) and combining it with the password before hashing creates unique hashes across each user’s password. bcrypt allows us to choose the value of salt rounds.
Basic Authentication
HTTP is a stateless protocol.
We need a way to store user data between requests, in order to associate a request to any other request.
A web cookie is a packet of data that a computer receives and then sends back without changing or altering it.
A cookie is created by a server and sent to the browser when we visit a website.
Persistent cookies - work by tracking our online preferences. This is how browsers remember and store our login information, language selections, menu preferences, etc.
A Cookie-based authentication uses HTTP cookies to authenticate the client requests and maintain session information on the server over the stateless HTTP protocol.
Cookie-based authentication has been the default, tried-and-true method for handling user authentication for a long time.
Cookie Implementation
Cookie Implementation
A role is a group of permissions.
Roles can be assigned to a single user or user group.
A user or user group can have multiple roles.
public class UserEntity extends AbstractEntity {
(...)
@Column(nullable = false)
private String encryptedPassword;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private UserRole role;
}
public enum UserRole {
CUSTOMER,
EMPLOYEE,
ADMIN
}
@GetMapping("/{userId}")
@PreAuthorize("@authorized.isUser(#userId) ||" +
"@authorized.hasRole(\"EMPLOYEE\") ||" +
"@authorized.hasRole(\"ADMIN\")")
public ResponseEntity<UserDetailsDto> getUserById(@PathVariable long userId) {
(...)
}
Car Rental - Rest API with Security