Spring 4 Hello World Example With Java Configuration

Spring framework is a lightweight, modular and non-invasive application framework for building java applications. Older versions of spring framework required verbose XML files for configuring java beans. Since spring 3.0, bean configuration can be specified in java code itself and is commonly know as java config(java configuration). Now it is possible to build a full fledged spring application without a single line of XML!

The following example shows how java config and annotations can be used to build a simple hello world program using spring dependency injection. This example requires the following tools,

  • Java 8
  • Spring framework 4.3.7.RELEASE or above
  • Spring Tool Suite (With Gradle 3.4) 3.8.4.RELEASE or above

Ensure that you have java 8 on your system. To verify availability of java 8, run the following command from console,

java -version

If you don't have java 8 installed, download it from here.

Step 1: Create a simple gradle java project from spring tool suite. Delete any java classes created under src/main/java and src/test/java folders.

Step 2: Create a java class to represent our service layer. In this example, we will write a very simple service class named HelloWorldService(in demo package) with a simple method to write "Hello World!" to the console. Save HelloWorldService.java in src/main/java/demo folder. Note the use of @Service annotation which tells framework that this is a component and its lifecycle must be managed by spring IoC container.

HelloWorldService.java

package demo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Service;

@Service
public class HelloWorldService {
    public static final Log LOG = LogFactory.getLog(HelloWorldService.class);

    public void sayHello() {
        LOG.info("Hello World!");
    }
}

Step 3: Create a java class to represent entry point to our application. In this example, we will name it as SpringDemoApp under demo package. We will also annotate this class with @Configuration and @ComponentScan. The @Configuration tells the spring framework that SpringDemoApp contains bean definitions and @ComponentScan tells spring framework to look for beans in the project and register them automatically. Spring looks for annotations such as @Component, @Service and even @Configuration to identify classes which are managed bean components. Hence SpringDemoApp and HelloWorldService are automatically added as managed beans.

Note the use of @Autowired on the HelloWorldService member variable in SpringDemoApp. This tells the spring framework to provide an instance of HelloWorldService if it has one. Spring already knows about the HelloWorldService bean through the @Service annotation. When the main method is called, SpringDemoApp initializes the spring framework which in turn loads all the beans and processes any dependency injection required. We call the injected HelloWorldService to print hello world message on the console.

SpringDemoApp.java

package demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class SpringDemoApp {

    // This will be created and injected by spring IoC
    @Autowired
    private HelloWorldService hello;

    public static void main(String[] args) {
        ApplicationContext context = getAppContext();

        SpringDemoApp app = context.getBean("springDemoApp", SpringDemoApp.class);
        app.runApp();
    }

    public void runApp() {
        hello.sayHello();
    }

    public static ApplicationContext getAppContext() {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringDemoApp.class);
        return context;
    }
}

The default name of a bean configured by spring is the name of the class with first letter changed to lowercase. This is why getBean uses the name "springDemoApp" to refer to SpringDemoApp bean instance. Note that we have used a package name for our classes. Component scan has issues with default package and if you create your classes without a package you may see the following error,

Caused by: java.lang.IllegalArgumentException: @EnableAsync annotation metadata was not injected

Step 4: Replace the build.gradle with the following one. We simply indicate that this is a runnable Java application with dependency on spring-context 4.3.7.RELEASE. This in turn transitively adds spring-core and spring-beans projects.

build.gradle

apply plugin: 'application'

mainClassName = "demo.SpringDemoApp"

repositories {
    jcenter()
}

dependencies {
    compile 'org.springframework:spring-context:4.3.7.RELEASE'
}

Step 5: From the gradle tasks window in STS, execute the gradle application/run task. If everything goes well, Hello World! will be printed on the console.

In the above example we have used @Configuration, but it is not needed since we haven't programmatically defined any beans. We need it if we define our HelloWorldService using the @Bean annotation in the SpringDemoApp.java as shown below,

@Bean
public HelloWorldService getHello() {
    return new HelloWorldService();
}

In this case we can remove @Service and @ComponentScan annotations. Why? @Configuration already tells spring that SpringDemoApp is a bean and @Bean tells it that HelloWorldService is a bean. Hence we don't need @Service and @ComponentSCan. It is also possible to use a mix of @ComponentScan and @Bean annotations in a single program.

The following picture shows the hello world project structure in spring tool suite,

spring-4-hello-world-example

How to Write a Minimal Spring Application

Spring framework and spring boot are very popular these days for building enterprise java applications. Over years spring framework ecosystem has grown considerably. Almost all infrastructural component needs are now provided by spring framework. Spring boot simplifies development further by providing a set of defaults and automatic configuration.

Under the hood, spring framework is powered by an IoC container providing dependency injection. Spring manages the dependencies between objects providing object instances automatically whenever needed. A set of libraries known as the core container provides the basic features for a spring application. Core container consists of spring-core, spring-beans, spring-context, spring-context-support and spring-expression.

The minimal set of libraries required to build a spring application using dependency injection include spring-core and spring-beans. Our aim here is to build the simplest spring application and hence we have excluded spring-context. However for any non-trivial spring application, spring-context is needed since it contains features such as annotation configuration, java configuration and an enhanced bean factory called application context.

The following hello world spring application demo uses,

  • Java 8
  • Spring framework 4.7.3 (spring-core and spring-beans)
  • Gradle 3.4
  • Spring Tool Suite IDE

Writing a Minimalist Spring Framework App

Install Spring Tool Suite (STS) and create a simple gradle java application. Add the following classes under src/main/java folder. To demonstrate the use of dependency injection, our application has two classes. SpringCoreDemoApp contains the entry point to the application (main method). It uses an instance of a class HelloWord to print a message on the console using apache commons logging (it is always available to spring applications as a transitive dependency). Since HelloWorld is created and supplied by spring, we just provide a setter and getter method for the same in SpringCoreDemoApp. Note that we are using the IoC container to provide an instance of SpringCoreDemoApp itself.

HelloWorld.java

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class HelloWorld {
    public static final Log LOG = LogFactory.getLog(HelloWorld.class);

    public void sayHello() {
        LOG.info("Hello World!");
    }
}

SpringCoreDemoApp.java

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;

public class SpringCoreDemoApp {
    // This will be created and injected by spring IoC
    private HelloWorld hello;

    public HelloWorld getHello() {
        return hello;
    }

    public void setHello(HelloWorld hello) {
        this.hello = hello;
    }

    public static void main(String[] args) {
        BeanFactory beanFactory = getBeanFactory();

        SpringCoreDemoApp app = beanFactory.getBean("app", SpringCoreDemoApp.class);
        app.runApp();
    }

    public void runApp() {
        hello.sayHello();
    }

    public static BeanFactory getBeanFactory() {
        DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf);
        reader.loadBeanDefinitions(new ClassPathResource("beans.xml", SpringCoreDemoApp.class));
        return xbf;
    }

}

We then write a simple XML file (beans.xml) configuring all the beans that we expect spring IoC container to manage. We also specify the dependency of SpringCoreDemoApp on HelloWorld. This file needs to be in classpath and hence we create it under src/main/resources. Gradle adds this folder automatically to the classpath.

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <bean id="helloWorld" class="HelloWorld">
    </bean>

    <bean id="app" class="SpringCoreDemoApp">
        <property name="hello" ref="helloWorld"></property>
    </bean>

</beans>

Finally we modify the gradle build file to specify the dependency on spring-beans. The spring-core is automatically added since it is a dependency of spring-beans. The only other library loaded through transitive dependency is apache commons logging 1.2.

build.gradle

apply plugin: 'application'

mainClassName = "SpringCoreDemoApp"

repositories {
    jcenter()
}

dependencies {
    compile 'org.springframework:spring-beans:4.3.7.RELEASE'

}

From the STS IDE, open gradle tasks window and execute the run task under application group. You will see Hello World! printed on the console. We have built a spring application using the minimal set of libraries possible!

How to Send Email From Spring Boot Applications

Spring framework provides a number of library classes for sending email. It provides an easy to use mail interface called JavaMailSender built on top of the JavaMail API. JavaMail API is a messaging framework available as part of Java SE and Java EE platforms.

Spring boot provides a starter project and auto configuration for JavaMailSender API. To use spring email features, just add the starter project spring-boot-starter-mail as a dependency on your project. Then configure the required spring.mail properties in the application.properties. With just these 2 steps, you can send email from spring boot applications.

The spring-boot-starter-mail adds the following dependent libraries to your project,

  • javax.mail
  • spring-context
  • spring-context-support
  • spring-boot-starter

Following are the configurable spring.mail properties,

  • spring.mail.host - Email server host
  • spring.mail.port - Email port
  • spring.mail.username - Email server username
  • spring.mail.password - Email server password
  • spring.mail.jndi-name - Session jndi name. If set overrides other properties
  • spring.mail.protocol - Protocol for email (smtp)
  • spring.mail.properties.* - JavaMail session properties

For a complete list of spring.mail.properties.* see this link. For example, the property spring.mail.properties.mail.smtp.timeout can be set to configure the socket read timeout.

Sending a Simple Text Email in Spring Boot Apps

Read this spring boot and sts tutorial first if you don't know how to create spring boot applications using spring tool suite. Note that all our sample projects use gradle build system (which is superior to maven).

Let us build a simple spring boot application which sends an email using a gmail account whenever a url is accessed. For simplicity, we will write the email functionality in the controller itself.

Step 1: Create a spring boot starter project with gradle support and web starter library. See this spring boot tutorial if you are not familiar with this step.

Step 2: Configure email properties in application.properties (src/main/resources).

spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=username
spring.mail.password=password
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=5000
spring.mail.properties.mail.smtp.writetimeout=5000

Step 3: Modify the build.gradle to add the spring-boot-starter-mail dependency.

buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-mail')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

Step 4: Write a simple controller class and use JavaMailSender and MimeMessageHelper to send simple text email.

package com.quickprogrammingtips.springboot;

import javax.mail.internet.MimeMessage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SimpleEmailController {
    
    @Autowired
    private JavaMailSender sender;

    @RequestMapping("/simpleemail")
    @ResponseBody
    String home() {
        try {
            sendEmail();
            return "Email Sent!";
        }catch(Exception ex) {
            return "Error in sending email: "+ex;
        }
    }

    private void sendEmail() throws Exception{
        MimeMessage message = sender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message);
        
        helper.setTo("set-your-recipient-email-here@gmail.com");
        helper.setText("How are you?");
        helper.setSubject("Hi");
        
        sender.send(message);
    }
}

Step 5: Since we are using gmail to send email, we need to enable "less secure access" on the gmail account. Log out from all the Google accounts from the browser. Login in to the gmail account configured in application.properties. Then click on this link and turn on access for less secure apps. In some cases, you may need to access this link as well! If you don't follow these steps, you will get the following error,

org.springframework.mail.MailAuthenticationException: Authentication failed; nested exception is javax.mail.AuthenticationFailedException: 534-5.7.14 Please log in via your web browser and 534-5.7.14 then try again.

Less secure sign in option is required for Yahoo mail as well. This article contains configuration details for other email providers such as Yahoo mail, sendgrid and mailgun.

Step 6: Access the URL http://localhost:8080/simpleemail to send email. Check the recipient email account to verify that email is working.

Sending Email Attachments in Spring Boot Apps

The following controller sends an email with a picture attached to it. We load a cat picture from the classpath. Before running this example, ensure that you have a cat.jpg file copied to the src/main/resources folder.

package com.quickprogrammingtips.springboot;

import javax.mail.internet.MimeMessage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SimpleEmailController2 {
    
    @Autowired
    private JavaMailSender sender;

    @RequestMapping("/simpleemail2")
    @ResponseBody
    String home() {
        try {
            sendEmail();
            return "Email Sent!";
        }catch(Exception ex) {
            return "Error in sending email: "+ex;
        }
    }

    private void sendEmail() throws Exception{
        MimeMessage message = sender.createMimeMessage();
        
        // Enable the multipart flag!
        MimeMessageHelper helper = new MimeMessageHelper(message,true);
        
        helper.setTo("set-your-recipient-email-here@gmail.com");
        helper.setText("How are you?");
        helper.setSubject("Hi");
        
        ClassPathResource file = new ClassPathResource("cat.jpg");
        helper.addAttachment("cat.jpg", file);
        
        sender.send(message);
    }
}

Note that the above controller is configured on a different path(/simpleemail2). Also note the multipart flag passed as the second parameter to MimeMessageHelper constructor. If this is not set to true, you will get the following error,

java.lang.IllegalStateException: Not in multipart mode - create an appropriate MimeMessageHelper via a constructor that takes a 'multipart' flag if you need to set alternative texts or add inline elements or attachments.

Sending Email with Inline Resources in Spring Boot Apps

Before running this example, ensure that you have a cat.jpg file copied to the src/main/resources folder. The following controller embeds the cat picture to the email as an inline resource. The recipient will see the resource embedded in the email (not as an attachment). Note that resources must be added to the helper after adding the text.

package com.quickprogrammingtips.springboot;

import javax.mail.internet.MimeMessage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SimpleEmailController3 {

    @Autowired
    private JavaMailSender sender;

    @RequestMapping("/simpleemail3")
    @ResponseBody
    String home() {
        try {
            sendEmail();
            return "Email Sent!";
        } catch (Exception ex) {
            return "Error in sending email: " + ex;
        }
    }

    private void sendEmail() throws Exception {
        MimeMessage message = sender.createMimeMessage();

        // Enable the multipart flag!
        MimeMessageHelper helper = new MimeMessageHelper(message, true);

        helper.setTo("set-your-recipient-email-here@gmail.com");
        helper.setText("<html><body>Here is a cat picture! <img src='cid:id101'/><body></html>", true);
        helper.setSubject("Hi");

        ClassPathResource file = new ClassPathResource("cat.jpg");
        helper.addInline("id101", file);

        sender.send(message);
    }
}

Sending Email with Velocity Templates in Spring Boot Apps

In any non trivial email application, it is important to use templates. This enables separation of the structure of an email from the actual data that is sent in each email. For example, you can create a welcome email template and then substitute just the name placeholder in the template to send personalized email to each user. The following example uses the popular velocity templating engine to send email.

Velocity is not an actively maintained project and hence as of spring 4.3 velocity engine is deprecated in favor of freemarker. We will ignore this warning in the following program. Also note that velocity auto configuration works only till spring boot 1.4.5.RELEASE. If you are using spring boot version 1.5.0.RELEASE or above and need auto configuration, you should switch to freemarker.

Let us first create a simple velocity template for welcome message in the folder src/main/resources with the name welcome.vm.

<html>
    <body>
        <h3>Hi ${user}, welcome to the application!</h3>
    </body>
</html>

Add spring-boot-starter-velocity to the build.gradle file and change the spring boot version to 1.4.5.RELEASE,

buildscript {
    ext {
        springBootVersion = '1.4.5.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-mail')
    compile('org.springframework.boot:spring-boot-starter-velocity')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

Finally let us create a controller to process velocity template and send the email,

package com.quickprogrammingtips.springboot;

import java.util.HashMap;
import java.util.Map;

import javax.mail.internet.MimeMessage;

import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.velocity.VelocityEngineUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SimpleEmailController4 {

    @Autowired
    private JavaMailSender sender;

    @Autowired
    private VelocityEngine velocityEngine;

    @RequestMapping("/simpleemail4")
    @ResponseBody
    String home() {
        try {
            sendEmail();
            return "Email Sent!";
        } catch (Exception ex) {
            return "Error in sending email: " + ex;
        }
    }

    private void sendEmail() throws Exception {
        MimeMessage message = sender.createMimeMessage();

        MimeMessageHelper helper = new MimeMessageHelper(message);

        Map<String, Object> model = new HashMap();
        model.put("user", "qpt");
        String text = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, "welcome.vm", model);

        helper.setTo("set-your-recipient-email-here@gmail.com");
        helper.setText(text, true); // set to html
        helper.setSubject("Hi");

        sender.send(message);
    }
}

Sending Email with FreeMarker Templates in Spring Boot Apps

Spring boot provides a starter project for using freemarker templates. Just add the spring-boot-starter-freemarker dependency to your project.

Let us start by creating a simple freemarker template named welcome.ftl in the src/main/resources folder,

<html>
    <body>
        <h3>Hi ${user}, welcome to the application!</h3>
    </body>
</html>

Modify the build.gradle to add a dependency on spring-boot-starter-freemarker. Note that we are using the latest version of spring boot (1.5.2.RELEASE),

buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-mail')
    compile('org.springframework.boot:spring-boot-starter-freemarker')
 
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

Finally create a controller to process freemarker template and send an email using JavaMailSender,

package com.quickprogrammingtips.springboot;

import java.util.HashMap;
import java.util.Map;

import javax.mail.internet.MimeMessage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import freemarker.template.Configuration;
import freemarker.template.Template;

@Controller
public class SimpleEmailController5 {

    @Autowired
    private JavaMailSender sender;

    @Autowired
    private Configuration freemarkerConfig;

    @RequestMapping("/simpleemail5")
    @ResponseBody
    String home() {
        try {
            sendEmail();
            return "Email Sent!";
        } catch (Exception ex) {
            return "Error in sending email: " + ex;
        }
    }

    private void sendEmail() throws Exception {
        MimeMessage message = sender.createMimeMessage();

        MimeMessageHelper helper = new MimeMessageHelper(message);

        Map<String, Object> model = new HashMap();
        model.put("user", "qpt");
        
        // set loading location to src/main/resources
        // You may want to use a subfolder such as /templates here
        freemarkerConfig.setClassForTemplateLoading(this.getClass(), "/");
        
        Template t = freemarkerConfig.getTemplate("welcome.ftl");
        String text = FreeMarkerTemplateUtils.processTemplateIntoString(t, model);

        helper.setTo("set-your-recipient-email-here@gmail.com");
        helper.setText(text, true); // set to html
        helper.setSubject("Hi");

        sender.send(message);
    }
}

Using Log4J2 with Spring Boot

Spring boot starter projects enable quick development boot applications.  Starter projects has a default dependency on spring-boot-starter-logging. This library configures logback as the default logging implementation. However some may prefer to use log4J2 instead of Logback to write application logs. Both works with the SLF4J facade API and hence it is possible to switch from Logback to log4j2 without changing any code.

Modify the gradle file of your spring boot project to use log4j2 instead of logback implementation,

  • Exclude the spring-boot-starter-logging dependency.
  • Add an explicit dependency on spring-boot-starter-log4j2.

Sample Gradle File to Configure Log4J2 (build.gradle)

buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter-web') {
       exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'   
    }
    
    compile('org.springframework.boot:spring-boot-starter-log4j2')
    
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

Sample POM File to Configure Log4J2 (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.quickprogrammingtips.springboot</groupId>
    <artifactId>logdemo2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>logdemo2</name>
    <description>Spring Boot Demo</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath />
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Sample Spring Boot Controller Class (SLF4J Logging)

package com.quickprogrammingtips.springboot;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloWorldController {
    private static final Logger LOG = LoggerFactory.getLogger(HelloWorldController.class);

    @RequestMapping("/hello")
    @ResponseBody
    String home() {
        LOG.warn("sending hello world response...");
        return "Hello World!";
    }
}

The logging configuration in application.properties works even when you replace logback with log4j2. However if you want to fine tune log4j2 logging, you can add log4j2.xml or log4j2-spring.xml to the src/main/resources folder of your spring boot project.

Why Replace Logback with Log4J2?

Logback and log4j2 are good logging implementations designed to replace the older log4J implementation. However log4j2 has some advantages over logback,

  • Log4j2 is designed as an audit logging framework. During reconfiguration, events are retained and exceptions from appenders can be visible to the application.
  • Uses high speed asynchronous implementation. In multithreaded applications up to 10x performance compared to logback.
  • Simple and easy to use plugin system and support for message objects.
  • Advanced support for filters, custom log levels, layouts and Java 5 concurrency support.

Dependencies of Spring-boot-starter-logging

  • logback-classic - An SLF4J native logging implementation
  • jcl-over-slf4j - Apache Commons Logging implemented over SLF4J
  • jul-to-slf4j - JDK logging to SLF4J bridge
  • log4j-over-slf4j - Log4J implemented over SLF4J

Dependencies of Spring-boot-starter-log4j2

  • log4j-slf4j-impl - SLF4J logging binding of log4j2 core
  • log4j-api - Log4j2 API
  • log4j-core - Log4j2 implementation
  • jcl-over-slf4j - Apache Commons Logging implemented over SLF4J
  • jul-to-slf4j - JDK logging to SLF4J bridge

JDK Logging (JUL) in Spring Boot Applications

When writing spring boot applications, it is recommended to use SLF4J API for logging. However it is also possible to use the logging implementation provided by JDK directly. However JDK logging implementation (JUL) provides a different set of logging levels compared to SLF4J or Log4J. The logging levels used by JDK library are,

FINEST > FINER > FINE > CONFIG > INFO > WARNING > SEVERE

SLF4J and other logging implementations such as Log4j use the following log levels,

ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF

Spring boot's SLF4J bridge maps the log levels to the JDK logging levels using the following table.

FINEST  => TRACE
FINER   => DEBUG
FINE    => DEBUG
CONFIG  => INFO
INFO    => INFO
WARNING => WARN
SEVERE  => ERROR

Spring boot propagates the logging configuration set in application.properties to JDK logging classes as well. Hence even if you are using both logging libraries in your application (JDK logging and Logback with SLF4J), the same set of configuration in application.properties can be used! For example, the following configuration sets the root logger to WARN (WARNING) level and all application classes to TRACE (FINEST). Note that you need to use the log levels defined by SLF4J.

# application.properties values
logging.level.root=WARN
logging.level.com.quickprogrammingtips=TRACE

To configure only the logback system, create a custom logback.xml or logback-spring.xml. To configure only the JDK logging classes (JUL), create a custom logging.properties file. These files should be created in src/main/resources folder.

Using JDK Logging (JUL) in Spring Boot Classes

Here is a simple spring boot controller which uses the JDK logging library,

package com.quickprogrammingtips.springboot;

import java.util.logging.Logger;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloWorld2Controller {
    private static final Logger LOG = Logger.getLogger(HelloWorld2Controller.class.getName());

    @RequestMapping("/hello2")
    @ResponseBody
    String home() {
        LOG.finest("Sending hello world message!");
        return "Hello World!";
    }
}