I have the following class structure
public class Outer{
private Mapper a;
....
private class MapperA implements Mapper {
}
private class MapperB implements Mapper {
}
}
In my Spring config file I would like to create a an Outer bean, and assign one of MapperA or MapperB as a property. Is this possible?
<bean id="outer" class="mypackage.Outer">
<property name="a" ?????='????' />
</bean>
Edit: Some more info, based on the feedback from answers:
I got lazy with my above example. I do have a public setter/getter for the Mapper instance variable.
The reason all of the Mapper classes are inner classes is because there could potentially be many of them, and they will only ever be used in this class. I just don't want a ton of cruft classes in my project. Maybe a factory method is a better idea.
Spring can instantiate private inner classes. The actual problem with your config is that they are also non-static, so you need a <constructor-arg .../>:
<bean id="outer" class="mypackage.Outer">
<property name = "a">
<bean class = "mypackage.Outer.MapperA">
<constructor-arg ref = "outer" />
</bean>
</property>
</bean>
Normally you'd need a setter for the Mapper within Outer, and an instance of the required Mapper. But as these are:
private
inner
classes, that becomes a bit tricky (as you've identified). If you make them public, I'm sure you could creae an instance using Outer$MapperA etc. But that seems a little nasty. So:
do they need to be inner and private ?
perhaps Outer can take a String, and determine from that whether to instantiate MapperA or MapperB. i.e. there's some factory capability here.
The simplest thing to do is to really determine if they need to be inner/private. If so, then they really shouldn't be referenced within the config, which should be talking about publicly accessible classes.
As far as I know, it's impossible until you make MapperA and MapperB usual public classes.
But if you do want to keep them as inner private classes then you can "inject" them manually.
You'll need to create method with #PostInit annotation and initialize your a field there (a = new MapperA () for example, or something more complex). With this approach you should also check that initialization callbacks are switched-on in your spring configuration.
Related
Is it possible to chain methods in factory-method in spring to create beans. For example, I have the following API:
SomeObject.builder().build();
Is there some way I can create this bean in spring XML config directly without creating 2 beans? For example,
<bean id="fooBar" class="com.foo.bar.SomeObject" factory-method="builder().build"/>
Note: The SomeObject.builder() call returns a SomeObjectBuilder object(private static class within SomeObject).
You can't do that. You just specify a single method (even without the brackets). But in SomeObject class you can create a static method that does that for you. For example:
static SomeObject newFactoryMethod(){
return builder().build();
}
And add it to the XML:
<bean id="fooBar" class="com.foo.bar.SomeObject" factory-method="newFactoryMethod"/>
I am trying to reproduce an assignment in Java code with an equivalent bean definition in Spring. As far as I can tell, though, Spring only lets you assign values to the fields within an object (provided that the class defines the appropriate setter methods). Is there a way to simply capture a reference to an object using Spring beans?
Here's an example of how I would expect this to work:
<!-- Non-working example. -->
<bean id="string" class="java.lang.String">
<value>"I am a string."</value>
</bean>
I realize that in this particular case I could just use a <constructor-arg>, but I'm looking for more general solution, one that also works for classes that don't provide parameterized constructors.
String class is immutable. No property setter method is available in java.lang.String class. If you want to inject the property value you can use below:
<bean id="emp" class="com.org.emp">
<property name="name" value="Alex" />
</bean>
in above for the obj emp, its name property will be set as Alex.
The thing to use here is a factory-method, possibly in conjunction with a factory-bean. (Non-static functions must be instantiated by a bean of the appropriate type.) In my example problem, I wanted to capture the output of a function that returns a String. Let's say the function looks like this:
class StringReturner {
public String gimmeUhString(String inStr) {
return "Your string is: " + instr;
}
}
First I need to create a bean of type StringReturner.
<bean name="stringReturner" class="how.do.i.java.StringReturner" />
Then I instantiate my String bean by calling the desired function as a factory-method. You can even provide parameters to the factory method using <constructor-arg> elements:
<bean id="string" factory-bean="stringReturner" factory-method="gimmeUhString">
<constructor-arg>
<value>I am a string.</value>
</constructor-arg>
</bean>
This is (for my purposes) equivalent to saying:
StringReturner stringReturner = new StringReturner();
String string = stringReturner.gimmeUhString("I am a string.");
String is not a Bean, Bean is a Objet that his Class that have a Constructor with empty arguments and the properties are accessible by Getters Methods and are modifiable by Setters Methods
The issue
Awhile back I started using MongoDB and Spring Data. I'd left most of the default functionality in place, and so all of my documents were stored in MongoDB with a _class field pointing to the entity's fully-qualified class name.
Right away that didn't "smell" right to me, but I left it alone. Until recently, when I refactored a bunch of code, and suddenly none of my documents could be read back from MongoDB and converted into their (refactored/renamed) Java entities. I quickly realized that it was because there was now a fully-qualified-classname mismatch. I also quickly realized that--given that I might refactor again sometime in the future--if I didn't want all of my data to become unusable I'd need to figure something else out.
What I've tried
So that's what I'm doing, but I've hit a wall. I think that I need to do the following:
Annotate each entity with #TypeAlias("ta") where "ta" is a unique, stable string.
Configure and use a different TypeInformationMapper for Spring Data to use when converting my documents back into their Java entities; it needs to know, for example, that a type-alias of "widget.foo" refers to com.myapp.document.FooWidget.
I determined that I should use a TypeInformationMapper of type org.springframework.data.convert.MappingContextTypeInformationMapper. Supposedly a MappingContextTypeInformationMapper will scan my entities/documents to find #TypeAlias'ed documents and store an alias->to->class mapping. But I can't pass that to my MappingMongoConverter; I have to pass a subtype of MongoTypeMapper. So I am configuring a DefaultMongoTypeMapper, and passing a List of one MappingContextTypeInformationMapper as its "mappers" constructor arg.
Code
Here's the relevant part of my spring XML config:
<bean id="mongoTypeMapper" class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
<constructor-arg name="typeKey" value="_class"></constructor-arg>
<constructor-arg name="mappers">
<list>
<ref bean="mappingContextTypeMapper" />
</list>
</constructor-arg>
</bean>
<bean id="mappingContextTypeMapper" class="org.springframework.data.convert.MappingContextTypeInformationMapper">
<constructor-arg ref="mappingContext" />
</bean>
<bean id="mappingMongoConverter"
class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
<constructor-arg ref="mongoDbFactory" />
<constructor-arg ref="mappingContext" />
<property name="mapKeyDotReplacement" value="__dot__" />
<property name="typeMapper" ref="mongoTypeMapper"/>
</bean>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory" />
<constructor-arg ref="mappingMongoConverter" />
</bean>
Here's a sample entity/document:
#Document(collection="widget")
#TypeAlias("widget.foo")
public class FooWidget extends Widget {
// ...
}
One important note is that any such "Widget" entity is stored as a nested document in Mongo. So in reality you won't really find a populated "Widget" collection in my MongoDB instance. Instead, a higher-level "Page" class can contain multiple "widgets" like so:
#Document(collection="page")
#TypeAlias("page")
public class Page extends BaseDocument {
// ...
private List<Widget> widgets = new ArrayList<Widget>();
}
The error I'm stuck on
What happens is that I can save a Page along with a number of nested Widgets in Mongo. But when I try to read said Page back out, I get something like the following:
org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.myapp.document.Widget]: Is it an abstract class?
I can indeed see pages in Mongo containing "_class" : "page", with nested widgets also containing "_class" : "widget.foo" It just appears like the mapping is not being applied in the reverse.
Is there anything I might be missing?
In the default setting, the MappingMongoConverter creates a DefaultMongoTypeMapper which in turn creates a MappingContextTypeInformationMapper.
That last class is the one responsible for maintaining the typeMap cache between TypeInformation and aliases.
That cache is populated in two places:
In the constructor, for each mappingContext.getPersistentEntities()
When writing an object of an aliased type.
So if you want to make sure the alias is recognized in any context, you need to make sure that all your aliased entities are part of mappingContext.getPersistentEntities().
How you do that depends on your configuration. For instance:
if you're using AbstractMongoConfiguration, you can overwrite its getMappingBasePackage() to return the name of a package containing all of your entities.
if you're using spring boot, you can use #EntityScan to declare which packages to scan for entities
in any case, you can always configure it with a custom set (from a static list or a custom scan) using mongoMappingContext.setInitialEntitySet()
One side note, for an entity to be discovered by a scan, it has to be annotated with either #Document or #Persitent.
More informations can be found in the spring-data-commons Developer Guide
I spent a bunch of time with my debugger and the Spring Data source code, and I learned that Spring Data isn't as good as it probably should be with polymorphism as it should be, especially given the schema-less nature of NoSQL solutions like MongoDB. But ultimately what I did was to write my own type mapper, and that wasn't too tough.
The main problem was that, when reading in my Page document, the default mappers used by Spring Data would see a collection called widgets, then consult the Page class to determine that widgets pointed to a List, then consult the Widget class to look for #TypeAlias information. What I needed instead was a mapper that scanned my persistent entities up front and stored an alias-to-class mapping for later use. That's what my custom type mapper does.
I wrote a blog post discussing the details.
If you extend AbstractMongoConfiguration, you can override method getMappingBasePackage to specify the base package for your documents.
#Configuration
class RepositoryConfig extends AbstractMongoConfiguration {
#Override
protected String getMappingBasePackage() {
return "com.example";
}
}
Update: In spring-data-mongodb 2+ you should use:
#Configuration
class RepositoryConfig extends AbstractMongoConfiguration {
#Override
protected Collection<String> getMappingBasePackages(){
return Arrays.asList("com.example");
}
}
because getMappingBasePackage() is no deprecated and won't work.
Today I ran into the exact same issue. After more research I found out that my subclass was missing a repository. It appears that Spring Data is using the repositories to determine which concrete subclass to create and when it is missing, it falls back to the superclass which in this case is abstract.
So please try to add a FooWidgetRepository and map it to FooWidget with correct ID type. It might work in your case as well.
If you use spring boot with auto-configuration, declaring the following bean can help:
#Bean
MongoMappingContext mongoMappingContext(ApplicationContext applicationContext, MongoCustomConversions conversions) throws ClassNotFoundException {
MongoMappingContext context = new MongoMappingContext();
context.setInitialEntitySet(new EntityScanner(applicationContext).scan(Persistent.class));
context.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
return context;
}
what does the trick is the following line:
new EntityScanner(applicationContext).scan(Persistent.class)
Instead of scanning for Documents it will scan for both Document and TypeAlias, since both of these annotations are Persistent
Andreas Svensson is right, this can be done much simpler than described by Dave Taubler.
I posted a slightly more elaborate answer than Andreas' (including sample code) in this post. Excerpt:
So all you need to do is to declare an "unused" Repository-Interface
for your sub-classes, just like you proposed as "unsafe" in your OP:
public interface NodeRepository extends MongoRepository<Node, String> {
// all of your repo methods go here
Node findById(String id);
Node findFirst100ByNodeType(String nodeType);
... etc.
}
public interface LeafType1Repository extends MongoRepository<LeafType1, String> {
// leave empty
}
public interface LeafType2Repository extends MongoRepository<LeafType2, String> {
// leave empty
}
We have a large Spring based App that includes different modules but also custom APIs. While there are modules that need a Spring context since they just wrap parts of the domain model we think that some of our APIs do not.
For example we wrote a wrapper around Jackson / Jersey to provide and consume REST services. There is a central interface to access this API. The implementation however needs another class and that another and so on. So a simple initialization would be
A a = new A(new B(new C(), new D(new E())))
Which we currently are saved from using #Inject. The context for this API scans the package and then gets imported into the target app.
<import resource="classpath:api-context.xml" />
We don't feel that comfortable with this and want to kick the spring context out of the REST wrapper, meaning we want the API not to require one, but are not sure how to do it. It should mean either of the following two:
constructor arguments
In the target context this will require the creation of several beans each initialized with its dependencies
<bean id="a" class="...A">
<constructor-arg>
<ref = b " />
</constructor-arg>
</bean>
<bean id="b" class="...B">
<constructor-arg>
<ref = c " />
</constructor-arg>
</bean>
<!-- And so on -->
Getters and Setters
Or, assuming that A is a specific implementation of the AInterface and AInterface is the central access we could just say, A uses a certain implementation of BInterface by default and so on and actually set them internally with new:
public class A implements AInterface {
private BInterface b = new B();
public getB() {return b;}
public setB(B b) {this.b = b) }
}
// and so on
Then in my target context I can initialize the central access with one line if I want to use the default configuration
<bean id="a" class="...A" />
or use properties to set its B. Then however if I want to change something farther down the line I'd have to initialize all beans and set the properties.
Also it does not seem clean to me if I use new for a service outside my tests.
So we're wondering how do other API developers make their interfaces and beans accessible without relying on a context import (which btw also clutters up the target context with many potentially unneeded beans, like for example if an API provides several services and I only want to use one)?
edit
Not sure if any of this is better:
public class A implements AInterface {
private BInterface b
public A() {
b = new B();
}
public getB() {return b;}
public setB(B b) {this.b = b) }
}
or
public class A implements AInterface {
private BInterface b
public A(B b) {
this.b = b;
}
}
The latter feels the best from test point of view but it brings us back to the chain I described above where I'll have to initialize all depending beans in my context before I can initialize A. That feels like too much configuration overhead.
One could argue that that's quite normal that all dependencies need to be initialized before using a class and that we should refactor our code. Then however we'll end up with a lot of utility / helper classes which are also not the best design as they are hard to replace or test.
Basically, if your API does not need the Spring context, there is really no reason for putting it there.
Please note that the second method you suggested:
public class A implements AInterface {
private BInterface b = new B();
public getB() {return b;}
public setB(B b) {this.b = b) }
}
Its a bit problematic becasue you initialize interface inside your class, that will cause you problems with testing since you cannot mock these objects. A better solution is to initialize it in the constructor of the class using it.
Just define all your beans to load lazily by default, and then you won't instantiate services you don't use.
<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>
See http://static.springsource.org/spring/docs/2.0.x/reference/beans.html#beans-factory-lazy-init for more details.
I have two abstract classes
class abstract A {
//some methods .
}
class abstract B extends A {
private C c ;
//other methods
}
Spring config file :
<bean id="b" class="B" abstract="true">
<property name="c" ref="C" /> //I have reference for C else where
</bean>
When I run the program , the class c is not getting injected . It is coming as null . Am i missing something ?
abstract=true means that the bean specification is a 'template' for other bean declarations to extend, it does not mean the class is abstract. I suspect bean with id b is not being created since it is a template/abstract definition. Remove abstract=true and make B a concrete type and it should work.
Documentation here: http://static.springsource.org/spring/docs/3.0.x/reference/beans.html#beans-child-bean-definitions
You don't show a setter for C in the abstract class B. You must use either setting or constructor injection. The code as posted can't work.
You should also specify B as the parent bean for C; likewise A for B.
Although the use of 'abstract="true"' is not meant to indicate that the bean specification is for an abstract class, it is still required for an abstract class bean definition so that pre-instantiation is not attempted on that class (which would fail for an abstract class). This is indicated in a note below the section that the above link points to (http://static.springsource.org/spring/docs/3.0.x/reference/beans.html#beans-child-bean-definitions). If this were a situation where the super class was not an abstract class, then yes, 'abstract="true"' should not be used.