It's a somewhat rare occassion that I get to fuss around with code these days. This time, it happens to be in assistance of one of our Global Services consultants for his West Coast Symposium workshop.
As many of you know, Liferay's Service Builder allows you to completely avoid worrying about the persistence layer, handing your own query and entity caching, etc. It really cuts down on the amount of tedious development associated with JEE applications. However, using Spring MVC together with Service Builder has been problematic as combining the two leads to two root Spring ApplicationContexts:
- one for the ServiceBuilder generated services
- a second for the Spring MVC beans.
Of course, Spring doesn't like it when you attempt to have multiple root application contexts within the same servlet context. To use both ServiceBuilder services and Spring MVC, developers would take the strategy of packaging the services in one WAR and the Spring MVC portlets in a second WAR.
I won't bore you with the ins and outs of ClassLoader hierarchies, aggregate classloaders, why we have multiple spring contexts, and etc. Suffice it to say, for 6.1, you will be able to package Spring MVC with ServiceBuilder services within the same plugin war.
You will need to do the following:
- Use the Spring ContextLoaderListener to your web.xml to initialize Spring MVC related config files. (e.g. WEB-INF/applicationContext.xml)
- Place all custom spring configs required by ServiceBuilder services in something like:
This will ensure services related beans are loaded in the ServiceBuilder ApplicationContext and Spring MVC related beans are loaded in the Spring MVC ApplicationContext.
There are some constraints/limitations:
- You cannot inject any depencies from the Spring MVC context into beans held in the ServiceBuilder context. However, this should never happen anyway since the two architectural layers should be kept separate.
- You cannot inject services (e.g. MyLocalService) into the Spring MVC beans. Instead, in your Spring MVC beans, you must call MyLocalServiceUtil.doXYZ(...)
(2) may cause some consternation for some IoC practitioners since this breaks the rule that all dependencies should be injected. However, you can easily overcome this inconvenience by injecting a mock object into the MyLocalServiceUtil as part of your unit tests.