From b4cda625c49e947c65c5cf5aa3784f3f428f0df5 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 22 Feb 2016 16:01:23 +0000 Subject: [PATCH] Break cycle caused by JndiDataSourceAutoConfiguration There's a long cycle when Spring Data REST, Data JPA and Actuator are used in an app that retrieves its DataSource from JNDI. The cycle is: - WebMvcAutoConfiguration - HttpMessageConverters - MappingJackson2HttpMessageConverter (needs an ObjectMapper) - SpringBootRepositoryRestMvcConfiguration - ObjectMapper - RepositoryResourceMappings (part of a custom Jackson module) - Repositories - EntityManagerFactory (Triggered by application's Spring Data JPA repository) - HibernateJpaAutoConfiguration - JndiDataSourceAutoConfiguration - MBeanExporter (Used to prevent export of DataSource MBean that's already in JMX) - EndpointMBeanExportAutoConfiguration - ObjectMapper (Used to format JSON produced by the exported endpoints) Spring Data Rest caused the ObjectMapper to depend on JPA. JPA depends on the DataSource. JnidDataSourceAutoConfiguration depends on the MBeanExporter. Actuator's MBeanExporter requires an ObjectMapper to produce JSON strings. This commit breaks the cycle by making JndiDataSourceAutoConfiguration access the MBeanExporter lazily. Rather than using `@Lazy`. which does not work with `@Autowired(required=false)`, the application context is injected and the MBeanExporter is retrieved manually when it is needed. Closes gh-4980 --- .../jdbc/JndiDataSourceAutoConfiguration.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfiguration.java index f929826830..eb492cfdba 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfiguration.java @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.jdbc; import javax.sql.DataSource; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -25,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; @@ -48,8 +50,8 @@ import org.springframework.jmx.support.JmxUtils; @EnableConfigurationProperties(DataSourceProperties.class) public class JndiDataSourceAutoConfiguration { - @Autowired(required = false) - private MBeanExporter mbeanExporter; + @Autowired + private ApplicationContext context; @Bean(destroyMethod = "") @ConditionalOnMissingBean @@ -61,8 +63,14 @@ public class JndiDataSourceAutoConfiguration { } private void excludeMBeanIfNecessary(Object candidate, String beanName) { - if (this.mbeanExporter != null && JmxUtils.isMBean(candidate.getClass())) { - this.mbeanExporter.addExcludedBean(beanName); + try { + MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class); + if (JmxUtils.isMBean(candidate.getClass())) { + mbeanExporter.addExcludedBean(beanName); + } + } + catch (NoSuchBeanDefinitionException ex) { + // No exporter. Exclusion is unnecessary } }