Injecting a Spring dependency into a JPA EntityListener - spring

I am trying to inject a Spring dependency into an JPA EntityListener. Here is my listener class:
#Configurable(autowire = Autowire.BY_TYPE, dependencyCheck = true)
public class PliListener {
#Autowired
private EvenementPliRepository evenementPliRepository;
#PostPersist
void onPostPersist(Pli pli) {
EvenementPli ev = new EvenementPli();
ev.setPli(pli);
ev.setDateCreation(new Date());
ev.setType(TypeEvenement.creation);
ev.setMessage("Création d'un pli");
System.out.println("evenementPliRepository: " + evenementPliRepository);
evenementPliRepository.save(ev);
}
}
Here is my Entity class:
#RooJavaBean
#RooToString
#RooJpaActiveRecord
#EntityListeners(PliListener.class)
public class Pli implements Serializable{
...
However, my dependency (i.e. evenementPliRepository) is always null.
Can anyone please help?

A hack to inject dependencies on stateless beans, is to define the dependency as "static", create a setter method so that Spring can inject the dependency (assigning it to the static dependency).
Declare the dependency as static.
static private EvenementPliRepository evenementPliRepository;
Create a method so that Spring can inject it.
#Autowired
public void init(EvenementPliRepository evenementPliRepository)
{
MyListenerClass.evenementPliRepository = evenementPliRepository;
logger.info("Initializing with dependency ["+ evenementPliRepository +"]");
}
More details at: http://blog-en.lineofsightnet.com/2012/08/dependency-injection-on-stateless-beans.html

This is actually an old question but I found an alternative solution :
public class MyEntityListener {
#Autowired
private ApplicationEventPublisher publisher;
#PostPersist
public void postPersist(MyEntity target) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
publisher.publishEvent(new OnCreatedEvent<>(this, target));
}
#PostUpdate
public void postUpdate(MyEntity target) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
publisher.publishEvent(new OnUpdatedEvent<>(this, target));
}
#PostRemove
public void postDelete(MyEntity target) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
publisher.publishEvent(new OnDeletedEvent<>(this, target));
}
}
Probably not the best one but better than static variables w/o AOP + weaving.

I annotated the listener with #Component annotation, then created a non static setter to assign the injected Spring bean, it works well
My code looks like :
#Component
public class EntityListener {
private static MyService service;
#Autowired
public void setMyService (MyService service) {
this.service=service;
}
#PreUpdate
public void onPreUpdate() {
service.doThings()
}
#PrePersist
public void onPersist() {
...
}
}

Since Spring V5.1 (and Hibernate V5.3) it should work out of the box as Spring registers as the provider of those classes.
see documentation of SpringBeanContainer

And what about this solution?
#MappedSuperclass
#EntityListeners(AbstractEntityListener.class)
public abstract class AbstractEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "creation_date")
private Date creationDate;
#Column(name = "modification_date")
private Date modificationDate;
}
Then the Listener...
#Component
public class AbstractEntityListener {
#Autowired
private DateTimeService dateTimeService;
#PreUpdate
public void preUpdate(AbstractEntity abstractEntity) {
AutowireHelper.autowire(this, this.dateTimeService);
abstractEntity.setModificationDate(this.dateTimeService.getCurrentDate());
}
#PrePersist
public void prePersist(AbstractEntity abstractEntity) {
AutowireHelper.autowire(this, this.dateTimeService);
Date currentDate = this.dateTimeService.getCurrentDate();
abstractEntity.setCreationDate(currentDate);
abstractEntity.setModificationDate(currentDate);
}
}
And the helper...
/**
* Helper class which is able to autowire a specified class. It holds a static reference to the {#link org
* .springframework.context.ApplicationContext}.
*/
public final class AutowireHelper implements ApplicationContextAware {
private static final AutowireHelper INSTANCE = new AutowireHelper();
private static ApplicationContext applicationContext;
private AutowireHelper() {
}
/**
* Tries to autowire the specified instance of the class if one of the specified beans which need to be autowired
* are null.
*
* #param classToAutowire the instance of the class which holds #Autowire annotations
* #param beansToAutowireInClass the beans which have the #Autowire annotation in the specified {#classToAutowire}
*/
public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) {
for (Object bean : beansToAutowireInClass) {
if (bean == null) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
}
}
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) {
AutowireHelper.applicationContext = applicationContext;
}
/**
* #return the singleton instance.
*/
public static AutowireHelper getInstance() {
return INSTANCE;
}
}
Works for me.
Source:
http://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/

I started to go down the path of using AOP to inject a spring bean into an Entity listener. After a day and a half of research and trying different things I came across this link which stated:
It is not possible to inject spring managed beans into a JPA EntityListener class. This is because the JPA listener mechanism should be based on a stateless class, so the methods are effectively static, and non-context aware. ... No amount of AOP will save you, nothing gets injected to the ‘object’ representing the listener, because the implementations don’t actually create instances, but uses the class method.
At this point I regrouped and stumbled across the EclipseLink DescriptorEventAdapter. Using this information I created a listener class that extended the Descriptor Adapter.
public class EntityListener extends DescriptorEventAdapter {
private String injectedValue;
public void setInjectedValue(String value){
this.injectedValue = value;
}
#Override
public void aboutToInsert(DescriptorEvent event) {
// Do what you need here
}
}
In order to use the class I could have used the #EntityListeners annotation on my entity class. Unfortunately, this method would not allow Spring to control the creation of my listener and as a result would not allow for dependency injection. Instead I added the following 'init' function to my class:
public void init() {
JpaEntityManager entityManager = null;
try {
// Create an entity manager for use in this function
entityManager = (JpaEntityManager) entityManagerFactory.createEntityManager();
// Use the entity manager to get a ClassDescriptor for the Entity class
ClassDescriptor desc =
entityManager.getSession().getClassDescriptor(<EntityClass>.class);
// Add this class as a listener to the class descriptor
desc.getEventManager().addListener(this);
} finally {
if (entityManager != null) {
// Cleanup the entity manager
entityManager.close();
}
}
}
Add a little Spring XML configuration
<!-- Define listener object -->
<bean id="entityListener" class="EntityListener " init-method="init">
<property name="injectedValue" value="Hello World"/>
<property name="entityManagerFactory" ref="emf"/>
</bean>
Now we have a situation where Spring creates a entity listener, injects it with whatever dependencies are needed, and the listener object registers itself with the entity class to which it intends to listen.
I hope this helps.

try use ObjectFactory like this
#Configurable
public class YourEntityListener {
#Autowired
private ObjectFactory<YourBean> yourBeanProvider;
#PrePersist
public void beforePersist(Object target) {
YourBean yourBean = yourBeanProvider.getObject();
// do somthing with yourBean here
}
}
I found this solution in org.springframework.data.jpa.domain.support.AuditingEntityListener from spring-data-jpa.
demo: https://github.com/eclipseAce/inject-into-entity-listener

I tested out the approach suggested in https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/ and worked. Not very clean but does the job. Slightly modified AutowireHelper class for me looked like this:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class AutowireHelper implements ApplicationContextAware {
private static ApplicationContext applicationContext;
private AutowireHelper() {
}
public static void autowire(Object classToAutowire) {
AutowireHelper.applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) {
AutowireHelper.applicationContext = applicationContext;
}
}
Then called this from entity listener like this:
public class MyEntityAccessListener {
#Autowired
private MyService myService;
#PostLoad
public void postLoad(Object target) {
AutowireHelper.autowire(this);
myService.doThings();
...
}
public void setMyService(MyService myService) {
this.myService = myService;
}
}

The problem with JPA Listeners is that:
they are not managed by Spring (so no injections)
they are (or might be) created before Spring's Application Context is ready (so we can't inject beans on a constructor call)
My workaround to deal with the issue:
1) Create Listener class with public static LISTENERS field:
public abstract class Listener {
// for encapsulation purposes we have private modifiable and public non-modifiable lists
private static final List<Listener> PRIVATE_LISTENERS = new ArrayList<>();
public static final List<Listener> LISTENERS = Collections.unmodifiableList(PRIVATE_LISTENERS);
protected Listener() {
PRIVATE_LISTENERS.add(this);
}
}
2) All JPA listeners that we want to be added to Listener.LISTENERS has to extend this class:
public class MyListener extends Listener {
#PrePersist
public void onPersist() {
...
}
...
}
3) Now we can get all listeners and inject beans just after Spring's Application Context is ready
#Component
public class ListenerInjector {
#Autowired
private ApplicationContext context;
#EventListener(ContextRefreshedEvent.class)
public void contextRefreshed() {
Listener.LISTENERS.forEach(listener -> context.getAutowireCapableBeanFactory().autowireBean(listener));
}
}

I believe it is because this listener bean is not under control of Spring. Spring is not instantiating it, how can Spring know how to find that bean and do the injection?
I haven't tried on that, but seems that you can make use of AspectJ Weaver with Spring's Configurable annotation to have Spring control non-Spring-instantiated beans.
http://static.springsource.org/spring/docs/3.1.2.RELEASE/spring-framework-reference/html/aop.html#aop-using-aspectj

Since version 5.3 of Hibernate and version 5.1 of Spring (that's version 2.1 of Spring Boot), there's an easy solution.
No hack, no need to use AOP, no helper classes, no explicit autowiring, no init block to force injection.
You just need to:
Make the listener a #Component and declare the autowired bean, as usual.
Configure JPA in your Spring application to use Spring as the bean provider.
Here's how (in Kotlin)...
1) Entity listener
#Component
class EntityXyzListener(val mySpringBean: MySpringBean) {
#PostLoad
fun afterLoad(entityXyz: EntityXyz) {
// Injected bean is available here. (In my case the bean is a
// domain service that I make available to the entity.)
entityXyz.mySpringBean= mySpringBean
}
}
2) JPA datasource config
Get access to LocalContainerEntityManagerFactoryBean in your application. Then add to jpaPropertyMap the following key-value pair: AvailableSettings.BEAN_CONTAINER => the application context's bean factory.
In my Spring Boot application I already had the code below to configure a datasource (boilerplate code found here for example). I only had to add the line of code that puts the BEAN_CONTAINER property in the jpaPropertyMap.
#Resource
lateinit var context: AbstractApplicationContext
#Primary
#Bean
#Qualifier("appDatasource")
#ConfigurationProperties(prefix = "spring.datasource")
fun myAppDatasource(): DataSource {
return DataSourceBuilder.create().build()
}
#Primary
#Bean(name = ["myAppEntityManagerFactory"])
fun entityManagerFactoryBean(builder: EntityManagerFactoryBuilder): LocalContainerEntityManagerFactoryBean {
val localContainerEntityManagerFactoryBean =
builder
.dataSource(myAppDatasource())
.packages("com.mydomain.myapp")
.persistenceUnit("myAppPersistenceUnit")
.build()
// the line below does the trick
localContainerEntityManagerFactoryBean.jpaPropertyMap.put(
AvailableSettings.BEAN_CONTAINER, SpringBeanContainer(context.beanFactory))
return localContainerEntityManagerFactoryBean
}

Another option:
Create a service to make AplicationContext accessible:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import lombok.Setter;
#Service
class ContextWrapper {
#Setter
private static ApplicationContext context;
#Autowired
public ContextWrapper(ApplicationContext ac) {
setContext(ac);
}
}
Use it:
...
public class AuditListener {
private static final String AUDIT_REPOSITORY = "AuditRepository";
#PrePersist
public void beforePersist(Object object){
//TODO:
}
#PreUpdate
public void beforeUpdate(Object object){
//TODO:
}
#PreRemove
public void beforeDelete(Object object) {
getRepo().save(getAuditElement("DEL",object));
}
private Audit getAuditElement(String Operation,Object object){
Audit audit = new Audit();
audit.setActor("test");
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
audit.setDate(timestamp);
return audit;
}
private AuditRepository getRepo(){
return ContextWrapper.getContext().getBean(AUDIT_REPOSITORY, AuditRepository.class);
}
}
This class is created as a listener from jpa:
...
#Entity
#EntityListeners(AuditListener.class)
#NamedQuery(name="Customer.findAll", query="SELECT c FROM Customer c")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
...
Since the listener is not under Spring's control, it can not access the context bean. I have tried multiple options (#Configurable (...)) and none has worked except to create a class that static access to the context. Already in that dilemma I think that this is an elegant option.

Building on the answer of Paulo Merson, here is a variation of how to set the SpringBeanContainer by utilizing JpaBaseConfiguration. Here are both steps:
Step 1: Define the listener as a Spring component. Note that autowiring works through constructor injection.
#Component
public class PliListener {
private EvenementPliRepository evenementPliRepository;
public PliListener(EvenementPliRepository repo) {
this.evenementPliRepository = repo;
}
#PrePersist
public void touchForCreate(Object target) {
// ...
}
#PostPersist
void onPostPersist(Object target) {
// ...
}
}
Step 2: Set the SpringBeanContainer, which enables autowiring in the listener. SpringBeanContainer JavaDoc might be worth a look.
#Configuration
public class JpaConfig extends JpaBaseConfiguration {
#Autowired
private ConfigurableListableBeanFactory beanFactory;
protected JpaConfig(DataSource dataSource, JpaProperties properties,
ObjectProvider<JtaTransactionManager> jtaTransactionManager) {
super(dataSource, properties, jtaTransactionManager);
}
#Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
#Override
protected Map<String, Object> getVendorProperties() {
Map<String, Object> props = new HashMap<>();
// configure use of SpringBeanContainer
props.put(org.hibernate.cfg.AvailableSettings.BEAN_CONTAINER,
new SpringBeanContainer(beanFactory));
return props;
}
}

The most natural way is, in my opinion, to intervene into the process of instantiating of EntityListener.
This way significantly differs in Hibernate pre-5.3 versions and post-5.3 ones.
1) In Hibernate versions earlier than 5.3 org.hibernate.jpa.event.spi.jpa.ListenerFactory is responsible for EntityListener instantiation. The instantiation of this factory can be intercepted if you provide your own CDI-based javax.enterprise.inject.spi.BeanManager. The CDI interfaces are (unnecessary for Spring DI world) verbose, but it's not difficult to implement Spring BeanFactory-backed CDI Bean manager.
#Component
public class SpringCdiBeanManager implements BeanManager {
#Autowired
private BeanFactory beanFactory;
#Override
public <T> AnnotatedType<T> createAnnotatedType(Class<T> type) {
return new SpringBeanType<T>(beanFactory, type);
}
#Override
public <T> InjectionTarget<T> createInjectionTarget(AnnotatedType<T> type) {
return (InjectionTarget<T>) type;
}
...
// have empty implementation for other methods
}
and the implementation of type-dependent SpringBeanType<T> will look like this:
public class SpringBeanType <T> implements AnnotatedType<T>, InjectionTarget<T>{
private BeanFactory beanFactory;
private Class<T> clazz;
public SpringBeanType(BeanFactory beanFactory, Class<T> clazz) {
this.beanFactory = beanFactory;
this.clazz = clazz;
}
#Override
public T produce(CreationalContext<T> ctx) {
return beanFactory.getBean(clazz);
}
...
// have empty implementation for other methods
}
Now, the only thing left is to inject into Hibernate Configuration Settings our implementation of BeanManager under a property name javax.persistence.bean.manager. There are, probably, many ways to do so, let me bring just one of them:
#Configuration
public class HibernateConfig {
#Autowired
private SpringCdiBeanManager beanManager;
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(){
#Override
public Map<String, Object> getJpaPropertyMap(){
Map<String, Object> jpaPropertyMap = super.getJpaPropertyMap();
jpaPropertyMap.put("javax.persistence.bean.manager", beanManager);
return jpaPropertyMap;
}
};
// ...
return jpaVendorAdapter;
}
}
Just remember that two things have to be Spring beans:
a) SpringCdiBeanManager, so that BeanFactory could be injected/autowired to it;
b) your EntityListener class, so that line return beanFactory.getBean(clazz); will be successful.
2) In Hibernate versions 5.3 and later things are much easier for Spring beans, as #AdrianShum very correctly pointed out. Since 5.3 Hibernate uses org.hibernate.resource.beans.container.spi.BeanContainer concept and there is its ready-to-use implementation for Spring Beans, org.springframework.orm.hibernate5.SpringBeanContainer. In this case, just follow its javadoc.

As others have pointed out, it appears SpringBeanContainer is the way to wire up Spring to Hibernate's ManagedBeanRegistryImpl, which is responsible for creating instances of EntityListeners when Hibernate is creating it's callback objects. Calls to create beans are delegated to SpringBeanContainer which can create Spring beans with both constructor injection and autowiring. For example a EntityListener would look like
public class MyEntityListener {
#Autowired
private AnotherBean anotherBean;
private MyBean myBean;
public InquiryEntityListener(MyBean myBean) {
this.myBean = myBean;
}
public MyEntityListener() {
}
}
Note that the EntityListener does NOT require #Component annotation as this only creates an extra instance which is not used by Hibernate.
However when using SpringBeanContainer there are some important limitations and caveats that must be kept in mind. In our use case, instances of our EntityListener were created during the creation of Hibernate EntityManager. As this happened fairly early during the Spring lifecycle, many beans did not exist at this time. This led to the following discovery:
The SpringBeanContainer will only autowire/constructor bean dependencies that exist at the time when the EntityListener is created. Constructor dependencies that don't exist will cause the default constructor to be called. Essentially there is a race condition when using SpringBeanContainer.
The work around for this is to inject a DefaultListableBeanFactory instance into the EntityListener. Later when the EntityListeners lifecycle methods are called (i.e. #PostLoad, #PostPersist, etc.) instances of the desired bean can be pulled out of the BeanFactory as the beans would've been created by Spring at this point.

Related

how to add a dynamically created bean in a #PostConstruct in Spring Boot

I need to add a dynamically created bean when my 'normal' bean gets created. I tried this so far:
//generate a health bean dynamically, and register it
#PostConstruct
public void init(){
solrhealth = new SolrHealthIndicator(solr);
//context.??
}
I build a SolrHealthIndicatior bean programatically, as I am not using Spring Solr Data. Now I want it registered so it shows up in /health.
I have my context wired, but cannot find how to register the newly created bean in there...
You should be able to programatically define your bean by using the #Bean annotation within a #Configuration class.
#Bean
public SolrHealthIndicator solrHealthIndicatior() {
//you can construct the object however you want
return new SolrHealthIndicator();
}
Then you can just inject it like any other bean(#Autowired constructor, field, setter injection, etc.), if there are multiple beans with the same type you can use #Qualifier to distinguish between them.
You need to use #Lookup annotation.
#Component
public class SolrHealthIndicator {
public SolrHealthIndicator(Solr solr) {
}
}
public class BeanInQuestion {
#PostConstruct
public void init() {
solrHealthIndicator = getHealthIndicatorBean();
}
#Lookup
public SolrHealthIndicator getHealthIndicatorBean() {
//Spring creates a runtime implementation for this method
return null;
}
}
You could make the class containing your #PostConstruct implement BeanDefinitionRegistryPostProcessor. Then you'd then be able to register your beans programmatically:
#Bean
public class MyBean implements BeanDefinitionRegistryPostProcessor {
private BeanDefinitionRegistry registry;
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;
}
#PostConstruct
public void init(){
registry.registerBeanDefinition("solrHealthIndicator", new SolrHealthIndicator(solr));
}
}

how to autowire spring beans from a non-bean object created at runtime?

I have some Jpa repositories and several Entity class. I need to create a helper object for one of my Entity. Inside that helper I use #Autowire to access the Jpa repositories.
#Entity
class A {
#Transient
Helper helper;
...
}
class Helper {
A a;
#Autowired
CRepository repo;
public Helper(A a) {
this.a = a;
}
}
However, the repo is always null. I've tried using SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this) and #Configurable, but both of them failed. Can anybody provide some hint for me?
BTW, A is instantiated inside a rest controller.
Thanks!.
You can use a BeanUtil class to get any bean that created in Springl
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Then you can get the bean.
MyBean obj = BeanUtil.getBean(MyBean.class);
Use constructor injection instead of field injection; this is a best practice all the time anyway. Then it's trivial to inject your A into the controller and pass it as a constructor argument.
#Configurable annotation works fine, but you need to use #EnableSpringConfigured annotation in any configuration class in order to make it work. Read my answer in other stackoverflow post: spring autowiring not working from a non-spring managed class
Entity class should not contain any helpers, even if transient. For a clean design you need to separate concerns, so the entity should not be aware of your business logic. I cannot help you more since I don't know which is the goal of that helper, but here you have other alternatives:
ALTERNATIVE 1 (based on your description seems that helper is an stateful bean, so it is not candidate to be a #Service, which I personally think it should be)
#Controller
public MyController {
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
Helper helper = new Helper(a); // CRepository is successfully autowired
}
}
#Configurable(autowire = Autowire.BY_TYPE)
public class Helper {
A a;
#Autowired
CRepository repo;
}
#Configuration
#EnableSpringConfigured
public Application {
...
}
ALTERNATIVE 2 (make your Helper class stateless so that spring is aware of your beans without the need of extra stuff like #Confgurable/#EnableSpringConfigured)
#Controller
public MyController {
#Autowired Helper helper; // CRepository is correctly autowired
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
helper.doSomething(a);
}
}
#Service
public class Helper {
// A a; remove dependency to A to make it stateless
#Autowired
CRepository repo;
public Helper() {
}
public void doSomething(A a) {
...
repo.save(a);
}
}
You cannot autowire nothing in your Helper class because it isn't managed by Spring.
You can use this approach:
public class HelperManager {
#Autowired
private ApplicationContext context;
public Helper getHelper(A a) {
return context.getBean(Helper.class, a);
}
Configure Helper to be a prototype bean:
#Configuration
public class MyConfiguration {
#Bean
public HelperManager helperManager() {
return new HelperManager();
}
#Bean
#Scope("prototype")
public Helper helper(A a) {
return new Helper(a);
}
}
And finally in your controller:
#Controller
public class MyController {
#Autowired
private HelperManager helperManager;
public someMethodWhereToInstanceYourHelper(A a) {
...
Helper helper = helperManager.getHelper(a);
...
}
}

Create spring managed beans with factory

I have a spring application in which I am trying to inject many beans of the same type. I do not know how many of these beans there will be before runtime, so it seems natural to use the factory pattern, as I cannot configure each bean in my java config class. However, these beans need to have some of their fields wired by Spring, and when I create them with "new" in my factory, they are of course not Spring managed.
Is there a way to have the beans I create in my factory class be managed by Spring? Or is the factory pattern the wrong way to go about this?
I am fairly new to posting, so please let me know if any more information is necessary.
You can define a beanFactory wired with the dependencies needed for your bean, then manually injected them in each new bean created by the beanFactory. For example:
public class MyBean {
private Dependency1 dep1;
private Dependency2 dep2;
public MyBean(Dependency1 dep1, Dependency2 dep2) {
this.dep1 = dep1;
this.dep2 = dep2;
}
}
#Component
public class MyBeanFactory {
#Autowired
private Dependency1 dep1;
#Autowired
private Dependency2 dep2;
public MyBean createInstance() {
return new MyBean(dep1, dep2);
}
}
#Component
public class MyBeanConsumer {
#Autowired
private MyBeanFactory myBeanFactory;
public void foo() {
final MyBean bean = myBeanFactory.createInstance();
}
}
You can't use #Autowired because of the variable number of beans, but you can still make use of ApplicationContextAware to create obtain the beans.
Using that you can programmatically create prototype beans from your Java code if the type of bean has been defined before in the configuration, or alternatively you can create the new object in your factory using new, and then set the dependencies by using this same method.
This is an example of an implementation:
public final class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
public void setApplicationContext(ApplicationContext context) throws BeansException {
CONTEXT = context;
}
public static Object getBean(String beanName) {
return CONTEXT != null ? CONTEXT.getBean(beanName) : null;
}
public static <T> T getBean(Class<T> objectClass) {
return CONTEXT != null ? CONTEXT.getBean(objectClass) : null;
}
}

#Transactional on #PostConstruct method

I want to read text data fixtures (CSV files) at the start on my application and put it in my database.
For that, I have created a PopulationService with an initialization method (#PostConstruct annotation).
I also want them to be executed in a single transaction, and hence I added #Transactional on the same method.
However, the #Transactional seems to be ignored :
The transaction is started / stopped at my low level DAO methods.
Do I need to manage the transaction manually then ?
Quote from legacy (closed) Spring forum:
In the #PostConstruct (as with the afterPropertiesSet from the InitializingBean interface) there is no way to ensure that all the post processing is already done, so (indeed) there can be no Transactions. The only way to ensure that that is working is by using a TransactionTemplate.
So if you would like something in your #PostConstruct to be executed within transaction you have to do something like this:
#Service("something")
public class Something {
#Autowired
#Qualifier("transactionManager")
protected PlatformTransactionManager txManager;
#PostConstruct
private void init(){
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//PUT YOUR CALL TO SERVICE HERE
}
});
}
}
I think #PostConstruct only ensures the preprocessing/injection of your current class is finished. It does not mean that the initialization of the whole application context is finished.
However you can use the spring event system to receive an event when the initialization of the application context is finished:
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
// do startup code ..
}
}
See the documentation section Standard and Custom Events for more details.
As an update, from Spring 4.2 the #EventListener annotation allows a cleaner implementation:
#Service
public class InitService {
#Autowired
MyDAO myDAO;
#EventListener(ContextRefreshedEvent.class)
public void onApplicationEvent(ContextRefreshedEvent event) {
event.getApplicationContext().getBean(InitService.class).initialize();
}
#Transactional
public void initialize() {
// use the DAO
}
}
Inject self and call through it the #Transactional method
public class AccountService {
#Autowired
private AccountService self;
#Transactional
public void resetAllAccounts(){
//...
}
#PostConstruct
private void init(){
self.resetAllAccounts();
}
}
For older Spring versions which do not support self-injection, inject BeanFactory and get self as beanFactory.getBean(AccountService.class)
EDIT
It looks like that since this solution has been posted 1.5 years ago developers are still under impression that if a method,
annotated with #Transactional, is called from a #PostContruct-annotated method invoked upon the Bean initialization, it won't be actually executed inside of Spring Transaction, and awkward (obsolete?) solutions get discussed and accepted instead of this very simple and straightforward one and the latter even gets downvoted.
The Doubting Thomases :) are welcome to check out an example Spring Boot application at GitHub which implements the described above solution.
What actually causes, IMHO, the confusion: the call to #Transactional method should be done through a proxied version of a Bean where such method is defined.
When a #Transactional method is called from another Bean, that another Bean usually injects this one and invokes its proxied (e.g. through #Autowired) version of it, and everything is fine.
When a #Transactional method is called from the same Bean directly, through usual Java call, the Spring AOP/Proxy machinery is not involved and the method is not executed inside of Transaction.
When, as in the suggested solution, a #Transactional method is called from the same Bean through self-injected proxy (self field), the situation is basically equivalent to a case 1.
#Platon Serbin's answer didn't work for me. So I kept searching and found the following answer that saved my life. :D
The answer is here No Session Hibernate in #PostConstruct, which I took the liberty to transcribe:
#Service("myService")
#Transactional(readOnly = true)
public class MyServiceImpl implements MyService {
#Autowired
private MyDao myDao;
private CacheList cacheList;
#Autowired
public void MyServiceImpl(PlatformTransactionManager transactionManager) {
this.cacheList = (CacheList) new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
#Override
public Object doInTransaction(TransactionStatus transactionStatus) {
CacheList cacheList = new CacheList();
cacheList.reloadCache(MyServiceImpl.this.myDao.getAllFromServer());
return cacheList;
}
});
}
The transaction part of spring might not be initialized completely at #PostConstruct.
Use a listener to the ContextRefreshedEvent event to ensure, that transactions are available:
#Component
public class YourService
implements ApplicationListener<ContextRefreshedEvent> // <= ensure correct timing!
{
private final YourRepo repo;
public YourService (YourRepo repo) {this.repo = repo;}
#Transactional // <= ensure transaction!
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
repo.doSomethingWithinTransaction();
}
}
Using transactionOperations.execute() in #PostConstruct or in #NoTransaction method both works
#Service
public class ConfigurationService implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationService.class);
private ConfigDAO dao;
private TransactionOperations transactionOperations;
#Autowired
public void setTransactionOperations(TransactionOperations transactionOperations) {
this.transactionOperations = transactionOperations;
}
#Autowired
public void setConfigurationDAO(ConfigDAO dao) {
this.dao = dao;
}
#PostConstruct
public void postConstruct() {
try { transactionOperations.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(final TransactionStatus status) {
ResultSet<Config> configs = dao.queryAll();
}
});
}
catch (Exception ex)
{
LOG.trace(ex.getMessage(), ex);
}
}
#NoTransaction
public void saveConfiguration(final Configuration configuration, final boolean applicationSpecific) {
String name = configuration.getName();
Configuration original = transactionOperations.execute((TransactionCallback<Configuration>) status ->
getConfiguration(configuration.getName(), applicationSpecific, null));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
}
}

requestfactory complain about find method

I have a spring (3.1) application with a service and dao layer.
I try to use requestfactory (gwt 2.4) withi this spring layer.
Here some of my class
My domain class
public class Account {
Long id;
String username;
// get, set
}
The bridge between spring and gwt
public class SpringServiceLocator implements ServiceLocator {
#Override
public Object getInstance(Class<?> clazz) {
HttpServletRequest request = RequestFactoryServlet.getThreadLocalRequest();
ServletContext servletContext = request.getSession().getServletContext();
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
return context.getBean(clazz);
}
}
My account proxy
#ProxyFor(value=Account.class, locator = AccountLocator.class)
public interface AccountProxy extends EntityProxy{
public Long getId();
public String getUsername();
public void setUsername(String userName);
public void setId(Long id);
}
RequestContext class
#Service(locator = SpringServiceLocator.class, value =AccountService.class)
public interface AccountRequest extends RequestContext {
Request<List<AccountProxy>> loadAllAccounts();
}
My requestFactory class
public interface AccountRequestFactory extends RequestFactory {
AccountRequest accountRequest();
}
My spring service
public interface AccountService {
public List<Account> loadAllAccounts();
}
#Service
public class AccountServiceImpl implements AccountService{
#Autowired
private AccountDAO accountDAO;
}
Account locator to avoid to put method in the entity
public class AccountLocator extends Locator<Account, Long> {
#Autowired
private AccountDAO accountDAO;
#Override
public Account create(Class<? extends Account> clazz) {
return new Account();
}
}
applicationContext.xml
<context:annotation-config />
<context:component-scan base-package="com.calibra" />
<bean id="accountService" class="org.calibra.server.service.AccountServiceImpl"/>
<bean id="accountDAO" class="org.calibra.server.dao.AccountDAOImpl"/>
The demo work but i get this error:
com.google.web.bindery.requestfactory.server.UnexpectedException: Could not find static method with a single parameter of a key type
Also on my AccountProxy i get this complain (a warning)
The domain type org.calibra.domain.Account has no Account findAccount(java.lang.Long) method. Attempting to send a AccountProxy to the server will result in a server error.
I don't want to add a find methond in my domain class.
I tried to put this method in my spring service, but i get the same warning.
Edit with the Locator that work fine
Just strange i need to put bean in the applicationContext, context:annotation and context:component-scan seem useless
Any idea?
The domain type org.calibra.domain.Account has no Account findAccount(java.lang.Long) method.
If you don't provide a find method of some kind, RequestFactory has no way of reconstituting objects when they get to the server - it can only create brand new ones, which prevents it from merging with existing data. Take this away, and you might as well have RPC again.
If you don't want static methods, provide a Locator instance which is able to find objects. From https://developers.google.com/web-toolkit/doc/latest/DevGuideRequestFactory#locators:
What if you don't want to implement persistence code in an entity itself? To implement the required entity locator methods, create an entity locator class that extends Locator:
public class EmployeeLocator extends Locator<Employee, Long> {
#Override
public Employee create(Class<? extends Employee> clazz)
{
return new Employee();
}
...
}
Then associate it with the entity in the #ProxyFor annotation:
#ProxyFor(value = Employee.class, locator = EmployeeLocator.class)
public interface EmployeeProxy extends EntityProxy {
...
}
You'll need to implement all of the methods, not just create - and the main one you are interested in is find(Class, Long). It may be possible to use one single Locator type for all proxies - as of 2.4.0 and 2.5.0-rc1 it is safe to fail to implement getDomainType(), and all of the other methods that need to know the exact type are provided with it as an argument.
Here is an example of what this can look like with JPA and Guice, but I think the idea is clear enough that it can be implemented with Spring and whatever persistence mechanism you are using. Here, all entities are expected to implement HasVersionAndId, allowing the locator to generalize on how to invoke getVersion and getId - you might have your own base class for all persisted entities.
(from https://github.com/niloc132/tvguide-sample-parent/blob/master/tvguide-client/src/main/java/com/acme/gwt/server/InjectingLocator.java)
public class InjectingLocator<T extends HasVersionAndId> extends Locator<T, Long> {
#Inject
Provider<EntityManager> data;
#Inject
Injector injector;
#Override
public T create(Class<? extends T> clazz) {
return injector.getInstance(clazz);
}
#Override
public T find(Class<? extends T> clazz, Long id) {
return data.get().find(clazz, id);
}
#Override
public Class<T> getDomainType() {
throw new UnsupportedOperationException();//unused
}
#Override
public Long getId(T domainObject) {
return domainObject.getId();
}
#Override
public Class<Long> getIdType() {
return Long.class;
}
#Override
public Object getVersion(T domainObject) {
return domainObject.getVersion();
}
}

Resources