Overriding key strings of an #autowired/#Inject Map<String,Object>? - java

I have several Spring beans of type CustomerApiLink, say the name of those beans are 'ableCustomer', 'bravoCustomer' and 'charlieCustomer' respectively (this is just an example). Now I inject all of them into a Map through
//DI through annotation
#Inject
private Map<String,CustomerApiLink> apis;
But I found that by default Spring IoC container always use their name as map key, I want to override this, to get a Map of:
able-> bean ref="ableCustomer";
bravo-> bean ref="bravoCustomer";
charlie-> bean ref="charlieCustomer";
Is it possible to do this with annotation? Or I have to create another util bean in an xml file?

I have done this multiple times. I usually #Inject a Set of the object I want to the constructor or a setter, and create a Map at that point.
public class MyObject {
private Map<String, CustomerApiLink> apiLinks;
#Inject
public MyObject(Set<CustomerApiLink> apis) {
apiLinks = new HashMap<String, CustomerApiLink>();
for(CustomerApiLink api : apis) {
apiLinks.put(api.getName(), api);
}
}
}
Of course, with this solution, it requires a way to get the key from the CustomerApiLink object. In this case, I assumed a method called getName() would be present.

No XML needed, but you can annotate a setter method instead of a field and process the supplied map yourself in the method.
#Inject
public void setApis(Map<String,CustomerApiLink> apis){
this.apis = new HashMap<String,CustomerApiLink>();
// now copy values from the incoming map to your internal map
// using keys of your own choice
}
private Map<String,CustomerApiLink> apis;

Related

Jackson: passing exta objects during deserialization

During deserialization, how can I pass in an extra object that's needed to initialize some class member? If I were doing deserialization "manually," the implementation might look like:
public class MyClass {
private MyDocumentObject do;
private String food;
public MyClass(JsonNode node, MyDocument document) {
this.do = document.createMyDocumentObject();
this.food = node.get("food").asText();
}
public String getFood() {
return this.food;
}
}
But I'd like to use Jackson's automatic mapping facilities and use a decorated constructor or custom deserializer, etc. and avoid implementing the deserialization within the class itself. Looking at example implementations using #JsonCreator or extending StdDeserializer, I can't see a way of saying "hey, please use this MyDocument object when you call the constructor." I'd like to avoid implementing and exposing a separate method that accepts a MyDocument that I have to invoke on every object that gets deserialized, e.g.
public createDocumentObject(MyDocument document) {
this.do = document.createMyDocumentObject();
}
I don't want to have this method at all, but if I had to, I'd want Jackson to call this method for me right after deserialization. That means I'd still have to somehow tell Jackson which MyDocument to use.

Using the instantiated collection type for Jackson deserialization instead of initializing a new one?

If I have a class
class DTO {
final MySet<Types> values = MySetWrapper(EnumSet.of(Types.class));
public MySet getValues() {
return values;
}
}
where MySet extends Set. Jackson complains that
Cannot find a deserializer for non-concrete Collection type MySet
which I understand, but I already instantiated the collection. What I want is for jackson to just call add for each value after it created an instance, something like:
DTO o = new DTO();
MySet<Types> values = o.getValues();
for (Types type : jsonArray) {
values.add(type );
}
I don't want it to try to create a new collection itself.
That error message means that the DTO class is configured (by default or explicitly) to deserialize the values part of the JSON input into the DTO values field of DTO :
Cannot find a deserializer for non-concrete Collection type MySet
If you consider that Jackson should not perform the deserialization directly on this field, you could define a constructor to set values and also make sure that Jackson will not perform automatically the deserialization work : to achieve it, remove setter for that field (or add #JsonIgnore on it) and any jackson module that will use reflection to deserialize to fields.
It would give :
final MySet<Types> values = MySetWrapper(EnumSet.of(Types.class));
#JsonCreator
public MyFoo(Set<Types> values) {
this.values.addAll(values);
}
Note that I specified in the constructor Set and not MySet (should not be an issue as interface doesn't declare fields), otherwise you would get the same issue since you didn't define a deserializer for MySet.
But if you implement a deserializer for that you could of course do :
public MyFoo(MySet<Types> values) {
this.values.addAll(values);
}
Found an answer using #JsonProperty:
#JsonProperty
private void setValues(Set<Types> types) {
values.addAll(types);
}
Pretty short and simple thankfully.
Edit: seems like you don't even need the annotation.

Make instance of class available to all classes in spring?

I have a class which basically just contains a HashMap.
The class also has a simple getter method which allows other parts of my application to send the "key" and get back the value from the map.
That much works, however, I don't want to keep instantiating that class every time I need to access it, because this particular data doesn't really change, and I only want it in the map for the sake of querying it quickly for the value I need.
I would like my main application class to create an instance of this, and then have it be available to all of my other classes automatically.
I have been reading about this a lot this morning, but am kind of confused about how it could be achieved, am I trying to do some sort of singleton? Or am I trying to use a service?
Anyway, if anyone has a snippet I can learn from - that would really help me out. I am currently using Annotations for most stuff in spring, and would LIKE to keep it that way.
I think you may not even need a separate class to hold your map. Directly create a bean of the Map which you may want and then Autowire the map where ever you need them.
#Configuration
public class MapConfiguration {
#Bean
public Map<String, Object> dataMap() {
// Create and return your Map here
}
}
Whereever you want your map, just use autowiring.
#Autowired
#Qualifier("dataMap")
private Map<String, Object> dataMap;
If you want to continue to use Spring, you can use the #Component to create a simple component. By default all components are singletons. You can use the #PostConstruct to initialize the data. Below is an example. I'm using a String as the map key, but you can modify it to match your application.
#Component
public class MyMap {
private Map<String, Object> theMap = new HashMap<>();
// PostConstruct runs after the application context instantiates the bean
#PostConstruct
public void init() {
// initialize the data in theMap
}
public Object get(String key) {
return theMap.get(key);
}
}
You can then use the #Autowired annotation or your application context to retrieve the bean:
public class AnotherClass {
#Autowired
MyMap myMap;
// ...
}
If you would like to avoid Spring, another option is to create a simple Java singleton. Here's an example
public class MyMap {
private final static Map<String, Object> theMap = new HashMap<>();
// Use a static block to initialize the map with data
static {
// populate theMap with data
}
public Object get(String key) {
return theMap.get(key);
}
}
One thing to note is that if your map ever does actually get updated, then you will need to handle concurrent reads and updates.

Read annotation value from interface?

I am developing a framework which allows developers to do database operations through service layer. Service classes will send the database request dto object which will be annotated with sql ID to use as ID in MyBatis. Later I will read the annotation value by reflection.
First of all, I created a custom annotation interface.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface MyBatisMapper {
String namespace() default "";
String sqlId() default "";
}
And interface for database request dto object.
public interface IReqDto {
public String getDaoType();
}
And database request dto object which will implement the above IReqDto interface.
#MyBatisMapper(namespace="User", sqlId="userInsert")
public class UserInsertReqDto implements IReqDto{
//beans and getters/setters
}
The above bean may vary as requirement of the developer. This is not part of the framework. Developer must implement IReqDto interface in any kind of database request object he use.
What I am trying is to read the annotated values (namespace and sqlId) from database invoker class by using reflection.
I understand that I can get the annotated value by doing this.
Class<UserInsertReqDto> ReqDto = UserInsertReqDto.class;
for(Annotation annotation : ReqDto.getAnnotations()) {
System.out.println(annotation.toString());
}
But my problem is, as the UserInsertReqDto will vary, I tried to use reflection to IReqDto interface.
Class<IReqDto> ReqDto = IReqDto.class;
Well, surely it doesn't work.
The question is - how can I read the annotated value from database request object in this situation? Thanks.
Maybe I'm still misunderstanding your question, so correct me if necessary.
You will be given an object of a custom implementation of ReqDto
ReqDto object = ...; // get instance
Class<?> clazz = object.getClass(); get actual type of the instance
for(Annotation annotation : clazz.getAnnotations()) { // these are class annotations
System.out.println(annotation.toString());
}
or
MyBatisMapper mapperAnnotation = clazz.getAnnotation(MyBatisMapper.class);
if (mapperAnnotation != null) {
System.out.println(mapperAnnotation.namespace()
System.out.println(mapperAnnotation.sqlId()
}
Reflection works regardless of the type. So, instead of referring to the concrete class, simply use Object#getClass() and/or Class<?>. E.g.
public Metadata getMetadata(Object pojo) {
Annotation annotation = pojo.getAnnotation(MyBatisMapper.class);
if (annotation == null) {
return null;
}
return new Metadata(annotation.getNamespcae(), annotation.getSqlId());
}
where Metadata is just a value class that you can use later on that contains the values about the object. You can also directly work with the MyBatisWrapper annotation.

how to prevent a return value from cache to be changed in spring cacheable

i'm working around spring 3.1 annotation cache with ehcache as a cache implement.
a method with return value like this
#Cacheable("cache")
public MyObject getObj(Object param);
i got a myobject return value for the first time,and it's editable.
ehcache can do something for that by setting "copyOnRead" or "copyOnWrite".
it will force serialize object on read/write.
but at the first time spring will not get value from cache,it always return by method itself.
is there some way to get a readonly return value?
You could write your own aspect that always creates a copy of the returned value, which would make you independent of some Ehcache settings.
At first, a marker annotation like #CopyReturnValue would be nice for expressing the pointcut:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface CopyReturnValue {
}
Now, the aspect can use this annotation for the pointcut expression:
#Aspect
#Component
public class CopyReturnValueAspect {
#Around("#annotation(CopyReturnValue)")
public Object doCopyReturnValue(ProceedingJoinPoint pjp) throws Throwable {
Object retVal = pjp.proceed();
Object copy = BeanUtils.cloneBean(retVal); // create a copy in some way
return copy;
}
}
Finally, add the annotation to your method:
#CopyReturnValue
#Cacheable("cache")
public MyObject getObj(Object param);
For the CopyReturnValueAspect I use BeanUtils to create a copy of the returned value - just as an example. For further information on that topic, you might want to look at How to copy properties from one Java bean to another?
Oh, don't forget to enable #AspectJ support in you Spring configuration if you haven't already:
<aop:aspectj-autoproxy />
I had the same problem with the spring cache. I didn't want to receive the same java objects from the cache.
In my case i want to cache big java objects with many fields and so on. So it is very painful to copy all the data classes with deep copy. I read the article about copy java objects with serialization.
http://www.javaworld.com/article/2077578/learn-java/java-tip-76--an-alternative-to-the-deep-copy-technique.html
This brought me to the idea to cache only the serialized data. Every time a object is read from the cache it is deserialized.
For the serialization i used apache commons helper methods
#Override
public SerializedQuestions readUserQuestion(UID questionId, Locale locale) {
byte[] serializedData = readCachedUserQuestion(questionId, locale);
Object deserializedobject = org.apache.commons.lang.SerializationUtils.deserialize(serializedData);
return (SerializedQuestions) deserialize;
}
#Override
#Cacheable(value = SpringCacheKeys.USER_QUESTION_CACHE)
public byte[] readCachedUserQuestion(UID questionId, Locale locale) {
//read object from db
SerializedQuestions questions = new SerializedQuestions()
return org.apache.commons.lang.SerializationUtils.serialize(questions);
}
It depends on the spring configuration if the call to readCachedUserQuestion could be in the same class or not. Per default only extern calls to a method are cached.
I've found a little dirty solution that worked for me. by creating another class that it contains the same proprieties of The returned Object and then I've mapped the returned Object to my new Object using ModelMapper.
for example i have a class MyObject:
public class MyObject {
private Long id;
private Long label;
//getters and setters
}
the new Created class:
public class MyObjectCopy {
private Long id;
private Long label;
//getters and setters
}
and a cachable method that returns MyObject:
#Cacheable("cache")
public MyObject getMyObject();
and to prevent cache to be modified : i must map that object to my classCopy then i work on it:
MyObjectCopy myObjectCopy = modelMapper.map(myobject, MyObjectCopy.class);
dont forget to create a copy class for the nested objects;

Categories