Doing transactional work in Spring service using @PostConstruct method

Apparently, the @PostConstruct idiom in Spring / JSE isn't that well defined: If you have a service class annotated with @Transactional and a method defined as @PostConstruct tries to do transactional work (calling on DAOs, for instance), it will fail due to non existing transaction in context, sad as true.

Yeah, you could add some boolean init member and check it on every invocation of every service method, perhaps even via an interceptor, but why not use a new annotation that causes a method to get called after the object is fully initialized?

 

Well, high time to meet @PostInitialize. It'll do just that. Written by jbaruch, published on Spring forums and patched by yours truely:

 

http://forum.springsource.org/showthread.php?p=252616#post252616

 

Comments

Zvika,

 

This is exactly the issue we are experiencing and are trying to resolve in a Stripes application that leverages Spring.  It was great to happen to come across the @PostInitialize code you wrote however it doesn't work for us... Spring 2.5.6-SEC01, GlassFish v3 Final, Java 1.6.0... .

The odd thing is that in an earlier more constrained example it seemed to work.  This is the code we have and all beans are getting constructed fine its just that the initService is not getting called...

 

I really like your solution and your approach and would love to see it integrated as a part of Spring... and would love to vote for it... I just need to get it to work AND it simply appears that the Annotation is just not getting picked up however I can't tell why that is the case.

Any ideas????  Thanks You!

 

@Service("modalityService")
public class ModalityServiceImpl implements ModalityService {

    @Autowired
    private ModalityDao modalityDao;

    private ModalityCache modalityCache;

    @PostInitialize
    public void initService() {
        List<Modality> modalityList = this.modalityDao.findAll();
        this.modalityCache = new ModalityCache();
        this.modalityCache.init(modalityList);
    }
}
 

I decided to try the following code and clearly the @PostConstruct gets executed while the @PostInitialize does not... which at least implies that there are no issues in the code outside the @PostInitialize (for some reason).

However we can't use the @PostConstruct as the modalityDao bean is not fully available for executing a JPA call on it... hence why we REALLY need @PostInitialize... (very similar reasons to @Transactional not working).

I can't imagine that the issue would be difficult to solve???

Thanks in Advance!

@Service("modalityService")
public class ModalityServiceImpl implements ModalityService {

    @Autowired
    private ModalityDao modalityDao;

    private ModalityCache modalityCache;

    @PostConstruct
    public void initService2() {
System.out.println("******************************************************

************************");
    }

    @PostInitialize
    public void initService() {
System.out.println("//////////////////////////////////////////////////////////////////////////////");
        List<Modality> modalityList = this.modalityDao.findAll();
        this.modalityCache = new ModalityCache();
        this.modalityCache.init(modalityList);
    }

hi nikolaos,

 

I can think of 2 things that might have gone wrong:

either the annotation processor (PostInitializerRunner) wasn't loaded and invoked, or the annotation you've put on your service was not read properly for some reason.

 

Start by making sure that you load the annotation processor in your context. debug it, breakpoint the onApplicationEvent method, and see what if it gets called... the class implements ApplicationListener, so if the method isn't called, it means that the @Component annotation was not picked up by spring. If you are using spring annotations, just add util.spring(or whatever package you renamed it to) to your scanner configuration. if not, just add is via xml.

 

if PostInitializerRunner is indeed invoked but does not pick up the annotated service, it might be a classpath issue or a bug. my guess is that it's the other problem. let me know if it isn't.

 

btw this code was not written by me, I just improved Baruch Sadovsky's initial idea...

 

thanks for using this code, it is always nice to know my work could help others!

 

 

cheers,

zvika

 

 

Once I added the component scan the @PostInitialize worked GREAT!!!

 

However, it didn't solve my issue as the item that required initialization had a dependency on a Servlet Filter that is initialized long after Spring context is created but the code Baruch and you provided allowed me to simply create a @DelayedInitialize whereby I simply added all the classes to a satic list and provided a static method that would be triggered later after Servlet Filter initialization that would iterate over the list and invoke the methods marked with @DelayedInitialize (so in essence I simply split the flow of the @PostInitialize into a find chunk and invoke chunk).

 

Thanks again as that did the trick and all worked out well...

 

Much Appreciated!!!!

 

glad to have been able to help :)

I must add, though,

if your filter is the one that does the initialization, you don't need the annotation mechanism, which is actually a self-bootstrap mechanism... and the separation into 2 phases - just my opinion - breaks the encapsulation: what if tomorrow the context is initialized outside a web app (for example, tests, automatic scheduler, etc.)?

Zvika,

 

There are a few challenges in what we are doing - the first being that not everything originates from Spring ApplicationContext.xml... we are using Stripes which does a lot of things for you... with very minimal Spring configuration.  I imagine we could Springify the Stripes filter definition using a custom filter factory bean but would simply push configuration from web.xml into ApplicationContext.xml using beans.

 

However, you are correct we don't need a 2 phase separation and that is an excellent point.  If we are triggering the mechanism AFTER servlet filter creation then we could easily do it in a single phase.  However, I still prefer using an annotation mechanism because it eliminates the code we would have to write to have key classes register their interest in being initialized externally at some point after Servlet filter creation.

 

 

On 2nd thought when we configure our service hot reload mechanism we will most likely use that to register and init each service so yeah the annotation mechanism will most likely be dropped.  But I'm glad I have the @PostInitialize and we'll make the @DelayedInitialize 1 phase just in case we ever need it.  Thanks Again....