I have a problem with Guice. I would like to show this with an example. I want to pass the parameters of this constructor always dynamically, so e.g. new Test("bob", 765);. I also want some fields (like here SomeOtherObject) to be injected by Guice. How can I do this? Thanks in advance!
public class Test {
private String name;
private int id;
//Needs to be injected by Guice
#Inject
SomeOtherObject object;
public Test(String name, int id) {
this.name = name;
this.id = id;
}
}
Guice's AssistedInject feature is a good way to handle this.
Basically you define a factory to get Test objects with:
public interface TestFactory {
public Test create(String name, int id);
}
Augment the Test class's #Inject constructor with #Assisted annotation:
public class Test {
#Inject
public Test(SomeOtherObject object, #Assisted String name, #Assisted int id) {
this.object = object;
this.name = name;
this.id = id;
}
}
And then use FactoryModuleBuilder in your Module's configure:
install(new FactoryModuleBuilder().build(TestFactory.class));
And instead of constructing Tests directly, inject a TestFactory and use it to create Tests:
public class OtherThing {
#Inject
TestFactory factory;
public Test doStuff(Stirng name, int id) {
return factory.create(name, id);
}
}
Note: looking at the docs now, it appears that AutoFactory has been introduced as the preferred way to do this, and may be simpler.
Related
I am trying to see if I can replace my existing Pojos with the new Record classes in Java 14. But unable to do so. Getting following error:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot
construct instance of com.a.a.Post (no Creators, like default
construct, exist): cannot deserialize from Object value (no delegate-
or property-based Creator)
I get that the error is saying the record has no constructors, but from what I see the record class takes care of it in the background and relevant getters are also set in the background (not getters exactly but id() title() and so on without the get prefix). Is it cos Spring has not adopted the latest Java 14 record yet? Please advice. Thanks.
I am doing this in Spring Boot version 2.2.6 and using Java 14.
The following works using the usual POJOs.
PostClass
public class PostClass {
private int userId;
private int id;
private String title;
private String body;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
Method to call rest service which works now as I am using the above POJO.
public PostClass[] getPosts() throws URISyntaxException {
String url = "https://jsonplaceholder.typicode.com/posts";
return template.getForEntity(new URI(url), PostClass[].class).getBody();
}
But if I switch to following where I am using record instead, I am getting the above error.
The new record class.
public record Post(int userId, int id, String title, String body) {
}
Changing the method to use the record instead which fails.
public Post[] getPosts() throws URISyntaxException {
String url = "https://jsonplaceholder.typicode.com/posts";
return template.getForEntity(new URI(url), Post[].class).getBody();
}
EDIT:
Tried adding constructors as follows to the record Post and same error:
public record Post(int userId, int id, String title, String body) {
public Post {
}
}
or
public record Post(int userId, int id, String title, String body) {
public Post(int userId, int id, String title, String body) {
this.userId = userId;
this.id = id;
this.title = title;
this.body = body;
}
}
It is possible with some Jackson Annotations, which cause Jackson to use fields instead of getters. Still far less verbose than a pre-Java 14 class (without Lombok or similar solutions).
record Foo(#JsonProperty("a") int a, #JsonProperty("b") int b){
}
This probably works because according to https://openjdk.java.net/jeps/359:
Declaration annotations are permitted on record components if they are
applicable to record components, parameters, fields, or methods.
Declaration annotations that are applicable to any of these targets
are propagated to implicit declarations of any mandated members.
See also: When is the #JsonProperty property used and what is it used for?
It is also possible to make use #JsonAutoDetect
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
record Bar(int a, int b){
}
If configuring the Objectmapper to use field Visibility globally, this annotation on class level is not needed.
See also: How to specify jackson to only use fields - preferably globally
Example:
public class Test {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper om = new ObjectMapper();
System.out.println(om.writeValueAsString(new Foo(1, 2))); //{"a":1,"b":2}
System.out.println(om.writeValueAsString(new Bar(3, 4))); //{"a":3,"b":4}
}
record Foo(#JsonProperty("a") int a, #JsonProperty("b") int b){
}
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
record Bar(int a, int b){
}
}
There is also a Github issue for that feature: https://github.com/FasterXML/jackson-future-ideas/issues/46
This is slated for jackson 2.12
https://github.com/FasterXML/jackson-future-ideas/issues/46
The compiler generates the constructor and other accessor method for a Record.
In your case,
public final class Post extends java.lang.Record {
public Post(int, int java.lang.String, java.lang.String);
public java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object);
public int userId();
public int id();
public java.lang.String title();
public java.lang.String body();
}
Here you can see that there is not default constructor which is needed got Jackson. The constructor you used is a compact constructor,
public Post {
}
You can define a default/no args constructor as,
public record Post(int userId, int id, String title, String body) {
public Post() {
this(0,0, null, null);
}
}
But Jackson uses Getter and Setters to set values. So in short, you can not use Record for mapping the response.
EDIT as PSA: Jackson can properly serialize and deserialize records as of 2.12 which has been released.
Use the parameter names module for jackson, https://github.com/FasterXML/jackson-modules-java8/tree/master/parameter-names (make sure the compiler sets -parameters) or add `#JsonProperty("name") to each field in the record
add #JsonCreator to the constructor. I can't tell if the inheritance will work properly, so you might have to explicitly declare the constructor and annotate it.
If a public accessor method or (non-compact) canonical constructor is declared explicitly, then it only has the annotations which appear on it directly; nothing is propagated from the corresponding record component to these members.
From https://openjdk.java.net/jeps/384
So add
new ObjectMapper().registerModules(new ParameterNamesModule())
and try
#JsonCreator record Value(String x);
or something like
record Value(String x) {
#JsonCreator
public Value(String x) {
this.x = x;
}
}
or all the way to
record Value(#JsonProperty("x") String x) {
#JsonCreator
public Value(#JsonProperty("x") String x) {
this.x = x;
}
}
This is how I get immutable pojos with lombok and jackson to work, and I don't see why records wouldn't work under the same format. My setup is Jackson parameter names module, -parameters compiler flag for java 8 (I don't think this is required for like jdk9+), #JsonCreator on the constructor. Example of a real class working with this setup.
#Value
#AllArgsConstructor(onConstructor_ = #JsonCreator)
public final class Address {
private final String line1;
private final String line2;
private final String city;
private final String region;
private final String postalCode;
private final CountryCode country;
}
I have two beans in a spring-boot application:
#Component
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class Shape {
#Resource
private ShapeService shapeService;
private String name;
private String description;
public Shape(String name) {
this.name = name;
this.description = shapeService.getDescription();
}
}
#Service
public class ShapeService {
public String getDescription() {
return "This is a shape.";
}
}
I created the Shape instance using the following code:
Shape shape = beanFactory.getBean(Shape.class, "shape");
But I got a NullPointerException on the following line:
this.description = shapeService.getDescription();
shapeService is null. Is there any way to use shapeService inside Shape's constructor?
The problem is that Spring has to create an object before it can do field injection on it. So the field you are referencing hasn't been set yet by Spring, but will be later on, after the object is fully constructed. If that line were in a regular method, it would work.
To fix this, you have to have Spring pass the reference to your ShapeService to your constructor via a constructor argument. Change your code for your Shape class to look like this:
#Component
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class Shape {
private ShapeService shapeService;
private String name;
private String description;
public Shape(String name, ShapeService shapeService) {
this.name = name;
this.shapeService = shapeService;
this.description = shapeService.getDescription();
}
}
I prefer constructor argument injection over autowiring even if it isn't necessary, like it is in your case. Constructor injection is generally considered to be better form. Here's an article that explains why
I use MongoDBRepository in spring boot, and when I save some object in database everything is ok. but when I find object by id spring does not allow do that.
I try to change VehicleRoutingProblemSolution type to Object type, but VehicleRoutingProblemSolution have other object field PickupService and it without default constructor to. And yes, this class has immutable... I can't create default constructors, what can I do?
import com.fasterxml.jackson.annotation.JsonProperty;
import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection = "vrp_solutions")
public class VrpSolutionHolder {
// Specifies the solution id
#Id
#JsonProperty("id")
private String id;
// Specifies the solution id
#JsonProperty("solution")
private VehicleRoutingProblemSolution vehicleRoutingProblemSolution;
// Created at timestamp in millis
#JsonProperty("created_at")
private Long created_at = System.currentTimeMillis();
public VrpSolutionHolder(String id, VehicleRoutingProblemSolution vehicleRoutingProblemSolution) {
this.id = id;
this.vehicleRoutingProblemSolution = vehicleRoutingProblemSolution;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public VehicleRoutingProblemSolution getVehicleRoutingProblemSolution() {
return vehicleRoutingProblemSolution;
}
public void setVehicleRoutingProblemSolution(VehicleRoutingProblemSolution vehicleRoutingProblemSolution) {
this.vehicleRoutingProblemSolution = vehicleRoutingProblemSolution;
}
public Long getCreated_at() {
return created_at;
}
public void setCreated_at(Long created_at) {
this.created_at = created_at;
}
}
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
org.springframework.data.mapping.model.MappingInstantiationException:
Failed to instantiate
com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution
using constructor NO_CONSTRUCTOR with arguments
I ran into the exact same problem. A persistent immutable class containing other class instances, throwing that aforementioned exception when retrieved by this repository method:
public interface ProjectCodeCacheRepository extends MongoRepository<CachedCode, String> {
public CachedCode findByCode(String code);
public List<CachedCode> findByClientId(UUID clientId);
}
...
List<CachedCode> cachedForClient = this.codeCacheRepo.`**findByClientId**`(clientId);
...
Following Erwin Smouts hints, this is nicely fixed by giving it a special constructor annotated org.springframework.data.annotation.PersistenceConstructor like so:
#Document(collection="cachedcodes")
public class CachedCode {
#PersistenceConstructor
public CachedCode(String code, UUID clientId, LocalDateTime expiration) {
this.code = code;
this.clientId = clientId;
this.expiration = expiration;
}
public CachedCode(String code, UUID clientId, long secondsExpiring) {
this.code = code;
this.clientId = clientId;
this.expiration = LocalDateTime.now().plusSeconds(secondsExpiring);
}
public UUID getClientId( ) {
return this.clientId;
}
public String getCode() {
return this.code;
}
public boolean hasExpired(LocalDateTime now) {
return (expiration.isBefore(now));
}
...
#Id
private final String code;
private final UUID clientId;
private final LocalDateTime expiration;
}
So, you should check if your VehicleRoutingProblemSolution has a) a constructor that matches the database fields (check in mongo client) and b) is annotated to be the one used by the driver (or whichever piece of Spring magic under the hood).
If your framework tool requires (visible) no-arg constructors (plus accompanying setters), and the class you have is required to stay as is, then you could roll your own, say, MutableVehicleRoutingProblemSolution where in the setters you could have :
this.vehicleRoutingProblemSolution = new VehicleRoutingProblemSolution(vehicleRoutingProblemSolution.getId(), newSolution);
Thus your MutableVehicleRoutingProblemSolution wraps around the existing VehicleRoutingProblemSolution.
Hacky smell to it, but it fits the requirements.
(Or you could try to find a tool that is able to use, not annotations on the contained fields, but annotations on constructor arguments.)
This is a problem where the corresponding class does not have a no-arg constructor like - I was facing an issue with java.io.File.
Solution:
In general - change the declaration to Object class and convert where we are using the class.
from
class MyClass{
File myfile;
}
to
class MyClass{
Object myFile;
}
For anyone using lombok, you need to remove the #Builder annotation on your class and use #Data instead, or follow the above solution to provide a specialized constructor
Oddly, I received this when I attempted to decorate a custom interface with ...
#Document(collection = "Person")
Example:
package test.barry.interfaces;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
#Document(collection = "Person")
public interface CustomRepository
{
void updatex(Query filterPredicate, UpdateDefinition updatePredicate);
}
I ran into an error while implementing assisted injection.
Assisted injection worked up until I introduced another class called Manager which relies on assisted Person class. Manager wants to use Person (#Assited Address). The code breaks at the point of constructing injector graph. It does not go further.
Injector injector = Guice.createInjector(myModule);
Intuitively, I understand when object A is assisted then B (who depends on A) is in fact also implicitly assisted through A.
Pls note, I checked SO. I think someone like ColinD would definitely know the answer
How to use Guice's AssistedInject?
How to bind Assisted Injected class to interface?
Out of curiosity, are there good techniques/tools to spot Guice misconfiguration and ease learning curve? I turned on ProvisionListener and using graph library. That helps a bit.
public class Person implements PersonInterface {
private String name;
private Address address;
#Inject
public Person(String name, #Assisted Address address) {
this.name = name;
this.address = address;
}
}
public interface PersonInterface {
public String getName();
public Address getAddress();
}
public interface PersonFactory {
public PersonInterface create(Address address);
}
public class Address {
private final String address;
public Address(String address) {
super();
this.address = address;
}
}
public class Manager implements IManager {
private final Person person;
#Inject
public Manager(Person person) {
this.person=person;
}
...
}
configure() {
install(new FactoryModuleBuilder()
.implement(PersonInterface.class, Person.class)
.build(PersonFactory.class));
//
bind(IManager.class).to(Manager.class);
}
Actual error is
com.google.inject.CreationException: Unable to create injector, see the following errors:
1) No implementation for ...assisted_inject.Address annotated with #com.google.inject.assistedinject.Assisted(value=) was bound.
while locating ....assisted_inject.Address annotated with #com.google.inject.assistedinject.Assisted(value=)
for parameter 2 at ....assisted_inject.Person.<init>(Person.java:13)
When you put this binding into your module:
bind(IManager.class).to(Manager.class);
Guice will try to create a new instance of The Manager class. It looks for either one (but only one) constructor annotated with #Inject or a as a fallback zero-argument constructor that is not private. This is the constructor that Guice will use:
#Inject
public Manager(Person person) {
this.person=person;
}
Now following the same rule Guice will try to instantiate a Person by using an appropriate constructor and it will get stuck here:
#Inject
public Person(String name, #Assisted Address address) {
this.name = name;
this.address = address;
}
It will give up when trying to instantiate the address because of the #Assisted annotation. This annotation is a BindingAnnotation and Guice treats these specially - it tries to find explicit bindings for them and there are none. Read about binding annotations and you will understand why.
Since your manager is stateful and apparently manages a single person you may want to create a factory for these managers, e.g.:
public interface IManagerFactory {
public IManager getIManager(PersonInterface p);
}
Then you will have an IManager, e.g.:
public interface IManager {
public String getPersonName();
}
And an implementation that uses assisted injection:
public class Manager implements IManager {
private final PersonInterface person;
#Inject
public Manager(#Assisted PersonInterface person) {
this.person = person;
}
#Override
public String getPersonName() {
return person.getName();
}
}
You can bind these in your module:
class MyModule extends AbstractModule {
protected void configure() {
install(new FactoryModuleBuilder()
.implement(PersonInterface.class, Person.class)
.build(PersonFactory.class));
install(new FactoryModuleBuilder()
.implement(IManager.class, Manager.class)
.build(IManagerFactory.class));
}
}
Inject the factories:
#Inject
PersonFactory pf;
#Inject
IManagerFactory manF;
And use them accordingly, e.g.:
public void testGuice() {
PersonInterface pi = pf.create(new Address("boh"));
IManager im = manF.getIManager(pi);
System.out.println(im.getPersonName());
}
I am using Picocontainer in a study project. I am having doubts about how to use it.
The following is the class I have :
public class DependencySupplier {
public static MutablePicoContainer pico;
static {
pico = new DefaultPicoContainer();
pico.registerComponentImplementation(CollectionDao.class, CollectionDaoImpl.class);
pico.registerComponentImplementation(ReadingDao.class, ReadingDaoImpl.class);
pico.registerComponentImplementation(CollectionDetails.class, CollectionDetailsImpl.class);
pico.registerComponentImplementation(Reading.class, ReadingImpl.class);
}
public static CollectionDao getCollectionDao() {
return (CollectionDao) pico.getComponentInstance(CollectionDao.class);
}
public static ReadingDao getReadingDao() {
return (ReadingDao) pico.getComponentInstance(ReadingDao.class);
}
}
My doubts are:
Is this the right way to use pico ?
The AddressImpl class is as follows:
public class AddressImpl implements Address {
private String address1;
private String address2;
private String address3;
private String address4;
public AddressImpl(String address1, String address2, String address3,
String address4) {
super();
this.address1 = address1;
this.address2 = address2;
}
public String getAddress1() {
return address1;
}
public void setAddress1(String address1) {
this.address1 = address1;
}
public String getAddress2() {
return address2;
}
public void setAddress2(String address2) {
this.address2 = address2;
}
public String getAddress3() {
return address3;
}
}
How can I instantiate the Address object with the above implementation as 'address1' and 'address2' has to be supplied by user and will be available on run time ?
Well, actually it's not a right way to use pico...
In most cases you should never directly lookup components from the pico context.
You need to register in pico your DAOs, services, other logic classes... They need to obtain referenced DAOs etc just declaring them as constructor arguments. Then in your bootstrap class that registers components you need to obtain from container your main logic class and call its method(s) (or use Startable lifecycle)
As for address instances, I'm not sure you need to instantiate them from the pico at all (cause I don't see ANY dependencies that container may fulfill for the Address, so what point?)
But still if you need to, you may register ready instances like pico.registerComponentInstance(new AddressImpl(...)) then you can inject all available instances with constructor argument Address[] addrs. There's another way, to instantiate several instances directly from the pico, but I think you just don't need it