Liferay hook working with Spring + MyBatis(Updated 3 ways now)

本文介绍了在使用Liferay框架进行项目开发时,遇到如何在自定义服务类中获取Spring管理的Bean的问题,并分享了三种解决方案:通过手动读取Spring配置文件、使用SpringUtil类来间接获取Bean、以及利用BeanFactory直接获取Bean。最后讨论了在Servlet上下文中获取Bean时可能遇到的异常问题。
摘要由CSDN通过智能技术生成
In some cases, we need to customize Liferay. In my case, I choose hook to do that. But it ran into an issue how to get beans in the service class.

For example:

public class MyOrganizationLocalServiceImpl extends OrganizationLocalServiceWrapper {

	public MyOrganizationLocalServiceImpl(OrganizationLocalService organizationLocalService) {
		super(organizationLocalService);
	}
	
	
	public Organization updateOrganization(long companyId, long organizationId,
			long parentOrganizationId, String name, String type, boolean recursable, long regionId,
			long countryId, int statusId, String comments, boolean site,
			ServiceContext serviceContext) throws PortalException, SystemException {

//...more


This is my own service class, I have to get the beans which will use to connect the database and do some operation. Because OrganizationLocalServiceWrapper doesn't have default constructor,  we can't ask Spring to initialize the class for us. That means we can't use Constructor or setting injection in the class. 

Actually, when you deploy the project, tomcat will read web.xml, then tomcat will initialize beans for use if we do settings like this:

<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/SpringContextConfiguration.xml</param-value>
	</context-param>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<listener>
		<listener-class>com.liferay.portal.kernel.servlet.HookContextListener</listener-class>
	</listener>
Even though all beans are initialized, I can't get in service class, it doesn't work on my case. 

The only way I can get beans is read the SpringContextConfiguration.xml in the service class. Then use ApplicationContext to get beans. Hard code.

private static ApplicationContext context = new ClassPathXmlApplicationContext("../SpringContextConfiguration.xml");
private IOrganizationStatusManager organizationStatusManager = context.getBean(
			"organizationStatusManager", IOrganizationStatusManager.class);

This is my solution to do that, if anyone has good solution, please let me know. Thanks in advance!

--------------------------------------------------------------------The Second Way-----------------------------------------------------------------------------------------------------

Here is to show another way, a better way to do this. I need to thanks my coworker Bin. He helped me figure it out.

The basic idea is to use a spring util class, let me show you the code.

SpringUtil.java. The class is singleton.

public class SpringBeanUtil {
	
private static SpringBeanUtil instance = new SpringBeanUtil();
	
	private static IOrganizationStatusManager organizationStatusManager;
	
	private SpringBeanUtil () {}

	public static SpringBeanUtil getInstance() {
		return instance;
	}
	
	public void setInstance(SpringBeanUtil newInstance) {
		instance = newInstance;
	}

	public IOrganizationStatusManager getOrganizationStatusManager() {
		return organizationStatusManager;
	}
	
	public void setOrganizationStatusManager(IOrganizationStatusManager organizationStatusManager) {
		SpringBeanUtil.organizationStatusManager = organizationStatusManager;
	}
	
}

In our springcontext.xml, we need to define the beans, it doesn't work by using annotation. We have to explicitly wire the beans.

<bean id="springBeanUtil" class="org.myproject.organizationhook.util.SpringBeanUtil">
		<property name="organizationStatusManager" ref="organizationStatusManager"></property>
</bean>

OrganizationStatusManager.java

@Service("organizationStatusManager")
@Transactional("organizationTransactionManager")
public class OrganizationStatusManager implements IOrganizationStatusManager {

	@Autowired
	@Qualifier("organizationStatusDao")
	private IOrganizationStatusDao organizationStatusDao;

	//isDeleted = 0 means the organization isn't deleted
	public int addOrganizationStatus(long organizationId) {		
		return null;
	}

	public String getSNByOrganizationId(long organizationId) {
		return null;
	}

	public long countOrgStatuses() {
		return 0L;
	}

	public void updateOrganizationStatus(long organizationId) {
	}

	public int addLastUpdateStatus() {	
		return 0;
	}

	public void deleteOrganizationStatus(long organizationId) {	
	}

}

The last thing we need to do is when we deploy the hook project, load the configuration file. We add listener in web.xml

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/springcontext.xml</param-value>
</context-param>


<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
In web.xml, if we don't specify context-param to give a specific name for the file, the default name is applicationContext.xml

Now, in our MyOrganizationLocalServiceImpl.java, we use the IOrganizationManager class like this:



That's all! The tricky thing here is we can't add annotation on SpringBeanUtil, even though we add that, in MyOrganizationLocalServiceImpl class also get null. We have to explicit wire those beans in springcontext.xml.

public class MyOrganizationLocalServiceImpl extends OrganizationLocalServiceWrapper {
	
private IOrganizationStatusManager organizationStatusManager = SpringBeanUtil.getInstance().getOrganizationStatusManager();

//...more

The second way is better than the first one. We don't take care of the spring beans manually, tomcat takes care of that.

-------------------------------------------------------------The Third Way----------------------------------------------------------

I just learned a new way from my co-worker Bin to get the bean. This would be the best way to do that. Spring is so powerful to meet our requirements.

This way is very easy, you just need to write another class and directly call it. We need to get the help of BeanFactory.

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;

@Component("beanFactoryUtil")
public class BeanFactoryUtil implements BeanFactoryAware{
	
	private static BeanFactory beanFactory = null;

	public void setBeanFactory(BeanFactory beanFactory1) throws BeansException {
		beanFactory = beanFactory1;
	}
	
	public static BeanFactory getBeanFactory(){
		return beanFactory;
	}

}

This class is only used to get BeanFactory, then we use it to get beans in our util class:

private IOrganizationStatusManager organizationStatusManager = (IOrganizationStatusManager) BeanFactoryUtil.getBeanFactory().getBean("organizationStatusManager");
That's all. It's really easy right.


Note:

If you're developing on Servlet, you don't need to write the BeanFactoryUtil class, you can directly get beans like this:

BeanFactory bf = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());

I tried to get ServletContext in my wrapper class, but it throws Exception which I didn't find a good answer until now.

PortletBag portletBag = com.liferay.portal.kernel.portlet.PortletBagPool.get(serviceContext.getPortletId());
ServletContext servletContext = portletBag.getServletContext();
BeanFactory bf = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);


Exception message:

21:00:14,035 ERROR [render_portlet_jsp:154] java.lang.IllegalStateException: Context attribute is not of type WebApplicationContext: Root WebApplicationContext: startup date [Fri Dec 21 20:25:57 GMT 2012]; root of context hierarchy
	at com.rujuan.organizationhook.custom.MyOrganizationLocalServiceImpl.updateOrganization(MyOrganizationLocalServiceImpl.java:63)
	at com.liferay.portal.kernel.bean.ClassLoaderBeanHandler.invoke(ClassLoaderBeanHandler.java:54)
	at com.liferay.portal.spring.aop.ServiceBeanMethodInvocation.proceed(ServiceBeanMethodInvocation.java:112)
	at com.liferay.portal.spring.transaction.TransactionInterceptor.invoke(TransactionInterceptor.java:71)
	at com.liferay.portal.spring.aop.ServiceBeanMethodInvocation.proceed(ServiceBeanMethodInvocation.java:108)
	at com.liferay.portal.spring.aop.ServiceBeanAopProxy.invoke(ServiceBeanAopProxy.java:211)
	at com.liferay.portal.service.impl.OrganizationServiceImpl.updateOrganization(OrganizationServiceImpl.java:587)
	at com.liferay.portal.spring.aop.ServiceBeanMethodInvocation.proceed(ServiceBeanMethodInvocation.java:112)
	at com.liferay.portal.spring.transaction.TransactionInterceptor.invoke(TransactionInterceptor.java:71)
	at com.liferay.portal.spring.aop.ServiceBeanMethodInvocation.proceed(ServiceBeanMethodInvocation.java:108)
	at com.liferay.portal.spring.aop.ServiceBeanAopProxy.invoke(ServiceBeanAopProxy.java:211)
	at com.liferay.portal.service.OrganizationServiceUtil.updateOrganization(OrganizationServiceUtil.java:427)
	at com.liferay.portlet.usersadmin.action.EditOrganizationAction.updateOrganization(EditOrganizationAction.java:239)
	at com.liferay.portlet.usersadmin.action.EditOrganizationAction.processAction(EditOrganizationAction.java:86)
	at com.liferay.portal.struts.PortletRequestProcessor.process(PortletRequestProcessor.java:175)
	at com.liferay.portlet.StrutsPortlet.processAction(StrutsPortlet.java:212)
	at com.liferay.portlet.FilterChainImpl.doFilter(FilterChainImpl.java:70)
	at com.liferay.portal.kernel.portlet.PortletFilterUtil.doFilter(PortletFilterUtil.java:48)
	at com.liferay.portlet.InvokerPortletImpl.invoke(InvokerPortletImpl.java:651)
	at com.liferay.portlet.InvokerPortletImpl.invokeAction(InvokerPortletImpl.java:686)
The reason is the root of ServletContext I passed in is not WebApplicationContext rather than PortletApplicationContext.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值