spring与struts2的几种集成方案(部分内容转载网络)

对于spring与struts2的集成有多种方案,在网上找到下面说明还算清楚,所以这里将它直接引用过来,后面将对其原理详加分析

 一、需要的JAR文件为:Spring和Struts2框架本身需要的JAR文件以及他们所依赖的JAR文件,比如commons-logging.jar等等,另外还需要Struts2发布包中的struts2-spring-plugin-x.xx.jar。
二、在web.xml中增加WebApplicationContext的相应配置,以下两种配置方式本质是一样的。
1.         Servlet 2.3及以上版本可以使用监听器,相应配置如下:
<context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<listener>
              <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
如果spring配置文件被命名为applicationContext.xml,并且放在WEB-INF目录下,则不需要配置<context-param>,因为ContextLoaderListener默认在WEB-INF目录下寻找名为applicationContext.xml的文件。若存在多个Spring配置文件,则在<param-value>中依次列出,之间以逗号隔开。
2.         Servlet 2.3以下版本由于不支持<listener>,需要配置<servlet>,格式如下:
<context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<servlet>
              <servlet-name>contextLoaderServlet</servlet-name>
              <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
              <load-on-startup>1</load-on-startup>
</servlet>
如果spring配置文件被命名为applicationContext.xml,并且放在WEB-INF目录下,则不需要配置<context-param>,因为ContextLoaderListener默认在WEB-INF目录下寻找名为applicationContext.xml的文件,或者是名字为contextConfigLocation的ServletContext参数所指定的文件。由于该Servlet配置只是为了在容器启动时能启动ContextLoaderServlet使其工作,而不需要引用该Servlet,所以不需要配置<servlet-mapping>。
三、在web.xml中完成加载WebApplicationContext之后,接下来就可以做到Spring和Struts2的整合了。整合有两种方法,分别叙述如下:
1.         第一种实现方法:
1)        将Struts的业务逻辑控制器类配置在Spring的配置文件中,业务逻辑控制器中引用的业务类一并注入。注意,必须将业务逻辑控制器类配置为scope=”prototype”!
示例如下:
<bean id=”LoginAction” class=”yaso.struts.action.LoginAction”>
                     <property name=”loginDao” ref=”LoginDao”/>
</bean>
2)        在struts.xml或者等效的Struts2配置文件中配置Action时,指定<action>的class属性为Spring配置文件中相应bean的id或者name值。示例如下:
<action name=”LoginAction” class=”LoginAction”>
                     <result name=”success”>/index.jsp</result>
</action>
2.         第二种实现方法:
1)        业务类在Spring配置文件中配置,业务逻辑控制器类不需要配置,Struts2的Action像没有整合Spring之前一样配置,<action>的class属性指定业务逻辑控制器类的全限定名。
2)        业务逻辑控制器类中引用的业务类不需要自己去初始化,Struts2的Spring插件会使用bean的自动装配将业务类注入进来,其实业务逻辑控制器也不是Struts2创建的,而是Struts2的Spring插件创建的。默认情况下,插件使用by name的方式装配,可以通过增加Struts2常量来修改匹配方式:设置方式为:struts.objectFactory.spring.autoWire = typeName,可选的装配参数如下:
a)         name:等价于Spring配置中的autowire=”byName”,这是缺省值。
b)        type:等价于Spring配置中的autowire=”byType”。
c)         auto:等价于Spring配置中的autowire=”autodetect”。
d)        constructor:等价于Spring配置中的autowire=” constructor”。
四、如果原先在Struts2中使用了多个object factory,则需要通过Struts2常量显式指定object factory,方式如下:struts.objectFactory = spring;如果没有使用多个object factory,这一步可以省略。
五、可以通过设增加Struts2常量来指定是否使用Spring自身的类缓存机制。可以设定的值为true或false,默认为true。设置方式为:struts.objectFactory.spring.useClassCache = false。
六、至此,完成了两种方式的整合。比较这两种整合方式,其本质是一样的。不同之处在于,使用第二种自动装配的方式时,由于没有在Spring中配置业务逻辑控制器,所以需要对其配置一些AOP之类的内容时就很难实现了。

 

上面的两种集成方案我认为是当下最好的两种集成方案,至于为什么可以这样集成需要对spring以及struts2(确切的说是xwork)都要有所有了解才能很好的理解其中的含义,尤其是xwork的内部结构及运行源程序要有所了解.xwork内部组件是由自己的容器来管理的,并且这些组件大部分都是可以替换的,其中有一个组件负责其它组件的创建,其中最主要的xwork三个最重要的组件:Action,Interceptor,Result还有相应的快捷方法,这个类就是ObjectFactory它是一个独立的类,没有实现任何的接口,主要业务集中在两个方法,在下面的用红色标注的两个方法

/**
     * Build a generic Java object of the given type.
     *
     * @param clazz the type of Object to build
     * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
     */
    public Object buildBean(Class clazz, Map extraContext) throws Exception {
        return clazz.newInstance();
    }

    /**
     * @param obj
     */
    protected Object injectInternalBeans(Object obj) {
        if (obj != null && container != null) {
            container.inject(obj);
        }
        return obj;
    }

    /**
     * Build a generic Java object of the given type.
     *
     * @param className the type of Object to build
     * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
     */
    public Object buildBean(String className, Map extraContext) throws Exception {
        return buildBean(className, extraContext, true);
    }
    
    /**
     * Build a generic Java object of the given type.
     *
     * @param className the type of Object to build
     * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
     */
    public Object buildBean(String className, Map extraContext, boolean injectInternal) throws Exception {
        Class clazz = getClassInstance(className);
        Object obj = buildBean(clazz, extraContext);
        if (injectInternal) {
            injectInternalBeans(obj);
        }
        return obj;
    }

这两个方法之间有依赖关系,即字集串构建方法(后一个方法)需要调用前一个方法(类对象构建),上面就是xwork原始ObjectFactory对象构建对象的核心方法,另外需要注意一点是上面还有一个方法injectInternalBeans它用来注入xwork内部管理组件的,与现在讨论问题不是很紧密,但在整个流程中也是很重要环节,如果需要了解详情请参考其它资料.

在xwork中为了和spring集成专门对ObjectFactory进行了继承扩充,这个类被称为:SpringObjectFactory这个类对上面两个方法进行覆盖,赋予了新含义,具体的代码如下:

/**
     * Looks up beans using Spring's application context before falling back to the method defined in the {@link
     * ObjectFactory}.
     *
     * @param beanName     The name of the bean to look up in the application context
     * @param extraContext
     * @return A bean from Spring or the result of calling the overridden
     *         method.
     * @throws Exception
     */
    public Object buildBean(String beanName, Map extraContext, boolean injectInternal) throws Exception {
        Object o = null;
        try {
            o = appContext.getBean(beanName);
        } catch (NoSuchBeanDefinitionException e) {
            Class beanClazz = getClassInstance(beanName);
            o = buildBean(beanClazz, extraContext);
        }
        if (injectInternal) {
            injectInternalBeans(o);
        }
        return o;
    }

    /**
     * @param clazz
     * @param extraContext
     * @throws Exception
     */
    public Object buildBean(Class clazz, Map extraContext) throws Exception {
        Object bean;

        try {
            bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
        } catch (UnsatisfiedDependencyException e) {
            // Fall back
            bean = super.buildBean(clazz, extraContext);
        }

        bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());
        // We don't need to call the init-method since one won't be registered.
        bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());
        return autoWireBean(bean, autoWiringFactory);
    }


在创建对象的时候第一个方法会先在spring容器的上下文中进行查询对象(这里实际实际对应上面的集成方案一);如果在上下文中没有查找action定义,退而求其次调用第二个方法使用autoWiringFactory(这是一个与spring上下文相关的对象)创建action实例对象并注入容器中的其它依赖对象(这里实际对应上面的集成方案二).在struts2中的有一个与spring的集成插件名称为:StrutsSpringObjectFactory它实际只是一个适配器,让它更适用于struts2自身它实际完全使用SpringObjectFactory功能

通过上面的分析基本可以很清楚的看到在struts的action配置中的有一个class属性,例如:<action name="login" class="com.risen.core.action.ProductAction" method="listProduct">在没有与spring进行集成的时候,此处只能填写对应action类的全名称,从ObjectFactory中的buildBean方法可以看它只支持类的名称,当它与spring进行集成之后,这个属性的值便有了更多的选择,理论上它可以填写任何标识,因为SpringObjectFactory采用的策略是先到spring容器中使用这个名称在容器中检索一次,如果可以检索成功,就会使用检索到的这个对象(当然它已经完成依赖注入),就会直接使用这个对象(前面的方式一),如果没有检索到这个对象会将这个名称作为action类的全名称,进行实例化,并使用使用spring容器中的对象对这个实例化对象进行注入操作,再将这个注入后的对象返回(方式二)从代码上看这第二种方式的代价似乎大一些,但是它不需要管理action

在实际中action究竟需不需要管理,我觉得没有一个定论,但是作为开发者你要清楚集成机制,比如开发者可能会使用spring的扫描组件对需要管理的组件进行扫描,在配置文件使用<context:component-scan base-package="com.xxx.core.action" />,这时你就需要十分的清醒,对action扫描之后它们在spring容器中的检索名称是什么,即name或id值,如果在进行扫描的时候没有在@Repository 、@Service 、@Controller 等标注中指定名称,它们的默认名称是类名称的首字母小写形式,此时你就需要在对应的action类型属性中填写这个名称,而不是action的全类名,如果需要一个简单的原则,可以归纳成:如果action配置使用全路径名称,就不要对action对象进行管理,如果不使用全路径类名称,则需要对action进行管理,但千万要把两都的标识名称匹配上,否则会出错.

另外有一个关于自动装配的问题如果你使用组件扫描功能的时候,例如<context:component-scan base-package="xxxAction" />,此时如果需要使用自动注入的功能,就必须使用@Autowired标注才可以办得到,因为此时你没有地方设置自动装配的策略,容器只会使用@Autowired来完成注入的功能,即使用你将struts2中的装配策略改了也是如此,因为它设置的装配策略是针对action写全类名称才会有效果.提醒一下就是@Autowired使用的是先类型,后名称策略,所以虽然多写了一点还是有好处的

 关于这上面两种方案优缺点上面方案已经提到,第一个方案配置文件写得多一些,但是可控性会更强一些,第二种方案配置会更少,因为action不需要在spring配置文件中出现,这种自动化更,但程序员介入手段相对更少,两种方案各有千秋.

 另外上面只着眼于aciton对象的生成与创建,实际通过上面的分析你会发现xwork中其它组件的创建也会从中得利,即像Interceptor,Result等对象也可以很容易的获取spring容器中的对象,当然至于需不需要那是另一回事

=============================================================================================

下面是关于webwork与spring集成一些方法,可以在集成的时候进行参考

参考:
http://wiki.opensymphony.com/display/WW/WebWork+2+Spring+Integration
http://forum.iteye.com/viewtopic.php?t=8509
http://forum.iteye.com/viewtopic.php?t=9939

下载:
http://xwork-optional.dev.java.net/


  昨天使用webwork和spring,使用的是SpringObjectFactory方法,突然发现validator没发生作用,折腾半天,
  换成external-ref的方法,可以了.但是觉得external-ref的方法太麻烦了.
  就是去http://xwork-optional.dev.java.net/下载了一下最新的源码,换回SpringObjectFactory的方式,发现问题解决了(重载getClassInstance解决了这个问题).
 
  于是仔细研究了一下几种组合的方法,根据前面参考中的文章,整理了一下.
 
  看了xwork-optional的源码,发现其实三种方法的源码都包含在这里了,当然也包括external-ref这种方法的源码,只是换了包名和文件名,但是源码基本没变.你可以仔细看看.
 
  1.External-Ref
 
  这种方法看起来比较烦琐,可能现在都改用第3种方法了.
 
  第一步:在web.xml里面增加一个listener,如下
 

Java代码 复制代码  收藏代码
  1. <listener>   
  2.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   
  3. </listener>   
  4.   
  5. <listener>   
  6.     <listener-class>com.opensymphony.xwork.spring.SpringExternalReferenceResolverSetupListener</listener-class>   
  7. </listener>  
		<listener>
			<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
		</listener>
		
		<listener>
			<listener-class>com.opensymphony.xwork.spring.SpringExternalReferenceResolverSetupListener</listener-class>
		</listener>



第二步:在Spring里面配置类似Dao之类的bean,例如

Java代码 复制代码  收藏代码
  1. <bean id="myDAO" class="com.ryandaigle.persistence.MyDAO" singleton="true" />  
		<bean id="myDAO" class="com.ryandaigle.persistence.MyDAO" singleton="true" />



第三步:配置XWork.xml,例如

Java代码 复制代码  收藏代码
  1. <package name="default" extends="webwork-default"  
  2.     externalReferenceResolver="com.opensymphony.xwork.spring.SpringExternalReferenceResolver">          
  3.     <interceptors>   
  4.         <interceptor name="reference-resolver" class="com.opensymphony.xwork.interceptor.ExternalReferencesInterceptor"/>   
  5.         <interceptor-stack name="myDefaultWebStack">   
  6.             <interceptor-ref name="defaultStack"/>   
  7.             <interceptor-ref name="reference-resolver"/>   
  8.         </interceptor-stack>   
  9.     </interceptors>   
  10.        
  11.     <default-interceptor-ref name="myDefaultWebStack"/>   
  12.        
  13.     <action name="myAction" class="com.ryandaigle.web.actions.MyAction">   
  14.         <external-ref name="DAO">myDAO</external-ref>   
  15.         <result name="success" type="dispatcher">   
  16.             <param name="location">/success.jsp</param>   
  17.         </result>   
  18.     </action>   
  19.   
  20. </package>   
		<package name="default" extends="webwork-default"
			externalReferenceResolver="com.opensymphony.xwork.spring.SpringExternalReferenceResolver">		
			<interceptors>
				<interceptor name="reference-resolver" class="com.opensymphony.xwork.interceptor.ExternalReferencesInterceptor"/>
				<interceptor-stack name="myDefaultWebStack">
					<interceptor-ref name="defaultStack"/>
					<interceptor-ref name="reference-resolver"/>
				</interceptor-stack>
			</interceptors>
			
			<default-interceptor-ref name="myDefaultWebStack"/>
			
			<action name="myAction" class="com.ryandaigle.web.actions.MyAction">
				<external-ref name="DAO">myDAO</external-ref>
				<result name="success" type="dispatcher">
					<param name="location">/success.jsp</param>
				</result>
			</action>
		
		</package>
	


 
 
 
 
  2.SpringObjectFactory
  我一直用这种方法,因为以前觉得是xwork本身提供的方法,升级有保障.
 
  配置方法:
  第一步.在spring的 applicationContext.xml (根据实际情况决定) 里面定义你的action,例如
 

Java代码 复制代码  收藏代码
  1. <bean name="some-action" class="fully.qualified.class.name" singleton="false">   
  2.     <property name="someProperty"><ref bean="someOtherBean"/></property>   
  3. </bean>   
		<bean name="some-action" class="fully.qualified.class.name" singleton="false">
		    <property name="someProperty"><ref bean="someOtherBean"/></property>
		</bean>
		



可以看到,可以使用Spring的特性来给你的action设置属性等,当然也可以使用Spring的拦截器等 (可以使用不一定等于推荐使用)

注意一定是singleton="false",因为xwork的action是这样要求的.

  第二步.在xwork.xml里定义你的action定义
 

Java代码 复制代码  收藏代码
  1. <action name="myAction" class="some-action">   
  2.     <result name="success">view.jsp</result>   
  3. </action>    
		<action name="myAction" class="some-action">
		    <result name="success">view.jsp</result>
		</action>  


 
  第三步.要使上面的关联生效,还要用我们的SpringObjectFactory来替换Xwork的默认ObjectFactory.
    最新的SpringObjectFactory里面,有两种方法,其中我觉得A方法更直观一些.
   
    A:修改web.xml
   

Java代码 复制代码  收藏代码
  1.         <!-- 这个是spring的listener,可以改为你的自定义的spring的Listenter -->   
  2. <listener>   
  3.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   
  4. </listener>   
  5.   
  6.   
  7. <!-- 这个必须在 Spring ContextLoaderListener 的后面 -->   
  8. <listener>   
  9.   <listener-class>com.opensymphony.xwork.spring.SpringObjectFactoryListener</listener-class>   
  10. </listener>  
  	  		<!-- 这个是spring的listener,可以改为你的自定义的spring的Listenter -->
			<listener>
				<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
			</listener>
			
		
			<!-- 这个必须在 Spring ContextLoaderListener 的后面 -->
			<listener>
			  <listener-class>com.opensymphony.xwork.spring.SpringObjectFactoryListener</listener-class>
			</listener>


   
    B.在spring的applicationContext.xml (根据实际情况决定)里面定义一个bean,例如
   

Java代码 复制代码  收藏代码
  1. <bean id="spring-object-factory" class="com.opensymphony.xwork.spring.SpringObjectFactory"    
  2.     init-method="initObjectFactory"/>  
  	  	<bean id="spring-object-factory" class="com.opensymphony.xwork.spring.SpringObjectFactory" 
  	  		init-method="initObjectFactory"/>


   
    这样Spring会自动调用initObjectFactory方法来替换Xwork的默认ObjectFactory
   
   
 
  3.ActionAutowiringInterceptor
    这个方法是最近出现的,可能是最简洁的方法,但是不知道性能上有没有问题,我觉得jdk1.4以后应该没有任何问题吧,至于实际效果你的自己测试一下.
   
    第一步:配置web.xml

Java代码 复制代码  收藏代码
  1.         <!-- 这个是spring的listener,可以改为你的自定义的spring的Listenter -->   
  2. lt;listener>   
  3. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   
  4. lt;/listener>   
  	  		<!-- 这个是spring的listener,可以改为你的自定义的spring的Listenter -->
			<listener>
				<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
			</listener>
	


第二步配置xwork.xml里面的拦截器

Java代码 复制代码  收藏代码
  1. <interceptors>   
  2.   <interceptor name="autowire" class="com.opensymphony.xwork.spring.interceptor.ActionAutowiringInterceptor">   
  3.     <param name="autowireStrategy">@org.springframework.beans.factory.config.AutowireCapableBeanFactory@AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE</param>   
  4.   </interceptor>   
  5.   <interceptor-stack name="autowireDefault">   
  6.     <interceptor-ref="autowire" />   
  7.     <interceptor-ref="defaultStack" />   
  8.   </interceptor-stack>   
  9. </interceptors>  
		<interceptors>
		  <interceptor name="autowire" class="com.opensymphony.xwork.spring.interceptor.ActionAutowiringInterceptor">
		    <param name="autowireStrategy">@org.springframework.beans.factory.config.AutowireCapableBeanFactory@AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE</param>
		  </interceptor>
		  <interceptor-stack name="autowireDefault">
		    <interceptor-ref="autowire" />
		    <interceptor-ref="defaultStack" />
		  </interceptor-stack>
		</interceptors>

   
    你的拦截器里都要包含autowire,例如这个autowireDefault就相当于以前的默认的拦截器了.
    其中的拦截器策略可以配置,上面配置的是根据类型,如果不配置,默认是根据名字.
   
    一共有四种策略:
    AUTOWIRE_CONSTRUCTOR
    AUTOWIRE_BY_TYPE
    AUTOWIRE_BY_NAME
    AUTOWIRE_AUTODETECT
   
   
    这种方法执行原理就是查找你的action的所有字段,如果和Spring的定义bean有相同的,就自动设置.
   
    假设你的Spring的applicationContext.xml里面有这样一个定义:
   

Java代码 复制代码  收藏代码
  1. <bean id="userManager" class="com.test.UserManager" />  
    	<bean id="userManager" class="com.test.UserManager" />


   
  如果你在xwork.xml  里面定义的某个action有一个字段叫userManager,那么在运行时刻就会自动被设置为Spring的配置文件里定义的Bean.

以上如有不对指出,还请指出

=============================================================================================

最近突然想到一个问题

以前在用struts2(注解)+spring

struts的action拖给spring管了(spring的bean在Ioc容器范围内默认都是singlen的),但是没有加@scope("prototype")却从来没有出现过线程安全问题。

而一年前还在学校时做ssh2(没用注解)练习时,不在bean后加prototype都会出现线程安全问题,当时所有的action后都加了scope=prototype。

难道注解和不用注解,struts2创建action的方式不一样?

于是做了个测试

Java代码 复制代码  收藏代码
  1. public class TestAction extends BaseAction {   
  2.     @Autowired  
  3.     private UserService userService;   
  4.     @Action("test")   
  5.     public String test() throws Exception {   
  6.         System.out.println("action HashCode:"+this.hashCode());   
  7.         userService.getUserName();         
  8.         return "success";   
  9.     }   
  10. }  
public class TestAction extends BaseAction {
	@Autowired
	private UserService userService;
	@Action("test")
	public String test() throws Exception {
		System.out.println("action HashCode:"+this.hashCode());
		userService.getUserName();		
		return "success";
	}
}
Java代码 复制代码  收藏代码
  1. @Service  
  2. public class UserService {   
  3.  public void getUserName(){   
  4.   System.out.println("service HashCode:"+hashCode()); }   
  5. }  
@Service
public class UserService {
 public void getUserName(){
  System.out.println("service HashCode:"+hashCode()); }
}

 连续运行3次发现输出:

Java代码 复制代码  收藏代码
  1. action HashCode:9928297  
  2. service HashCode:32262619  
  3. action HashCode:13620718  
  4. service HashCode:32262619  
  5. action HashCode:19792917  
  6. service HashCode:32262619  
action HashCode:9928297
service HashCode:32262619
action HashCode:13620718
service HashCode:32262619
action HashCode:19792917
service HashCode:32262619

 输出结果说明 每次的action是不一样的

而每次的service是同一个也就是单例的

(难道action并没有交个spring托管)

又把以前在学校做的老项目(纯xml)拿来 把以前的scope="prototype"去掉

发现action确实是单例的   再加上scope="prototype"后action不是单例了

 

所以struts2(注解) 在和spring集成时action默认是new的,不用注解spring扫描action的话也是用的new。

而非注解时如果把action加入bean的话默认是单例的。

所以如果大家struts2是用注解的话就不需要在action上加@scope("prototype")了;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值