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进行管理,但千万要把两都的标识名称匹配上,否则会出错.

关于这上面两种方案优缺点上面方案已经提到,第一个方案配置文件写得多一些,但是可控性会更强一些,第二种方案配置会更少,因为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. <listener>
  5. <listener-class>com.opensymphony.xwork.spring.SpringExternalReferenceResolverSetupListener</listener-class>
  6. </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. <beanid="myDAO"class="com.ryandaigle.persistence.MyDAO"singleton="true"/>
<bean id="myDAO" class="com.ryandaigle.persistence.MyDAO" singleton="true" />



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

Java代码 复制代码 收藏代码
  1. <packagename="default"extends="webwork-default"
  2. externalReferenceResolver="com.opensymphony.xwork.spring.SpringExternalReferenceResolver">
  3. <interceptors>
  4. <interceptorname="reference-resolver"class="com.opensymphony.xwork.interceptor.ExternalReferencesInterceptor"/>
  5. <interceptor-stackname="myDefaultWebStack">
  6. <interceptor-refname="defaultStack"/>
  7. <interceptor-refname="reference-resolver"/>
  8. </interceptor-stack>
  9. </interceptors>
  10. <default-interceptor-refname="myDefaultWebStack"/>
  11. <actionname="myAction"class="com.ryandaigle.web.actions.MyAction">
  12. <external-refname="DAO">myDAO</external-ref>
  13. <resultname="success"type="dispatcher">
  14. <paramname="location">/success.jsp</param>
  15. </result>
  16. </action>
  17. </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. <beanname="some-action"class="fully.qualified.class.name"singleton="false">
  2. <propertyname="someProperty"><refbean="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. <actionname="myAction"class="some-action">
  2. <resultname="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. <!--这个必须在SpringContextLoaderListener的后面-->
  6. <listener>
  7. <listener-class>com.opensymphony.xwork.spring.SpringObjectFactoryListener</listener-class>
  8. </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. <beanid="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. <interceptorname="autowire"class="com.opensymphony.xwork.spring.interceptor.ActionAutowiringInterceptor">
  3. <paramname="autowireStrategy">@org.springframework.beans.factory.config.AutowireCapableBeanFactory@AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE</param>
  4. </interceptor>
  5. <interceptor-stackname="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. <beanid="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. publicclassTestActionextendsBaseAction{
  2. @Autowired
  3. privateUserServiceuserService;
  4. @Action("test")
  5. publicStringtest()throwsException{
  6. System.out.println("actionHashCode:"+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. publicclassUserService{
  3. publicvoidgetUserName(){
  4. System.out.println("serviceHashCode:"+hashCode());}
  5. }
@Service public class UserService { public void getUserName(){ System.out.println("service HashCode:"+hashCode());} }

连续运行3次发现输出:

Java代码 复制代码 收藏代码
  1. actionHashCode:9928297
  2. serviceHashCode:32262619
  3. actionHashCode:13620718
  4. serviceHashCode:32262619
  5. actionHashCode:19792917
  6. serviceHashCode: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、付费专栏及课程。

余额充值