How to inject dependencies in entities with Spring-Data and Hibernate [duplicate] - java

Is it possible to inject beans to a JPA #Entity using Spring's dependency injection?
I attempted to #Autowire ServletContext but, while the server did start successfully, I received a NullPointerException when trying to access the bean property.
#Autowired
#Transient
ServletContext servletContext;

You can inject dependencies into objects not managed by the Spring container using #Configurable as explained here: http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-atconfigurable.
As you've realized by now, unless using the #Configurable and appropriate AspectJ weaving configuration, Spring does not inject dependencies into objects created using the new operator. In fact, it doesn't inject dependencies into objects unless you've retrieved them from the ApplicationContext, for the simple reason that it simply doesn't know about their existence. Even if you annotate your entity with #Component, instantiation of that entity will still be performed by a new operation, either by you or a framework such as Hibernate. Remember, annotations are just metadata: if no one interprets that metadata, it does not add any behaviour or have any impact on a running program.
All that being said, I strongly advise against injecting a ServletContext into an entity. Entities are part of your domain model and should be decoupled from any delivery mechanism, such as a Servlet-based web delivery layer. How will you use that entity when it's accessed by a command-line client or something else not involving a ServletContext? You should extract the necessary data from that ServletContext and pass it through traditional method arguments to your entity. You will achieve a much better design through this approach.

Yes, of course you can. You just need to make sure the entity is also registered as a Spring managed bean either declaratively using <bean> tags (in some spring-context.xml) or through annotations as shown below.
Using annotations, you can either mark your entities with #Component (or a more specific stereotype #Repository which enables automatic exception translation for DAOs and may or may not interfere with JPA).
#Entity
#Component
public class MyJAPEntity {
#Autowired
#Transient
ServletContext servletContext;
...
}
Once you've done that for your entities you need to configure their package (or some ancestor package) for being scanned by Spring so that the entities get picked up as beans and their dependencies get auto wired.
<beans ... xmlns:context="..." >
...
<context:component-scan base-package="pkg.of.your.jpa.entities" />
<beans>
EDIT : (what finally worked and why)
Making the ServletContext static. (remove #Autowired)
#Transient
private static ServletContext servletContext;
Since, JPA is creating a separate entity instance i.e. not using the Spring managed bean, it's required for the context to be shared.
Adding a #PostConstruct init() method.
#PostConstruct
public void init() {
log.info("Initializing ServletContext as [" +
MyJPAEntity.servletContext + "]");
}
This fires init() once the Entity has been instantiated and by referencing ServletContext inside, it forces the injection on the static property if not injected already.
Moving #Autowired to an instance method but setting the static field inside.
#Autowired
public void setServletContext(ServletContext servletContext) {
MyJPAEntity.servletContext = servletContext;
}
Quoting my last comment below to answer why do we have to employ these shenanigans:
There's no pretty way of doing what you want since JPA doesn't use the Spring container to instantiate its entities. Think of JPA as a separate ORM container that instantiates and manages the lifecycle of entities (completely separate from Spring) and does DI based on entity relationships only.

After a long time I stumbled across this SO answer that made me think of an elegant solution:
Add to your entities all the #Transient #Autowired fields you need
Make a #Repository DAO with this autowired field:
#Autowired private AutowireCapableBeanFactory autowirer;
From your DAO, after fetching the entity from DB, call this autowiring code:
String beanName = fetchedEntity.getClass().getSimpleName();
autowirer.autowireBean(fetchedEntity);
fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName);
Your entity will then be able to access the autowired fields as any #Component can.

Related

Constructor injection preventing custom resource processing

In my (non-trivial) Spring Boot 1.5.4 application with Spring Data REST and HATEOAS leveraging Spring websockets, I have some custom resource processors, some custom controllers, and some custom repositories. Sadly, when I use constructor injection in one particular Spring #Service class for a MessageSendingOperations dependency, my custom resource processors no longer get invoked. Reverting the constructor injection restores the execution of my custom resource processors, i.e. reverting from:
private final MessageSendingOperations<String> messageTemplate;
#Autowired
public ChannelHandler(MessageSendingOperations<String> messageTemplate) {
this.messageTemplate = messageTemplate;
}
to:
#Autowired
private MessageSendingOperations<String> messageTemplate;
"re-enables" my custom resource processors which results in a null messageTemplate. So, there's a problem somewhere...but where??? Any ideas how to track this down?
Have you tried making messageTemplate a lazily injected proxy? For example:
public ChannelHandler(#Lazy MessageSendingOperations<String> messageTemplate) {
this.messageTemplate = requireNonNull(messageTemplate, "messageTemplate");
}
From the Javadoc:
In addition to its role for component initialization, this annotation
may also be placed on injection points marked with Autowired or
Inject: In that context, it leads to the creation of a lazy-resolution
proxy for all affected dependencies, as an alternative to using
ObjectFactory or Provider.
This usually affects the initialization order of your beans, in this case allowing ChannelHandler to be initialized before MessageSendingOperations. Without #Lazy, MessageSendingOperations will be initialized first.
Also: as of Spring 4.3, #Autowired is no longer required for single argument constructors.
+1 for using constructor injection and final fields.

Add custom beans to Spring context

I have some classes, with a custom annotation, that shouldn't be instantiated (abstract class, and it's just a subcomponents for a real beans). But on top of this classes, on runtime, on context initialization phase, I want to put extra beans into application context.
So, basically I need to scan classpath, process results, and introduce new beans into curent application context.
It seems that spring-mvc, spring-tasks and spring-integration are doing this (I tried to learn it from sources - no luck)
I found that I can create my own BeanFactoryPostProcessor, scan classpath and call registerSingleton for my custom bean. But I'm not sure that it's a good way for introducing new beans (seems that it's used only for postprocess of exising beans only). And I believe there are some Spring internal tools that I may reuse to simplify process.
What is a conventional way to introduce extra beans on Spring context initialization?
Your observation is actually correct, BeanFactoryPostProcessor is one of the two ways Spring provides a mechanism to modify the bean definition/instances before making them available to the application(the other is using BeanPostProcessors)
You can absolutely use BeanFactoryPostProcessors to add/modify bean definitions, here is one sample from Spring Integration codebase that adds a errorChannel if not explicitly specified by a user, you can probably use a similar code for registering your new beans:
RootBeanDefinition errorChannelDef = new RootBeanDefinition();
errorChannelDef.setBeanClassName(IntegrationNamespaceUtils.BASE_PACKAGE
+ ".channel.PublishSubscribeChannel");
BeanDefinitionHolder errorChannelHolder = new BeanDefinitionHolder(errorChannelDef,
IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(errorChannelHolder, registry);
There are at least two ways to include custom annotated classes as bean definitions:
Annotate the custom annotation with #Component
Add a include filter with type annotation to <context:component-scan/>
for example:
<context:component-scan base-package="org.example">
<context:include-filter type="annotation" expression="org.example.Annotation"/>
</context:component-scan>
Then you can use a BeanPostProcessor to instantiate them, for example:
public class CustomAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
#Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanClass.isAnnotationPresent(org.example.Annotation.class)) {
Object bean = createBeanInstance();
...
return bean:
}
return null;
}
}
Or use a BeanFactoryPostProcessor to process the ScannedGenericBeanDefinitions.
See AnnotationConfigUtils.registerAnnotationConfigProcessors() for sample code of internal Spring annotation postprocessors.

How to persist entities, how to create table from entities without "main class"?

I'm developing an application, and I started with creating my JPA entities (annotation + persistence.xml). Then in my persistence.xml file, I created a connection for a MySql data base (the connection is fine).
The problem is that I just don't know how to persist my entities without creating a "main class".
Do I have to run my main class for every single entity that I'm going to create?
To persist an entity, you need an instance of entityManager. Since you have a Java EE container you can get an instance of entityManager using the annotation #PersistenceContext in some bean
What I mean by some bean ? It's a bean managed by the Java EE container. So, for instance you have to define a bean like this :
#Stateless
public class MyController{
#PersistenceContext
private EntityManager em;
public void persistIt(Object anEntity){
em.persist(anEntity);
}
}
The annotation #Stateless indicate to the container that it must manage this class and take care of providing an instance of the entityManager when needed.
So to answer precisely to your question: it is not mandatory to have another class to persist an entity, as soon as you find a way to get access to an entityManager instance.
But:
it is definitively NOT a best practice.
to take advantages of entityManager injection: you must use another managed bean so that entityManager can be injected by the container.
Additionally, the controller is where you can handle your transactions properly.

Strategy using different beans in jsf

i want to use 2 different beans (Spring) for one JSF-Page. I do not like to write every method into one bean, so i tried to separate into two beans like JobEditDataBean and JobEditActionBean.
I want to use the JobEdiDataBean just as "Container" for my data objects and move the actionstuff (like saving, update etc.) to the action bean.
What i did by now (and what seems to work, but feels wrong) is the following:
public class JobEditDataBean{
#Autowired
JobEditActionBean actionBean;
// some objects...
#PostConstruct
public void init() {
actionBean.setJobEditDataBean(this);
// do something ...
}
}
public class JobEditActionBean{
JobEditDataBean dataBean;
// some objects...
}
Do you have any hints or tipps how this can be done better, nicer?
Indeed, you don't need to have one bean per each page. You can use as much beans you want for any page, it is fine, as whenever an expression like #{someMB} is found in your XHTML, JSF will find a bean with that name and create a new instance if necessary.
If you need to inject one bean to another, just use #Autowired already:
#Component
#Scope("request")
public class JobEditActionBean {
#Autowired
private JobEditDataBean dataBean;
#PostConstruct
public void init() {
// dataBean.youCanUseDataBeanMethodsHereAlready()
}
}
You just have to make sure both beans are in the Spring container (annotating them with #Component will do), and choosing the right scope for each one. Beware of the scopes of the beans which you are injecting, cause it usually only makes sense to inject beans of broader scope to beans of the same or more restrict scope.
Having said that, I recommend reading following thread about choosing the right scopes:
How to choose the right bean scope?
One more thing: this is only valid if your JSF beans are really being managed by the Spring container (that was my assumption after you used #Autowired). If you are letting JSF container manage the beans (using #ManagedBean with #RequestScoped or #ViewScoped, for example), the way you inject them is with a #ManagedProperty annotation:
...
#ManagedProperty("#{jobEditDataBean}")
private JobEditDataBean dataBean;

spring mvc annotation #Inject does not work

i have the following in my app-servlet.xml
<mvc:annotation-driven />
<context:component-scan base-package="com.merc.myProject.web.controllers"/>
<context:component-scan base-package="com.merc.myProject.web.forms"/>
what ever I have in my controller package gets injected but the same thing in the forms package is always null.
my form looks something like this
public class SelectDatesForm {
#Inject IUserService userService;
.....
}
my controllers looks like this
#Controller
public class SelectDates {
#Inject IUserService userService;
.....
}
somebody please help
<context:component-scan> looks for classes annotated with things like #Component, #Controller, #Service, and so on, and configures those as beans. If those classes have properties injected with #Inject or #Resource, then those will be processed also.
However, if your class isn't annotated to start with, then #Inject will not be processed. This is the case for SelectDatesForm. If you annotate this with #Component, it should get picked up.
I'd be a bit careful here, though - forms are generally not good candidates for Spring beans, since they tend to be throw-away, transient objects.
I guess your SelectDatesForm is instantiated manually with new rather than obtained from the Spring context. In this case it is not a Spring bean and therefore not a subject for dependency injection.
Usually you don't need to inject dependencies into manually created objects. If you actually need to do so, you have several options:
Declare your SelectDatesForm as a prototype-scoped bean and obtain a fresh instance of it from Spring context instead of creating it with new:
#Component #Scope("prototype")
public class SelectDatesForm { ... }
and when you need to obtain a new instance of it:
SelectDatesForm newForm = applicationContext.getBean(SelectDatesForm.class);
However, this approach couples your code with Spring's ApplicationContext.
If your have no control over instantiation of SelectDatesForm (i.e. it happens outside of your code), you can use #Configurable
Also you can manually facilitate autowiring of the object created with new:
SelectDatesForm newForm = new SelectDatesForm();
applicationContext.getAutowireCapableBeanFactory().autowireBean(newForm);

Categories

Resources