Spring Injection - access to the injected object within a constructor - java

I have a resource (Spring bean) which has some of its fields injected by Spring, for example:
#Repository(value="appDao")
public class AppDaoImpl implements AppDao {
#PersistenceContext
EntityManager entityManager;
public Resource() {
... use entityManager ... // doesn't work
}
}
I know that I can't access the injected entityManager in the constructor and should use a #PostConstruct annotation on a different method. But what are the reasons for this?

Because Spring can't access any fields or methods before the object is created (which is done through the constructor). So Spring instantiates the object using the constructor and then injects the properties.
The only way around this is to use Constructor Injection (which can be cumbersome if you have multiple dependencies). I think what you should do is move your code out of the constructor and into an initialization method using the #PostConstruct annotation:
#PostConstruct
public void init(){
// do stuff with entitymanager here
}

The reason is in the lifecycle of the bean:
The container (spring application context) instantiates the object
then it sets all the dependencies (incl. the entityManager in your example)
after all dependencies have been set, it invokes the #PostConstruct method, if any.
Spring (and no one) can set fields to an object before actually constructing that object.
You can use constructor-injection - passing the dependencies to a non-default constructor, but it is not possible with #PersistenceContext

Related

#PersistenceContext is not applicable to parameters. How to inject EntityManager through a constructor?

It is a best practice to use constructor injection. However I can't acheive this with #PersistenceContext.
I would like to have the following constructor:
private final EntityManager entityManager;
#Autowired
public MyService(#PersistenceContext EntityManager entityManager) {
this.entityManager = entityManager;
}
But I can't since #PersistenceContext is only applicable to TYPE, METHOD and FIELD.
Q: How do I inject a container-managed EntityManager through constructor injection?
You seem to be using spring so your solution will be rather easy :
#Component
#Scope("prototype")
public class MyPersistenceContainer
{
#PersistenceContext
private EntityManager em;
public EntityManager getEntityManager()
{
return em;
}
}
And now you can simply inject an instance of this class in your constructor, it will always hold a valid EntityManager (because of the bean scope). Mind you : in a web environment you probably should use #SessionScope or even #RequestScope instead of prototype, this will save resources
But there is something to consider :
When using singleton-scoped beans that have dependencies on beans that
are scoped as prototypes, please be aware that dependencies are
resolved at instantiation time. This means that if you dependency
inject a prototype-scoped bean into a singleton-scoped bean, a brand
new prototype bean will be instantiated and then dependency injected
into the singleton bean... but that is all. That exact same prototype
instance will be the sole instance that is ever supplied to the
singleton-scoped bean, which is fine if that is what you want.
However, sometimes what you actually want is for the singleton-scoped
bean to be able to acquire a brand new instance of the
prototype-scoped bean again and again and again at runtime. In that
case it is no use just dependency injecting a prototype-scoped bean
into your singleton bean, because as explained above, that only
happens once when the Spring container is instantiating the singleton
bean and resolving and injecting its dependencies. If you are in the
scenario where you need to get a brand new instance of a (prototype)
bean again and again and again at runtime, you are referred to the
section entitled Section 4.3.7, “Method Injection”
So if you want to inject your "entity manager container-bean" into singleton beans (which is the default scope), have a look at https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-method-injection
Its rather important to set your scopes correctly, otherwise you might have (and will have) database inconsistencies, deadlocks or worse
It sould be possible using Spring Data. But if you would not like to use Spring Data in your project for some reason (e.g. you're just making a legacy project a bit better), you can create the following FactoryBean to make EntityManager injectable via constructor injection:
/**
* Makes the {#link EntityManager} injectable via <i>#Autowired</i>,
* so it can be injected with constructor injection too.
* (<i>#PersistenceContext</i> cannot be used for constructor injection.)
*/
public static class EntityManagerInjectionFactory extends AbstractFactoryBean<EntityManager> {
#PersistenceContext
private EntityManager entityManager;
#Override
public Class<?> getObjectType() {
return EntityManager.class;
}
#Override
protected EntityManager createInstance() {
return entityManager;
}
}
Please note, that because we use the #PersistenceContext annotation internally, the returned EntityManager will be a proper thread-safe proxy, as it would have been injected directly at the place of usage with field injection.

CDI best way for passing services

I have several service like that:
#Singleton
public SimpleService {
...
}
I have Managed Bean #ViewScoped which should create some complex objects. These objects should execute business-logic. I need to pass these services to this object.
Example of Managed Bean:
#ManagedBean
#ViewScoped
public class ExampleBean {
#Inject
private SimpleService simpleService;
...
public void customLogic() {
// in this method I should create complex object which should have services and some data.
// current implementation
ComplexObject object = new ComplexObject(SimpleService simpleService, ...)
}
}
Services are injected to Managed Bean by #Inject annotation. For creating these objects - I'm using the constructor and pass these services as params. The question is: can I have better solution than passing services in constructor?
You can:
Inject by method:
private MyService myService;
#Inject
public void setMyService(MyService ms) {
this.myService = ms;
}
Inject by field:
#Inject
private MyService myService;
Fetch reference through CDI (not recommended, except in advanced usecases):
...
MyService myService = CDI.current().select(MyService.class).get();
...
Fetch reference through BeanManager (not recommended, except in advanced usecases or CDI 1.0):
...
BeanManager beanManager = CDI.getBeanManager(); // you can fetch a BeanManager reference by several other methods, I use CDI for simplicity here
MyService myService = beanManager.getReference(MyService.class);
...
If your #Singleton annotation is javax.ejb.Singleton and not javax.inject.Singleton, then your bean is actually a EJB and you can also use any mechanism that allows you to access EJB, such as #Resource annotations, or through the JNDI context.
Personally I tend to inject by method as I find it the most flexible option most of the time. In my opinion it is also the most "portable" to other frameworks (e.g: Spring)
Remember that when you use either the CDI.current() or the BeanManager methods to fetch #Dependent beans, you are responsible to manually destroy the fetched bean when you are done with it, so that you do not fall into this CDI-related memory leak. When using CDI.current() it is as easy as saving the Instance reference and invoking it afterwards:
...
Instance<MyService> msInstance = CDI.current().select(MyService.class);
MyService myService = msInstance.get();
...
msInstance.destroy(myService);
...
The BeanManager method is too low-level and should only be used in CDI 1.0 environments (back when the CDI class did not exist yet). You can read the linked StackOverflow question for more details.
What you are doing is perfectly fine. You are using the ManagedBean as a bridge to inject the services and then passing the injected variables to a ComplexObject that need the services.
The only restriction that should be considered is, could the ComplexObject class be a ManagedBean itself? That way you could inject everything directly on it, but if it is not possible, you may use the bean for that.
I prefer the inject by field option mentioned because I think it is a little more readable.

#PostConstruct annotation and spring lifecycle

I'm new to Spring, I would like to know:
I have a java class annotated with #Component (spring) and inside I have a method annotated with #PostConstruct. The class is then referenced by #Autowired annotated field in another class. Can I assume that the class is only injected after #PostConstruct is called?
#Component
class AuthenticationMetrics {
private static final MetricRegistry metrics = new MetricRegistry();
final Counter requestsTotal;
final Meter guestLogins;
final Meter kfUserLogins;
final Timer guestLoginResponseTime;
final Timer kfLoginResponseTime;
#PostConstruct
public void populateMetricsRegistry() {
metrics.counter("authentication.requests.totals");
}
}
If you are asking is injection of given class happening after #PostConstruct in that bean is called, then the answer is yes - #PostConstruct is executed before bean is considered as "injectable"
If you are asking if #PostConstruct on given bean is executed after all injections has been done (on the same bean) - then yes - #PostConstruct is executed after injections are commited to given bean. This is the reason it exists. Normally you could put #PostConstruct actions into the constructor. However, when new object is created (constructor is called) injections are not performed yet - so any initialization that depends on injected objects would fail due to NPE. That is why you need #PostConstruct
The handling of annotations such as #PostConstruct, #Resource, #PreDestroy is done via a BeanPostProcessor, in this case the CommonAnnotationBeanPostProcessor. You can see in the following diagram from Spring that these BPP's are handled after Dependency Injection but before Bean Ready For Use (Which means as much as injectable).
Yes. Bean creation workflow is:
constructior call
#Autowired fields
#Autowired setters
BeanPostProcessor's postProcessBeforeInitialization(), i.e. #PostConstruct called by CommonAnnotationBeanPostProcessor
InitializingBean.afterPropertiesSet()
BeanPostProcessor's postProcessAfterInitialization()
Bean is ready and can be injected to other bean

inject stateless bean into a singleton bean

I have this requirement: I have a singleton bean and I have a method annotated with #PostConstruct where I perform some initialization. One of the initialization is to read some values from a DB, so I want to inject in this method a Stateless bean which is a service bean that access the DB. I don't want to inject the stateless bean as a field in the singleton bean because it is needed only in this method (nowhere else in the singleton bean). To do so I did wrote this in singleton bean:
#Singleton
public class MySingletonBean {
#PostConstruct
#EJB
public void init(SLSBService service) { /* use service to read from DB */ };
...
}
The problem is that the Singleton bean can not be instantiated. Any idea? Thank you in advance.
As the #PostConstruct annotated (callback) method is actually called after all references are resolved (all beans injected) I do not think this construct works.
What you could do or try out is to remove the #PostConstruct and using normal setter injection. However, be aware that other injected resources have not necessarily been resolved at this time.
#EJB
public void setService(SLSBService service){
service.doSmg();
}
#Stateless
public class SLSBService{
#PersistenceContext
private EntityManager em;
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public void doSmg() {
Member member = new Member();
member.setEmail("bla#bla.de");
member.setName("fubu");
member.setPhoneNumber("453454534535");
em.persist(member);
}
}
/* edit */
Just had some time for trying it out. The construct should be usable for DAOs, as the method is executed within a transaction and also the EntityManager (within the SLBService) is injected properly. And as expected references to other EJBs have not be resolved yet, so be aware of that.

#Transactional method called from another method doesn't obtain a transaction

In Spring, a method that is annotated with #Transactional will obtain a new transaction if there isn't one already, but I noticed that a transactional method does not obtain any transaction if it is called from a non-transactional one. Here's the code.
#Component
public class FooDao {
private EntityManager entityManager;
#PersistenceContext
protected void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Transactional
public Object save(Object bean) {
return this.entityManager.merge(bean);
}
public Object saveWrap(Object bean) {
return save(bean);
}
}
#Component
public class FooService {
private FooDao fooDao;
public void save(Object bean) {
this.fooDao.saveWrap(bean); // doesn't work.
this.fooDao.save(bean); // works
}
}
saveWrap() is a regular method that calls save() which is transactional, but saveWrap() won't persist any changes.
I'm using Spring 3 and Hibernate 3. What am I doing wrong here? Thanks.
It is one of the limitations of Springs AOP. Because the dao bean is in fact a proxy when it is created by spring, it means that calling a method from within the same class will not call the advice (which is the transaction). The same goes for any other pointcut
Yes, this is expected behaviour. #Transactional tells spring to create a proxy around the object. The proxy intercepts calls to the object from other objects. The proxy does not intercept calls within the object.
If you want to make this work, add #Transactional on the method that is invoked from "outside".
This is a bit late I know, but would just like to add a way to overcome this limitation is that within the method obtain the spring bean from the application context and invoke the method. When the spring bean is obtained from the application context it will be the proxy bean not the original bean . Since the proxy bean is now invoking the method instead of the original bean the transaction advice will be implemented on it.
A possible workaround is to call the method like if it was invoked from "outside"
You can do it by getting the current proxy of the component and then call the method :
((MyService) AopContext.currentProxy()).innerMethod();
Source: https://www.programmersought.com/article/58773839126/

Categories

Resources