Skip to content

Commit

Permalink
Ignore CannotLoadBeanClassException in config props validator
Browse files Browse the repository at this point in the history
If the bean definition type contains a placeholder value, beanFactory.getType
can throw a CannotLoadBeanClassException. We can ignore this exception while
validating the bean definitions for constructor binding beans.

Fixes gh-19207
  • Loading branch information
mbhave committed Dec 4, 2019
1 parent f4db8c8 commit 68bc82c
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 5 deletions.
Expand Up @@ -18,6 +18,7 @@

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
Expand Down Expand Up @@ -57,12 +58,18 @@ public int getOrder() {
}

private void validate(ConfigurableListableBeanFactory beanFactory, String beanName) {
Class<?> beanClass = beanFactory.getType(beanName, false);
if (beanClass != null && BindMethod.forType(beanClass) == BindMethod.VALUE_OBJECT) {
throw new BeanCreationException(beanName,
"@EnableConfigurationProperties or @ConfigurationPropertiesScan must be used to add "
+ "@ConstructorBinding type " + beanClass.getName());
try {
Class<?> beanClass = beanFactory.getType(beanName, false);
if (beanClass != null && BindMethod.forType(beanClass) == BindMethod.VALUE_OBJECT) {
throw new BeanCreationException(beanName,
"@EnableConfigurationProperties or @ConfigurationPropertiesScan must be used to add "
+ "@ConstructorBinding type " + beanClass.getName());
}
}
catch (CannotLoadBeanClassException ex) {
// Ignore
}

}

/**
Expand Down
Expand Up @@ -23,7 +23,9 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SampleSpringXmlApplication implements CommandLineRunner {

private static final String CONTEXT_XML = "classpath:/META-INF/application-context.xml";
Expand Down
@@ -0,0 +1,25 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package smoketest.xml.service;

public class OtherService {

public String getMessage() {
return "Hello Other World";
}

}
@@ -0,0 +1,59 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package smoketest.xml;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import smoketest.xml.service.OtherService;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for XML config with placeholders in bean definitions.
*
* @author Madhura Bhave
*/
@SpringBootTest(
classes = { SampleSpringXmlApplication.class, SampleSpringXmlPlaceholderBeanDefinitionTests.TestConfig.class })
@ExtendWith(OutputCaptureExtension.class)
class SampleSpringXmlPlaceholderBeanDefinitionTests {

@Test
void beanWithPlaceholderShouldNotFail(CapturedOutput output) throws Exception {
assertThat(output).contains("Hello Other World");
}

@Configuration(proxyBeanMethods = false)
@ImportResource({ "classpath:/META-INF/context.xml" })
static class TestConfig {

@Bean
CommandLineRunner testCommandLineRunner(OtherService service) {
return (args) -> System.out.println(service.getMessage());
}

}

}
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>
<context:property-placeholder/>

<bean id="otherService" class="${bean.name}"/>

</beans>
@@ -0,0 +1 @@
bean.name=smoketest.xml.service.OtherService

0 comments on commit 68bc82c

Please sign in to comment.