Appearance
Spring Boot: “How-to” Guides - Part2
Previous section: Logging.
1. Data Access
Spring Boot includes a number of starters for working with data sources. This section answers questions related to doing so.
1.1. Configure a Custom DataSource
To configure your own DataSource, define a @Bean of that type in your configuration. Spring Boot reuses your DataSource anywhere one is required, including database initialization. If you need to externalize some settings, you can bind your DataSource to the environment (see “Third-party Configuration”).
The following example shows how to define a data source in a bean:
Java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "app.datasource")
public SomeDataSource dataSource() {
return new SomeDataSource();
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
The following example shows how to define a data source by setting properties:
Properties
app.datasource.url=jdbc:h2:mem:mydb
app.datasource.username=sa
app.datasource.pool-size=30Assuming that SomeDataSource has regular JavaBean properties for the URL, the username, and the pool size, these settings are bound automatically before the DataSource is made available to other components.
Spring Boot also provides a utility builder class, called DataSourceBuilder, that can be used to create one of the standard data sources (if it is on the classpath). The builder can detect the one to use based on what is available on the classpath. It also auto-detects the driver based on the JDBC URL.
The following example shows how to create a data source by using a DataSourceBuilder:
Java
import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
To run an app with that DataSource, all you need is the connection information. Pool-specific settings can also be provided. Check the implementation that is going to be used at runtime for more details.
The following example shows how to define a JDBC data source by setting properties:
Properties
app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30However, there is a catch. Because the actual type of the connection pool is not exposed, no keys are generated in the metadata for your custom DataSource and no completion is available in your IDE (because the DataSource interface exposes no properties). Also, if you happen to have Hikari on the classpath, this basic setup does not work, because Hikari has no url property (but does have a jdbcUrl property). In that case, you must rewrite your configuration as follows:
Properties
app.datasource.jdbc-url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30You can fix that by forcing the connection pool to use and return a dedicated implementation rather than DataSource. You cannot change the implementation at runtime, but the list of options will be explicit.
The following example shows how create a HikariDataSource with DataSourceBuilder:
Java
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
public HikariDataSource dataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
You can even go further by leveraging what DataSourceProperties does for you — that is, by providing a default embedded database with a sensible username and password if no URL is provided. You can easily initialize a DataSourceBuilder from the state of any DataSourceProperties object, so you could also inject the DataSource that Spring Boot creates automatically. However, that would split your configuration into two namespaces: url, username, password, type, and driver on spring.datasource and the rest on your custom namespace (app.datasource). To avoid that, you can redefine a custom DataSourceProperties on your custom namespace, as shown in the following example:
Java
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@Primary
@ConfigurationProperties("app.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@ConfigurationProperties("app.datasource.configuration")
public HikariDataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
This setup puts you in sync with what Spring Boot does for you by default, except that a dedicated connection pool is chosen (in code) and its settings are exposed in the app.datasource.configuration sub namespace. Because DataSourceProperties is taking care of the url/jdbcUrl translation for you, you can configure it as follows:
Properties
app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.configuration.maximum-pool-size=30Note:Spring Boot will expose Hikari-specific settings to
spring.datasource.hikari. This example uses a more genericconfigurationsub namespace as the example does not support multiple datasource implementations.
Tip:Because your custom configuration chooses to go with Hikari,
app.datasource.typehas no effect. In practice, the builder is initialized with whatever value you might set there and then overridden by the call to.type().
See “Configure a DataSource” in the “Spring Boot features” section and the DataSourceAutoConfiguration class for more details.
1.2. Configure Two DataSources
If you need to configure multiple data sources, you can apply the same tricks that are described in the previous section. You must, however, mark one of the DataSource instances as @Primary, because various auto-configurations down the road expect to be able to get one by type.
If you create your own DataSource, the auto-configuration backs off. In the following example, we provide the exact same feature set as the auto-configuration provides on the primary data source:
Java
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration(proxyBeanMethods = false)
public class MyDataSourcesConfiguration {
@Bean
@Primary
@ConfigurationProperties("app.datasource.first")
public DataSourceProperties firstDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
@ConfigurationProperties("app.datasource.first.configuration")
public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
@Bean
@ConfigurationProperties("app.datasource.second")
public BasicDataSource secondDataSource() {
return DataSourceBuilder.create().type(BasicDataSource.class).build();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Note:
firstDataSourcePropertieshas to be flagged as@Primaryso that the database initializer feature uses your copy (if you use the initializer).
Both data sources are also bound for advanced customizations. For instance, you could configure them as follows:
Properties
app.datasource.first.url=jdbc:mysql://localhost/first
app.datasource.first.username=dbuser
app.datasource.first.password=dbpass
app.datasource.first.configuration.maximum-pool-size=30
app.datasource.second.url=jdbc:mysql://localhost/second
app.datasource.second.username=dbuser
app.datasource.second.password=dbpass
app.datasource.second.max-total=30You can apply the same concept to the secondary DataSource as well, as shown in the following example:
Java
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration(proxyBeanMethods = false)
public class MyCompleteDataSourcesConfiguration {
@Bean
@Primary
@ConfigurationProperties("app.datasource.first")
public DataSourceProperties firstDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
@ConfigurationProperties("app.datasource.first.configuration")
public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
@Bean
@ConfigurationProperties("app.datasource.second")
public DataSourceProperties secondDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@ConfigurationProperties("app.datasource.second.configuration")
public BasicDataSource secondDataSource(
@Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource.class).build();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
The preceding example configures two data sources on custom namespaces with the same logic as Spring Boot would use in auto-configuration. Note that each configuration sub namespace provides advanced settings based on the chosen implementation.
1.3. Use Spring Data Repositories
Spring Data can create implementations of @Repository interfaces of various flavors. Spring Boot handles all of that for you, as long as those @Repositories are included in one of the auto-configuration packages, typically the package (or a sub-package) of your main application class that is annotated with @SpringBootApplication or @EnableAutoConfiguration.
For many applications, all you need is to put the right Spring Data dependencies on your classpath. There is a spring-boot-starter-data-jpa for JPA, spring-boot-starter-data-mongodb for Mongodb, and various other starters for supported technologies. To get started, create some repository interfaces to handle your @Entity objects.
Spring Boot determines the location of your @Repository definitions by scanning the auto-configuration packages. For more control, use the @Enable…Repositories annotations from Spring Data.
For more about Spring Data, see the Spring Data project page.
1.4. Separate @Entity Definitions from Spring Configuration
Spring Boot determines the location of your @Entity definitions by scanning the auto-configuration packages. For more control, use the @EntityScan annotation, as shown in the following example:
Java
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = City.class)
public class MyApplication {
// ...
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
1.5. Configure JPA Properties
Spring Data JPA already provides some vendor-independent configuration options (such as those for SQL logging), and Spring Boot exposes those options and a few more for Hibernate as external configuration properties. Some of them are automatically detected according to the context so you should not have to set them.
The spring.jpa.hibernate.ddl-auto is a special case, because, depending on runtime conditions, it has different defaults. If an embedded database is used and no schema manager (such as Liquibase or Flyway) is handling the DataSource, it defaults to create-drop. In all other cases, it defaults to none.
The dialect to use is detected by the JPA provider. If you prefer to set the dialect yourself, set the spring.jpa.database-platform property.
The most common options to set are shown in the following example:
Properties
spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy
spring.jpa.show-sql=trueIn addition, all properties in spring.jpa.properties.* are passed through as normal JPA properties (with the prefix stripped) when the local EntityManagerFactory is created.
Warning
You need to ensure that names defined under
spring.jpa.properties.*exactly match those expected by your JPA provider. Spring Boot will not attempt any kind of relaxed binding for these entries.For example, if you want to configure Hibernate’s batch size you must use
spring.jpa.properties.hibernate.jdbc.batch_size. If you use other forms, such asbatchSizeorbatch-size, Hibernate will not apply the setting.
Note:If you need to apply advanced customization to Hibernate properties, consider registering a
HibernatePropertiesCustomizerbean that will be invoked prior to creating theEntityManagerFactory. This takes precedence to anything that is applied by the auto-configuration.
1.6. Configure Hibernate Naming Strategy
Hibernate uses two different naming strategies to map names from the object model to the corresponding database names. The fully qualified class name of the physical and the implicit strategy implementations can be configured by setting the spring.jpa.hibernate.naming.physical-strategy and spring.jpa.hibernate.naming.implicit-strategy properties, respectively. Alternatively, if ImplicitNamingStrategy or PhysicalNamingStrategy beans are available in the application context, Hibernate will be automatically configured to use them.
By default, Spring Boot configures the physical naming strategy with CamelCaseToUnderscoresNamingStrategy. Using this strategy, all dots are replaced by underscores and camel casing is replaced by underscores as well. Additionally, by default, all table names are generated in lower case. For example, a TelephoneNumber entity is mapped to the telephone_number table. If your schema requires mixed-case identifiers, define a custom CamelCaseToUnderscoresNamingStrategy bean, as shown in the following example:
Java
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyHibernateConfiguration {
@Bean
public CamelCaseToUnderscoresNamingStrategy caseSensitivePhysicalNamingStrategy() {
return new CamelCaseToUnderscoresNamingStrategy() {
@Override
protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
return false;
}
};
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
If you prefer to use Hibernate 5’s default instead, set the following property:
Properties
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImplAlternatively, you can configure the following bean:
Java
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {
@Bean
PhysicalNamingStrategyStandardImpl caseSensitivePhysicalNamingStrategy() {
return new PhysicalNamingStrategyStandardImpl();
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
See HibernateJpaAutoConfiguration and JpaBaseConfiguration for more details.
1.7. Configure Hibernate Second-Level Caching
Hibernate second-level cache can be configured for a range of cache providers. Rather than configuring Hibernate to lookup the cache provider again, it is better to provide the one that is available in the context whenever possible.
To do this with JCache, first make sure that org.hibernate:hibernate-jcache is available on the classpath. Then, add a HibernatePropertiesCustomizer bean as shown in the following example:
Java
import org.hibernate.cache.jcache.ConfigSettings;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyHibernateSecondLevelCacheConfiguration {
@Bean
public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager cacheManager) {
return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager());
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
This customizer will configure Hibernate to use the same CacheManager as the one that the application uses. It is also possible to use separate CacheManager instances. For details, see the Hibernate user guide.
1.8. Use Dependency Injection in Hibernate Components
By default, Spring Boot registers a BeanContainer implementation that uses the BeanFactory so that converters and entity listeners can use regular dependency injection.
You can disable or tune this behavior by registering a HibernatePropertiesCustomizer that removes or changes the hibernate.resource.beans.container property.
1.9. Use a Custom EntityManagerFactory
To take full control of the configuration of the EntityManagerFactory, you need to add a @Bean named ‘entityManagerFactory’. Spring Boot auto-configuration switches off its entity manager in the presence of a bean of that type.
1.10. Using Multiple EntityManagerFactories
If you need to use JPA against multiple data sources, you likely need one EntityManagerFactory per data source. The LocalContainerEntityManagerFactoryBean from Spring ORM allows you to configure an EntityManagerFactory for your needs. You can also reuse JpaProperties to bind settings for each EntityManagerFactory, as shown in the following example:
Java
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
@Configuration(proxyBeanMethods = false)
public class MyEntityManagerFactoryConfiguration {
@Bean
@ConfigurationProperties("app.jpa.first")
public JpaProperties firstJpaProperties() {
return new JpaProperties();
}
@Bean
public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(DataSource firstDataSource,
JpaProperties firstJpaProperties) {
EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(firstJpaProperties);
return builder.dataSource(firstDataSource).packages(Order.class).persistenceUnit("firstDs").build();
}
private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {
JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);
}
private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
// ... map JPA properties as needed
return new HibernateJpaVendorAdapter();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
The example above creates an EntityManagerFactory using a DataSource bean named firstDataSource. It scans entities located in the same package as Order. It is possible to map additional JPA properties using the app.first.jpa namespace.
Tip:When you create a bean for
LocalContainerEntityManagerFactoryBeanyourself, any customization that was applied during the creation of the auto-configuredLocalContainerEntityManagerFactoryBeanis lost. For example, in case of Hibernate, any properties under thespring.jpa.hibernateprefix will not be automatically applied to yourLocalContainerEntityManagerFactoryBean. If you were relying on these properties for configuring things like the naming strategy or the DDL mode, you will need to explicitly configure that when creating theLocalContainerEntityManagerFactoryBeanbean.
You should provide a similar configuration for any additional data sources for which you need JPA access. To complete the picture, you need to configure a JpaTransactionManager for each EntityManagerFactory as well. Alternatively, you might be able to use a JTA transaction manager that spans both.
If you use Spring Data, you need to configure @EnableJpaRepositories accordingly, as shown in the following examples:
Java
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "firstEntityManagerFactory")
public class OrderConfiguration {
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Java
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Customer.class, entityManagerFactoryRef = "secondEntityManagerFactory")
public class CustomerConfiguration {
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
1.11. Use a Traditional persistence.xml File
Spring Boot will not search for or use a META-INF/persistence.xml by default. If you prefer to use a traditional persistence.xml, you need to define your own @Bean of type LocalEntityManagerFactoryBean (with an ID of ‘entityManagerFactory’) and set the persistence unit name there.
See JpaBaseConfiguration for the default settings.
1.12. Use Spring Data JPA and Mongo Repositories
Spring Data JPA and Spring Data Mongo can both automatically create Repository implementations for you. If they are both present on the classpath, you might have to do some extra configuration to tell Spring Boot which repositories to create. The most explicit way to do that is to use the standard Spring Data @EnableJpaRepositories and @EnableMongoRepositories annotations and provide the location of your Repository interfaces.
There are also flags (spring.data.*.repositories.enabled and spring.data.*.repositories.type) that you can use to switch the auto-configured repositories on and off in external configuration. Doing so is useful, for instance, in case you want to switch off the Mongo repositories and still use the auto-configured MongoTemplate.
The same obstacle and the same features exist for other auto-configured Spring Data repository types (Elasticsearch, Solr, and others). To work with them, change the names of the annotations and flags accordingly.
1.13. Customize Spring Data’s Web Support
Spring Data provides web support that simplifies the use of Spring Data repositories in a web application. Spring Boot provides properties in the spring.data.web namespace for customizing its configuration. Note that if you are using Spring Data REST, you must use the properties in the spring.data.rest namespace instead.
1.14. Expose Spring Data Repositories as REST Endpoint
Spring Data REST can expose the Repository implementations as REST endpoints for you, provided Spring MVC has been enabled for the application.
Spring Boot exposes a set of useful properties (from the spring.data.rest namespace) that customize the RepositoryRestConfiguration. If you need to provide additional customization, you should use a RepositoryRestConfigurer bean.
Tip:If you do not specify any order on your custom
RepositoryRestConfigurer, it runs after the one Spring Boot uses internally. If you need to specify an order, make sure it is higher than 0.
1.15. Configure a Component that is Used by JPA
If you want to configure a component that JPA uses, then you need to ensure that the component is initialized before JPA. When the component is auto-configured, Spring Boot takes care of this for you. For example, when Flyway is auto-configured, Hibernate is configured to depend upon Flyway so that Flyway has a chance to initialize the database before Hibernate tries to use it.
If you are configuring a component yourself, you can use an EntityManagerFactoryDependsOnPostProcessor subclass as a convenient way of setting up the necessary dependencies. For example, if you use Hibernate Search with Elasticsearch as its index manager, any EntityManagerFactory beans must be configured to depend on the elasticsearchClient bean, as shown in the following example:
Java
import javax.persistence.EntityManagerFactory;
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.stereotype.Component;
/**
* {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
* {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
*/
@Component
public class ElasticsearchEntityManagerFactoryDependsOnPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
public ElasticsearchEntityManagerFactoryDependsOnPostProcessor() {
super("elasticsearchClient");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1.16. Configure jOOQ with Two DataSources
If you need to use jOOQ with multiple data sources, you should create your own DSLContext for each one. See JooqAutoConfiguration for more details.
Note:In particular,
JooqExceptionTranslatorandSpringTransactionProvidercan be reused to provide similar features to what the auto-configuration does with a singleDataSource.
2. Database Initialization
An SQL database can be initialized in different ways depending on what your stack is. Of course, you can also do it manually, provided the database is a separate process. It is recommended to use a single mechanism for schema generation.
2.1. Initialize a Database Using JPA
JPA has features for DDL generation, and these can be set up to run on startup against the database. This is controlled through two external properties:
spring.jpa.generate-ddl(boolean) switches the feature on and off and is vendor independent.spring.jpa.hibernate.ddl-auto(enum) is a Hibernate feature that controls the behavior in a more fine-grained way. This feature is described in more detail later in this guide.
2.2. Initialize a Database Using Hibernate
You can set spring.jpa.hibernate.ddl-auto explicitly and the standard Hibernate property values are none, validate, update, create, and create-drop. Spring Boot chooses a default value for you based on whether it thinks your database is embedded. It defaults to create-drop if no schema manager has been detected or none in all other cases. An embedded database is detected by looking at the Connection type and JDBC url. hsqldb, h2, and derby are candidates, and others are not. Be careful when switching from in-memory to a ‘real’ database that you do not make assumptions about the existence of the tables and data in the new platform. You either have to set ddl-auto explicitly or use one of the other mechanisms to initialize the database.
Tip:You can output the schema creation by enabling the
org.hibernate.SQLlogger. This is done for you automatically if you enable the debug mode.
In addition, a file named import.sql in the root of the classpath is executed on startup if Hibernate creates the schema from scratch (that is, if the ddl-auto property is set to create or create-drop). This can be useful for demos and for testing if you are careful but is probably not something you want to be on the classpath in production. It is a Hibernate feature (and has nothing to do with Spring).
2.3. Initialize a Database Using Basic SQL Scripts
Spring Boot can automatically create the schema (DDL scripts) of your JDBC DataSource or R2DBC ConnectionFactory and initialize its data (DML scripts).
By default, it loads schema scripts from optional:classpath*:schema.sql and data scripts from optional:classpath*:data.sql. The locations of these schema and data scripts can customized using spring.sql.init.schema-locations and spring.sql.init.data-locations respectively. The optional: prefix means that the application will start when the files do not exist. To have the application fail to start when the files are absent, remove the optional: prefix.
In addition, Spring Boot processes the optional:classpath*:schema-${platform}.sql and optional:classpath*:data-${platform}.sql files (if present), where ${platform} is the value of spring.sql.init.platform. This allows you to switch to database-specific scripts if necessary. For example, you might choose to set it to the vendor name of the database (hsqldb, h2, oracle, mysql, postgresql, and so on).
By default, SQL database initialization is only performed when using an embedded in-memory database. To always initialize an SQL database, irrespective of its type, set spring.sql.init.mode to always. Similarly, to disable initialization, set spring.sql.init.mode to never. By default, Spring Boot enables the fail-fast feature of its script-based database initializer. This means that, if the scripts cause exceptions, the application fails to start. You can tune that behavior by setting spring.sql.init.continue-on-error.
Script-based DataSource initialization is performed, by default, before any JPA EntityManagerFactory beans are created. schema.sql can be used to create the schema for JPA-managed entities and data.sql can be used to populate it. While we do not recommend using multiple data source initialization technologies, if you want script-based DataSource initialization to be able to build upon the schema creation performed by Hibernate, set spring.jpa.defer-datasource-initialization to true. This will defer data source initialization until after any EntityManagerFactory beans have been created and initialized. schema.sql can then be used to make additions to any schema creation performed by Hibernate and data.sql can be used to populate it.
Tip:The initialization scripts support
--for single line comments and/* */for block comments. Other comment formats are not supported.
If you are using a Higher-level Database Migration Tool, like Flyway or Liquibase, you should use them alone to create and initialize the schema. Using the basic schema.sql and data.sql scripts alongside Flyway or Liquibase is not recommended and support will be removed in a future release.
If you need to initialize test data using a higher-level database migration tool, please see the sections about Flyway and Liquibase.
2.4. Initialize a Spring Batch Database
If you use Spring Batch, it comes pre-packaged with SQL initialization scripts for most popular database platforms. Spring Boot can detect your database type and execute those scripts on startup. If you use an embedded database, this happens by default. You can also enable it for any database type, as shown in the following example:
Properties
spring.batch.jdbc.initialize-schema=alwaysYou can also switch off the initialization explicitly by setting spring.batch.jdbc.initialize-schema to never.
2.5. Use a Higher-level Database Migration Tool
Spring Boot supports two higher-level migration tools: Flyway and Liquibase.
2.5.1. Execute Flyway Database Migrations on Startup
To automatically run Flyway database migrations on startup, add the org.flywaydb:flyway-core to your classpath.
Typically, migrations are scripts in the form V<VERSION>__<NAME>.sql (with <VERSION> an underscore-separated version, such as ‘1’ or ‘2_1’). By default, they are in a directory called classpath:db/migration, but you can modify that location by setting spring.flyway.locations. This is a comma-separated list of one or more classpath: or filesystem: locations. For example, the following configuration would search for scripts in both the default classpath location and the /opt/migration directory:
Properties
spring.flyway.locations=classpath:db/migration,filesystem:/opt/migrationYou can also add a special {vendor} placeholder to use vendor-specific scripts. Assume the following:
Properties
spring.flyway.locations=classpath:db/migration/{vendor}Rather than using db/migration, the preceding configuration sets the directory to use according to the type of the database (such as db/migration/mysql for MySQL). The list of supported databases is available in DatabaseDriver.
Migrations can also be written in Java. Flyway will be auto-configured with any beans that implement JavaMigration.
FlywayProperties provides most of Flyway’s settings and a small set of additional properties that can be used to disable the migrations or switch off the location checking. If you need more control over the configuration, consider registering a FlywayConfigurationCustomizer bean.
Spring Boot calls Flyway.migrate() to perform the database migration. If you would like more control, provide a @Bean that implements FlywayMigrationStrategy.
Flyway supports SQL and Java callbacks. To use SQL-based callbacks, place the callback scripts in the classpath:db/migration directory. To use Java-based callbacks, create one or more beans that implement Callback. Any such beans are automatically registered with Flyway. They can be ordered by using @Order or by implementing Ordered. Beans that implement the deprecated FlywayCallback interface can also be detected, however they cannot be used alongside Callback beans.
By default, Flyway autowires the (@Primary) DataSource in your context and uses that for migrations. If you like to use a different DataSource, you can create one and mark its @Bean as @FlywayDataSource. If you do so and want two data sources, remember to create another one and mark it as @Primary. Alternatively, you can use Flyway’s native DataSource by setting spring.flyway.[url,user,password] in external properties. Setting either spring.flyway.url or spring.flyway.user is sufficient to cause Flyway to use its own DataSource. If any of the three properties has not been set, the value of its equivalent spring.datasource property will be used.
You can also use Flyway to provide data for specific scenarios. For example, you can place test-specific migrations in src/test/resources and they are run only when your application starts for testing. Also, you can use profile-specific configuration to customize spring.flyway.locations so that certain migrations run only when a particular profile is active. For example, in application-dev.properties, you might specify the following setting:
Properties
spring.flyway.locations=classpath:/db/migration,classpath:/dev/db/migrationWith that setup, migrations in dev/db/migration run only when the dev profile is active.
2.5.2. Execute Liquibase Database Migrations on Startup
To automatically run Liquibase database migrations on startup, add the org.liquibase:liquibase-core to your classpath.
Tip:When you add the
org.liquibase:liquibase-coreto your classpath, database migrations run by default for both during application startup and before your tests run. This behavior can be customized by using thespring.liquibase.enabledproperty, setting different values in themainandtestconfigurations. It is not possible to use two different ways to initialize the database (for example Liquibase for application startup, JPA for test runs).
By default, the master change log is read from db/changelog/db.changelog-master.yaml, but you can change the location by setting spring.liquibase.change-log. In addition to YAML, Liquibase also supports JSON, XML, and SQL change log formats.
By default, Liquibase autowires the (@Primary) DataSource in your context and uses that for migrations. If you need to use a different DataSource, you can create one and mark its @Bean as @LiquibaseDataSource. If you do so and you want two data sources, remember to create another one and mark it as @Primary. Alternatively, you can use Liquibase’s native DataSource by setting spring.liquibase.[driver-class-name,url,user,password] in external properties. Setting either spring.liquibase.url or spring.liquibase.user is sufficient to cause Liquibase to use its own DataSource. If any of the three properties has not been set, the value of its equivalent spring.datasource property will be used.
See LiquibaseProperties for details about available settings such as contexts, the default schema, and others.
2.5.3. Use Flyway for test-only migrations
If you want to create Flyway migrations which populate your test database, place them in src/test/resources/db/migration. A file named, for example, src/test/resources/db/migration/V9999__test-data.sql will be executed after your production migrations and only if you’re running the tests. You can use this file to create the needed test data. This file will not be packaged in your uber jar or your container.
2.5.4. Use Liquibase for test-only migrations
If you want to create Liquibase migrations which populate your test database, you have to create a test changelog which also includes the production changelog.
First, you need to configure Liquibase to use a different changelog when running the tests. One way to do this is to create a Spring Boot test profile and put the Liquibase properties in there. For that, create a file named src/test/resources/application-test.properties and put the following property in there:
Properties
spring.liquibase.change-log=classpath:/db/changelog/db.changelog-test.yamlThis configures Liquibase to use a different changelog when running in the test profile.
Now create the changelog file at src/test/resources/db/changelog/db.changelog-test.yaml:
YAML
databaseChangeLog:
- include:
file: classpath:/db/changelog/db.changelog-master.yaml
- changeSet:
runOrder: "last"
id: "test"
changes:
# Insert your changes hereThis changelog will be used when the tests are run and it will not be packaged in your uber jar or your container. It includes the production changelog and then declares a new changeset, whose runOrder: last setting specifies that it runs after all the production changesets have been run. You can now use for example the insert changeset to insert data or the sql changeset to execute SQL directly.
The last thing to do is to configure Spring Boot to activate the test profile when running tests. To do this, you can add the @ActiveProfiles("test") annotation to your @SpringBootTest annotated test classes.
2.6. Depend Upon an Initialized Database
Database initialization is performed while the application is starting up as part of application context refresh. To allow an initialized database to be accessed during startup, beans that act as database initializers and beans that require that database to have been initialized are detected automatically. Beans whose initialization depends upon the database having been initialized are configured to depend upon those that initialize it. If, during startup, your application tries to access the database and it has not been initialized, you can configure additional detection of beans that initialize the database and require the database to have been initialized.
2.6.1. Detect a Database Initializer
Spring Boot will automatically detect beans of the following types that initialize an SQL database:
DataSourceScriptDatabaseInitializerEntityManagerFactoryFlywayFlywayMigrationInitializerR2dbcScriptDatabaseInitializerSpringLiquibase
If you are using a third-party starter for a database initialization library, it may provide a detector such that beans of other types are also detected automatically. To have other beans be detected, register an implementation of DatabaseInitializerDetector in META-INF/spring.factories.
2.6.2. Detect a Bean That Depends On Database Initialization
Spring Boot will automatically detect beans of the following types that depends upon database initialization:
AbstractEntityManagerFactoryBean(unlessspring.jpa.defer-datasource-initializationis set totrue)DSLContext(jOOQ)EntityManagerFactory(unlessspring.jpa.defer-datasource-initializationis set totrue)JdbcOperationsNamedParameterJdbcOperations
If you are using a third-party starter data access library, it may provide a detector such that beans of other types are also detected automatically. To have other beans be detected, register an implementation of DependsOnDatabaseInitializationDetector in META-INF/spring.factories. Alternatively, annotate the bean’s class or its @Bean method with @DependsOnDatabaseInitialization.
3. NoSQL
Spring Boot offers a number of starters that support NoSQL technologies. This section answers questions that arise from using NoSQL with Spring Boot.
3.1. Use Jedis Instead of Lettuce
By default, the Spring Boot starter (spring-boot-starter-data-redis) uses Lettuce. You need to exclude that dependency and include the Jedis one instead. Spring Boot manages both of these dependencies so you can switch to Jedis without specifying a version.
The following example shows how to do so in Maven:
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>The following example shows how to do so in Gradle:
Gradle
dependencies {
implementation('org.springframework.boot:spring-boot-starter-data-redis') {
exclude group: 'io.lettuce', module: 'lettuce-core'
}
implementation 'redis.clients:jedis'
// ...
}4. Messaging
Spring Boot offers a number of starters to support messaging. This section answers questions that arise from using messaging with Spring Boot.
4.1. Disable Transacted JMS Session
If your JMS broker does not support transacted sessions, you have to disable the support of transactions altogether. If you create your own JmsListenerContainerFactory, there is nothing to do, since, by default it cannot be transacted. If you want to use the DefaultJmsListenerContainerFactoryConfigurer to reuse Spring Boot’s default, you can disable transacted sessions, as follows:
Java
import javax.jms.ConnectionFactory;
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
@Configuration(proxyBeanMethods = false)
public class MyJmsConfiguration {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory listenerFactory = new DefaultJmsListenerContainerFactory();
configurer.configure(listenerFactory, connectionFactory);
listenerFactory.setTransactionManager(null);
listenerFactory.setSessionTransacted(false);
return listenerFactory;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
The preceding example overrides the default factory, and it should be applied to any other factory that your application defines, if any.
5. Batch Applications
A number of questions often arise when people use Spring Batch from within a Spring Boot application. This section addresses those questions.
5.1. Specifying a Batch Data Source
By default, batch applications require a DataSource to store job details. Spring Batch expects a single DataSource by default. To have it use a DataSource other than the application’s main DataSource, declare a DataSource bean, annotating its @Bean method with @BatchDataSource. If you do so and want two data sources, remember to mark the other one @Primary. To take greater control, implement BatchConfigurer. See The Javadoc of @EnableBatchProcessing for more details.
For more info about Spring Batch, see the Spring Batch project page.
5.2. Running Spring Batch Jobs on Startup
Spring Batch auto-configuration is enabled by adding @EnableBatchProcessing to one of your @Configuration classes.
By default, it executes all Jobs in the application context on startup (see JobLauncherApplicationRunner for details). You can narrow down to a specific job or jobs by specifying spring.batch.job.names (which takes a comma-separated list of job name patterns).
See BatchAutoConfiguration and @EnableBatchProcessing for more details.
5.3. Running From the Command Line
Spring Boot converts any command line argument starting with -- to a property to add to the Environment, see accessing command line properties. This should not be used to pass arguments to batch jobs. To specify batch arguments on the command line, use the regular format (that is without --), as shown in the following example:
Bash
$ java -jar myapp.jar someParameter=someValue anotherParameter=anotherValueIf you specify a property of the Environment on the command line, it is ignored by the job. Consider the following command:
Bash
$ java -jar myapp.jar --server.port=7070 someParameter=someValueThis provides only one argument to the batch job: someParameter=someValue.
5.4. Storing the Job Repository
Spring Batch requires a data store for the Job repository. If you use Spring Boot, you must use an actual database. Note that it can be an in-memory database, see Configuring a Job Repository.
6. Actuator
Spring Boot includes the Spring Boot Actuator. This section answers questions that often arise from its use.
6.1. Change the HTTP Port or Address of the Actuator Endpoints
In a standalone application, the Actuator HTTP port defaults to the same as the main HTTP port. To make the application listen on a different port, set the external property: management.server.port. To listen on a completely different network address (such as when you have an internal network for management and an external one for user applications), you can also set management.server.address to a valid IP address to which the server is able to bind.
For more detail, see the ManagementServerProperties source code and “Customizing the Management Server Port” in the “Production-ready features” section.
6.2. Customize the ‘whitelabel’ Error Page
Spring Boot installs a ‘whitelabel’ error page that you see in a browser client if you encounter a server error (machine clients consuming JSON and other media types should see a sensible response with the right error code).
Tip:Set
server.error.whitelabel.enabled=falseto switch the default error page off. Doing so restores the default of the servlet container that you are using. Note that Spring Boot still tries to resolve the error view, so you should probably add your own error page rather than disabling it completely.
Overriding the error page with your own depends on the templating technology that you use. For example, if you use Thymeleaf, you can add an error.html template. If you use FreeMarker, you can add an error.ftlh template. In general, you need a View that resolves with a name of error or a @Controller that handles the /error path. Unless you replaced some of the default configuration, you should find a BeanNameViewResolver in your ApplicationContext, so a @Bean named error would be one way of doing that. See ErrorMvcAutoConfiguration for more options.
See also the section on “Error Handling” for details of how to register handlers in the servlet container.
6.3. Sanitize Sensitive Values
Information returned by the env and configprops endpoints can be somewhat sensitive so keys matching certain patterns are sanitized by default (that is their values are replaced by ******). Spring Boot uses sensible defaults for such keys: any key ending with the word "password", "secret", "key", "token", "vcap_services", "sun.java.command" is entirely sanitized. Additionally, any key that holds the word credentials (configured as a regular expression, that is .*credentials.*) as part of the key is also entirely sanitized.
Furthermore, Spring Boot sanitizes the sensitive portion of URI-like values for keys with one of the following endings:
addressaddressesuriurisurlurls
The sensitive portion of the URI is identified using the format <scheme>://<username>:<password>@<host>:<port>/. For example, for the property myclient.uri=http://user1:password1@localhost:8081, the resulting sanitized value is http://user1:******@localhost:8081.
6.3.1. Customizing Sanitization
Sanitization can be customized in two different ways.
The default patterns used by the env and configprops endpoints can be replaced using management.endpoint.env.keys-to-sanitize and management.endpoint.configprops.keys-to-sanitize respectively. Alternatively, additional patterns can be configured using management.endpoint.env.additional-keys-to-sanitize and management.endpoint.configprops.additional-keys-to-sanitize.
To take more control over the sanitization, define a SanitizingFunction bean. The SanitizableData with which the function is called provides access to the key and value as well as the PropertySource from which they came. This allows you to, for example, sanitize every value that comes from a particular property source. Each SanitizingFunction is called in order until a function changes the value of the sanitizable data. If no function changes its value, the built-in key-based sanitization is performed.
6.4. Map Health Indicators to Micrometer Metrics
Spring Boot health indicators return a Status type to indicate the overall system health. If you want to monitor or alert on levels of health for a particular application, you can export these statuses as metrics with Micrometer. By default, the status codes “UP”, “DOWN”, “OUT_OF_SERVICE” and “UNKNOWN” are used by Spring Boot. To export these, you will need to convert these states to some set of numbers so that they can be used with a Micrometer Gauge.
The following example shows one way to write such an exporter:
Java
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.Status;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyHealthMetricsExportConfiguration {
public MyHealthMetricsExportConfiguration(MeterRegistry registry, HealthEndpoint healthEndpoint) {
// This example presumes common tags (such as the app) are applied elsewhere
Gauge.builder("health", healthEndpoint, this::getStatusCode).strongReference(true).register(registry);
}
private int getStatusCode(HealthEndpoint health) {
Status status = health.health().getStatus();
if (Status.UP.equals(status)) {
return 3;
}
if (Status.OUT_OF_SERVICE.equals(status)) {
return 2;
}
if (Status.DOWN.equals(status)) {
return 1;
}
return 0;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
7. Security
This section addresses questions about security when working with Spring Boot, including questions that arise from using Spring Security with Spring Boot.
For more about Spring Security, see the Spring Security project page.
7.1. Switch off the Spring Boot Security Configuration
If you define a @Configuration with a WebSecurityConfigurerAdapter or a SecurityFilterChain bean in your application, it switches off the default webapp security settings in Spring Boot.
7.2. Change the UserDetailsService and Add User Accounts
If you provide a @Bean of type AuthenticationManager, AuthenticationProvider, or UserDetailsService, the default @Bean for InMemoryUserDetailsManager is not created. This means you have the full feature set of Spring Security available (such as various authentication options).
The easiest way to add user accounts is to provide your own UserDetailsService bean.
7.3. Enable HTTPS When Running behind a Proxy Server
Ensuring that all your main endpoints are only available over HTTPS is an important chore for any application. If you use Tomcat as a servlet container, then Spring Boot adds Tomcat’s own RemoteIpValve automatically if it detects some environment settings, and you should be able to rely on the HttpServletRequest to report whether it is secure or not (even downstream of a proxy server that handles the real SSL termination). The standard behavior is determined by the presence or absence of certain request headers (x-forwarded-for and x-forwarded-proto), whose names are conventional, so it should work with most front-end proxies. You can switch on the valve by adding some entries to application.properties, as shown in the following example:
Properties
server.tomcat.remoteip.remote-ip-header=x-forwarded-for
server.tomcat.remoteip.protocol-header=x-forwarded-proto(The presence of either of those properties switches on the valve. Alternatively, you can add the RemoteIpValve by customizing the TomcatServletWebServerFactory using a WebServerFactoryCustomizer bean.)
To configure Spring Security to require a secure channel for all (or some) requests, consider adding your own SecurityFilterChain bean that adds the following HttpSecurity configuration:
Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class MySecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// Customize the application security ...
http.requiresChannel((channel) -> channel.anyRequest().requiresSecure());
return http.build();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
8. Hot Swapping
Spring Boot supports hot swapping. This section answers questions about how it works.
8.1. Reload Static Content
There are several options for hot reloading. The recommended approach is to use spring-boot-devtools, as it provides additional development-time features, such as support for fast application restarts and LiveReload as well as sensible development-time configuration (such as template caching). Devtools works by monitoring the classpath for changes. This means that static resource changes must be "built" for the change to take effect. By default, this happens automatically in Eclipse when you save your changes. In IntelliJ IDEA, the Make Project command triggers the necessary build. Due to the default restart exclusions, changes to static resources do not trigger a restart of your application. They do, however, trigger a live reload.
Alternatively, running in an IDE (especially with debugging on) is a good way to do development (all modern IDEs allow reloading of static resources and usually also allow hot-swapping of Java class changes).
Finally, the Maven and Gradle plugins can be configured (see the addResources property) to support running from the command line with reloading of static files directly from source. You can use that with an external css/js compiler process if you are writing that code with higher-level tools.
8.2. Reload Templates without Restarting the Container
Most of the templating technologies supported by Spring Boot include a configuration option to disable caching (described later in this document). If you use the spring-boot-devtools module, these properties are automatically configured for you at development time.
8.2.1. Thymeleaf Templates
If you use Thymeleaf, set spring.thymeleaf.cache to false. See ThymeleafAutoConfiguration for other Thymeleaf customization options.
8.2.2. FreeMarker Templates
If you use FreeMarker, set spring.freemarker.cache to false. See FreeMarkerAutoConfiguration for other FreeMarker customization options.
8.2.3. Groovy Templates
If you use Groovy templates, set spring.groovy.template.cache to false. See GroovyTemplateAutoConfiguration for other Groovy customization options.
8.3. Fast Application Restarts
The spring-boot-devtools module includes support for automatic application restarts. While not as fast as technologies such as JRebel it is usually significantly faster than a “cold start”. You should probably give it a try before investigating some of the more complex reload options discussed later in this document.
For more details, see the Developer Tools section.
8.4. Reload Java Classes without Restarting the Container
Many modern IDEs (Eclipse, IDEA, and others) support hot swapping of bytecode. Consequently, if you make a change that does not affect class or method signatures, it should reload cleanly with no side effects.
9. Testing
Spring Boot includes a number of testing utilities and support classes as well as a dedicated starter that provides common test dependencies. This section answers common questions about testing.
9.1. Testing With Spring Security
Spring Security provides support for running tests as a specific user. For example, the test in the snippet below will run with an authenticated user that has the ADMIN role.
Java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@WebMvcTest(UserController.class)
class MySecurityTests {
@Autowired
private MockMvc mvc;
@Test
@WithMockUser(roles = "ADMIN")
void requestProtectedUrlWithUser() throws Exception {
this.mvc.perform(get("/"));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Spring Security provides comprehensive integration with Spring MVC Test and this can also be used when testing controllers using the @WebMvcTest slice and MockMvc.
For additional details on Spring Security’s testing support, see Spring Security’s reference documentation.
9.2. Use Testcontainers for Integration Testing
The Testcontainers library provides a way to manage services running inside Docker containers. It integrates with JUnit, allowing you to write a test class that can start up a container before any of the tests run. Testcontainers is especially useful for writing integration tests that talk to a real backend service such as MySQL, MongoDB, Cassandra and others. Testcontainers can be used in a Spring Boot test as follows:
Java
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@Testcontainers
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
This will start up a docker container running Neo4j (if Docker is running locally) before any of the tests are run. In most cases, you will need to configure the application using details from the running container, such as container IP or port.
This can be done with a static @DynamicPropertySource method that allows adding dynamic property values to the Spring Environment.
Java
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
@SpringBootTest
@Testcontainers
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
The above configuration allows Neo4j-related beans in the application to communicate with Neo4j running inside the Testcontainers-managed Docker container.
9.3. Structure @Configuration classes for inclusion in slice tests
Slice tests work by restricting Spring Framework’s component scanning to a limited set of components based on their type. For any beans that are not created through component scanning, for example, beans that are created using the @Bean annotation, slice tests will not be able to include/exclude them from the application context. Consider this example:
Java
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
return http.build();
}
@Bean
@ConfigurationProperties("app.datasource.second")
public BasicDataSource secondDataSource() {
return DataSourceBuilder.create().type(BasicDataSource.class).build();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
For a @WebMvcTest for an application with the above @Configuration class, you might expect to have the SecurityFilterChain bean in the application context so that you can test if your controller endpoints are secured properly. However, MyConfiguration is not picked up by @WebMvcTest’s component scanning filter because it doesn’t match any of the types specified by the filter. You can include the configuration explicitly by annotating the test class with @Import(MyConfiguration.class). This will load all the beans in MyConfiguration including the BasicDataSource bean which isn’t required when testing the web tier. Splitting the configuration class into two will enable importing just the security configuration.
Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration(proxyBeanMethods = false)
public class MySecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
return http.build();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
Java
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyDatasourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource.second")
public BasicDataSource secondDataSource() {
return DataSourceBuilder.create().type(BasicDataSource.class).build();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Having a single configuration class can be inefficient when beans of a certain domain need to be included in slice tests. Instead, structuring the application’s configuration as multiple granular classes with beans for a specific domain can enable importing them only for specific slice tests.
10. Build
Spring Boot includes build plugins for Maven and Gradle. This section answers common questions about these plugins.
10.1. Generate Build Information
Both the Maven plugin and the Gradle plugin allow generating build information containing the coordinates, name, and version of the project. The plugins can also be configured to add additional properties through configuration. When such a file is present, Spring Boot auto-configures a BuildProperties bean.
To generate build information with Maven, add an execution for the build-info goal, as shown in the following example:
XML
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.18</version>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>Note:See the Spring Boot Maven Plugin documentation for more details.
The following example does the same with Gradle:
Gradle
springBoot {
buildInfo()
}Note:See the Spring Boot Gradle Plugin documentation for more details.
10.2. Generate Git Information
Both Maven and Gradle allow generating a git.properties file containing information about the state of your git source code repository when the project was built.
For Maven users, the spring-boot-starter-parent POM includes a pre-configured plugin to generate a git.properties file. To use it, add the following declaration for the Git Commit Id Plugin to your POM:
XML
<build>
<plugins>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
</plugins>
</build>Gradle users can achieve the same result by using the gradle-git-properties plugin, as shown in the following example:
Gradle
plugins {
id "com.gorylenko.gradle-git-properties" version "2.4.1"
}Both the Maven and Gradle plugins allow the properties that are included in git.properties to be configured.
Note:The commit time in
git.propertiesis expected to match the following format:yyyy-MM-dd’T’HH:mm:ssZ. This is the default format for both plugins listed above. Using this format lets the time be parsed into aDateand its format, when serialized to JSON, to be controlled by Jackson’s date serialization configuration settings.
10.3. Customize Dependency Versions
The spring-boot-dependencies POM manages the versions of common dependencies. The Spring Boot plugins for Maven and Gradle allow these managed dependency versions to be customized using build properties.
Warning:Each Spring Boot release is designed and tested against this specific set of third-party dependencies. Overriding versions may cause compatibility issues.
To override dependency versions with Maven, see this section of the Maven plugin’s documentation.
To override dependency versions in Gradle, see this section of the Gradle plugin’s documentation.
10.4. Create an Executable JAR with Maven
The spring-boot-maven-plugin can be used to create an executable “fat” JAR. If you use the spring-boot-starter-parent POM, you can declare the plugin and your jars are repackaged as follows:
XML
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>If you do not use the parent POM, you can still use the plugin. However, you must additionally add an <executions> section, as follows:
XML
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>{spring-boot-version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>See the plugin documentation for full usage details.
10.5. Use a Spring Boot Application as a Dependency
Like a war file, a Spring Boot application is not intended to be used as a dependency. If your application contains classes that you want to share with other projects, the recommended approach is to move that code into a separate module. The separate module can then be depended upon by your application and other projects.
If you cannot rearrange your code as recommended above, Spring Boot’s Maven and Gradle plugins must be configured to produce a separate artifact that is suitable for use as a dependency. The executable archive cannot be used as a dependency as the executable jar format packages application classes in BOOT-INF/classes. This means that they cannot be found when the executable jar is used as a dependency.
To produce the two artifacts, one that can be used as a dependency and one that is executable, a classifier must be specified. This classifier is applied to the name of the executable archive, leaving the default archive for use as a dependency.
To configure a classifier of exec in Maven, you can use the following configuration:
XML
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>10.6. Extract Specific Libraries When an Executable Jar Runs
Most nested libraries in an executable jar do not need to be unpacked in order to run. However, certain libraries can have problems. For example, JRuby includes its own nested jar support, which assumes that the jruby-complete.jar is always directly available as a file in its own right.
To deal with any problematic libraries, you can flag that specific nested jars should be automatically unpacked when the executable jar first runs. Such nested jars are written beneath the temporary directory identified by the java.io.tmpdir system property.
Warning:Care should be taken to ensure that your operating system is configured so that it will not delete the jars that have been unpacked to the temporary directory while the application is still running.
For example, to indicate that JRuby should be flagged for unpacking by using the Maven Plugin, you would add the following configuration:
XML
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<requiresUnpack>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
</dependency>
</requiresUnpack>
</configuration>
</plugin>
</plugins>
</build>10.7. Create a Non-executable JAR with Exclusions
Often, if you have an executable and a non-executable jar as two separate build products, the executable version has additional configuration files that are not needed in a library jar. For example, the application.yaml configuration file might be excluded from the non-executable JAR.
In Maven, the executable jar must be the main artifact and you can add a classified jar for the library, as follows:
XML
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>lib</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>lib</classifier>
<excludes>
<exclude>application.yaml</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>10.8. Remote Debug a Spring Boot Application Started with Maven
To attach a remote debugger to a Spring Boot application that was started with Maven, you can use the jvmArguments property of the maven plugin.
See this example for more details.
10.9. Build an Executable Archive From Ant without Using spring-boot-antlib
To build with Ant, you need to grab dependencies, compile, and then create a jar or war archive. To make it executable, you can either use the spring-boot-antlib module or you can follow these instructions:
If you are building a jar, package the application’s classes and resources in a nested
BOOT-INF/classesdirectory. If you are building a war, package the application’s classes in a nestedWEB-INF/classesdirectory as usual.Add the runtime dependencies in a nested
BOOT-INF/libdirectory for a jar orWEB-INF/libfor a war. Remember not to compress the entries in the archive.Add the
provided(embedded container) dependencies in a nestedBOOT-INF/libdirectory for a jar orWEB-INF/lib-providedfor a war. Remember not to compress the entries in the archive.Add the
spring-boot-loaderclasses at the root of the archive (so that theMain-Classis available).Use the appropriate launcher (such as
JarLauncherfor a jar file) as aMain-Classattribute in the manifest and specify the other properties it needs as manifest entries — principally, by setting aStart-Classproperty.
The following example shows how to build an executable archive with Ant:
XML
<target name="build" depends="compile">
<jar destfile="target/${ant.project.name}-${spring-boot.version}.jar" compress="false">
<mappedresources>
<fileset dir="target/classes" />
<globmapper from="*" to="BOOT-INF/classes/*"/>
</mappedresources>
<mappedresources>
<fileset dir="src/main/resources" erroronmissingdir="false"/>
<globmapper from="*" to="BOOT-INF/classes/*"/>
</mappedresources>
<mappedresources>
<fileset dir="${lib.dir}/runtime" />
<globmapper from="*" to="BOOT-INF/lib/*"/>
</mappedresources>
<zipfileset src="${lib.dir}/loader/spring-boot-loader-jar-${spring-boot.version}.jar" />
<manifest>
<attribute name="Main-Class" value="org.springframework.boot.loader.JarLauncher" />
<attribute name="Start-Class" value="${start-class}" />
</manifest>
</jar>
</target>11. Traditional Deployment
Spring Boot supports traditional deployment as well as more modern forms of deployment. This section answers common questions about traditional deployment.
11.1. Create a Deployable War File
Warning:Because Spring WebFlux does not strictly depend on the servlet API and applications are deployed by default on an embedded Reactor Netty server, War deployment is not supported for WebFlux applications.
The first step in producing a deployable war file is to provide a SpringBootServletInitializer subclass and override its configure method. Doing so makes use of Spring Framework’s servlet 3.0 support and lets you configure your application when it is launched by the servlet container. Typically, you should update your application’s main class to extend SpringBootServletInitializer, as shown in the following example:
Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
The next step is to update your build configuration such that your project produces a war file rather than a jar file. If you use Maven and spring-boot-starter-parent (which configures Maven’s war plugin for you), all you need to do is to modify pom.xml to change the packaging to war, as follows:
XML
<packaging>war</packaging>If you use Gradle, you need to modify build.gradle to apply the war plugin to the project, as follows:
Gradle
apply plugin: 'war'The final step in the process is to ensure that the embedded servlet container does not interfere with the servlet container to which the war file is deployed. To do so, you need to mark the embedded servlet container dependency as being provided.
If you use Maven, the following example marks the servlet container (Tomcat, in this case) as being provided:
XML
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- ... -->
</dependencies>If you use Gradle, the following example marks the servlet container (Tomcat, in this case) as being provided:
Gradle
dependencies {
// ...
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
// ...
}Note:
providedRuntimeis preferred to Gradle’scompileOnlyconfiguration. Among other limitations,compileOnlydependencies are not on the test classpath, so any web-based integration tests fail.
If you use the Spring Boot build tools, marking the embedded servlet container dependency as provided produces an executable war file with the provided dependencies packaged in a lib-provided directory. This means that, in addition to being deployable to a servlet container, you can also run your application by using java -jar on the command line.
11.2. Convert an Existing Application to Spring Boot
To convert an existing non-web Spring application to a Spring Boot application, replace the code that creates your ApplicationContext and replace it with calls to SpringApplication or SpringApplicationBuilder. Spring MVC web applications are generally amenable to first creating a deployable war application and then migrating it later to an executable war or jar. See the Getting Started Guide on Converting a jar to a war.
To create a deployable war by extending SpringBootServletInitializer (for example, in a class called Application) and adding the Spring Boot @SpringBootApplication annotation, use code similar to that shown in the following example:
Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// Customize the application or call application.sources(...) to add sources
// Since our example is itself a @Configuration class (through
// @SpringBootApplication)
// we actually do not need to override this method.
return application;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Remember that, whatever you put in the sources is merely a Spring ApplicationContext. Normally, anything that already works should work here. There might be some beans you can remove later and let Spring Boot provide its own defaults for them, but it should be possible to get something working before you need to do that.
Static resources can be moved to /public (or /static or /resources or /META-INF/resources) in the classpath root. The same applies to messages.properties (which Spring Boot automatically detects in the root of the classpath).
Vanilla usage of Spring DispatcherServlet and Spring Security should require no further changes. If you have other features in your application (for instance, using other servlets or filters), you may need to add some configuration to your Application context, by replacing those elements from the web.xml, as follows:
A
@Beanof typeServletorServletRegistrationBeaninstalls that bean in the container as if it were a<servlet/>and<servlet-mapping/>inweb.xml.A
@Beanof typeFilterorFilterRegistrationBeanbehaves similarly (as a<filter/>and<filter-mapping/>).An
ApplicationContextin an XML file can be added through an@ImportResourcein yourApplication. Alternatively, cases where annotation configuration is heavily used already can be recreated in a few lines as@Beandefinitions.
Once the war file is working, you can make it executable by adding a main method to your Application, as shown in the following example:
Java
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}1
2
3
2
3
Tip
If you intend to start your application as a war or as an executable application, you need to share the customizations of the builder in a method that is both available to the
SpringBootServletInitializercallback and in themainmethod in a class similar to the following:Javaimport org.springframework.boot.Banner; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication public class MyApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return customizerBuilder(builder); } public static void main(String[] args) { customizerBuilder(new SpringApplicationBuilder()).run(args); } private static SpringApplicationBuilder customizerBuilder(SpringApplicationBuilder builder) { return builder.sources(MyApplication.class).bannerMode(Banner.Mode.OFF); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Applications can fall into more than one category:
- Servlet 3.0+ applications with no
web.xml. - Applications with a
web.xml. - Applications with a context hierarchy.
- Applications without a context hierarchy.
All of these should be amenable to translation, but each might require slightly different techniques.
Servlet 3.0+ applications might translate pretty easily if they already use the Spring Servlet 3.0+ initializer support classes. Normally, all the code from an existing WebApplicationInitializer can be moved into a SpringBootServletInitializer. If your existing application has more than one ApplicationContext (for example, if it uses AbstractDispatcherServletInitializer) then you might be able to combine all your context sources into a single SpringApplication. The main complication you might encounter is if combining does not work and you need to maintain the context hierarchy. See the entry on building a hierarchy for examples. An existing parent context that contains web-specific features usually needs to be broken up so that all the ServletContextAware components are in the child context.
Applications that are not already Spring applications might be convertible to Spring Boot applications, and the previously mentioned guidance may help. However, you may yet encounter problems. In that case, we suggest asking questions on Stack Overflow with a tag of spring-boot.
11.3. Deploying a WAR to WebLogic
To deploy a Spring Boot application to WebLogic, you must ensure that your servlet initializer directly implements WebApplicationInitializer (even if you extend from a base class that already implements it).
A typical initializer for WebLogic should resemble the following example:
Java
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.web.WebApplicationInitializer;
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
If you use Logback, you also need to tell WebLogic to prefer the packaged version rather than the version that was pre-installed with the server. You can do so by adding a WEB-INF/weblogic.xml file with the following contents:
XML
<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app
xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
https://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd
http://xmlns.oracle.com/weblogic/weblogic-web-app
https://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
<wls:container-descriptor>
<wls:prefer-application-packages>
<wls:package-name>org.slf4j</wls:package-name>
</wls:prefer-application-packages>
</wls:container-descriptor>
</wls:weblogic-web-app>