Spring #Configuration bean created in #Bean method not enhanced by CGLIB - spring

I'm trying to create a MainConfig that imports another Config by using an #Bean method instead of #Import like this :
#Configuration
public class MainConfig {
#Bean
public Service service() {
return new Service(infrastructureConfig().database());
}
#Bean
public OtherService otherService() {
return new OtherService(infrastructureConfig().database());
}
#Bean
public InfrastructureConfig intrastructureConfig() {
return new InfrastructureConfig();
}
}
#Configuration
public class InfrastructureConfig {
#Bean
public Database database() {
return new Database();
}
...
}
When using this technique, the Database is created twice because Spring doesn't seem to consider the #Configuration annotation on InfrastructureConfig. When using #Import, it works fine.
I don't want to use #Import because I want to mock my InfrastructureConfig like this :
#Configuration
public class TestConfig extends MainConfig {
#Override
public InfrastructureConfig infrastructureConfig() {
return mock(InfrastructureConfig.class);
}
}
Am I missing something or it is not supported ?
Thanks

When I first tried out Spring Java configuration I think I made the same assumption and was surprised when it didn't work.
I'm not sure this is the neatest way of solving this but I have used the following approach successfully.
To include that #Configuration class you can add this annotation to your MainConfig:
#ComponentScan(basePackages = "org.foo", includeFilters = {#Filter(filterType = ANNOTATION, value = CONFIGURATION)}, excludeFilters = {#Filter(filterType = ASSIGNABLE_TYPE, value = MainConfig)})
Since #Configuration classes are also candidates for component scanning this allows you to scan for all classes annotated with #Configuration. Since you're putting this annotation on MainConfig you need to exclude that with the ASSIGNABLE_TYPE filter since you'll get a circular reference.

I opened a Spring ticket SpringSource JIRA and they said that it is a known limitation and it is working as designed.

Related

Switch bean by changing properties in Spring boot

I have one interface MyInterface, and 2 implementation beans: FirstImpl & SeconImpl. I want to switch between using these 2 implementations while program is running without restarting it, by only changing a property in application.properties file, e.g: interface.bean.default=FirstImpl change to interface.bean.default=SecondImpl.
Anyone knows how to do that with Spring boot?
You could try to use #ConditionalOnProperty:
#Configuration
public class MyInterfaceConfiguration {
#Bean
#ConditionalOnProperty(value = "my.interfacte.impl", havingValue="firstImpl")
public MyInterface firstImpl(){
return new FirstImpl();
}
#Bean
#ConditionalOnProperty(value = "my.interfacte.impl", havingValue="secondImpl")
public MyInterface secondImpl(){
return new SecondImpl();
}
}
and when you update your property in application.properties with actuator/refresh to:
my.interfacte.impl=firstImpl
you will have your FirstImpl instance. When you have:
my.interfacte.impl=secondImpl
you will have your SecondImpl.
#Hasan, your update only works if I customize it a little bit as below:
#Configuration
#RefreshScope
public class MyInterfaceConfiguration {
#Value("${my.interfacte.impl}")
String impl;
#Bean
#RefreshScope
public MyInterface getBean(){
if ("firstImpl".equals(impl)) {
return new FirstImpl();
} else if ("secondImpl".equals(impl)) {
return new SecondImpl();
}
return null;
}
}
I have to use 2 #RefreshScope at class level and bean creation method level!

How inject DataSource(HikariCP) in Auto Configuration class?

I'm trying to use Spring Boot 2 + Spring Data + Custom Auto Configuration Classes but for some reason can't inject DataSource(provided by HikariCP) in the third class.
#Configuration
#AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public class InitialAutoConfiguration {
//Beans to load in theory first.
}
#Configuration
#AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
#AutoConfigureAfter(InitialAutoConfiguration.class)
#EntityScan(basePackageClasses = Asset.class)
#EnableJpaRepositories(basePackageClasses = AssetRepository.class,
repositoryBaseClass = BaseRepositoryImpl.class,
repositoryFactoryBeanClass = ExtendedJpaRepositoryFactoryBean.class)
public class JpaAutoConfiguration { //Load Jpa Classes
}
#Configuration
#AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
#AutoConfigureAfter(JpaAutoConfiguration.class)
#ComponentScan(basePackages = {"com.xxx"})
public class ServiceConfiguration {
#Inject
private DataSource dataSource; //Datasource is null
#Bean
public DbPropertySourcesPlaceholderConfigurer dbPropertySourcesPlaceholderConfigurer() {
DbPropertySourcesPlaceholderConfigurer placeholderConfigurer = new DbPropertySourcesPlaceholderConfigurer(dataSource);
placeholderConfigurer.setPlaceholderPrefix("%{");
placeholderConfigurer.setPlaceholderSuffix("}");
return placeholderConfigurer;
}
}
META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xetec.autoconfigure.InitialAutoConfiguration,\
com.xetec.autoconfigure.JpaAutoConfiguration,\
com.xetec.autoconfigure.ServiceConfiguration
Looks like my classes are loading before the Spring Data Classes(DataSourceAutoConfiguration.Hikari).
Is there one way to first load the Spring Boot Starters Classes then after my custom ones please?
Thanks
I am no sure if the following change can solve your issue, but i meet with the similar problem
add static in your method
#Bean
public static DbPropertySourcesPlaceholderConfigurer dbPropertySourcesPlaceholderConfigurer() {
....
}
Your use of highest and lowest precedence is the wrong way round. Your ServiceConfiguration is ordered with highest precedence which means that it will be evaluated first.
Rather than using absolute ordering, I would use #AutoConfigureAfter(DataSourceAutoConfiguration.class)

Spring conditional component scan configuration

I have a configuration class which registers beans based on a very simple condition (checking a property value in application.properties). The configuration class and the condition are the following:
#Configuration
#Conditional(DatabaseConfigurationCondition.class)
#ComponentScan(basePackageClasses = DBConfigComponents.class)
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
}
and
public class DatabaseConfigurationCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("configuration.type").contains("db");
}
}
In addition of the beans registered in this configuration class I have component scan which scans for other components. When the condition is not met, I expect the beans which are defined in the configuration class not to be registered (which happens to be a case), but also I expect other classes which are annotated with #Component (or #Repository, #Service, etc.. ) and are in same folder as DBConfigComponents.class marker interface not to be registered, which does not happen. Beans which are scanned are always registered, no matter if the condition is fulfilled or not.
When I put the #Conditional(DatabaseConfigurationCondition.class) on each #Component annotated class, than it's working correctly, but I don't want to put it on each class separately.
Any suggestion?
Fortunately, I managed to fix this. The problem in my case was that I had another #ComponentScan annotation placed in other configuration class in other Maven module - not conditional on any property. The components which are in same package as DBConfigComponents marker interface were actually scanned by the other configuration class.
The way #ComponentScan works is on package level. Although, in different Maven modules, both configuration classes were in same package. #ComponentScan works perfectly fine with #Conditional. No need #Conditional to be placed on each component separately.
The best way to achieve this is not to annotate these beans using #Component / #Service and #Repository annotations. Instead you should return these as part of the configuration you have setup which would be DatabaseConfigurationLoader. See sample below.
#Configuration
#Conditional(DatabaseConfigurationCondition.class)
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
#Bean
public SomeService someService() {
return new SomeService();
}
#Bean
public SomeComponent someComponent() {
return new SomeComponent();
}
}
Note: Typically #Configuration with #Conditional are used in libraries that you want to include in your spring boot application. Such libraries should not share the same package as your spring boot application. Thus they should not be picked up by #ComponentScan annotation. Beans from libraries should not be annotated with #Component / #Service / #Repository annotations. Spring suggests using AutoConfiguration for that. See https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html & https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html
No need to implement Condition interface, you need to use '#ConditionalOnProperty' annotation:
#Configuration
#ComponentScan(basePackageClasses = DBConfigComponents.class)
#ConditionalOnProperty(name = "configuration.type", havingValue = "db")
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
}
you can use 'prefix' instead of 'havingValue' depending on your needs.

Multiple Spring Configuration files (one per Profile)

I'm a Spring rookie and trying to benefit from the advantages of the easy 'profile' handling of Spring. I already worked through this tutorial: https://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile and now I'd like to adapt that concept to an easy example.
I've got two profiles: dev and prod. I imagine a #Configuration class for each profile where I can instantiate different beans (implementing a common interface respectively) depending on the set profile.
My currently used classes look like this:
StatusController.java
#RestController
#RequestMapping("/status")
public class StatusController {
private final EnvironmentAwareBean environmentBean;
#Autowired
public StatusController(EnvironmentAwareBean environmentBean) {
this.environmentBean = environmentBean;
}
#RequestMapping(method = RequestMethod.GET)
Status getStatus() {
Status status = new Status();
status.setExtra("environmentBean=" + environmentBean.getString());
return status;
}
}
EnvironmentAwareBean.java
public interface EnvironmentAwareBean {
String getString();
}
EnvironmentAwareBean.java
#Service
public class DevBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "development";
}
}
EnvironmentAwareBean.java
#Service
public class ProdBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "production";
}
}
DevConfig.java
#Configuration
#Profile("dev")
public class DevConfig {
#Bean
public EnvironmentAwareBean getDevBean() {
return new DevBean();
}
}
ProdConfig.java
#Configuration
#Profile("prod")
public class ProdConfig {
#Bean
public EnvironmentAwareBean getProdBean() {
return new ProdBean();
}
}
Running the example throws this exception during startup (SPRING_PROFILES_DEFAULT is set to dev):
(...) UnsatisfiedDependencyException: (...) nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [EnvironmentAwareBean] is defined: expected single matching bean but found 3: prodBean,devBean,getDevBean
Is my approach far from a recommended configuration? In my opinion it would make more sense to annotate each Configuration with the #Profile annotation instead of doing it for each and every bean and possibly forgetting some variants when new classes are added later on.
Your implementations of EnvironmentAwareBean are all annotated with #Service.
This means they will all be picked up by component scanning and hence you get more than one matching bean. Do they need to be annotated with #Service?
Annotating each #Configuration with the #Profile annotation is fine. Another way as an educational exercise would be to not use #Profile and instead annotate the #Bean or Config classes with your own implementation of #Conditional.

Spring: Properly setup #ComponentScan

I have following set up for my Spring Application Context.
#Configuration
public class RmiContext {
#Bean
public RmiProxyFactoryBean service() {
RmiProxyFactoryBean rmiProxy = new RmiProxyFactoryBean();
rmiProxy.setServiceUrl("rmi://127.0.1.1:1099/Service");
rmiProxy.setServiceInterface(Service.class);
return rmiProxy;
}
}
#Configuration
public class LocalContext {
#Bean
public Controller Controller() {
return new ControllerImpl();
}
}
#Configuration
#Import({RmiContext.class, LocalContext.class})
public class MainContext {
}
The above setup works fine, but I want to enable #ComponentScan annotating Controllers with #Component as there are many Controllers in my application which is tedious when declared one by one using #Bean.
#Configuration
#ComponentScan(basePackageClasses = {Controller.class})
public class LocalContext {
/* ... */
}
The problem is that when I do #ComponentScan(basePackageClasses = {Controller.class}), the previously fine working RmiProxyFactoryBean are not recognized or can't be created.
So, How do I configure my MainContext so that both beans via RMI and local beans are created?
#Configuration is also a candidate for component scan, so you can scan all the beans in RmiContext and all controllers in your controller package by:
#Configuration
#ComponentScan(basePackages = {"org.example.controllers", "package.of.RmiContext"})
public class MainContext {
}
--edit--
#Configuration is a candidate for component scan, here is the test case that works in my pc:
package scan.controllers;
#Controller
public class ExampleController {
}
package scan;
public interface RMIService {
}
package scan;
#Configuration
public class RmiContext {
#Bean
public RmiProxyFactoryBean service() {
RmiProxyFactoryBean rmiProxy = new RmiProxyFactoryBean();
rmiProxy.setServiceUrl("rmi://127.0.1.1:1099/Service");
rmiProxy.setServiceInterface(RMIService.class);
rmiProxy.setLookupStubOnStartup(false);
return rmiProxy;
}
}
package scan;
#Configuration
//MainContext will auto scan RmiContext in package scan and all controllers in package scan.controllers
#ComponentScan(basePackages = {"scan", "scan.controllers"})
public class MainContext {
}
package scan;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={MainContext.class})
public class TestContext {
#Autowired private RMIService rmi;
#Autowired private ExampleController controller;
#Test
public void test() {
//both controller and rmi service are autowired as expected
assertNotNull(controller);
assertNotNull(rmi);
}
}
May be you could try using the base packages of your classes (RMI, Controller):
#ComponentScan(basePackages = {"your controller package", "your rmi package"})
If the RMI classes package is different than controller then they will fail to instantiate by spring.
If I understand you correctly, you use "#ComponentScan(basePackageClasses" but it is not detecting and registering your spring beans?
I had the same issue a few minutes ago. I did not give up and tried all funny guesses. One guess did it.
I had to add an XML component-scan entry in XML. I just put a dummy package there, like below:
component-scan base-package="dummy.filler.to.enable.component.scan"
It seems that the component-scan in XML enables the #ComponentScan.
[Late Edit: I noticed, my spring xml schema is spring 2.5. Anyway, I dont know if this matters. Best Regards.]

Resources