欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring DM集成Strtus2(二)

程序员文章站 2022-05-23 11:37:23
...

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;
	}

}

 这样就大功告成了!