struts2学习笔记之spring整合
一、整合步骤:
1 配置classpath,将struts-spring-plugin.jar和spring.jar添加进去
如果少了spring.jar将报错,提示找不到相关类定义。
2 在web.xml中配置spring
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:META-INF/spring/**/*-context.xml</param-value> </context-param> 使用ContextLoaderListener <listener> <listener-class>org.springframework.web.context.ContextLoaderListenre</listener-class> </listener>
3 配置application-Context.xml
如:
<bean id="userService" class="service.impl.UserServiceImpl"> ... </bean>
4 在struts的action类中使用service
private UserService userService; public void setUserService(UserService userService){ this.userService = userService; }
通过以上方式,便可以直接在action中直接使用service进行逻辑处理。
二、原理分析:
1 spring的ApplicationContext的加载:
加载方式通常有两种:
A 通过ContextLoaderListener,如前面例子所示;但要求Web服务器支持servlet2.3以上的规范
B 通过ContextLoaderServlet,不需要服务器支持servlet2.3以上规范:
<context-param>... 配置spring配置文件位置,如上示例 <servlet> <servlet-name>context</servlet-name> <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class> <load-on-startup>1</load-on-startup>
<!--load-on-startup大于等于0时表示服务器启动时将实例化该servlet并调用init方法,数字表示优先级,越小则初始化的时机越先 --> </servlet>
然而spring的加载时机在listener方式中是更靠前的。
ContextLoaderListener与ContextLoaderServlet的实现原理是一样的,其都是通过ContextLoader来加载ApplicationContext:
this.contextLoader = createContextLoader(); this.contextLoader.initWebApplicationContext(event.getServletContext());
而ContextLoader中加载spring配置的代码如下:
this.context = createWebApplicationContext(servletContext, parent); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
可以看到,loader对象将加载后的WebAppApplication对象放入了servletContext中(application级别的内存对象),以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为key来访问。
2 struts2中如何与spring协作:
struts2加载配置文件的顺序为 struts-default.xml/struts-plugin.xml/struts.xml,因此插件中的配置文件优先级比默认配置文件中要高;
struts中的Action、Result、拦截器实例均通过以struts.objectFactory常量指定的实现类来创建,该类需要继承于com.opensymphony.xwork2.ObjectFactory
查看struts-default.xml可以找到:
<bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" />
而struts.objectFactory的默认值便是struts,因此默认情况下struts使用org.apache.struts2.impl.StrutsObjectFactory来创建Action、拦截器等实例。
再看看struts-spring-plugin.jar中的xml配置:
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" /> <!-- Make the Spring object factory the automatic default --> <constant name="struts.objectFactory" value="spring" />
于是,在加载了spring插件之后,struts.objectFactory将采用org.apache.struts2.spring.StrutsSpringObjectFactory
查看StrutsSpringObjectFactory的构造方法:
//获得原先加载的spring的context Object rootWebApplicationContext = servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); ... ApplicationContext appContext = (ApplicationContext) rootWebApplicationContext; this.setApplicationContext(appContext); //注入到当前实例
该类继承了SpringObjectFactory,查看源码发现其创建bean的时候都使用了属于spring的名为autoWiringFactory的对象向bean注入了属性,正如上面看到的
action中的setUserService被用于注入service;
此外还需要考虑一个autowireStrategy参数,其指定了自动装配的策略(属于spring的范畴),默认是name即按名称注入。
可以通过修改struts.objectFactory.spring.autoWire的常量值来改变autowireStrategy,可选的值包括:name/type(按类型)/auto(由spring自动检测)/constructor(构造器注入)
三、 另一种整合方式
此处介绍一下另外一种整合的方式:
1 action类与开头例子一致;
2 struts.xml中配置action的class属性值为spring配置文件中的bean名;
3 spring配置对应名称的bean,class指向真实的action类,此时action更加直观的由spring管理,获得了更高的可配置性。
查看SpringObjectFactory中的buildBean方法:
//当spring的context中存在bean名的定义时,直接使用spring管理的方式来构造对象,否则走混合注入的方式(开头例子所采用,也是推荐使用的) if (appContext.containsBeanDefinition(beanName)) { o = appContext.getBean(beanName); } else { Class beanClazz = getClassInstance(beanName); o = buildBean(beanClazz, extraContext); }
怎么样,一目了然了吧
然而这样的方式在大多数情况下显得很臃肿:action需要配置多处;另外spring部分的配置稍显繁杂。因此一般不推荐该整合方式,这里仅仅是介绍罢了。
作者: 美码师(zale)
出处: http://www.cnblogs.com/littleatp/, 如果喜欢我的文章,请关注我的公众号
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接 如有问题, 可留言咨询.