I have a service that I want to #Autowired, but it takes a complex object in constructor. How can I provide it using dependency injection?
#Service
class ProcessService
#Autowired
private PersonService service;
public void run() {
//create the person dynamically, eg based on some user input
Person person = new Person("test");
//new PersonService(person);
//I used to create the object myself. now switching to spring.
//how can I get the person object into the service using autowire?
}
}
#Service
class PersonService {
public PersonService(Person person) {
this.person = person; //the object the service can work with
}
}
You should annotate the costructor of your PersonService with #Autowired.
#Service
class PersonService {
#Autowired
public PersonService(Person person) {
this.person = person; //the object the service can work with
}
}
Then you must provide a Person bean somewhere.
But I guess the Person is an instance that is loaded from a database.
Since a PersonService is a singleton it makes no sense to bind it to one Person instance.
In this case you have to create a new PersonService instance for every Person object that you retrieve from the db.
If you want to do this then it also means that the PersonService is created by your code. Thus out of the spring containers control and therefore spring can not autowire it automatically.
Nevertheless you can use the AutowireCapableBeanFactory to autowire beans that have been instantiated outside the container. But this is a one way. These beans will not be available to those defined in the container.
AutowireCapableBeanFactory acbf = ...;
acbf.autowireBean(someInstance);
When I work with hibernate I usually use a PostLoadListener to autowire domain objects.
But there is also another approach using aspectj and load-wime weaving. Take a look at the spring documentation 8.4.1
If you need to advise objects not managed by the Spring container (such as domain objects typically), then you will need to use AspectJ. You will also need to use AspectJ if you wish to advise join points other than simple method executions (for example, field get or set join points, and so on).
Make a person a #Component and add #ConstructorProperties to the constructor
#Service
class PersonService {
#Autowired
#ConstructorProperties({"person"})
public PersonService(Person person) {
this.person = person; //the object the service can work with
}
}
Just add an empty constructor above the other
#Service
class PersonService {
public PersonService() {
}
public PersonService(Person person) {
this.person = person; //the object the service can work with
}
//Getters and setters
}
Then keep other class and if you want to call the person
Related
I'm making a small program using Spring, Maven and Hibernate. The current goal is to use JpaRepository to interact with a Postgresql database. However, when I try to call for it to list all entries in a table within the database, it spits out a NullPointerException. Online resources vary in their implementation, so it's been hard for me to understand what goes wrong.
My application can be summarized as follows:
Javatest3Application.java (Outermost layer, handles communication)
#SpringBootApplication
#EnableAutoConfiguration
#EnableJpaRepositories
#RestController
public class Javatest3Application {
//---VARIABLES---
private JavatestService service_handler = new JavatestService();
//---PUBLIC---
public static void main(String[] args) {
SpringApplication.run(Javatest3Application.class, args);
}
#PostMapping("/login")
public ResponseEntity<String> Login(#RequestBody Map<String, String> json_map) {
//>>Read json_map for account_name and account_pwd
//Ask Service layer to log user in
Long session_id = this.service_handler.OpenSession(account_name, account_pwd);
//>>Construct response, blah blah...
}
}
JavatestService.java (Middle layer, manages repository interaction)
#Service
public class JavatestService {
//---VARIABLES---
#Autowired
private JavatestRepository repo;
//---PUBLIC---
public JavatestService() {}
public Long OpenSession(String in_name, String in_pwd) {
//Call database for credentials
List<JavatestUser> user_listings = this.repo.findAll(); //<- THIS FAILS
//>>Go though list, blah blah...
}
}
JavatestRepository.java (Bottom layer, interface extention)
#Repository
public interface JavatestRepository extends JpaRepository<JavatestUser, Long> {
//List<JavatestUser> findAll(); <- Don't think I need to add this. I believe its already in JpaRepository
}
JavatestUser.java (Bottommost layer, DTO class for database entry)
#Entity
#Table(name = "javatest_table", schema = "javatest_schema")
public class JavatestUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long account_id;
private String account_name;
private String account_pwd;
public JavatestUser(){
}
//>>Getter and Setter functions, blah blah...
}
So, as far as I have understood it, we cannot instance objects of an interface. However, when using Spring, the program itself creates classes that implement the interface, and then hands such a derived class back to us via the #Autowired keyword.
Thus when we call the findAll() function, we use that derived class to fetch objects of the associated #Entity class.
From my research I've come to believe I might use the #Service keyword wrong, and that it perhaps should be a #Controller. However, as far as I can see, the are implementations of both alternatives, so my understanding of what differentiates them is somewhat lacking. Regardless of which I am using, the #Autowired doesn't seem to provide any JavatestRepository-derived object for me to call findAll() upon.
EDITS
Added #EnableJpaRepositories in accordance with Eugene Botyanovsky's suggestion.
You are probably missing annotation, which enables all your repositories:
#EnableJpaRepositories
It's similar to #EnableAutoConfiguration you used, but exactly for JPA repositories.
As JPA and Spring have different context management, it is not recommended to create a data object class with both annotations #Component and #Entity.
But without #Component data object can't be injected into a service by #Autowired.
But creating new instance of my data object with new seems like a regression for me.
Is there a good way to inject a data object (#Entity) in a spring managed service ?
Data object :
#Component
#Entity
#Table(name = "user")
public class UserDo {
//data object stuff ...
Service :
#Service("listAllGoods")
#Transactional(propagation = Propagation.REQUIRED)
public class ListAllGoods implements IListGoodService{
#Autowired
private IGoodDao goodDao;
#Autowired
private UserDo user;
//option 1 : works but not recommended because forces #Component on data object
#Override
public List<GoodDo> createGood() {
user.setName("Roger");
return goodDao.create(user);
}
//option 2 :
// without #Autowired UserDo
// regression feeling
#Override
public List<GoodDO> createGood() {
UserDo user = new UserDo();
user.setName("Roger");
return goodDao.create(user);
}
The main feature of Spring is dependency injection.
Dependency or coupling, a state in which one object
uses a function of another object
It's clear that User entity is not a dependency in your situation, so it's the most correct approach to create it with a new operator.
Also, you said that you want your "dependency" to be created every time you reference your service. It's the "How to update prototype bean in a singleton" problem which you can encounter on an interview. It's not in the scope of your question, but I highly recommend you to google this.
First of all, I'm a relative noob to Spring Boot, so keep that in mind.
I've got a REST api in which I'm trying to minimize database calls for the same object and I've determined that using a Spring Bean scoped to the Request is what I want. Assuming that is correct, here is what I'm trying to do:
1) Controller takes in a validated PhotoImportCommandDto command
PhotoCommandController
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> importPhoto(#Valid #RequestBody PhotoImportCommandDto command){
...
}
2) PhotoImportCommandDto is validated. Note the custom #UserExistsConstraint which validates that the user exists in the database by calling a service method.
PhotoImportCommandDto
#Component
public class PhotoImportCommandDto extends BaseCommand {
#NotNull(message = "userId must not be null!")
#UserExistsConstraint
private Long userId;
...
}
What I would like to do is somehow set a Spring Bean of the user that is validated in the #UserExistsConstraint and reference it in various methods that might be called throughout a Http request, but I'm not really sure how to do that. Since I've never really created my own Spring Beans, I don't know how to proceed. I've read various guides like this, but am still lost in how to implement it in my code.
Any help/examples would be much appreciated.
You can use the #Bean annotation.
#Configuration
public class MyConfiguration {
#Bean({"validUser"})
public User validUser() {
User user;
//instantiate user either from DB or anywhere else
return user;
}
then you can obtain the validUser.
#Component
public class PhotoImportCommandDto extends BaseCommand {
#Autowired
#Qualifier("validUser")
private User validUser;
...
}
I don't really know how to make annotations in Java. Anyway, in Spring, checking where the User exists in the DataBase or not is one line of code:
userRepository.findOne(user) == null
That is accomplished by the Spring Data JPA project:
Create a JPA Entity User.
Set the spring.datasource.url and login/password in the
resources/application.properties.
Create this interface:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
Note, Spring implements it behind the scences.
Inject this interface into your RestController (or any other Spring bean):
private UserRepository userRepository ;
**constructor**(UserRepository ur){
userRepository = ur;
}
Note, a Spring Bean is any class annotated #Component (this includes stereotype annotations like Controller, Repository - just look up the contents of an annotation, it may use #Component internally) or returned from a method which is annotated #Bean (can only be on the Component or Configuration class). A Component is injected by searching the classpath, Bean is injected more naturally.
Also note, injecting is specifying #Autowired annotation on field or constructor, on a factory method, or on a setter. The documentation recommends that you inject required dependencies into constructor and non-required into the setter.
Also note, if you're injecting into a constructor and it is clean by the arguments, you may omit #Autowired annotation, Spring will figure it out.
Call its method findOne.
So, you can do one of the following:
Inject the userRepository into the #RestController constructor (as shown above). I would do that.
Inject the userRepository into the #Service (internally #Component) class that will do this sorts of thing for you. Maybe you can play with it to create an annotation.
p.s. Use #PostMapping instead of #RequestMapping(method = RequestMethod.POST)
p.p.s. If ever in doubt, go to the official documentation page and just press CTRL-F: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ Note the current word, that will always take you to the latest version.
p.p.p.s Each Spring project has its own .io webpage as well as quick Get Started Guides where you can quickly see the sample project with explanations expecting you to know nothing.
Hope that helps! :)
Don't forget to mark the answer as accepted if you wish
Using Jose's input, I took a bit of a different route.
Here's what I did:
I created a ValidatedUser class:
#RequestScope
#Component
public class ValidatedUser {
private UserEntity user;
public UserEntity getUser() {
return user;
}
public void setUser(UserEntity user) {
this.user = user;
}
}
and I also created a wrapper class HttpRequestScopeConfig to capture all variables to use over the course of an Http Request to the api.
#Component
public class HttpRequestScopeConfig {
#Autowired
private ValidatedUser validatedUser;
...
public UserEntity getValidatedUser() {
return validatedUser.getUser();
}
public void setValidatedUser(UserEntity validatedUser) {
this.validatedUser.setUser(validatedUser);
}
...
}
In my UserExistsConstraintValidator (which is the impl of #UserExistsConstraint, I set the validatedUser in the httpRequestScopeConfig:
public class UserExistsConstraintValidator implements ConstraintValidator<UserExistsConstraint, Long> {
//private Log log = LogFactory.getLog(EmailExistsConstraintValidator.class);
#Autowired
private UserCommandService svc;
#Autowired
private HttpRequestScopeConfig httpRequestScope;
#Override
public void initialize(UserExistsConstraint userId) {
}
#Override
public boolean isValid(Long userIdField, ConstraintValidatorContext context) {
try {
UserEntity user = svc.findUserOfAnyStatus((Long) userIdField);
if (user != null) {
httpRequestScope.setValidatedUser(user);
return true;
}
} catch (Exception e) {
//log.error(e);
}
return false;
}
}
Now, I can access these variables throughout the rest of my service layers by autowiring HttpRequestScopeConfig where necessary.
Most of my classes wired up look something like:
#Component
public class MyClassImpl implements MyClass {
private MyService service;
#Autowired
public MyClass(MyService service) {
this.service = service;
}
}
So that makes sense to me, but if I want to do something like this:
#Component
public class MyClassImpl implements MyClass {
private MyService service;
private String id; // this is what I need
#Autowired
public MyClass(MyService service, String id) {
this.service = service;
this.id = id;
}
}
But the problem is String id is not known until runtime. Is there any way to do this? From what I can tell, Spring checks all the dependencies by default at runtime so if I try the second example, it complains about the constructor arguments.
I've seen some examples where you can use a factory to create the actual value later down the line. Or I can create getters and setters in order to set the id when I need it, but that would also mean I'd need to add those getters and setters to the interface MyClass as well. Is there a cleaner way to do this?
You can create a #Bean method for id with logic needed to calculate it.
#Configuration
public class Config {
#Bean
public String idForService() {
return calculateId();
}
}
And then your service constructor will be look like this
#Autowired
public MyClass(MyService service, #Qualifier("idForService") String id) {
this.service = service;
this.id = id;
}
But as others have already mentioned it looks like a bad practice so you'd better consider to redesign your service class.
I am afraid what you are trying to do is not in the spirit of what Spring was created for. Please do have a look at: Runtime dependency injection with Spring
Having said that, there might be a workaround to your problem.
If you have some class that calculates that ID for you, just inject that class in a constructor and bind the result of its calculation to the field id in your class.
I am trying to autowire a member in a class using the constructor.
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(ClassA objectA) {
myMember = objectA;
}
}
If I have multiple sources that create beans of ClassA, is it possible to have a duplicate constructor definition that instantiates based on the bean that was autowired into this class?
I want to do something like this:
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(#Qualifier ("qualifierA") ClassA objectA) {
myMember = objectA;
}
#Autowire
public MyClass(#Qualifier ("qualifierB") ClassA objectB) {
myMember = objectB;
}
}
I tried using #Qualifier this way, but it didn't work.
Is it possible to do what I'm trying to do, with Spring? How can I disambiguate based on the name (qualifierA) or (qualifierB), if the bean definition is like:
#Bean (name = "qualifierA")
public ClassA getQualifierA() {
...
}
#Bean (name = "qualifierB")
public ClassA getQualifierB() {
...
}
You can't have two constructors with the exact same signature in a single class in Java. Nor any other programming language I've ever encountered. You might use method-injection instead, with two methods (named differently, of course), mark them as #Autowired(required = false) and use the proper #Qualifier(...) to specify the instance you want to inject. You might want to handle the case when both instances are present in the spring context, so no unexpected things happen.
The short answer is: no, that is not possible. In Java you cannot have two constructors with exactly the same signature. And also, you can assign only one value to your "myMember".
However, what are you trying to accomplish here? It seems that in some occasions MyClass needs to use "objectA" and in other occasions, you need "objectB".
For these scenarios, you should not use autowiring (you can't), but simply use explicit wiring:
#Bean
MyClass myObject() {
return new MyClass(qualifierA());
}