How #Autowired Work using Reflection and how invoke appropriate Setter Method - spring

My Main Problem is that i can use Reflection API and i can invoke the setter method in my User bean class.But I have Only have UserBean objects. I does not know which setter method to invoke.i want extract all information in my User bean Object and the invoke appropriate Method using Reflection.
//-->This My User Bean Class
public class User {
private String name;
private int age;
private String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
private int getAge() {
return age;
}
private void setAge(int age) {
this.age = age;
}
void callMethod() { System.out.println("Name :- "+name+" Age :-"+age); }
#Override
public String toString() { return "User [name=" + name + ", age=" + age + "]";}
}
//--->This My Main Method
public class TestCase {
public static Object getBean(String beanClassName) throws Exception
{
Class klass = Class.forName(beanClassName);
Object obj = klass.newInstance();
Method[] b = klass.getDeclaredMethods();
String MethodName1 = "setName";
String MethodName2 = "setAge";
String name ="sanket";
int age = 21;
for(int i=0;i<b.length;i++)
{
if(b[i].getParameterTypes().toString().equals(MethodName1))
{
b[i].setAccessible(true);
b[i].invoke(obj,name);
}
if(b[i].getName().equals(MethodName2))
{
b[i].setAccessible(true);
b[i].invoke(obj,age);
}
}
return obj;
}
public static void main(String ars[]) throws Exception
{
Object obj1 = getBean("www.Fouth.User");
System.out.println(obj1.toString());
}
}
I my case I can invoke the setter method manually because i can invoke the setter method bases of method name.
I want Extract information in User Bean Class, and identify on the bases of value which setter to invoked.
In spring #AutoWired is does that.How they will identify which setter to invoke and inject dependency.

The Spring identifies which setters to inject dependencies by figuring out which setters are annotated with #Autowired , #Inject , #Resources etc.
Then it figures out which value to inject into the setter by checking the type of the setter arguments and injects the bean that has the same type.
If multiple beans have the same type, it then check the bean name. It will inject the bean that has the same name as configured in the injection point using #Qaulifer or #Resources.
If there are still multiple beans that satisfy to inject , NoUniqueBeanDefinitionException happens.
If there are no beans that satisfy to inject, NoSuchBeanDefinitionException happens.

Related

How Spring HttpMessageConverter parse request body to the model that I define

I am a bit curious how spring handle this, and I did some experiment.
Here is my request handling method:
#PostMapping(value = "/testRequestBody")
public String testRequestBody(#RequestBody MyRequestBody requestBody) {
System.out.println(requestBody);
return "Success";
}
I have tried three types of MyRequestBody.
Tyep 1:
public class MyRequestBody {
private int id;
private String name;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "MyRequestBody(id="+id+",name="+name+")";
}}
Type 2:
public class MyRequestBody {
private int id;
private String name;
public MyRequestBody(int id, String name) {
this.id = id;
this.name = name;
}
public String toString() {
return "MyRequestBody(id="+id+",name="+name+")";
}}
Type 3:
public class MyRequestBody {
private int id;
private String name;
public String toString() {
return "MyRequestBody(id="+id+",name="+name+")";
}}
Both Type 1 and Type 2 I can get MyRequestBody(id=1,name=test name) in the console, for input {"id": 1,"name": "test name"}, and Type 3 gives me MyRequestBody(id=0,name=null). It seems Spring is able to choose different way to parse my model based on how setters and constructors are defined for my model. I would like to know how does Spring achieve that?
MappingJackson2HttpMessageConverter delegates to Jackson, the work of converting json to a java object. So, the behaviour that you are seeing can be explained by the rules jackson follows.
Following is a simplified sequence of the operations that Jackson performs, which explains the behavior that you are seeing with the three MyRequestBody definitions.
An object of the class is created by invoking the constructor. If the constructor accepts parameters, the values for the parameters are taken from the json string. Following are the rules to decide which constructor should be invoked.
If there is a constructor annotated with #JsonCreator, that constructor is invoked.
If there is no constructor annotated with #JsonCreator
If there is exactly one constructor, that constructor is invoked. This could be either a parameterized constructor or a default constructor.
If there are multiple constructors, including a default constructor, the default constructor is invoked.
In other cases, an exception is thrown.
Field values are set by calling setX() methods.
Java reflection is used for examining the constructor/methods and invoking them.
So, in your case
For the first RequestBody definition, default constructor is invoked and setX() methods are called.
For the second RequestBody definition, parameterized constructor is invoked (there is no default constructor)
The default constructor is invoked.

#value not able to read from application.properties in springboot

I am trying to read value from properties file using #value as follows.
#Value("${abc}")
private String abc;
public List<Record> fetchRecords(String label, String predicate) {
System.out.println(abc);
}
but value of abc is coming as null. Whereas when I try to print the same using #PostConstruct, I am getting the expected value.
#PostConstruct
public void postconstruct() {
System.out.println(abc);
}
Any lead why I am not able to get the value in fetchRecords() method?
For reference, here goes the code
#Component
public class AuditRecord {
private String subject;
private String predicate;
private String oldObject;
private String newObject;
private String readOnlyAuthInfo;
#Value("${registry.system.base}")
private String registrySystemContext;
public void record(DatabaseProvider provider) throws AuditFailedException {
System.out.println("---registrySystemContext value showing null here---"+registrySystemContext);
...
}
#PostConstruct
public void postconstruct() {
System.out.println("---registrySystemContext value showing here as expected---"+registrySystemContext);
}
}
The way I am calling is as follows:
#Component
public class RegistryDaoImpl implements RegistryDao {
...
private void addOrUpdateVertexAndEdge(Vertex v, Vertex dbVertex, GraphTraversalSource dbGraph, String methodOrigin){
...
AuditRecord record = new AuditRecord();
record
.subject(dbVertex.label())
.predicate(e.label())
.oldObject(null)
.newObject(existingV.label())
.record(databaseProvider);
}
}
P.S. registry.system.base is in application.yml.
You need to autowire AuditRecord and not use new directly. Only that way you will have your class in Spring's context.
We don't know your exact usage of the class but you might be interested in Spring's FactoryBean.

Hibernate -validator group sequence provider getDefaultSequenceProvider gets null as input

I am using the hibernate validator group sequence and want to execute the groups in a sequence based on business rules. But the input to the groupSequenceProvider for its getValidationGroups is always null, and hence custom sequence never gets added.
My request object:
#GroupSequenceProvider(BeanSequenceProvider.class)
public class MyBean {
#NotEmpty
private String name;
#NotNull
private MyType type;
#NotEmpty(groups = Special.class)
private String lastName;
// Getters and setters
}
Enum type:
public enum MyType {
FIRST, SECOND
}
My custom sequence provider:
public class BeanSequenceProvider implements DefaultGroupSequenceProvider<MyBean> {
#Override
public List<Class<?>> getValidationGroups(MyBean object) {
final List<Class<?>> classes = new ArrayList<>();
classes.add(MyBean.class);
if (object != null && object.getType() == MyType.SECOND) {
classes.add(Special.class);
}
return classes;
}
}
Group annotation:
public interface Special {
}
When I execute the above code, I get the input MyBean object as null and cannot add the custom sequence. What am I missing? I am using hibernate-validator version as 5.4.1.Final

Spring #Requestbody not mapping to inner class

I am doing Spring Rest Api project with Spring 4.x
This Works:
Controller.java
#PostMapping("newTransaction")
TransactionRequestModel insertNewTransaction(#RequestBody TransactionRequestModel model){
//do something
}
TransactionRequestModel.java
public class TransactionRequestModel {
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}
KeyValue.java
public class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
Request Body Json
{
"id": 1
"keyValueList": [
{
"key": "dummy",
"value": "dummy"
}
]
}
Spring message converter using jackson is working fine.
This Won't:
When i change TransactionRequestModel.java to following (and delete KeyValue.java)
public class TransactionRequestModel {
public class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}
means, making KeyValue an inner class, got following error.
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document: No suitable constructor found for type
[simple type, class
com.example.model.TransactionRequestModel$KeyValue]: can not
instantiate from JSON object (missing default constructor or creator,
or perhaps need to add/enable type information?)
Why?
All the related post in SO mentions the first scenario. I would like to know why this wont work. Please help.
You have to make your inner class static.
public class TransactionRequestModel {
public static class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}

Spring rest Json issue

I found below answered question
Different names of JSON property during serialization and deserialization
Unfortunately this does not work when we use Spring Restful webservice. I am not sure what is cauisng the issue but it gives some Field abiguity exception.
What I want to do is Serialize and deserialize a field name with different names.
For e.g.
class Test {
private String name;
#JsonProperty("myName")
public String getName() {
return name;
}
#JsonProperty("yourName")
public void setName(String name) {
this.name = name;
}
}
This does not work in Spring rest
You can not set #JsonProperty for both (getter & setter). You can set for the field or setter method.
But you want different name for request and response, Create two classes like this.
class StudentResponse{
#JsonProperty(name="student_name)
private String name;
//getter & setter
}
class StudentRequest{
#JsonProperty(name="name)
private String name;
//getter & setter
}
Damith is right, you seem to not be able to mark both methods within the same class, however there is a way to solve this:
First off, you will have to Create a custom deserializer (or serializer, depends on your preference).
My example object:
#JsonDeserialize(using = ObjectDeserializer.class)
public class MyObject {
private String name;
public void setName(String name) {
this.name = name;
}
#JsonProperty("SomeOtherName")
public String getName() {
return name;
}
}
Note, i mark the getter as the property with the first name. And I give the class a custom deserializer. Which looks like that:
public class ObjectDeserializer extends JsonDeserializer<MyObject> {
#Override
public MyObject deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
MyObject object = new MyObject();
JsonNode node = jp.getCodec().readTree(jp);
JsonNode jsonNode = node.get("MyCustomSerializeName");
object.setName(jsonNode.getTextValue());
return object;
}
}
This class will create my custom object and get the name of the setter field description (rather than relying on the property name).
Put together, i get:
public class DeserializeTest {
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
MyObject o = new MyObject();
o.setName("Hello");
String writeValueAsString = mapper.writeValueAsString(o);
System.out.println(writeValueAsString);
String jsonObj = "{\"MyCustomSerializeName\":\"Other Test\"}";
MyObject readValue = mapper.readValue(jsonObj, MyObject.class);
System.out.println(readValue.getName());
}
}
And this outputs:
{"SomeOtherName":"Hello"}
Other Test
I hope that helps you.

Resources