How to inject FactoryBean instead of object it produces? - java

Let's say I have following Spring config (version of Spring is 3.0.3):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="theFactoryBean" class="wax.MyFactoryBean"/>
<bean id="factoryBeanUser" class="wax.FactoryBeanUser">
<!-- what should be placed here?-->
</bean>
</beans>
I have instance of FactoryBean implementation and some other instance. I need Spring to inject to other instance FactoryBean, not the object it produces.
There are two possible ways to solve it.
First one, obvious and malfunctional:
<bean id="factoryBeanUser" class="wax.FactoryBeanUser">
<property name="myFactoryBean" ref="&theFactoryBean"/>
</bean>
With this config Spring throws following exception on start:
[skipped irrelevant part]
Caused by: org.xml.sax.SAXParseException: The reference to entity "theFactoryBean" must end with the ';' delimiter.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:195)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:174)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:388)
at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1414)
I found this solution Spring: Getting FactoryBean object instead of FactoryBean.getObject(), this question is maked as answered and four people voted for it. So I assume that this solution might work, but currently there is something wrong in my code.
Second one, working but awkward:
public class FactoryBeanUser implements ApplicationContextAware{
private MyFactoryBean myFactoryBean;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
myFactoryBean = (MyFactoryBean)applicationContext.getBean("&theFactoryBean");
}
}
My question is it possible to make first approach functional or I should stick with a second way?

It seems the XML parser interprets the ampersand (&) as a start of an XML-entity. You can try using ref="&theFactoryBean".
The spring docs is not clear whether this syntax is allowed in an xml file, or only with programatic lookup. But then the xml configuration is used by the app context, so I assume the & should work (although it seems it has not been the best choice for a special symbol)
Here's why I'd suggest another thing - if you really need the factory bean rather than its product, create another bean, that does not implement FactoryBean, define a method createObject() or something like that, and use it in all factories that need it.
A sidenote - better reference the xsd with the version included:
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

Related

Are we not encouraged to write XML configuration using Maven + Spring?

Currently learning to build spring apps. I have been quite sucessful deploying mock applications for now, but one thing has been annoying me, which is not understanding the mechanisms behind the numerous annotations we add to the code. Look, I'm not saying I don't know which purpose they serve, where they act, nor am I questioning their helpfulness.
My point is that I feel that skipping the changes that should be made (or are being made?) in the XML files actually makes me feel that at the end of the day I don't know what I am truly writing. Let me be more specific, so you could answer me with regards to the following example. This is from Spring manual.
Let’s assume we have the following configuration that defines firstMovieCatalog as the primary MovieCatalog
#Configuration
public class MovieConfiguration {
#Bean
#Primary
public MovieCatalog firstMovieCatalog() { ... }
#Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
With such configuration, the following MovieRecommender will be autowired with the
firstMovieCatalog.
public class MovieRecommender {
#Autowired
private MovieCatalog movieCatalog;
// ...
}
The corresponding bean definitions appear as follows.
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog" primary="true">
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
Okay, so, I think if you can answer me 2 questions regarding this example, It would clear a lot the understanding I am lacking here.
The first thing that is not clear for me: is the annotation process a SEPARATED process from the XML configuration, or is it GENERATING this equivalent XML configuration in some hidden fashion?
Where actually IS this XML configuration file? All spring applications I generated through Initializr just generate the pom.xml file, and it does not include configuration. If I were not using the annotations, would I have to manually write an equivalent configuration in the pom?
is the annotation process a SEPARATED process from the XML configuration, or is it GENERATING this equivalent XML configuration in some hidden fashion?
Spring is not generating any XML or annotation in any case. Spring use XML and annotation processing to get info about which components (classes) are available to use and which beans (instances) to create, inject and use for processing. Then, all these beans could be retrieved by application context (not to confuse with xml of the same name).
Where actually IS this XML configuration file?
Spring first version used XML to configure your app. Later (starting in Spring 3), Spring added annotation support and processing to ease application configuration. Annotations are just another way to configure your components and beans without the hassle of maintaining big XML files (over 1000 lines or even more) or just to avoid dealing with XML at all. Current Spring versions support both configurations, you could also use a mix: using XML and using annotations.
Note that Spring's ApplicationContext has several implementations with different entry points for configuration:
AnnotationConfigApplicationContext accepts a class decorated with #Configuration.
ClassPathXmlApplicationContext accepts the path of a XML file available in application classpath.
If I were not using the annotations, would I have to manually write an equivalent configuration in the pom?
First thing first: POM files are for maven processing, not for Spring. Since you're using Maven, and you want to try using a Spring Boot application without annotations, then you can have this project structure:
- src
- main
- java
/* your Java packages and files */
- com.example
+ App <--- Main entry point
- com.example.service
+ Car <--- Component 1
+ Engine <--- Component 2
- resources
+ my-beans.xml <--- XML configuration. Name can be anything
App class:
package com.example;
import com.example.service.Car;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
#Configuration
#ImportResource("classpath:my-beans.xml")
public class App {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(App.class, args);
Car car = ctx.getBean(Car.class);
car.move();
}
}
Car.class:
package com.example.service;
public class Car {
private Engine engine;
public void move() {
engine.start();
}
}
Engine.class:
package com.example.service;
public class Engine {
public void start() {
System.out.println("engine starts");
}
}
my-beans.xml file:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean class="com.example.service.Engine" />
<bean class="com.example.service.Car" />
<property name="engine" ref="engine"></property>
</bean>
</beans>
is the annotation process a SEPARATED process from the XML configuration, or is it GENERATING this equivalent XML configuration in some hidden fashion
When you initialize a Spring application, you instantiate an ApplicationContext: it's responsible to load all the context definition (the beans, services...).
ApplicationContext is actually an interface which has several implementations depending on how your context is defined:
ClassPathXmlApplicationContext which reads an XML file
AnnotationConfigApplicationContext for annotations based approach
...
Thus you can see it as a unique process, only the datasource is different: either XML or annotations. But they describe the same thing: a context.

MongoDB throws NullPointerException in Class DBTCPConnector

I'm trying to get a grip using MongoDB with the Spring Data MongoDB framework. I tried severeal approaches to connect to my local DB and insert + retrieve some collections and documents, using the official Spring Reference Documentation and some simple examples like this Hello-World-Demo.
Actually, for the beginning I'm going to use the MongoTemplate to keep it simple. But now I'run into the following problem.
When I use the Spring Configuration with annotations configure the setting needed to connect to my local DB, everything works fine.
Otherwise When I use XML for the configuration, I run into an java.lang.NullPointerException at com.mongodb.DBTCPConnector.getClusterDescription
Here are my configuration files and the example code for connecting to the DB:
Use Case 1 - Spring Configuration with annotations:
//package, imports etc. here
#Configuration
public class MongoConfiguration {
public #Bean MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient(), "Test1");
}
public #Bean MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
}
Using the configuration class like this ...
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MongoConfiguration.class);
MongoOperations mongoOperation = (MongoOperations) ctx.getBean("mongoTemplate");
ctx.close();
for (String s : mongoOperation.getCollectionNames()) {
System.out.println(s);
}
.. creates this output:
documents
leute
system.indexes
system.users
Use Case 2 - XML configuration (SpringConfig2.xml):
<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"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<mongo:mongo host="127.0.0.1" port="27017" />
<mongo:db-factory dbname="Test1" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
</beans>
Using the configuration file like this ...
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("SpringConfig2.xml");
MongoOperations mongoOperation = (MongoOperations) ctx.getBean("mongoTemplate");
ctx.close();
for (String s : mongoOperation.getCollectionNames()) {
System.out.println(s);
}
.. results in this error
java.lang.NullPointerException
at com.mongodb.DBTCPConnector.getClusterDescription(DBTCPConnector.java:404)
at com.mongodb.DBTCPConnector.getMaxBsonObjectSize(DBTCPConnector.java:653)
at com.mongodb.Mongo.getMaxBsonObjectSize(Mongo.java:641)
at com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:81)
at com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:66)
at com.mongodb.DB.getCollectionNames(DB.java:510)
at org.springframework.data.mongodb.core.MongoTemplate$13.doInDB(MongoTemplate.java:1501)
at org.springframework.data.mongodb.core.MongoTemplate$13.doInDB(MongoTemplate.java:1499)
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:394)
at org.springframework.data.mongodb.core.MongoTemplate.getCollectionNames(MongoTemplate.java:1499)
at test.main(Test.java:28)
When debugging DBTCPConnector.getClusterDescription, it seems that in the second case the private class variable cluster is for some reason not instantiated, leading to the described error.
What I'd like to know is: am I doing anything wrong within my XML-configuration or when using this config / context? Why does using XML-configuration end in an error, while using annotation configuration just works fine?
Basically (in the end) I just "copy+paste"'d the code examples from the official references for Spring / Spring Data MongoDB.
I'd appreciate any help / suggestions :)
Thanks to the hint from Tushar Mishra in the comments I was able to track down the origin of the error and why it occurs in one case but not in the other.
I'll try to explain in short, maybe it'll save someone some research time (or remember me if I perhaps run into this or a similar error again).
When closing either the AnnotationConfigApplicationContext-object (UC1) or the AnnotationConfigApplicationContext-object (UC2) with ctx.close(), from somewhere
org.springframework.beans.factory.DisposableBean#destroy() is invoked. As far as I understood, within there used singleton beans get destroyed by invoking the destroy()-methods
of each of those beans.
In short: closing the XXApplicationContext-object also destroys the MongoFactoryBean and with it closes the connection to the MongoDB.
So using c**tx.close()** at the described position leads to using a MongoOperations-object on a closed connection at the next line, resulting in the described NullPointerException.
And for why the sample code with Annotations (UC1) runs fine, whereas the sample code configured with XML (UC2) just breaks with an NullPointerException:
In UC1, the org.springframework.beans.factory.DisposableBean#destroy()-call is interupted by some DisposableBeanMethodInterceptor, which tries to redirect to a dispose() method.
But there is no method implemented in MongoFactoryBean, so the Mongo-connection stays alive and can be used even after ctx.close() was invoked. I think the connection will get killed later by the garbage collector, leading to the same error.
I'm not sure what to make of this, yet. If I should implement a dispose()-method myself or something alike. But at least I figured out, why this code sample behave different, although supposed to do the same thing.
Maybe it helps someone. Thanks again, Tushar, for giving a hint to the right direction.

Overriding Spring bean

I have the following scenario:
Spring project A with multiple bean configurations, including a bean named "searchHelper":
<bean name="searchHelper" class="com.test.SearchHelperImpl"/>
where SearchHelperImpl implements "SearchHelper" interface
Spring project B depends on A with a custom SearchHelperBImpl
What I was thinking of making is just copying the whole configuration into the new project and changing what needs to be changed, but that's not convenient and there must be an easier way of doing this.
My question is, how do I override the definition of the "searchHelper" bean to use SearchHelperBImpl instead of SearchHelperImpl? I want to use the same bean name in order for everything that uses this name to use the new implementation. I am using Spring 3.2.2
Thanks
You should be able to utilize the primary xml attribute on your bean element.
<bean name="searchHelper" primary="true" class="com.test.SearchHelperBImpl"/>
Alternatively, if you are using JavaConfig, you can utilize the #Primary annotation.
#Primary
#Bean
public SearchHelper searchHelper() {
return new SearchHelperBImpl();
}
One interesting "feature" (some consider it a bug) of Spring is that a bean with the same name declared later in configuration will override a bean declared earlier in configuration. So if your project B depends on A, and the configuration in A is included in B, and B defines a bean by the same name after the A configuration then the B instance will "win" and that's the instance you will get.
I would not recommend depending on this behavior, but would go with the answer regarding the Primary annotation. I just thought I would bring this up so you would be aware that even without the primary, or in case the one in project A is also primary you would know that the latest definition wins.
NOTE
This answer relates to how to avoid duplicate bean definitions. For overriding see the answer by nicholas.hauschild.
More effective solution to avoid copying is to place all the beans that are common for both projects in a separate XML configuration file, say "common-beans.xml". And in the configuration XML file for the project B (and any other project that needs those beans) you import that file like this:
<import resource="common-beans.xml" />
Simple example
example-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"
<!--
Assuming common-beans.xml is a Spring config file
that contains the definitions you need to use
in the current project.
-->
<import resource="common-beans.xml" />
<!-- Defining a bean with the implementaion you need -->
<bean name="searchHelper" class="com.test.SearchHelperBImpl"/>
<!-- Some other configurations if needed -->
</beans>
Useful reading:
Composing XML-based configuration
metadata

dependency-check in Spring3.x

Spring2.5 had a feature called dependency-check which can be given for a bean tag and default-dependency-check which can be given at the parent level <beans> tag.
Eg: <bean id="soandSo" class="..." dependecy-check=""/>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"
default-dependency-check="all">
however in Spring3.x these attributes are deprecated, is there any alternate way of setting dependency-check apart from using #Required attribute and any equivalent to default-dependency-check in Spring3.x??
This feature is no loger supported since Spring 3.x. You can achieve similar result by telling Spring to respect #Required and #Autowired annotations. It can be done using any of the following configuration options within XML context file:
<context:annotation-config/>
<context:component-scan base-package="*"/>
You can skip those options by registering appropriate BeanPostProcessors such as AutowiredAnnotationBeanPostProcessor and RequiredAnnotationBeanPostProcessor.
I think no, because In Spring 2.5 annotations were fully embraced and as the framework evolves through these types of changes, its evident that annotations will continue to be a strong driver in keeping the framework as clear and straightforward as possible.
Spring team now encourages developers to use the following alternatives going forward (with Spring 3 and beyond).
Use constructors (constructor injection instead of setter injection)
exclusively to ensure the right properties are set. Create setters
with a dedicated init method implemented.
Create setters with #Required annotation when the property is required.
Use #Autowired-driven injection which also implies a required property by
default.

Override default-lazy-init=true for Spring bean definitions

I am maintaining a large Java EE system. Most of the business logic is converted from EJB:s into POJO:s configured in several spring context configuration files. EJB:s are mostly used as Facades, that looks up the business logic spring beans from a context composed of all spring context configuration files mentioned earlier. For this we use the AbstractStatelessSessionBean provided with the spring framework.
All these configuration files have the default-lazy-init=true directive, which means that the business logic beans are not created until they are actually used by the system. This is preferable most of the time since republishing in developer mode becomes faster.
But when large merges are made, we are having problems finding all the configuration errors, such as missing dependencies.
My idea is to write some form of integration test, with the purpose of finding those errors. This means, i think, that I need to find a way to override all default-lazy-init=true declarations when creating the application context.
Is there any way of doing this programmatically, or perhaps with some test-only context file that includes all the actual context files?
Let's say currently you have a single applicationContext.xml file containing all bean definitions:
<beans default-lazy-init="true">
<!-- all your beans -->
</beans>
Rename it to applicationContext-main.xml or something and remove default-lazy-init="true" attribute. Now create two applicationContext.xml files:
<beans default-lazy-init="true">
<import resource="applicationContext-core.xml"/>
</beans>
and:
<beans default-lazy-init="false">
<import resource="applicationContext-core.xml"/>
</beans>
As you can see the only difference is the value of default-lazy-init. During development your team can use the former version of applicationContext.xml that includes all the beans with lazy-init. On staging and testing environments switch it to the latter so that all beans included in applicationContext-core.xml are created eagerly.
I believe that the best way is to control lazy init of beans is to leave the default-lazy-init out of all config files except the topmost as Tomasz Nurkiewicz suggests. I did however in this case need a quick and dirty fix to verify all bean definitions. (It is a bit of a process to change the lazy init policy.)
I came up with a simple BeanFactoryPostProcessor which seems to do the job:
public class NonLazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanName : beanFactory.getBeanDefinitionNames()) {
beanFactory.getBeanDefinition(beanName).setLazyInit(false);
}
}
}
If included in a context file, it will override the lazy init flag set by any included context files.
<beans default-lazy-init="false">
<bean class="example.NonLazyInitBeanFactoryPostProcessor" />
<import resource="applicationContext-core.xml"/>
</beans>
If I try to create a context from the above xml, configuration errors previously hidden by lazy initialization will show up immediately as exceptions.
There is one 'but' in this PostProcessor
for (String beanName : beanFactory.getBeanDefinitionNames()) {
beanFactory.getBeanDefinition(beanName).setLazyInit(false);
}
This for loop will iterate only over top most beans not including e.g internal(local) bean defintions ...
You can't access the scanner from the context - it's completely private, but since you can step into the source code, you can see what's required to set up your own. I used Spring's own ReflectionTestUtils to set my own configured scanner in the context:
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
BeanDefinitionDefaults defaults = new BeanDefinitionDefaults();
defaults.setLazyInit(true);
scanner.setBeanDefinitionDefaults(defaults);
ReflectionTestUtils.setField(context, "scanner", scanner);
context.scan("com.some.path");
You can do this anywhere you have access to the Application Context before the component scan takes place.

Categories

Resources