Spring DM集成Strtus2(二)

Spring DM与Struts2集成

      上一篇文章已经将struts2集成到OSGi环境中了,但要在struts2中使用OSGi的服务还是很麻烦,要自己手动查找服务,而Spring DM则提供了相应的标签来支持查找OSGi服务,所以现在的目标就是让Struts2中的配置文件可以使用Spring DM中定义的Bean。

      首先我们要搞清楚SpringDM的工作原理,SpringDM是将每个Bundle下面的/META-INF/Spring/*.xml文件加载进来,创建Spring的上下文,但bundle之间的上下文是隔离的,是存放在org.springframework.osgi.extender.internal.activator.LifecycleManager类中的一个成员变量managedContexts中,此为一个Map,键为Bundle的id,值为bundle对应的Spring上下文,只要我们把bundle对应的Spring上下文找到,要完成在struts2中使用Spring的Bean就容易了。我的做法是将这个map发布成一个服务,然后在Struts的bundle中引用。先将这个Map开放出来。先将org.springframework.osgi.extender以插件的形式导入到工程中来,然后把LifecycleManager的源代码放到src下,在LifecycleManager类中增加方法

public Map<Long, ConfigurableOsgiBundleApplicationContext> getManagedContexts() {
		return managedContexts;
	}

 然后再编写一个自己的类,持有这个Map

 

 

 

/**
 * 用于存放Spring DM的上下文环境。
 * @author Dream.Lee
 * @version 2013-6-6
 */
public class SpringContextHolder {
	public SpringContextHolder(
			Map<Long, ConfigurableOsgiBundleApplicationContext> bundleContexts) {
		super();
		this.bundleContexts = bundleContexts;
	}

	/**
	 * key为bundle的id,value为bunlde对应的Spring上下文。
	 */
	private Map<Long, ConfigurableOsgiBundleApplicationContext> bundleContexts;

	public Map<Long, ConfigurableOsgiBundleApplicationContext> getBundleContexts() {
		return bundleContexts;
	}

	public void setBundleContexts(
			Map<Long, ConfigurableOsgiBundleApplicationContext> bundleContexts) {
		this.bundleContexts = bundleContexts;
	}
}

 

然后修改org.springframework.osgi.extender.internal.activator.ContextLoaderListener的start()方法,在最后加上

//将SpringContextHolder发布成服务,在其它bundle中可以通过bundleid来取得spring的上下文。
holderSF=context.registerService(SpringContextHolder.class.getName(), new SpringContextHolder(lifecycleManager.getManagedContexts()), null);

 SpringDM的代码已经改造完成,下面是改造Struts2的代码

 

由于Struts2中并不能在运行过程中取得自己所在的Bundle,所以我们只有将action所在的bundle保存起来,我的办法是,在解析配置文件的时候在package的标签中加上bundleid,然后在addPackage的方法中,有一个全局变量把action所有的bundle保存下来,以namespace.action为键,bundleid为值,相应的代码如下

 

if ("package".equals(nodeName)) {
					if(!configuration.getPackageConfigNames().contains(child.getAttribute("name"))){
						PackageConfig cfg = addPackage(child);
						//FIXME 将action的全路径名与对应的bundle存储到map中。
						if(child.getAttribute("bundleid")!=null&&child.getAttribute("bundleid").length()!=0){
							for(Entry<String, ActionConfig> entry:cfg.getActionConfigs().entrySet()){
								actionNameMapping.put(cfg.getNamespace()+"."+entry.getValue().getName(), Long.parseLong(child.getAttribute("bundleid")));
							}
						}
						if (cfg.isNeedsRefresh()) {
							reloads.add(child);
						}
					}
				}

   

 

 

 

 

if ("package".equals(nodeName)) {
						PackageConfig cfg = addPackage(child);
						//FIXME 将action的全路径名与对应的bundle存储到map中。
						if(child.getAttribute("bundleid")!=null&&child.getAttribute("bundleid").length()!=0){
							for(Entry<String, ActionConfig> entry:cfg.getActionConfigs().entrySet()){
								actionNameMapping.put(cfg.getNamespace()+"."+entry.getValue().getName(), Long.parseLong(child.getAttribute("bundleid")));
							}
						}
						if (cfg.isNeedsRefresh()) {
							reloads.add(child);
						}
					}

 就是上一篇文章提供的那个xmlProvider文件中的方法,

 

然后是修改ObjectFactory这个类,原来的ObjectFactory在从buildAction调用buildBean的时候,虽然namespace和actionname都传给了buildAction,但buildAction并没有把namespace传给buildBean,所以我们要加一个四个参数的buildBean方法,并设置成protected,默认返回null,这样不会影响其它调用。

public Object buildAction(String actionName, String namespace, ActionConfig config, Map<String, Object> extraContext) throws Exception {
    	//FIXME 先调用增加的四个参数的buildBean方法,如果返回null,再调用原来的buildBean方法
    	Object retObj=buildBean(actionName, namespace, config.getClassName(), extraContext);
    	if(retObj==null) retObj=buildBean(config.getClassName(), extraContext);
        return retObj;
    }

 

/**
     * 用于集成Spring DM,默认实现将直接返回空,这样不影响以前的调用。
     * @param actionName
     * @param namespace
     * @param className
     * @param extraContext
     * @return
     */
    protected Object buildBean(String actionName,String namespace,String className,Map<String, Object> extraContext) throws Exception{
    	return null;
    }

 然后增加自己的ObjectFactory实现,这儿是实现我们刚才增加的那个四个参数的buildBean方法

/**
 * Struts2集成Spring DM所用的对象工厂。
 * @author Dream.Lee
 * @version 2013-6-4
 */
public class SpringOSGiObjectFactory extends StrutsObjectFactory {

	private static final long serialVersionUID = -6286345242515916560L;

	@Override
	protected Object buildBean(String actionName, String namespace,
			String className, Map<String, Object> extraContext) throws Exception {
		Object obj=null;
		BundleContext context =Activator.getContext();
		ServiceTracker<SpringContextHolder, SpringContextHolder> st=new ServiceTracker<SpringContextHolder, SpringContextHolder>(context,SpringContextHolder.class.getName(),null);
		st.open();
		SpringContextHolder holder=st.getService();
		long bundleid=OSGiXmlConfigurationProvider.actionNameMapping.get(namespace+"."+actionName);
		ConfigurableOsgiBundleApplicationContext appcontext=holder.getBundleContexts().get(bundleid);
		if(appcontext!=null){
			try{
				obj=appcontext.getBean(className);
				injectInternalBeans(obj);
			}catch(Exception e){
				buildBean(className, extraContext, true);
			}
		}
		st.close();
		return obj;
	}

}

 这样就大功告成了!

你可能感兴趣的:(struts2,osgi,SpringDM)