Back to Master Spring Boot
    Topic 1

    The First Spring Boot Application

    Create your first Spring Boot application from scratch, understand the project structure, and explore auto-configuration

    To understand Spring Boot, let's write our first Spring Boot application and see how it differs from the Spring applications we've written before.

    We create a springboot-hello project and set up a standard Maven directory structure.

    Project Directory Structure

    Project Structure
    springboot-hello
    ├── pom.xml
    ├── src
    │   └── main
    │       ├── java
    │       └── resources
    │           ├── application.yml
    │           ├── logback-spring.xml
    │           ├── static
    │           └── templates
    └── target

    Within src/main/resources directory, note the following important files:

    application.yml - Configuration File

    This is the default configuration file for Spring Boot. It uses YAML format instead of .properties format, and the filename must be application.yml.

    Using .properties format:

    application.properties
    # application.propertiesspring.application.name=${APP_NAME:unnamed}spring.datasource.url=jdbc:hsqldb:file:testdbspring.datasource.username=saspring.datasource.password=spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriverspring.datasource.hikari.auto-commit=falsespring.datasource.hikari.connection-timeout=3000spring.datasource.hikari.validation-timeout=3000spring.datasource.hikari.max-lifetime=60000spring.datasource.hikari.maximum-pool-size=20spring.datasource.hikari.minimum-idle=1

    Using YAML format:

    application.yml
    # application.ymlspring:application:name: ${APP_NAME:unnamed}datasource:url: jdbc:hsqldb:file:testdb
    username: sa
    password:driver-class-name: org.hsqldb.jdbc.JDBCDriver
    hikari:auto-commit:falseconnection-timeout:3000validation-timeout:3000max-lifetime:60000maximum-pool-size:20minimum-idle:1

    YAML Advantages

    YAML is a hierarchical format that removes many repeated prefixes and offers greater readability.

    Using Environment Variables

    In configuration files, we often use environment variables with default values:

    Environment Variables
    app:db:host: ${DB_HOST:localhost}user: ${DB_USER:root}password: ${DB_PASSWORD:password}

    The syntax ${DB_HOST:localhost} means: first check the environment variable DB_HOST. If defined, use its value; otherwise, use localhost.

    Running with environment variables:

    Command Line
    $ DB_HOST=10.0.1.123 DB_USER=prod DB_PASSWORD=xxxx java-jar xxx.jar

    logback-spring.xml - Logging Configuration

    This is the Spring Boot logback configuration file. A standard configuration looks like this:

    logback-spring.xml
    <?xml version="1.0" encoding="UTF-8"?><configuration><includeresource="org/springframework/boot/logging/logback/defaults.xml"/><appendername="CONSOLE"class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${CONSOLE_LOG_PATTERN}</pattern><charset>utf8</charset></encoder></appender><appendername="APP_LOG"class="ch.qos.logback.core.rolling.RollingFileAppender"><encoder><pattern>${FILE_LOG_PATTERN}</pattern><charset>utf8</charset></encoder><file>app.log</file><rollingPolicyclass="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"><maxIndex>1</maxIndex><fileNamePattern>app.log.%i</fileNamePattern></rollingPolicy><triggeringPolicyclass="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"><MaxFileSize>1MB</MaxFileSize></triggeringPolicy></appender><rootlevel="INFO"><appender-refref="CONSOLE"/><appender-refref="APP_LOG"/></root></configuration>

    Note: The static and templates directories are placed directly in src/main/resources (not src/main/webapp) because a dedicated webapp directory is no longer needed in Spring Boot.

    Source Code Directory Structure

    Java Source Structure
    src/main/java
    └── com
    └── itranswarp
    └── learnjava
    ├── Application.java
    ├── entity
    │   └── User.java
    ├── service
    │   └── UserService.java
    └── web
    └── UserController.java

    Important!

    Spring Boot requires that the startup class containing main() method must be placed in the root package. Sub-packages like entity, service, web should be under the root package.

    Application.java - The Main Class

    Application.java
    @SpringBootApplicationpublicclassApplication{publicstaticvoidmain(String[] args)throwsException{SpringApplication.run(Application.class, args);}}

    Starting a Spring Boot application requires only one line of code and one annotation @SpringBootApplication, which actually includes:

    @SpringBootConfiguration
    @Configuration
    @EnableAutoConfiguration
    @AutoConfigurationPackage
    @ComponentScan

    This annotation is equivalent to enabling automatic configuration and automatic scanning.

    pom.xml - Maven Configuration

    pom.xml
    <project...><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.0</version></parent><modelVersion>4.0.0</modelVersion><groupId>com.itranswarp.learnjava</groupId><artifactId>springboot-hello</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><java.version>17</java.version><pebble.version>3.2.0</pebble.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- Pebble View Template --><dependency><groupId>io.pebbletemplates</groupId><artifactId>pebble-spring-boot-starter</artifactId><version>${pebble.version}</version></dependency><!-- JDBC Driver --><dependency><groupId>org.hsqldb</groupId><artifactId>hsqldb</artifactId></dependency></dependencies></project>

    Best Practice

    Using Spring Boot, spring-boot-starter-parent inheritance is strongly recommended because it allows you to incorporate Spring Boot's built-in configurations. Version numbers don't need to be specified for Spring Boot managed dependencies.

    Adding WebMvcConfigurer Bean

    Add Pebble configuration to application.yml:

    Pebble Configuration
    pebble:# Default is ".peb", change to "":suffix:# Disable template caching during development:cache:false

    Modify Application to add WebMvcConfigurer Bean:

    WebMvcConfigurer Bean
    @SpringBootApplicationpublicclassApplication{...@BeanWebMvcConfigurercreateWebMvcConfigurer(@AutowiredHandlerInterceptor[] interceptors){returnnewWebMvcConfigurer(){@OverridepublicvoidaddResourceHandlers(ResourceHandlerRegistry registry){// Map path `/static/` to classpath:
    registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");}};}}

    Running the Application

    Run Application and observe the Spring Boot startup logs:

    Spring Boot Startup Logs
      .   ____          _            __ _ _
    /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
    \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
    '  |____| .__|_| |_|_| |_\__, | / / / /
    =========|_|==============|___/=/_/_/_/
    :: Spring Boot ::                (v3.0.0)
    2022-11-25T10:49:31.100+08:00  INFO 13105 --- [main] c.i.learnjava.Application : Starting Application...
    2022-11-25T10:49:32.404+08:00  INFO 13105 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
    2022-11-25T10:49:32.423+08:00  INFO 13105 --- [main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
    2022-11-25T10:49:32.669+08:00  INFO 13105 --- [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
    2022-11-25T10:49:32.998+08:00  INFO 13105 --- [main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection
    2022-11-25T10:49:33.619+08:00  INFO 13105 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080
    2022-11-25T10:49:33.637+08:00  INFO 13105 --- [main] c.i.learnjava.Application : Started Application in 3.151 seconds

    Success!

    When you see Started Application in xxx seconds, your Spring Boot application has started successfully. Access it at localhost:8080.

    Understanding AutoConfiguration

    Where were the data source, declarative transaction, and JdbcTemplate created? These automatically created beans are a feature of Spring Boot: AutoConfiguration.

    When importing spring-boot-starter-jdbc:

    • DataSourceAutoConfiguration - Creates DataSource from application.yml
    • DataSourceTransactionManagerAutoConfiguration - Creates JDBC transaction manager
    • JdbcTemplateAutoConfiguration - Creates JdbcTemplate

    When importing spring-boot-starter-web:

    • ServletWebServerFactoryAutoConfiguration - Creates embedded Tomcat
    • DispatcherServletAutoConfiguration - Creates DispatcherServlet
    • HttpEncodingAutoConfiguration - Creates CharacterEncodingFilter
    • WebMvcAutoConfiguration - Creates MVC-related beans

    How Conditional Configuration Works

    Spring Boot uses Spring's Conditional feature for auto-configuration. Here's how JdbcTemplateAutoConfiguration works:

    JdbcTemplateAutoConfiguration
    @Configuration(proxyBeanMethods =false)@ConditionalOnClass({DataSource.class,JdbcTemplate.class})@ConditionalOnSingleCandidate(DataSource.class)@AutoConfigureAfter(DataSourceAutoConfiguration.class)@EnableConfigurationProperties(JdbcProperties.class)@Import({JdbcTemplateConfiguration.class,NamedParameterJdbcTemplateConfiguration.class})publicclassJdbcTemplateAutoConfiguration{}

    The actual creation is done by JdbcTemplateConfiguration:

    JdbcTemplateConfiguration
    @Configuration(proxyBeanMethods =false)@ConditionalOnMissingBean(JdbcOperations.class)classJdbcTemplateConfiguration{@Bean@PrimaryJdbcTemplatejdbcTemplate(DataSource dataSource,JdbcProperties properties){JdbcTemplate jdbcTemplate =newJdbcTemplate(dataSource);JdbcProperties.Template template = properties.getTemplate();
    jdbcTemplate.setFetchSize(template.getFetchSize());
    jdbcTemplate.setMaxRows(template.getMaxRows());if(template.getQueryTimeout()!=null){
    jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());}return jdbcTemplate;}}

    Key Insight

    If you create your own JdbcTemplate bean, Spring Boot will not create a duplicate one due to @ConditionalOnMissingBean.

    Summary

    Spring Boot is a suite of ready-to-use components based on Spring that allows us to quickly build a complete application with minimal configuration and code.

    Spring Boot has a very powerful AutoConfiguration feature, which is achieved through automatic scanning and conditional configuration.

    The startup class with @SpringBootApplication must be in the root package for component scanning to work correctly.

    💬 Comments & Discussion