- Using VSCode Dev Containers for Java Development
- Set up GitLab Repository
- Create a Java Spring Boot Project
- Implement OpenAPI and Swagger UI
- Spring Boot Layerd Architecture
- Controllers
- Services
- Repositories
- Models
- Filters
- @Configuration
- Configure File: application.properties
- Dockerfile
- Connecting to MongoDB
Using VSCode Dev Containers for Java Development
- Open VSCode, press
F1
orCtrl+Shift+P
, typeDev Containers: Open Folder in containers...
, and select your project directory. - Choose Java as the Dev Container image and wait for the container to start.
- Open TERMINAL, and verify Java installation with the following commands:
Copy code
$ java -version
$ javac -version
In the Linux-based Dev Container, check the OS version and Java installation path:
Copy code
# Check OS
cat /etc/os-release
# Find Java installation
which javac
Set up GitLab Repository
- Create a new blank project on GitLab.
- Execute these commands to push your local data to GitLab:
Copy code
git init
git remote add origin https://gitlab.com/<group>/<project>.git
git add .
git commit -m "Initial commit"
git push -u origin master
Create a Java Spring Boot Project
- Install the “Spring Initializr Java Support” extension in VS Code.
- Press
Ctrl+Shift+X
, search for the extension, and clickInstall in Dev Container: Java
. - Open Command Palette (
Ctrl+Shift+P
orF1
) and type “spring”. Choose Maven or Gradle to create the project.
This tutorial uses a Spring Boot API project managed by Maven. Select Spring Web, Spring Boot DevTools, and Spring Data as dependencies. Check Maven installation with mvn -v
.
Create a package with Maven:
mvn package
Run the generated JAR file (e.g., backend-api-0.0.1-SNAPSHOT.jar):
java -jar ./target/backend-api-0.0.1-SNAPSHOT.jar
During development, use this command to compile and restart automatically after code changes:
mvn spring-boot:run
Implement OpenAPI and Swagger UI
To display Swagger UI with Spring Boot 3.0.0, use springdoc-openapi v2.1.0. Refer to the official springdoc-openapi v2.1.0 documentation. Add the following text to pom.xml:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.1.0</version>
</dependency>
The Swagger UI page can be accessed at http://server:port/context-path/swagger-ui.html, and the OpenAPI Spec (JSON) is located at http://server:port/context-path/v3/api-docs.
To generate only the OpenAPI document (JSON) without the UI, include this dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<version>2.1.0</version>
</dependency>
For Spring Boot 2.x versions, use the following package:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>
The paths for Swagger UI and OpenAPI Spec remain the same.
Spring Boot Layerd Architecture
Recommended source code directory structure:
src/
├── main/
│ ├── java/
│ │ ├── beginning/
│ │ │ ├── example/
| │ │ │ ├── backendapi/
| │ │ │ │ ├── filters/
| │ │ │ │ ├── configs/
| │ │ │ │ ├── controllers/
| │ │ │ │ ├── services/
| │ │ │ │ ├── repositories/
| │ │ │ │ └── models/
| │ │ │ └── BackendApiApplication.java
| │ │ └── resources/
| │ │ └── application.properties
| │ └── ...
│ └── ...
└── ...
- controllers: Receive HTTP requests and respond with HTTP responses.
- models: Define data structures for transfer and processing.
- services: Handle data processing and business logic.
- repositories: Connect to backend databases for data access.
- filters: Middleware with OncePerRequestFilter as the base class, enabling tasks such as logging or security checks on HTTP requests and responses.
- configs: Implement WebMvcConfigurer to add filters to the appropriate locations.
Controllers
Controllers simplify the development of RESTful web services. By using the @RestController
annotation on a class, it can handle various HTTP requests such as GET, POST, and others.
Services
Services handle business logic and act as intermediaries for other layers (e.g., controllers). By annotating a class with @Service
, Spring framework automatically creates an instance at startup. Controllers can declare a service variable using @Autowired
to automatically obtain its instance. If multiple services exist, use @Qualifier
to specify one.
For example, if the program contains userService1 and userService2:
@Service("userService1")
public class UserService1Impl implements UserService {
// ...
}
@Service("userService2")
public class UserService2Impl implements UserService {
// ...
}
If the controller selects one of the services, use the following pattern:
@RestController
public class UserController {
private final UserService userService;
@Autowired
public UserController(@Qualifier("userService1") UserService userService) {
this.userService = userService;
}
// ...
}
// You can also set @Qualifier directly on the service variable without using a constructor for more concise code.
@RestController
public class UserController {
@Autowired
@Qualifier("userService1")
private UserService userService;
// ...
}
If you want to use both services, use the following pattern:
@RestController
public class UserController {
private final UserService userService1;
private final UserService userService2;
@Autowired
public UserController(@Qualifier("userService1") UserService userService1, @Qualifier("userService2") UserService userService2) {
this.userService1 = userService1;
this.userService2 = userService2;
}
// ...
}
Repositories
Repositories are responsible for handling data access, such as connecting to databases and performing read and write operations.
Models
Models define the object form of data, such as the data required for HTTP requests and responses, or the data formats needed for internal conversions. To map a model directly to an SQL type database using JPA, annotate with @Entity
, @Table
, and @Column
as shown below:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "email")
private String email;
// getter and setter methods...
}
Filters
Filters in Spring Boot act as middleware for HTTP requests, intercepting HTTP request and response information for processing, such as adding logs, performing security checks, and handling encryption/decryption tasks.
To create a filter, simply extend OncePerRequestFilter
and override the doFilterInternal
method.
After completing the filter code, it needs to be registered with Spring using the configure
method.
@Configuration
Use @Configuration
to define the filter as a @Bean
for injection into the Dependency Injection Container. Spring Boot searches for the Filter
interface in the DI Container and calls it when receiving an HTTP request. The Filter
interface is defined as follows:
package javax.servlet;
import java.io.IOException;
public interface Filter {
void init(FilterConfig filterConfig) throws ServletException;
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
void destroy();
}
Configure File: application.properties
After executing mvn package
, the application.properties
file is copied to the target/classes directory. application.properties
can be used to store data that varies depending on the installation environment, such as database connection locations.
Dockerfile
Create a Docker Image:
docker build -t try-spring-boot .
When executed repeatedly, the old try-spring-boot
image is not deleted but is renamed to <none>
. Use the following commands to delete these unused <none>
images:
docker image prune -f
# Or chain with the previous build command
docker build -t try-spring-boot . && docker image prune -f
Run the container:
docker run -d --name try-spring-boot -p 8080:8080 try-spring-boot
To view the logs of a running container, use the following commands:
docker logs try-spring-boot
# Or continuously output logs until Ctrl-C is pressed
docker logs try-spring-boot -f
Stop the container:
docker stop try-spring-boot
Open the web pages:
- Open Swagger UI
- Open OpenAPI Spec
Connecting to MongoDB
First, we can try running two containers, one for MongoDB and the other for mongosh. We can connect and access data through the assigned Docker network. The following example will create and enter the test database using the mongo shell.
docker network create try-spring-boot-network
docker run --name try-spring-boot-mongo --rm --network try-spring-boot-network -v ${pwd}/temp-db-storage:/data/db -d mongo
docker run --name try-spring-boot-mongo-sh --rm --network try-spring-boot-network -it mongo mongosh --host try-spring-boot-mongo test
In the above commands, the second command sets the host name to the container name by default when the --hostname parameter is not specified. Therefore, the shell can connect to MongoDB through this host name. However, to connect to the container from the local machine, you still need to use localhost. To exit the mongo shell, enter .exit.
If you want to set the username and password through environment variables, you can use the following commands:
docker network create try-spring-boot-network
docker run --name try-spring-boot-mongo --rm --network try-spring-boot-network -v ${pwd}/temp-db-storage:/data/db -d -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=example mongo
docker run --name try-spring-boot-mongo-sh --rm --network try-spring-boot-network -it mongo /bin/bash
# After entering try-spring-boot-mongo-sh, enter the following command to start mongosh
mongosh --host try-spring-boot-mongo --username root --password example --authenticationDatabase admin
To connect to the above-created mongo in the dev container, you need to specify the same network in ./devcontainer/devcontainer.json
.
{
"name": "Java",
"image": "mcr.microsoft.com/devcontainers/java:0-17",
// Add the dev container to the try-spring-boot-network
"runArgs": [
"--network=try-spring-boot-network"
],
"features": {
"ghcr.io/devcontainers/features/java:1": {
"version": "none",
"installMaven": "true",
"installGradle": "false"
}
}
}
Also, modify application.properties
and vscode launch.json
to configure the connection.
spring.data.mongodb.uri=mongodb://${MONGO_USERNAME:root}:${MONGO_PASSWORD:example}@${MONGO_HOST:mongo}:${MONGO_PORT:27017}/${MONGO_DATABASE:test}?authSource=${MONGO_AUTH_SOURCE:admin}&authMechanism=${MONGO_AUTHMECHANISM:SCRAM-SHA-1}
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "BackendApiApplication",
"request": "launch",
"mainClass": "beginning.example.backendapi.BackendApiApplication",
"projectName": "backend-api",
"env": {
"MONGO_USERNAME": "root",
"MONGO_PASSWORD": "example",
"MONGO_HOST": "try-spring-boot-mongo",
"MONGO_PORT": "27017",
"MONGO_DATABASE": "test",
"MONGO_AUTH_SOURCE": "admin",
"MONGO_AUTHMECHANISM": "SCRAM-SHA-1"
}
}
]
}
Using docker-compose is a more convenient choice for running multiple containers simultaneously. The following method can be used to start the Java program as well.
version: '3.1'
services:
mongo:
image: mongo
container_name: try-spring-boot-mongo
restart: always
volumes:
- ${PWD}/temp-db-storage:/data/db
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
networks:
- try-spring-boot-network
mongo-express:
image: mongo-express
container_name: try-spring-boot-mongo-express
restart: always
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: example
ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/
networks:
- try-spring-boot-network
try-spring-boot:
image: try-spring-boot
container_name: try-spring-boot
ports:
- "8080:8080"
environment:
MONGO_USERNAME: root,
MONGO_PASSWORD": example,
MONGO_HOST: try-spring-boot-mongo,
MONGO_PORT: 27017,
MONGO_DATABASE: test,
MONGO_AUTH_SOURCE: admin,
MONGO_AUTHMECHANISM: SCRAM-SHA-1
networks:
- try-spring-boot-network
networks:
try-spring-boot-network:
driver: bridge
The above yaml can be started or stopped using the following commands:
docker-compose up -d
docker-compose down
By using the provided docker-compose.yml
, you can run MongoDB, Mongo Express, and your Spring Boot application simultaneously. This setup simplifies managing and connecting multiple containers, making development and testing more efficient.
Remember to modify your Spring Boot application.properties
and your VSCode launch.json
accordingly to ensure proper connection to the MongoDB instance.
Once everything is set up, you can test your Spring Boot application’s connection to MongoDB, perform database operations, and build your application as needed.
Setting Up a DevContainer Debug Environment
To set up a DevContainer testing environment for debugging within DevContainer, you can refer to the PowerShell script setup-mongo-container.ps1
located in the root directory of the project. Also, make sure to configure the env
information in the .vscode launch.json
file accordingly.
The contents of setup-mongo-container.ps1
are as follows:
# 1. Check if the directory does not exist, then create it
if (!(Test-Path -Path .\temp-db-storage)) {
New-Item -ItemType Directory -Path .\temp-db-storage
}
# 2. Check if the try-spring-boot-network does not exist, then create it
$networkExists = docker network ls --filter name=try-spring-boot-network --format "{{.Name}}" -q
if (!$networkExists) {
docker network create try-spring-boot-network
}
# 3. Check if the container exists, stop and remove it if it does, then run the new container
$containerExists = docker container ls -a --filter name=try-spring-boot-mongo --format "{{.Names}}" -q
if ($containerExists) {
docker container stop try-spring-boot-mongo
docker container rm try-spring-boot-mongo
}
docker run --name try-spring-boot-mongo --rm --network try-spring-boot-network -v ${pwd}/temp-db-storage:/data/db -d -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=example mongo
This PowerShell script performs the following steps:
- Creates a
temp-db-storage
directory if it doesn’t already exist. - Creates a
try-spring-boot-network
Docker network if it doesn’t already exist. - Checks for the existence of a
try-spring-boot-mongo
container. If it exists, the script stops and removes it before running a new container with the same name.
By executing this script, you can easily set up the testing environment for debugging within DevContainer. This will allow you to create and manage the necessary resources (such as the Docker network and container) for your Spring Boot and MongoDB integration.
Once you’ve set up the testing environment, you can proceed with debugging and developing your application inside the DevContainer environment.