I have two spring projects,
Project A: is built using xml bean confguration.
Project B : is built using Annotations;
and A depends on B;
how can I load B beans inside the A applicationContext.
I searched but I found how to load xml beans using annotations : #ImportResource
Is there a way to do so without having to create two application context :
ApplicationContext applicationContextA = new ClassPathXmlApplicationContext("classpath:/applicationContextA.xml");
ApplicationContext applicationContextB = new AnnotationConfigApplicationContext(BConfiguration.class);
Yes, You Can Use One Application Context
You can use a combination of both xml and annotation based configuration using either ApplicationContext implementation. So you can choose one and implement it using one of the following methods.
With ClassPathXmlApplicationContext
When using xml you want to activate package scanning in your xml file.
In the xml:
<context:component-scan base-package="com.yourdomain.packetstoscan"/>
<bean id="myXmlConfiguredBean" class="com.yourdomain.MyXmlConfiguredBean"/>
With AnnotationConfigApplicationContext
When using annotations you want to include the xml file with an annotation on your config class.
In your Java configuration class:
#Configuration
#ImportResource("classpath:yourContext.xml")
public class MyAnnotationConfiguredBean { }
Bean Creation Order
When testing this I noticed just one minor difference, which was the bean creation order. However, I do not think this should have any influence on your application. I'm just adding this for the sake of completeness.
For Xml based config:
INFO com.yourdomain.MyXmlApp - BEAN: myAnnotationConfiguredBean
INFO com.yourdomain.MyXmlApp - BEAN: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
INFO com.yourdomain.MyXmlApp - BEAN: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
INFO com.yourdomain.MyXmlApp - BEAN: org.springframework.context.event.internalEventListenerProcessor
INFO com.yourdomain.MyXmlApp - BEAN: org.springframework.context.event.internalEventListenerFactory
INFO com.yourdomain.MyXmlApp - BEAN: myXmlConfiguredBean
For Annotation based:
INFO com.yourdomain.MyApp - BEAN: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
INFO com.yourdomain.MyApp - BEAN: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
INFO com.yourdomain.MyApp - BEAN: org.springframework.context.event.internalEventListenerProcessor
INFO com.yourdomain.MyApp - BEAN: org.springframework.context.event.internalEventListenerFactory
INFO com.yourdomain.MyApp - BEAN: myAnnotationConfiguredBean
INFO com.yourdomain.MyApp - BEAN: myXmlConfiguredBean
Related
I've been using this guide to set up spring session data with mongodb
https://docs.spring.io/spring-session-data-mongodb/docs/2.1.1.RELEASE/reference/htmlsingle/#introduction
However I am having problems with configuration. I'm using Mongodb with Spring boot and I'm trying to config my session time and session name for Spring boot web application, but it keeps defaulting to 30 minutes and the collection name in mongodb is still 'sessions'
These are what I have tried:
Added these to application.properties:
server.session.timeout=1
spring.session.mongodb.collection-name=TestSESSIONS
and this
server.servlet.session.timeout=60s
spring.session.mongodb.collection-name=TestSESSIONS
none of those config work
I've looked over this URL for spring common application properties for mongodb but none of it help to config the session time and collection name for mongodb.
After doing hours of research it seems like spring boot uses some kind of autoconfig with this "org.springframework.boot.autoconfigure"
so then I added this in my application.properties
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
to disable the autoconfigure.
but now it just give me this error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method mongoSessionRepository in org.springframework.session.data.mongo.config.annotation.web.http.MongoHttpSessionConfiguration required a bean of type 'org.springframework.data.mongodb.core.MongoOperations' that could not be found.
The following candidates were found but could not be injected:
- Bean method 'mongoTemplate' in 'MongoDataAutoConfiguration' not loaded because AnyNestedCondition 0 matched 2 did not; NestedCondition on MongoDataAutoConfiguration.AnyMongoClientAvailable.FallbackClientAvailable #ConditionalOnBean (types: com.mongodb.client.MongoClient; SearchStrategy: all) did not find any beans of type com.mongodb.client.MongoClient; NestedCondition on MongoDataAutoConfiguration.AnyMongoClientAvailable.PreferredClientAvailable #ConditionalOnBean (types: com.mongodb.MongoClient; SearchStrategy: all) did not find any beans of type com.mongodb.MongoClient
Action:
Consider revisiting the entries above or defining a bean of type 'org.springframework.data.mongodb.core.MongoOperations' in your configuration.
this is the #bean from the spring.io guide 'mongoSessionConverter' from above link
this is the java file MongoHttpSessionConfiguration from spring that that's autoconfig by spring; I have tried extending "MongoHttpSessionConfiguration" and overriding the setter methods my self. Such as the "setMaxInactiveIntervalInSeconds" for sessionTime and
"setCollectionName" for mongododb database collection name.
but I've have this error:
Description:
The bean 'mongoSessionRepository', defined in class path resource [com/khatpass/app/config/SessionListenerConfig.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfiguration.class] and overriding is disabled.
I am stuck on trying to configure spring boot session with Mongodb. The session always defaulting to 30 minutes and the collection name is always 'sessions' in mongodb collections. Not sure how to change that serverSelectionTimeout='30000 ms' and mongodb collections name "sessions" I don't know what to do, need help.
2019-02-24 13:39:54.501 INFO 36113 --- [ main] org.mongodb.driver.cluster : Cluster created with settings {hosts=[localhost:27017], mode=MULTIPLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500}
After doing so much research and then finally, going through the source code, I found the solution:
#EnableMongoHttpSession(maxInactiveIntervalInSeconds = 24 * 60 * 60)
public class SessionConfiguration {}
To override the default collection name, there is another annotation attribute collectionName.
This is working for Spring Boot 2.1.1
After looking over the class MongoOperationsSessionRepository from org.springframework.session.data.mongo it seems like it can't be config through application.properties because the class is using static final values
public static final int DEFAULT_INACTIVE_INTERVAL = 1800;
and
public static final String DEFAULT_COLLECTION_NAME = "sessions";
only way to change the value is intercept the object before it gets saved. No getters or setters for those fields, it can't be change in an easy way, what a joke!
I have an example project here. In this multi module project. I have module promotion-service on which the PromotionConfiguration relies on other configurations to be completed first.
#Configuration
#AutoConfigureAfter({RulesConfiguration.class, PointsConfiguration.class})
public class PromotionConfiguration
{
#Bean
public PromotionService promotionService(ObjectProvider<List<Rule>> rules)
{
System.out.println("Adding PromotionService to context");
List<Rule> ruleList = rules.getIfAvailable();
if (!ruleList.isEmpty())
{
ruleList.sort(Comparator.comparingInt(Rule::getOrder));
}
return new PromotionServiceImpl(ruleList);
}
}
But when the spring boot application that has a dependency of module promotion-service the rules are added to the context after the promotion-service is
2018-02-18 11:20:26.743 INFO 11582 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
Adding PromotionService to context
Adding PointRule to context.
Adding PromotionRule to context.
2018-02-18 11:20:27.087 INFO 11582 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for #ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#196a42c3: startup date [Sun Feb 18 11:20:25 EST 2018]; root of context hierarchy
There is a difference between parsing the configuration and its structure and effectively creating beans at runtime. What is the concrete problem here?
If I run your project, the promotionService is created with 2 rules as you'd expect. If I add #ConditionalOnMissingBean(Rule.class) on promotionService it is not created (which proves the context knows that at least one Rule bean is going to be created).
You shouldn't worry too much about the runtime part, the context is free to invoke the necessary factory methods (i.e. #Bean annotated methods) according to its optimized plan (it namely does smart stuff to resolve cycles).
The reason why you get this log output is that you're not asking the context to resolve the Rule beans. ObjectProvider is a proxy that won't do anything until you ask for something (getAvailable in this case).
I've changed the injection point to use List<Rule> and I got the following:
Adding PointRule to context.
Adding PromotionRule to context.
Adding PromotionService to context
So all is good. But please, rename your auto-configuration so that they ends with AutoConfiguration rather than Configuration.
I have 3 DataSource beans
<bean id="dataSource1" name="dataSource1" autowire="byName" .....>
<bean id="dataSource2" name="dataSource2" autowire="byName" .....>
<bean id="dataSource3" name="dataSource3" autowire="byName" .....>
I have set autowire "byName" in the beans in xml
I am creating 3 JdbcTemplates
#Autowired
#Qualifier("dataSource1")
BasicDataSource dataSource1;
#Autowired
#Qualifier("dataSource2")
BasicDataSource dataSource2;
#Autowired
#Qualifier("dataSource3")
BasicDataSource dataSource3;
#Autowired
#Bean(name="jdbcTemplate1")
public JdbcTemplate createJdbcTemplate(){
return new JdbcTemplate((DateSource)dataSource1)
}
#Autowired
#Bean(name="jdbcTemplate2")
public JdbcTemplate createJdbcTemplate(){
return new JdbcTemplate((DateSource)dataSource2)
}
#Autowired
#Bean(name="jdbcTemplate3")
public JdbcTemplate createJdbcTemplate(){
return new JdbcTemplate((DateSource)dataSource3)
}
StackTrace :
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2017-11-16 23:50:04.562 ERROR 15180 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
APPLICATION FAILED TO START
Description:
Field dataSource1 in com.MyClass required a single bean, but 3 were found:
- dataSource1: defined in class path resource [datasource.xml]
- dataSource2: defined in class path resource [datasource.xml]
- dataSource3: defined in class path resource [datasource.xml]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
When I add EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) everything works fine.
Can anyone explain why do I need to exclude the AutoConfiguration despite autowiring-byName.
You don't need to ignore the auto-configuration, you just need to mark one of the datasources as #Primary, as auto-configurations might try to get one by type. This leads to the error you have.
Also, feel free to check the dedicated section in the reference documentation about dealing with multiple datasources; there are better ways to configure multiple datasources and still leverage Spring Boot features and infrastructure.
I have a Java 8/spring 4.3.5.RELEASE web application tha runs on a wildfly 10 server. I use a persistence.xml file. I enabled trace logging on the jboss jpa and hibernate classes and I can see this file gets picked up and is resolved smoothly into a persistence unit:
DEBUG [] [org.hibernate.jpa.internal.util.LogHelper] PersistenceUnitInfo [
name: testcontext
persistence provider classname: org.hibernate.jpa.HibernatePersistenceProvider
classloader: ModuleClassLoader for Module "deployment.BasicWebapp.war:main" from Service Module Loader
excludeUnlistedClasses: false
JTA datasource: org.jboss.as.connector.subsystems.datasources.WildFlyDataSource#fb80232
Non JTA datasource: null
Transaction type: JTA
PU root URL: vfs:/C:/Users/Me/Wildfly 10.0.0/standalone/deployments/BasicWebapp.war/WEB-INF/classes/
Shared Cache Mode: UNSPECIFIED
Validation Mode: AUTO
Jar files URLs []
Managed classes names [
com.company.project.data.User]
Mapping files names []
Properties [
jboss.entity.manager.jndi.name: persistence/testcontext]
I now want a dao class to have an entity manager injected by spring:
UserDao.class
#Repository
public class UserDao
{
#PersistenceContext(unitName = "testcontext")
private EntityManager entityManager;
}
I have component scanning and annotation config so both the #Repository and the #PersistenceContext annotation gets processed upon starting my application:
spring-servlet.xml
<context:component-scan base-package="com.company.project" />
<context:annotation-config/>
<jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/testcontext"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"/>
<tx:annotation-driven/>
The injection fails though, in two possible ways:
If I use #PersistenceContext(unitName = "testcontext"), the error is:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'testcontext' available
If I use #PersistenceContext, the error is:
Caused by: java.lang.NullPointerException
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:580)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:546)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:707)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:680)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:354)
... 44 more
So I'm missing something in this configuration to tell the part of Spring that processes the #PersistenceContext annotation to look at either the entityManagerFactory bean that I declared in the spring-servlet.xml or just use the container's persistence unit directly. What do I need to add to achieve that?
I'm also a bit sketchy on the transactionmanager part. Does wildfly provide the transactionmanager or not? If it does, do I need to create a bean for it in Spring (will it pick up the one created by jboss or make its own one?)
I think you should double check you configuration setup against Spring official docs : https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#orm-jpa-setup-jndi .No, the transaction manager is provided by Spring. More info on this: https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#transaction-strategies
I developed a custom Spring Boot autoconfiguration to ease working with a proprietary messaging library.
The main autoconfiguration class is essentially as follows:
#Configuration
#ConditionalOnClass({LibServer.class, LibClient.class})
#EnableConfigurationProperties(LibProperties.class)
public class LibAutoConfiguration {
#Autowired
LibProperties props;
#Bean
#ConditionalOnMissingBean(LibServer.class)
public LibServer lbServ() {
// create and configure a server object
}
#Bean
#ConditionalOnMissingBean(LibClient.class)
public LibClient lbClient() {
//create and configure a client object
}
}
It seems however that the conditional annotation is not detecting beans declared in the main #SpringBootApplication annotated class.
It only detects beans declared in separate #Configuration annotated classes.
That is to say if I place two #Bean annotated methods returning a LibServer and a LibClient object in the main class I end up with two LibServer and two LibClient objects (the autoconfigured ones and the explicitly declared ones) in the context.
Native spring boot autoconfigurations (such as DataSource one) can instead detect beans declared in the main class too (such as a #Bean annotated jdbcTemplate method).
How do I get proper bean detection even for beans declared in the main class?
Edit
A complete multimodule maven project exhibiting the behaviour is at https://github.com/AlexFalappa/spring-boot-testcase
If you set the log level on debug in you application.properties (logging.level.org.springframework=DEBUG), you will notice that Spring will detect both definitions. However you will also see that the order in which this happens may not be what you expected, because it instantiate beans from the library configuration first, and AFTERWARDS from your main class , and thus you get 2 instances (stripped timestamps to make it friendlier):
Bean definitions
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'autoConfigurationReport'
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for #Bean method af.spring.boot.libbo.LibAutoConfiguration.lbServ()
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'org.springframework.boot.autoconfigure.condition.BeanTypeRegistry'
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'autoConfigurationReport'
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for #Bean method af.spring.boot.libbo.LibAutoConfiguration.lbClient()
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for #Bean method af.DemoLibboApplication.libServ()
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for #Bean method af.DemoLibboApplication.libClient()
Bean instantiation
o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'lbServ'
o.s.b.f.s.DefaultListableBeanFactory : Creating instance of bean 'lbServ'
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'libAutoConfiguration'
Autoconfiguring LibServer
o.s.b.f.s.DefaultListableBeanFactory : Eagerly caching bean 'lbServ' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory : Finished creating instance of bean 'lbServ'
o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'lbClient'
o.s.b.f.s.DefaultListableBeanFactory : Creating instance of bean 'lbClient'
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'libAutoConfiguration'
Autoconfiguring LibClient
o.s.b.f.s.DefaultListableBeanFactory : Eagerly caching bean 'lbClient' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory : Finished creating instance of bean 'lbClient'
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'lib.CONFIGURATION_PROPERTIES'
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor'
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store'
o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'libServ'
o.s.b.f.s.DefaultListableBeanFactory : Creating instance of bean 'libServ'
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'demoLibboApplication'
o.s.b.f.s.DefaultListableBeanFactory : Eagerly caching bean 'libServ' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory : Finished creating instance of bean 'libServ'
o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'libClient'
o.s.b.f.s.DefaultListableBeanFactory : Creating instance of bean 'libClient'
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'demoLibboApplication'
o.s.b.f.s.DefaultListableBeanFactory : Eagerly caching bean 'libClient' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory : Finished creating instance of bean 'libClient'
You can also see in the AUTO-CONFIGURATION REPORT that in your current implementation when the conditionals in the LibAutoConfiguration are evaluated, they match and normally it creates the beans:
Positive matches:
-----------------
...
LibAutoConfiguration#lbClient matched
- #ConditionalOnMissingBean (types: af.libbo.LibClient; SearchStrategy: all) found no beans (OnBeanCondition)
LibAutoConfiguration#lbServ matched
- #ConditionalOnMissingBean (types: af.libbo.LibServer; SearchStrategy: all) found no beans (OnBeanCondition)
...
However, if you add the same condition to your main class, you'll see that it will create the beans according to the definitions in LibAutoConfiguration, and when trying to create those for DemoLibboApplication, it will actually find the previously created beans and skip the instantiation:
Negative matches:
-----------------
...
DemoLibboApplication#libClient did not match
- #ConditionalOnMissingBean (types: af.libbo.LibServer; SearchStrategy: all) found the following [lbServ] (OnBeanCondition)
DemoLibboApplication#libServ did not match
- #ConditionalOnMissingBean (types: af.libbo.LibServer; SearchStrategy: all) found the following [lbServ] (OnBeanCondition)
...
You're not importing your LibAutoConfiguration yourself, do you?
This was the hint: your main class is in a parent package from your auto-configuration class. So you're actually importing the #Configuration yourself through component scan. It turns out that when you process that class (via an explicit import rather than via auto-configuration), no beans have been created yet so it does create them. Your application class is processed later and create those beans as well.
If you move the definition somewhere else, it might work (as you have figured out yourself with LibConfig) but that's not deterministic.
TL;DR make sure that your auto-configuration code is in a separate space and is not the target of component scan. I have moved your DemoLibboApplication to the demo package and it worked as expected.