I have a model for service to use. But the autowired annotation returns null value. However this autowire works well in service. Is there any to do in the Module class?
public class Module{
private int id;
private String name;
private ArrayList<Function> functions;
#Autowired
private SysModuleLgDao sysModuleLgDao;
public Module() {
sysModuleLgDao.doSth();
}
}
This is my repo class:
#Repository
public interface SysModuleLgDao extends JpaRepository<SysModuleLgEntity, Long> {
public List<SysModuleLgEntity> findByModuleLgIdAndLanguageId(long moduleLgId,long languageId);
}
Object's constructor is called before Spring has a chance to do the wiring, so sysModuleLgDao reference is in its default state (i.e. null). Spring's context loading happens in a later phase, long after the default constructor has been called.
You can work around the problem by performing doSth in a different lifecycle phase, after the Spring bean has been constructed. This can be achieved via the #PostContstruct annotation:
public class Module {
#Autowired
private SysModuleLgDao sysModuleLgDao;
#PostConstruct
public void initialise() {
// at this point sysModuleLgDao is already wired by Spring
sysModuleLgDao.doSth();
}
}
More details here: Why use #PostConstruct?
Related
I am learning Spring while I like the idea of using #Component and #Autowired to let Spring manage the dependent bean. For example, I have a Service class and Builder Class I can do with
// SomeService.java
#Service
public class SomeService {
// SomeBuilder is a #Component class
#Autowired
SomeBuilder someBuilder;
}
// SomeController.java
#Component
public class SomeController {
#Autowired
SomeService someSerivce;
}
Spring would take care of the creation of from SomeController to SomeService to SomeBuilder with the usage of #Autowired. However, now my SomeService class needs a private field which is NOT a Component class, just a plain context object, for example
// SomeService.java
#Service
public class SomeService {
#Autowired
SomeBuilder someBuilder;
private SomeContext someContext;
// Plan A: Using constructor to initiate the private field. However, I cannot use #Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
// Plan B: using #Autowired on constructor level, I cannot use this because SomeContext is not a #Component class
//public SomeService(SomeContext someContext) {
//this.someContext = someContext;
//}
// Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
//public void init(SomeContext someContext) {
// this.someContext = someContext;
//}
// demo usage of someContext
public someAnswer realMethod() {
System.out.println(someContext.getName());
}
}
Now I have no idea how to inject the someContext now, I used
plan A: Assign the private field using class constructor
plan B: Using #Autowired on constructor level
plan C: Using a wired method to assign the private field.
but I am not satisfied and don't have a clear way of doing the right approach.
First lets take a look at your plans and bust some myths/misunderstandings.
Plan A: Using constructor to initiate the private field. However, I cannot use #Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
Great plan, and the way to go. #Autowired doesn't depend on having a default constructor. It only indicates that you want the field to be injected with an object of that type. How that object comes to live (default constructor, constructor with arguments) doesn't matter for #Autowired. So that part of your understanding is just wrong.
using #Autowired on constructor level, I cannot use this because SomeContext is not a #Component class
If there is just a single constructor in a bean Spring will automatically use that to satisfy the dependencies. So in this case you don't need #Autowired. A bean doesn't have to be an #Component, a bean is just an instance of a class available to the application context. One way of achieving that is by marking it as an #Component but there are other ways as well. Like defining an #Bean method in in an #Configuration class to construct the bean.
#Configuration
#ComponentScan("your.base.package")
public class YourConfiguration {
#Bean
public SomeContext someContext() {
return new SomeContext();
}
}
Something along those lines. It will detect the #Component annotated classes through the #ComponentScan and will create a bean of type SomeContext for use as a bean.
Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
All your fields should be private not just the ones initialized in a constructor, so also the #Autowired ones. You don't want those fields to be, easily, accessible from the outside so they can be modified. So make them private.
That all being said, go with constructor injection over field injection or setters/methods for injection. It is clearer and less hidden than field injection and the way to go for mandatory dependencies (for optional dependencies you can use a setter/method).
So using the above config and below classes, it should "just work (tm)".
// SomeService.java
#Service
public class SomeService {
// SomeBuilder is a #Component class
private final SomeBuilder someBuilder;
private final SomeContext someContext;
public SomeService(SomeBuilder someBuilder, SomeContext someContext) {
this.someBuilder=someBuilder;
this.someContext=someContext;
}
}
// SomeController.java
#Component
public class SomeController {
private final SomeService someSerivce;
public SomeController(SomeService someService) {
this.someService=someService;
}
}
I have a Rest Controller in which I initialise a service like this :
class Config {
#Value(${"number.of.books"})
private final static String numberOfBooks;
}
class MyController {
private final Service myService = new ServiceImplementation(Config.numberOfBooks)
public ResponseEntity methodA() { ... }
}
The numberOfBooks field has a initialisation value but when it's passed in the ServiceImplementation constructor it comes null.
I'm thinking I'm missing something obvious over here.
What is the mistake and which would be the best practice to inject a value from a property file into a constructor?
I recommend you to directly inject numberOfBooks in your ServiceImplementation, as follows:
public class ServiceImplementation implements Service {
#Value("${number.of.books}")
private String numberOfBooks;
}
Otherwise use setter injection for static variables, as follows:
#Component
class Config {
public static String numberOfBooks;
#Value("${number.of.books}")
public void setNumberOfBooks(String numberOfBooks) {
numberOfBooks = numberOfBooks;
}
}
After studying a little I've found out that the dependency injection happens after the constructor has been called. This being said the approach used was to use Autowired on my services constructor.
class ServiceImplementation implements Service {
private final String numberOfBooks;
#Autowired
private ServiceImplementation(Config config) {
this.numberOfBooks = config.getNumberOfBooks();
}
}
In this way Spring creates the dependency tree and makes sure that Config is not null when injected.
In my Spring Boot application, i have a configuration, which reads entries from a Mongo database.
After this is done, my subclass of AbstractMongoEventListener is created, even though it operates on a different table and different scope (my own custom #CustomerScope).
Here is the listener:
#CustomerScoped
#Component
public class ProjectsRepositoryListener extends AbstractMongoEventListener<Project> {
#Override
public void onAfterSave(Project source, DBObject dbo) {
System.out.println("saved");
}
}
And here the configuration:
#Configuration
public class MyConfig {
#Autowired
private CustomersRepository customers;
#PostConstruct
public void initializeCustomers() {
for (Customer customer : customers.findAll()) {
System.out.println(customer.getName());
}
}
}
I find it surprising that the listener is instantiated at all. Especially since it is instantiated well after the call to the customers repository has finished.
Is there a way to prevent this? I was thinking of programmatically registering it per table/scope, without annotation magic.
To prevent auto-instantiation, the listener must not be annotated as #Component. The configuration needs to get ahold of the ApplicationContext, which can be autowired.
Thus, my configuration class looks like this:
#Autowired
private AbstractApplicationContext context;
private void registerListeners() {
ProjectsRepositoryListener firstListener = beanFactory.createBean(ProjectsRepositoryListener.class);
context.addApplicationListener(firstListener);
MySecondListener secondListener = beanFactory.createBean(MySecondListener.class);
context.addApplicationListener(secondListener);
}
Note that this works for any ApplicationListener, not just AbstractMongoEventListener.
I have been trying to inject a service to a contract:
#org.jvnet.hk2.annotations.Contract
public interface CategoryDAO{
}
#org.jvnet.hk2.annotations.Service
public class HibernateCategoryDAO implements CategoryDAO
}
#org.jvnet.hk2.annotations.Service
public class MyCategoryManager implements CategoryManager {
#javax.inject.Inject
CategoryDAO categoryDAO;
}
But categoryDAO is null.
I tried even to bind it:
public class ServiceBinder implements Binder {
#Override
public void bind(DynamicConfiguration config) {
config.bind(BuilderHelper.link(HibernateCategoryDAO.class).to(CategoryDAO.class).build());
}
}
But still it stays null.
I init the MyCategoryManager with Reflections framework like this:
Set<?> managers = Reflections.getSubTypesOf(CategoryManager.class);
If you create your own object but still want it injected with hk2 you can use the ServiceLocator.inject method. In your case the descriptor HibernateCategoryDAO must be already in the ServiceLocator (perhaps using ServiceLocatorUtilities.addClass). Be aware that the instances you created yourself will not be managed or tracked by HK2 so they cannot be injected into other objects. Of course, you can add the objects you created yourself using ServiceLocatorUtilities.addOneConstant if you DO want it to be injected into other objects.
Hope that helps.
I could make use of preConstruction=true for my domain class so that i can make use of autowired fields in the constructor such as this :
#Configurable(preConstruction=true)
public class MyDomain {
#Autowired private MyContext context;
public MyDomain() {
context.doSomething(this); // access the autowired context in the constructor
}
}
But then, what is the equivalence for preConstruction when i would like to access autowired fields in a class with the normal stereotype annotation such as #Repository or #Service aside from constructor injection (Currently using spring 3.x here ..) ?
#Repository
public class MyDomainRepository {
#Autowired private MyContext context;
public MyDomain() {
// cannot access the autowired context in the constructor
context.doSomething(this);
}
}
I don't think something like this is available for regular Spring beans, but the usual way to solve this problem is to use #PostConstruct-annotated method instead of constructor:
#PostConstruct
public void init() {
context.doSomething(this);
}
This method will be called by Spring after all dependencies are injected.