I recently switched from using Spring's XML configuration to its Java configuration and am encountering a strange issue.
The XML configuration was:
<util:map id="myMap">
<entry key="a" value="aValue"/>
<entry key="b" value="bValue"/>
<entry key="c" value="cValue"/>
</util:map>
<bean id="myBean" class="my.MyClass">
<property name="myMap" ref="myMap"/>
</bean>
The Java configuration is:
#Bean
public Map<String, Object> myMap() {
Map<String, Object> myMap = new HashMap<>();
myMap.put("a", "aValue");
myMap.put("b", "bValue");
myMap.put("c", "cValue");
return myMap;
}
#Bean
public MyClass myBean(#Qualifier("myMap") final Map<String, Object> myMap) {
MyClass myBean = new MyClass();
myBean.setMyMap(myMap);
return myBean;
}
Both beans are declared in different files, I grouped them here to make it easier to read. The map contains references too, not only strings.
I would expect to be able to use myMap in the second bean but instead Spring injects the following map:
{ myMap = { a=aValue, b=bValue, c=cValue } }
I don't understand why Spring wraps the map into another map, and why it doesn't behave the same way with the XML configuration.
Any ideas?
There is an issue #Autowired-ing a Map even defining the bean name and since as-per the comments you can't use the suggested #Resource annotation there is an alternative by using the #Value annotation defining the bean name:
#Bean
public MyClass myBean(#Value("#{myMap}") final Map<String, Object> myMap) {
//..
}
Related
I want to exclude some fields during mapping from a bean to HashMap.
Orika definition:
static {
final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(MyReq.class, Map.class)
.exclude("myproperty")
.byDefault()
.register();
MAPPER = mapperFactory.getMapperFacade();
}
Bean definitions:
public class MyReq {
private String myproperty;
}
Usage:
MyReq req = new MyReq;
Map map = MAPPER.map(req, Map.class);
Result: the Map contains the excluded myproperty field! Why?
I also faced this problem, but only with Map instances (it works OK when class that you defined is destination object). However, there is a workaround, since Orika has multiple ways to define mapping rules, something like this:
mapperFactory.classMap(MyReq.class, Map.class)
.fieldMap("myproperty").exclude().add()
.byDefault()
.register();
I am working on a Spring Boot project. I just have annotation configuration. I want to include dozer to transform Entities to DTO and DTO to Entities. I see in the dozer website, they explain i have to add the following configuration in spring xml configuration file. Since i have not xml file but annotation configuration Java class, i don't know how to translate this into Java Configuration class.
<bean id="org.dozer.Mapper" class="org.dozer.DozerBeanMapper">
<property name="mappingFiles">
<list>
<value>dozer-global-configuration.xml</value>
<value>dozer-bean-mappings.xml</value>
<value>more-dozer-bean-mappings.xml</value>
</list>
</property>
</bean>
If someone could you give me an example it'll be very useful. Thanks
I think something like this should work:
#Configuration
public class YourConfiguration {
#Bean(name = "org.dozer.Mapper")
public DozerBeanMapper dozerBean() {
List<String> mappingFiles = Arrays.asList(
"dozer-global-configuration.xml",
"dozer-bean-mappings.xml",
"more-dozer-bean-mappings.xml"
);
DozerBeanMapper dozerBean = new DozerBeanMapper();
dozerBean.setMappingFiles(mappingFiles);
return dozerBean;
}
...
}
If you are using DozerBeanMapperFactoryBean instead of DozerBeanMapper you may use something like this.
#Configuration
public class MappingConfiguration {
#Bean
public DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean(#Value("classpath*:mappings/*mappings.xml") Resource[] resources) throws Exception {
final DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean = new DozerBeanMapperFactoryBean();
// Other configurations
dozerBeanMapperFactoryBean.setMappingFiles(resources);
return dozerBeanMapperFactoryBean;
}
}
This way you can import your mappings automatically. Than simple inject your Mapper and use.
#Autowired
private Mapper mapper;
Update with Dozer 5.5.1
In dozer 5.5.1, DozerBeanMapperFactoryBean is removed. So if you want to go with an updated version you need do something like below,
#Bean
public Mapper mapper(#Value(value = "classpath*:mappings/*mappings.xml") Resource[] resourceArray) throws IOException {
List<String> mappingFileUrlList = new ArrayList<>();
for (Resource resource : resourceArray) {
mappingFileUrlList.add(String.valueOf(resource.getURL()));
}
DozerBeanMapper dozerBeanMapper = new DozerBeanMapper();
dozerBeanMapper.setMappingFiles(mappingFileUrlList);
return dozerBeanMapper;
}
Now inject mapper as told above
#Autowired
private Mapper mapper;
And use like below example,
mapper.map(source_object, destination.class);
eg.
mapper.map(admin, UserDTO.class);
Just in case someone wants to avoid xml dozer file. You can use a builder directly in java. For me it's the way to go in a annotation Spring context.
See more information at mapping api dozer
#Bean
public DozerBeanMapper mapper() throws Exception {
DozerBeanMapper mapper = new DozerBeanMapper();
mapper.addMapping(objectMappingBuilder);
return mapper;
}
BeanMappingBuilder objectMappingBuilder = new BeanMappingBuilder() {
#Override
protected void configure() {
mapping(Bean1.class, Bean2.class)
.fields("id", "id").fields("name", "name");
}
};
In my case it was more efficient (At least the first time). Didn't do any benchmark or anything.
Normally i would populate a field using annotations when I knew the property name like so :
#Value("${myproperties.myValue}")
private String myString
However I now want to loop through all the properties in a file, when their names are unknown, and store both there value and name. What's the best way with spring and java ?
Actually if you need only to read properties from a file and not to use these properties in Spring's property placeholders, then the solution is simple
public class Test1 {
#Autowired
Properties props;
public void printProps() {
for(Entry<Object, Object> e : props.entrySet()) {
System.out.println(e);
}
}
...
<util:properties id="props" location="/spring.properties" />
The #Value mechanism works through the PropertyPlaceholderConfigurer which is in turn a BeanFactoryPostProcessor. The properties used by it are not exposed at runtime. See this previous answer of mine for a possible solution.
I could not find a simpler solution than this
class PropertyPlaceholder extends PropertyPlaceholderConfigurer {
Properties props;
#Override
protected Properties mergeProperties() throws IOException {
props = super.mergeProperties();
return props;
}
}
public class Test1 {
#Autowired
PropertyPlaceholder pph;
public void printProps() {
for(Entry<Object, Object> e : pph.props.entrySet()) {
System.out.println(e);
}
}
...
...
<bean class="test.PropertyPlaceholder">
<property name="locations">
<value>/app.properties</value>
</property>
</bean>
Using enumeration to loop through Properties
I'm trying to add some Spring configuration to an existing utility class. It doesn't seem to work and I'm not sure why (my first time using these Spring options, I'm not even sure I'm doing it correctly).
The class in question
#Configurable(autowire=Autowire.BY_NAME, preConstruction=true)
public class DataUtility
{
private static final DataUtility INSTANCE = new DataUtility();
#Autowired(required=true) //This is the new field and annotation
private Map<String,String> dataFileMapping = new HashMap<String, String>();
public static DataUtility getInstance()
{
return INSTANCE;
}
private DataUtility()
{
//Do a bunch of setup work here
for (String s : dataFileMapping)
{
addDataToCache(dataFileMapping(s))
}
}
The spring config looks like this:
<context:annotation-config/>
<context:spring-configured/>
<bean id="util" class="com.myCompany.DataUtility">
<property name="dataFileMapping">
<map>
<entry key="data1" value="data/file1.dat"/>
<entry key="data2" value="data/file2.dat"/>
<entry key="data3" value="data/file3.dat"/>
</map>
</property>
</bean>
The problem is that when I step through my code in the debugger, I can see that dataFileMapping is empty. I'm not even sure if the spring config is even running.
I think you just need to add getters and setters for dataFileMapping
Also, remember that you can't iterate through the map in the constructor, spring wouldn't have had a chance to set it until after the constructor executes.
In addition to this, you can't make your constructor private and expect spring to be able to instantiate it.
The root of your problem is that you seem to be using a static reference INSTANCE to access the object. Spring is making a bean named 'util' and setting it up with your data, but that isn't becoming the object that INSTANCE points to. The initialization of static fields happens when the class is first loaded, long before spring ever gets a chance to create and inject beans.
You can sort of fake it like this, but of course attempts to access instance before bean initialization will fail:
#Configurable(autowire=Autowire.BY_NAME, preConstruction=true)
public class DataUtility
{
private static final DataUtility INSTANCE = null;
#Autowired(required=true) //This is the new field and annotation
private Map<String,String> dataFileMapping = new HashMap<String, String>();
public static DataUtility getInstance()
{
return INSTANCE;
}
public postInit()
{
INSTANCE = this;
//Do a bunch of setup work here
for (String s : dataFileMapping)
{
addDataToCache(dataFileMapping(s))
}
}
<bean id="util" class="com.myCompany.DataUtility" init-method="postInit">
<property name="dataFileMapping">
<map>
<entry key="data1" value="data/file1.dat"/>
<entry key="data2" value="data/file2.dat"/>
<entry key="data3" value="data/file3.dat"/>
</map>
</property>
</bean>
I've a Bean named as Bucket, it has a HashMap. I want to initialize the bean and polulate the HashMap in the faces-config.xml with a property file. How can I do that?
Bean:
public class BundleBean {
private Map<String, String> bundlePropertiesMap = new HashMap<String, String>();
private String bundleFileName;
// Setter, getter goes here....
}
Property file, named as bundle.properties, and it's on the classpath.
bucket.id=DL_SERVICE
faces-config.xml file :
<managed-bean>
<description>
Java bean class which have bundle properties.
</description>
<managed-bean-name>bundleBean</managed-bean-name>
<managed-bean-class>org.example.view.bean.BundleBean</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
<managed-property>
<property-name>bundleFileName</property-name>
<value>bundle.properties</value>
</managed-property>
</managed-bean>
That Map has to have the bucket.id as the key and DL_SERVICE as the value.
Thanks in Advanced~
Assuming the properties file is in the same ClassLoader context as BundleBean, call a method like this:
#SuppressWarnings("unchecked")
private void loadBundle(String bundleFileName, Map<String, String> map)
throws IOException {
InputStream in = BundleBean.class.getResourceAsStream(bundleFileName);
try {
Properties props = new Properties();
props.load(in);
((Map) map).putAll(props);
} finally {
in.close();
}
}
This is best invoked using the #PostConstruct annotation. If that is not an option, call it either in the bundleFileName setter or perform a lazy check in the bundlePropertiesMap getter.
you can do that with spring that has a more advanced dependency injection mechanism.
when you integrate spring with jsf, you can define your jsf bundlebean in the spring context
<bean id="injectCollection" class="CollectionInjection">
<property name="map">
<map>
<entry key="someValue">
<value>Hello World!</value>
</entry>
<entry key="someBean">
<ref local="oracle"/>
</entry>
</map>
</property>
If I'm not mistaken, JSF calls the corresponding setters when initializing the bean. Thus providing a method public void setBundleFileName(String filename) should work.