随笔-34  评论-1965  文章-0  trackbacks-0

IoC(Inversion of Control,以下译为控制反转)随着Java社区中轻量级容器(Lightweight Contianer)的推广而越来越为大家耳熟能详。在此,我不想再多费唇舌来解释“什么是控制反转”和“为什么需要控制反转”。因为互联网上已经有非常多的文章对诸如此类的问题作了精彩而准确的回答。大家可以去读一下Rod Johnson和Juergen Hoeller合著的《Expert one-on-one J2EE Development without EJB》或Martin Fowler所写的《Inversion of Control Containers and the Dependency Injection pattern》。

言归正传,本文的目的主要是介绍在Struts 2中实现控制反转。

历史背景

众所周知,Struts 2是以Webwork 2作为基础发展出来。而在Webwork 2.2之前的Webwork版本,其自身有一套控制反转的实现,Webwork 2.2在Spring 框架的如火如荼发展的背景下,决定放弃控制反转功能的开发,转由Spring实现。值得一提的是,Spring确实是一个值得学习的框架,因为有越来越多的开源组件(如iBATIS等)都放弃与Spring重叠的功能的开发。因此,Struts 2推荐大家通过Spring实现控制反转。

具体实现

首先,在开发环境中配置好Struts 2的工程。对这部分仍然有问题的朋友,请参考我的早前的文章。

然后,将所需的Spring的jar包加入到工程的构建环境(Build Path)中,如下图1所示:

图1 所依赖的Spring的jar包
图1 所依赖的Spring的jar包

本文使用的是Spring 2.0,Spring强烈建议大家在使用其jar包时,只引用需要的包,原因是Spring是一个功能非常强大的框架,其中有些功能是您不需要的;而且Spring提倡的是“按需所取”,而不是EJB的“爱我就要爱我的一切”。当然,如果你怕麻烦或者是不清楚每个包的作用,引用一个Spring的总包也未尝不可。

接下来,就要修改WEB-INF\web.xml文件了,内容为:

<? xml version="1.0" encoding="UTF-8" ?>
< web-app version ="2.4" xmlns ="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation
="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
>

   
< display-name > Struts 2 IoC Demo </ display-name >

   
< filter >
       
< filter-name > struts-cleanup </ filter-name >
       
< filter-class >
            org.apache.struts2.dispatcher.ActionContextCleanUp
       
</ filter-class >
   
</ filter >

   
< filter >
       
< filter-name > struts2 </ filter-name >
       
< filter-class >
            org.apache.struts2.dispatcher.FilterDispatcher
       
</ filter-class >
   
</ filter >

   
< filter-mapping >
       
< filter-name > struts-cleanup </ filter-name >
       
< url-pattern > /* </ url-pattern >
   
</ filter-mapping >

   
< filter-mapping >
       
< filter-name > struts2 </ filter-name >
       
< url-pattern > /* </ url-pattern >
   
</ filter-mapping >

   
< listener >
       
< listener-class >
            org.springframework.web.context.ContextLoaderListener
       
</ listener-class >
   
</ listener >

   
< welcome-file-list >
       
< welcome-file > index.html </ welcome-file >
   
</ welcome-file-list >
</ web-app >
清单1 WEB-INF\web.xml

大家一看便知道,主要是加入Spring的ContextLoaderListener监听器,方便Spring与Web容器交互。

紧接着,修改Struts.properties文件,告知Struts 2运行时使用Spring来创建对象(如Action等),内容如下:

struts.objectFactory = spring
清单2 classes\struts.properties

再下来,遵循Spring的原则——面向接口编程,创建接口ChatService,代码如下:

package tutorial;

import java.util.Set;

public interface ChatService {
   Set
< String > getUserNames();
}
清单3 tutorial.ChatService.java

然后,再创建一个默认实现ChatServiceImpl,代码如下:

package tutorial;

import java.util.HashSet;
import java.util.Set;

public class ChatServiceImpl implements ChatService {

   
public Set < String > getUserNames() {
       Set
< String > users = new HashSet < String > ();
       users.add(
" Max " );
       users.add(
" Scott " );
       users.add(
" Bob " );
       
return users;
   }


}
清单4 tutorial.ChatServiceImpl.java

接下来,就该新建Action了。tutorial.ChatAction.java的代码如下:

package tutorial;

import java.util.Set;

import com.opensymphony.xwork2.ActionSupport;

public class ChatAction extends ActionSupport {
   
private static final long serialVersionUID = 8445871212065L
   
   
private ChatService chatService;
   
private Set < String > userNames;

   
public void setChatService(ChatService chatService) {
       
this .chatService = chatService;
   }

   
   
public Set < String > getUserNames() {
       
return userNames;
   }

   
   @Override
   
public String execute() {
       userNames
= chatService.getUserNames();
       
return SUCCESS;
   }

   
}
清单5 tutorial.ChatAction.java

ChatAction类使用属性(Getter/Setter)注入法取得ChatService对象。

然后,配置Spring的applicationContext.xml(位于WEB-INF下)文件,内容如下:

<? xml version="1.0" encoding="UTF-8" ?>
< beans xmlns ="http://www.springframework.org/schema/beans"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" >
   
< bean id ="chatService" class ="tutorial.ChatServiceImpl" />
   
< bean id ="chatAction" class ="tutorial.ChatAction" scope ="prototype" >
       
< property name ="chatService" >
           
< ref local ="chatService" />
       
</ property >
   
</ bean >
</ beans >
清单6 WEB-INF\applicationContext.xml

上述代码有二点值得大家注意的:

  1. Struts 2会为每一个请求创建一个Action对象,所以在定义chatAction时,使用scope="prototype"。这样Spring就会每次都返回一个新的ChatAction对象了;
  2. 因为ChatServiceImpl被配置为默认的scope(也即是singleton,唯一的),所以在实现时应保证其线程安全(关于编写线程安全的代码的讨论已经超出本文的范围,更超出了本人的能力范围,大家可以参考Addison Wesley Professional出版的《Java Concurrency in Practice》)。

接下来,在classes/struts.xml中配置Action,内容如下:

<! DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd"
>
< struts >
   
< include file ="struts-default.xml" />    
    
   
< package name ="Struts2_IoC" extends ="struts-default" >
       
< action name ="Chat" class ="chatAction" >
           
< result > /UserList.jsp </ result >
       
</ action >
   
</ package >    
</ struts >
清单7 classes\struts.xml

这里的Action和平常不同的就是class属性,它对应于Spring所定义的bean的id,而不是它的类全名。

最后,让我们看看/UserList.jsp,内容如下:

<% @ page contentType = " text/html; charset=UTF-8 " %>
<% @ taglib prefix = " s " uri = " /struts-tags " %>
< html >
< head >
   
< title > User List </ title >
</ head >

< body >
   
< h2 > User List </ h2 >
   
< ol >
   
< s:iterator value ="userNames" >
       
< li >< s:property /></ li >
   
</ s:iterator >
   
</ ol >
</ body >
</ html >
清单8 /UserList.jsp

大功告成,分布运行应用程序,在浏览器中键入http://localhost:8080/Struts2_IoC/Chat.action,出现如图2所示页面:

图2 /ListUser.jsp
图2 /ListUser.jsp

总结

通过Spring在Struts 2上实现控制反转是强烈推荐的做法,当然您也可以组合其它的实现(如Pico等)。

posted on 2006-12-28 17:37 Max 阅读(33662) 评论(139)  编辑  收藏 所属分类: Struts 2.0系列
评论共2页: 上一页 1 2 

评论:
# re: 在Struts 2中实现IoC 2006-12-28 17:44 | BeanSoft
呵呵,支持一个... 偶正在初学 spring...  回复  更多评论
  
# re: 在Struts 2中实现IoC 2006-12-29 00:52 | AlleNny
我有个问题:

你在文中提到“告知Struts 2运行时使用Spring来创建对象(如Action等”,但是例子中由spring创建的是业务逻辑的实现:
< bean id ="chatService" class ="tutorial.ChatServiceImpl" />
为何要将业务接口注入Action中,我觉得直接在Action中调用也无妨啊,难道不允许容器“依赖”业务逻辑?

个人愚见,请指教
  回复  更多评论
  
# re: 在Struts 2中实现IoC 2006-12-29 09:27 | Tendy
@AlleNny
---
为何要将业务接口注入Action中,
---
re: 是业务接口的实现(tutorial.ChatServiceImpl) 注入 action

---
我觉得直接在Action中调用也无妨啊,难道不允许容器“依赖”业务逻辑?
---
re: 在 action 中 new tutorial.ChatServiceImpl() 也行
但当你需要使用不同的实现时,要改源代码,还要编译
用注入的方式,修改配置文件即可  回复  更多评论
  
# re: 在Struts 2中实现IoC 2006-12-29 14:16 | Max
@AlleNny
Tendy说得很正确,我来补充几点:
1、松耦合(Loose coupling)可以给程序的测试和维护带来很多好处;
2、通过Spring的IoC方式,可以简单地创建Singleton对象。
建议读一下Martin Fowler所写的《Inversion of Control Containers and the Dependency Injection pattern》,你可以google一下,应该有中文翻译的。  回复  更多评论
  
# re: 在Struts 2中实现IoC 2006-12-29 22:21 | Goingmm[匿名]
@"为何要将业务接口注入Action中"? 其实这也不是唯一的选择!!

这样配置<ref local="chatService" />, 他们之间的关联关系会比较明确(这种关系至少在配置文件的映射上很直观)
如想达到更简洁的配置效果(放弃上面这种直观的关联关系)可以这样配置(其他地方都不需要修改)
<bean id="chatService" class="tutorial.ChatServiceImpl" autowire="byName"/>
<bean id="chatAction" class = "tutorial.ChatAction" scope="prototype" autowire="byName"/>
  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-01-29 15:44 | Sophia
新手上路,照着做了一遍,指出一个小遗漏,tutorial.ChatAction.java文件中缺少setter方法,导致页面上的list显示不出来。  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-01-29 18:28 | Max
@Sophia
可能你漏了什么,显示userNames应该不用setter。  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-11 16:51 | praguesky
不能spring接管action吗...这样 2边都要配置action 麻烦了...  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-20 23:07 | 聂永
谢谢~
正是我所想要的.原来以为只有SPRING MVC才可以实现IOC,现在STRUTS实现了,可以舍弃SPRING MVC了。呵呵~
希望楼主,加油啊,期待老大的好文章的出现o:)))  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-22 20:00 | yangdamao
我照著做,怎麼做不出來啊,我引入的是spring.jar包其他完全一樣啊!!----新手,剛接觸struts  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-22 23:00 | Max
@yangdamao
那要看看你运行的后出什么异常,才知道问题所在。  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-23 17:25 | xiaolan
能不能用STRUTS2.06的做一个结合SPRING2的DEMO?
你上面的例子我运行不了,< package name ="Struts2_IoC" extends ="struts-default" >为什么是package name ="Struts2_IoC"??请指教.
能不能给发个EMAIL,xiaomeng1027@163.com  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-23 18:45 | yangdamao
HTTP Status 404 - /Struts2_IoC2/Chat.action

type Status report

message /Struts2_IoC2/Chat.action

description The requested resource (/Struts2_IoC2/Chat.action) is not available.

總是提示這個錯誤,我快沒轍了!!!  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-24 09:18 | yangdamao
請問一下是不是我哪裡配置錯了?可我是把上面的代碼copy過去的啊!!!  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-24 22:26 | Max
@xiaolan
本文的方法是Struts 2的通用做法,在Struts 2.0.6也是如此。  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-24 22:28 | Max
@yangdamao
这证明您的应用程序没有运行起来,可能有其它异常,请检查一下你的服务器的LOG。  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-26 10:10 | yangdamao
2007/3/26 上午 10:06:10 org.apache.catalina.core.StandardContext listenerStart
嚴重的: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanDefinitionStoreException: Line 4 in XML document from ServletContext resource [/WEB-INF/applicationContext.xml] is invalid; nested exception is org.xml.sax.SAXParseException: Document root element "beans", must match DOCTYPE root "null".
org.xml.sax.SAXParseException: Document root element "beans", must match DOCTYPE root "null".

這是提示的錯誤,請教一下該如何改呢?  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-26 10:13 | yangdamao
問題就出在applicationContext.xml中,麻煩將您的源碼發到我郵箱(anne.yang@stella.com.hk)中一份可以嗎?  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 09:16 | yangdamao
還有哪位高手做出來了,請多多指教!  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 09:42 | xinhang
有關struts2方面的學習資料太少了﹗
誰還有struts2的學習資料﹗請幫個忙
發到0907qw@163.com(謝謝﹗﹗當然要是中文滴﹗偶英文差﹗)
(Max的資料是好﹗但是資料太少了﹗)  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 10:23 | Max
@yangdamao
@xiaolan
源代码已经发送你们的邮箱。:-)  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 11:09 | yangdamao
Max先生,謝謝你,用你的源代碼效果出來了,我再仔細對比一下看是哪裡出了問題!  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 11:28 | javafan
也给我一份源码吧,非常感谢!
2003310cjs@163.com  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 11:41 | javafan
问个问题~
Struts.properties文件怎么配置?是如何读取的(在哪个文件中读取)?
在代码里没发现读取文件的信息.  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 13:05 | yangdamao
@javafan
替Max分擔一部分,源碼已發到你郵箱  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 13:53 | yangdamao
我所發現的唯一區別就是那几個jar文件不一樣,我的是從我同事那里考過來的,沒注意是那個版本,但是您的jar文件要比我原來的那個大十几kB.  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 13:57 | yangdamao
謝謝您提供的系列資料,受益匪淺!!  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 16:19 | jintian
為什么我的頁面上只顯示

User List

沒有其它的 1. Bob
等﹗﹗請問是不是那里沒設置??
怎么會出現這種情況﹗﹗
  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 16:24 | jintian
郁悶﹗﹗原來是Chat.action﹗﹗
哎﹗﹗  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-28 18:33 | javafan
邮件已收到!
非常感谢yangdamao和max二位  回复  更多评论
  
# re: 在Struts 2中实现IoC[未登录] 2007-03-29 18:54 | steven
你上面的例子我出404错误找不到地址,< package name ="Struts2_IoC" extends ="struts-default" >为什么是package name ="Struts2_IoC"??请指教,而且地址是http://localhost:8080/Struts2_IoC/Chat.action。请把源码包括工程文件发到我邮箱:chenyufei_icewolf@hotmail.com。谢谢  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-29 18:58 | yangdamao
package 的name 可以是任意的,不用必須和 Struts2_IoC 一樣  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-29 19:00 | yangdamao
@steven
正好路過,發你郵箱了  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-03-30 16:39 | xiaolan
你好,我已经收的你的代码了.谢谢,项目正要用,真是雪中送炭.感激不尽  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-04-05 16:34 | panka
Action class [chatAction] not found - action - file:/D:/server/apache-tomcat-5.5.23/webapps/tstruts2ioc/WEB-INF/classes/struts.xml:8:42

我照例子调试好久,一直都是500错误,没有改动啊.
麻烦也给我发一份源码吧,我油箱是mrpanqinag@gmail.com  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-04-05 16:42 | panka
@panka
不好意思油箱写错了 ,真确的是 mrpanqiang@gmail.com  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-04-06 18:00 | yangdamao
@panqiang

I have sent to you!!  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-04-07 23:03 | Max
@yangdamao
I appreciate that.  回复  更多评论
  
# re: 在Struts 2中实现IoC 2007-04-11 22:54 | niehanzi
您好,我按照您的步骤做了一下,总是不成功,您能给我发一份源代码吗?
我的邮箱是:kedahanzi@163.com,万分感谢!  回复  更多评论
  
评论共2页: 上一页 1 2 

只有注册用户登录后才能发表评论。


网站导航: