I have a common Spring Boot project used by other Spring Boot projects. So, I have a multi-module setup.
The common project uses Java Mail and I want configure it in the common project.
Per Spring Boot doc,
If
spring.mail.host
and the relevant libraries (as defined by spring-boot-starter-mail) are available, a defaultJavaMailSender
is created if none exists.
So, here's my library.yml
file,
---
spring:
mail:
host: mobile.example.com
port: 25
And here's my gradle file:
plugins {
id 'java'
id 'eclipse'
id 'maven-publish'
id 'io.spring.dependency-management' version '1.0.3.RELEASE'
}
repositories {
mavenCentral()
mavenLocal()
}
group = 'com.example.test'
version = '1.0.0'
jar {
baseName = 'boot-library-test'
version = '1.0.0'
}
dependencies {
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework.boot:spring-boot-starter-mail')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement {
imports { mavenBom('org.springframework.boot:spring-boot-
dependencies:1.5.4.RELEASE') }
}
So, I think I've met the prerequisites for spring to create the JavaMailSender
. Right?
I have this configuration class:
package com.example.library;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
@Configuration
public class LibraryApplicationConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer dataProperties() {
PropertySourcesPlaceholderConfigurer
propertySourcesPlaceholderConfigurer = new
PropertySourcesPlaceholderConfigurer();
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ClassPathResource("library.yml"));
propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
return propertySourcesPlaceholderConfigurer;
}
}
Here's where I want Spring Boot to wire JavaMailSender:
package com.example.library.mail;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
@Component
public class Mail {
private final JavaMailSender javaMailSender;
public Mail(JavaMailSender javaMailSender) {
this.javaMailSender = javaMailSender;
}
public JavaMailSender getJavaMailSender() {
return javaMailSender;
}
}
Here's my test config:
package com.example.library;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages="com.example.library")
public class LibraryTestConfiguration {
}
And the test itself:
package com.example.library;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.library.mail.Mail;
@RunWith(SpringRunner.class)
@SpringBootTest(classes=LibraryTestConfiguration.class)
public class LibraryTest {
@Autowired
private Mail mail;
@Test
public void contextLoads() {
assertThat(mail.getJavaMailSender()).isNotNull();
}
}
But the test fails with
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.mail.javamail.JavaMailSender' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
If I change library.yml
to application.yml
(and remove PropertySourcesPlaceholderConfigurer
bean), it works. (I have all the code here so you should get the same result if you try.) But since this is a library, I can't name the file application.yml
since it will conflict with other Spring Boot projects that use this library. Furthermore, I want to use a yml file not a properties file (so I cannot use @PropertySource
).
How can I have Spring Boot create and wire JavaMailSender
in a library project using yml props?