I'm seeing an odd issue when using #SpringBootTest with #AutoConfigureMockMvc. I have a SpringBoot project setup with an application.yaml file that provides a custom server.servlet.context-path, this value is used in a platform layer, using a ServletContextAware bean, to get context information for creating an Open API specification. When the application is launched normally, everything works as expected. The ServletContext is updated with context path in ServerProperties.setContextPath(), then setServletContext() is called on my bean. When it is run via a JUnit, this happens in reverse order. I first see setServletContext() called on my bean before the application.yaml data is loaded and I do not get the updated context path.
Spring Boot App
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application .class, args);
}
}
application.yaml
server.servlet.context-path: /some/custom/path
Servlet context aware configuration bean
#Configuration
#EnableSwagger2
public class SwaggerConfiguration implements ServletContextAware {
...
public void setServletContext(#NonNull ServletContext servletContext) {
this.servletContext = servletContext;
}
}
JUnit
#SpringBootTest(classes = {Application .class})
#AutoConfigureMockMvc
public class ApplicationTest {
#Autowired
private MockMvc mockMvc;
...
}
I'm trying to load properties using annotations:
#PropertySource({"classpath:application.properties"})
and
#Value("${my.property}")
String myProperty;
as a result, myProperty is always null
While it is working using:
BatchConfiguration.class.getClassLoader().getResourceAsStream("application.properties");
This is my Batch conf class signature :
#Configuration
#ComponentScan
#EnableBatchProcessing
#PropertySource({"classpath:application.properties"})
public class BatchConfiguration {
#Value("${db.url}")
private String url;
...
}
And Application.java:
#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
I also tried to load it using:
#Autowired
public static Environment env;
But env is also null.
Pass following JVM Argument when running Spring boot application
--spring.config.location=properties file path
Remove {} in your #PropertySource({"classpath:application.properties"})
It should be
#PropertySource("classpath:application.properties")
or
#PropertySource(value="classpath:application.properties")
I'm using Spring (without spring-boot). I want to build standalone application that can be run with default configuration (logback.xml and application.properties in resource folder) or with -Dconfig.folder=/path/to/custom/external/directory
(logback.xml and application.properties in /path/to/custom/external/directory). When application will be run with -Dconfig.folder param AppConfig should load both logback and properties from external directory.
Is there anyway to make external folder act like a resource folder?
If not, what is a common solution for this?
My current implementation (using default resource folder only):
App.java
public class App {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
SampleAction p = context.getBean(SampleAction.class);
p.performTask();
}
}
AppConfig.java
#ComponentScan
#PropertySource("classpath:application.properties")
class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
SampleAction.java
#Component
public class SampleAction {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Value("${sample.prop}")
private String sampleProp;
public void performTask(){
logger.debug(sampleProp);
}
}
logback.xml and application.properties are not relevant to the problem
Unlike the other answer suggests, if you use file prefix in #PropertySource, you're screwed because it won't be able to load the default application.properties from the jar. What you should do is the following:
#PropertySource("${config.folder:'classpath:'}/application.properties")
public class AppConfig
For logback.xml:
#Value("${config.folder}:")
private String configFolder;
InputStream = Optional.of(new ClassPathResource(configFolder + "/logback.xml"))
.filter(r -> r.exists())
.orElse(new ClassPathResource("classpath:/logback.xml"))
.getInputStream();
In both cases, I gave preference to the command line argument over the default packaged files. Of course, I didn't compile the above, so there may be typos or minor errors, but you get the idea.
Edit:
Since OP claims to not understand where to run the above code -
public class AppConfig {
#PostConstruct
void init() {
// init logback here
}
}
For log4j.xml
-Dlog4j.configuration=C:\neon\log4j.xml as VM argument
In main() method:
String filename = System.getProperty("log4j.configuration");
DOMConfigurator.configure(filename);
For external properties file:
-Dext.prop.dir=C:\neon as VM argument
Change in your AppConfig class will be like
#PropertySource("file:///${ext.prop.dir}/application.properties")
public class AppConfig{
}
Run App class with VM arguments as below and both file will be use from external location
-Dlog4j.configuration=C:\neon\log4j.xml -Dext.prop.dir=C:\neon
I have some problem with configuration my unit test. I have mock-test class configuration:
#TestConfiguration
public class TestContext {
#Bean
// #Qualifier("userTestServiceImplWithMongoDB")
public UserService userService() {
return Mockito.mock(UserService.class, "userServiceImplWithMongoDB");
}
#Bean
// #Qualifier("taskTestServiceImplWithMongoDB")
public TaskService taskService() {
return Mockito.mock(TaskService.class, "taskServiceImplWithMongoDB");
}
}
This file is in src/test/java directory. I start my application with class:
#SpringBootApplication
#Configuration
#EnableMongoRepositories
#ComponentScan
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected final SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
My problem is that application starts with wrong configuration - this one from my test config class.
I use MongoDB repositories and I have service layer using thats repositories.
I followed this instructions: Some tutorial.
I am new to Spring, do you know what I can do to fox it?
Have a nice day :)
The tutorial you are following is not for spring, not spring-boot. It is still a good tutorial to use MockMvc, however it does not cover how to load the context through spring-boot.
Check this to setup a unit test with Spring-Boot: http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class YourTest {
...
}
I keep getting the ConflictingBeanDefinitionException error in my Spring boot application. I am not entirely sure as to how to address it, I have several #Configuration annotated classes helping to set up Thymeleaf, Spring Security and Web. Why is the application trying to setup the homeController twice? (and where is it trying to do this?)
The error is:
org.springframework.beans.factory.BeanDefinitionStoreException:
Failed to parse configuration class [org.kemri.wellcome.hie.Application]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException:
Annotation-specified bean name 'homeController' for bean class [org.kemri.wellcome.hie.HomeController] conflicts with existing, non-compatible bean definition of same name and class [org.kemri.wellcome.hie.controller.HomeController]
My spring boot main application initializer:
#EnableScheduling
#EnableAspectJAutoProxy
#EnableCaching
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected final SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
My database config file:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="org.kemri.wellcome.hie.repositories")
#PropertySource("classpath:application.properties")
public class DatabaseConfig {
#Autowired
private Environment env;
#Autowired
private DataSource dataSource;
#Autowired
private LocalContainerEntityManagerFactoryBean entityManagerFactory;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driverClassName"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory =
new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource);
// Classpath scanning of #Component, #Service, etc annotated class
entityManagerFactory.setPackagesToScan(
env.getProperty("spring.jpa.hibernate.entitymanager.packagesToScan"));
// Vendor adapter
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
// Hibernate properties
Properties additionalProperties = new Properties();
additionalProperties.put(
"hibernate.dialect",
env.getProperty("spring.jpa.hibernate.dialect"));
additionalProperties.put(
"hibernate.showsql",
env.getProperty("spring.jpa.hibernate.showsql"));
additionalProperties.put(
"hibernate.hbm2ddl.auto",
env.getProperty("spring.jpa.hibernate.hbm2ddl.auto"));
entityManagerFactory.setJpaProperties(additionalProperties);
return entityManagerFactory;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager =
new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
entityManagerFactory.getObject());
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
My Thymeleaf config file:
#Configuration
public class ThymeleafConfig {
#Bean
public ServletContextTemplateResolver templateResolver(){
ServletContextTemplateResolver thymeTemplateResolver = new ServletContextTemplateResolver();
thymeTemplateResolver.setPrefix("/WEB-INF/views/");
thymeTemplateResolver.setSuffix(".html");
thymeTemplateResolver.setTemplateMode("HTML5");
return thymeTemplateResolver;
}
#Bean
public SpringSecurityDialect springSecurityDialect(){
SpringSecurityDialect dialect = new SpringSecurityDialect();
return dialect;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.addTemplateResolver(templateResolver());
Set<IDialect> dialects = new HashSet<IDialect>();
dialects.add(springSecurityDialect());
engine.setAdditionalDialects(dialects);
return engine;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setViewClass(ThymeleafTilesView.class);
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
}
My Web config class:
#Configuration
#PropertySource("classpath:application.properties")
public class WebConfig extends WebMvcAutoConfigurationAdapter {
#Autowired
private Environment env;
#Bean
public JavaMailSenderImpl javaMailSenderImpl() {
JavaMailSenderImpl mailSenderImpl = new JavaMailSenderImpl();
mailSenderImpl.setHost(env.getProperty("smtp.host"));
mailSenderImpl.setPort(env.getProperty("smtp.port", Integer.class));
mailSenderImpl.setProtocol(env.getProperty("smtp.protocol"));
mailSenderImpl.setUsername(env.getProperty("smtp.username"));
mailSenderImpl.setPassword(env.getProperty("smtp.password"));
Properties javaMailProps = new Properties();
javaMailProps.put("mail.smtp.auth", true);
javaMailProps.put("mail.smtp.starttls.enable", true);
mailSenderImpl.setJavaMailProperties(javaMailProps);
return mailSenderImpl;
}
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}
My controller (where there is an error setting up the controller)
#Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "index.html";
}
}
What might be causing the ConflictingBeanDefinitionException error for my controller class?
I ran into the same problem but for a different reason.
This can also occur if you move your classes around in your project and fail to do a 'clean'.
I use gradle with spring-boot plugin. Now I usually run:
$> ./gradlew clean bootRun
I had the same problem on a Spring integration test when I ran it with InteliJ.
After a refactor, one of my controller class was actually duplicate in the /out/production/classes directory which is the default output directory for Intelij since version 2017.2.
Since the gradle output directory is different (It's build/classes), the gradle clean goal had no effect.
For me the solution was to manually remove /out/production/classes and re run my integration test.
For a possible durable solution not having 2 output directories see here
The solution, as I found out, is to disable double initialization by including a filter in the component scan. In my case:
#EnableScheduling
#EnableAspectJAutoProxy
#EnableCaching
#Configuration
#ComponentScan(basePackages = { "org.kemri.wellcome.hie" },
excludeFilters = {#Filter(value = Controller.class, type = FilterType.ANNOTATION)})
#EnableAutoConfiguration
#PropertySource("classpath:application.properties")
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I encountered this with mvn after changing several folder names and related package names. Than I applied maven clean and run spring boot again, all solved:
mvn clean
mvn spring-boot:run
It seems you have two entityManagerFactory, one you will autowire and one you resolve programmatically as Bean:
#Autowired
private LocalContainerEntityManagerFactoryBean entityManagerFactory;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
...
}
I think you just need your configured Factory in entityManagerFactory() method.
I was having the same problem with a generated .war file from spring-boot. the approved solution (Timothy Tuti's own solution) didn't quite work for me exactly as-is, but I tweaked it a little bit and it worked. I just added the following line to my Application.java:
#ComponentScan(basePackages = { "com.mypackage" })
For reference, here goes my full Application.java
package com.inmoment.devchallenge;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
#SpringBootApplication
#Configuration
#ComponentScan(basePackages = { "com.inmoment.devchallenge.controller" })
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
#Configuration
#EnableNeo4jRepositories(basePackages = "com.inmoment.devchallenge.repository")
static class ApplicationConfig extends Neo4jConfiguration {
public ApplicationConfig() {
setBasePackage("com.inmoment.devchallenge.repository");
}
#Bean
GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabase("accessingdataneo4j.db");
}
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
I solved my problem by adding a bean name on top of the class.
#Component("myBeanName1")
public class MyBean {
}
And initialize it with #Autowire in this way:
#Autowire
#Qualifier("myBeanName1")
MyBean myBean;
let's assume your package name - com.example.company and the class name is RestExceptionHandler. Then you need to add the full name with the package to be identical.
add annotation #Component("com.example.company.RestExceptionHandler")
It will identify your class without conflict.
I ran into same problem when one of dependencies(say module Y) of current module(say X) also had definition of same class. So I had to create a separate module(say Z) to store common classes and then add dependency on Z for both X and Y to use.