struts制作步骤如下(其实看个人喜欢了)
首先定义自己想要用户写的配置文件是怎么样子的,在这里仿照了struts的配置文件来写的代码,需要用户自己写的配置文件代码如下:
smart-struts.xml
<smart-struts>
<global-forwards>
<foward name="error" path="/jsp/error.jsp" redirect="true" >
</global-forwards>
<form-beans>
<form-bean name="loginForm" type="arg.smartstruts.form.LoginForm"/>
</form-beans>
<action-mappings>
<action path="/loginform" type="org.smartstruts.action.LoginAction">
<forward name="success" path="/go/loginform" redirenct="true"/>
</action>
</action-mappings>
</smart-struts>
配置文件需要如下相关类来封装
1,ForwardConfig.java类,封装了配置文件action元素下面的forward元素的属性,代码如下:
public class ForwardConfig{
private String name;
private String path;
private boolean redirect = false;//定义转发还是重定向,默认是转发
...get..() set..()....
}
2 ,ActionConfig.java //用来封装配置文件中action元素的属性,其中包含了forward子元素,代码如下:
import java.util.HashMap;
import java.util.Map;
public class ActioncConfig{
private String path;
private String type;
private String name;
private String scope = "session"; //设置scope属性,默认值是session范围(原来这样设置默认值OH)
private String attribute; //这个定义了放在session或request范围内的key值
private Map<String , ForwardConfig> forwards =
new HashMap<String , ForwardConfig> //定义了forward子元素
public void addForwardConfig(ForwardConfig forwaedCfg){ //表示在action元素中添加forward元素
forwards.put(forwardCfg.getName() , fowardCfg);
}
public ForwardConfig findForward(String name){
if(fowards.containsKey(name){
return forwards.get(name);
}
return null;
}
public void setName(String name){ //稍微修改一下setName方法
this.name = name;
if(attribute = null){
this.attrbute = name ; // 设置attibute的默认属性,不写的话就是这个
}
}
省略get set....
}
3,FormBeanConfig.java用来存放formbean元素的属性
public class FormBeanConfig{
private String name ;
private String type ;
省略get set...
}
4,ModuleConfig.java 这个类主要定义根元素smart-struts里面包含的元素,其主要包含global-forward、form-bean和action这三个直接的子元素,
public class ModuleConfig{
private Map<String , FormBeanConfig> formBeans = new HashMap<String,FormBeanConfig>();
private Map<String , ActionConfig> actions = new HashMap<String ,ActionConfig>();
private Map<String , ForwardConfig> globalForwards = new HashMap<String , ForwardConfig>();
public void addForwardConfig(ForwardConfig forwardCfg){
globalForwards.put(forwardCfg.getName() , forwardCfg) ; //根据name找到处理类
}
public void addFormBeanConfig(FormBeanConfig formBeanCfg){
formBeans.put(formBeanCfg.getName , formBeanCfg); //根据name找到formbean类
}
public void addActionConfig(ActionConfig actionCfg){
actions.put(actionCfg.getPath, actionCfg) ;//根据path找到对应的Action处理类
}
public ForwardConfig findForward(String name){
if(globalForwards.containsKey(name);
return globalForwards.get(name);
}
return null;
}
public ActionConfig findActionConfig(String path){
if(actions.containsKey(path){
return actions.get(path);
}
return null;
}
public FormBeanConfig findFormBeanConfig(String name){
if(formBeans.containsKey(name){
return formBeans.get(name);
}
return null
}
}
写完了配置文件相关类之后写它的解析文件,需要编写rule.xml文件还需要commons-degister.jar 、commons-beanutils.jar、commons-collections.jar、commons-lang.jar、commons-logging.jar、jsp-api.jar、servlet-api.jar 、log4j.jar包。由于这里建的是普通的java project工程,所以需要jsp-api.jar、servlet-api.jar 两个包。commons-degister.jar是用来解析smart-struts.xml文件的,这个包功能很强大,许多配置文件都是由它解析的,用这个架包需要写rule.xml文件,这个文件只需要知道大概意思就可以了,不需要去背它。
rule.xml文件:
<?xml version='1.0' encoding='UTF-8' ?>
<digester-rules>
<pattern value="smart-struts"> //smart-struts配置文件的根元素
<pattern value="form-beans/form-bean"/> form-bean元素用到的类是FormBeanConfig
<object-create-rule
classname="org.smartstruts.config.FormBeanConfig" />
<set-next-rule methodname="addFormBeanConfig"/> <!--想父元素添加子元素信息-->
<set-properties-rule/>
</pattern>
<pattern value="global-forwards/forward">
<object-create-rule
classname="org.smartstruts.config.ForwardConfig"/>
<set-next-rule method="addForwardConfig"/>
<set-properties-rule/>
</pattern>
<pattern value="action-mappings/action">
<object-create-rule
classname="org.smartstruts.config.ActionConfig"/>
<set-next-rule methodname="addActionConfig"/>
<set-properties-rules/>
<pattern value="forward"> <!-- 其下又包含了forward子元素-->
<object-create-rule
classname="org.smartstruts.config.ForwardConfig"/>
<set-next-rule methodname="addForwardConfig"/>
<set-properties-rules/>
</pattern>
</pattern>
</pattern>
</digester-rules>
写完了rule.xml,下面写两个Action类,一个叫Action,一个叫ActionForm,这就是为什么action类和formbean类继承这两个类的原因
1,Action.java,只有一个抽象方法,这里写的跟struts有点不同,返回的是string类型,定义成抽象方法会让每个继承了它的类都必须重写这个方法。
public abstract class Action{
public abstract String execute(ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception;
}
2,ActionForm.java类,里面没有属性和方法,是空的
public class ActionForm{
}
下面实现国际化,需要自己定义标签,自定义标签的过程是:
勾画出希望在页面展现的标签格式,如:<t:message key="" />,然后写一个类来封装标签属性,然后写配置文件,下面是定义类:
1,Message.java文件
import java.io.IOException;
import java.util.ResourceBundle; //用来读取资源文件
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class MessageTag extends SimpleTagSupport{
private String key; //定义标签属性
public void doTag()throws JspException,IOException{ //重写方法
PageContext pageCtx = (PageContext)this.getJspContext();//这样获得页面所有内置对象
ResourceBundle rb = (ResourceBundle)pageCtx.getRequest().getAttribute(
"org.smartstruts.message.resources");
String msg = rb.getString(key); //获得资源文件的key
pageCtx.getOut().write(msg); //相当于在jsp页面通过out.write(msg)输出信息一样
}
省略get set
}
2,smart-1.1.tld文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.2</tlibversion>
<jspversion>1.1</jspversion>
<shortname>smart</shortname>
<uri>http://smartstruts.org/taglib</uri> <!--随便取一个-->
<tag>
<name>message</name> <!--标签名称-->
<tagclass>org.smartstruts.taglib.MessageTag</tagclass>
<bodycontent>empty</bodycontent>
<attribute>
<name>key</name><!-- 属性名称-->
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
最后写ActionServlet类,这个类可以叫做前端控制器类,处理了请求响应和国际化等操作
public class ActionServlet extends HttpServlet{
private Logger logger = Logger.getLogger(ActionServlet.Class);//想要为这个类做日志,得通过.class获取这个类的信息
private static final long serialVersionUID=1L;
private static final String prefix = "/go"; // “/go”就相当于struts中的.action后缀一样,这里go是前缀了。
//在web.xml文件中配置成<url-pattern>/go/*</...>
private Map<String , Action> actions = Collections
.synchronizedMap<new HashMap<String , Action>()); //通过Map来缓存action,通过Collections
//将HashMap设置成线程安全的
private Map<Locale , ResourceBundle> resourceBundles = Collections //根据语言种类找到resource文件
.synchronizedMap(new HashMap<Locale , ResourceBundle> ());
private ModuleConfig moduleCfg;
public void init() throws ServletException{
try{
Digester digester = DigesterLoader
.createDigester(ActionServlet.class.getClassLoader()
.getResource("org/smartstruts/config/rule.xml"));
moduleCfg = new ModuelConfig();
digester.push(moduleCfg); //关联ModuleConfig类
digester.parse(ActionServlet.class.getClassLoader().getResource("smart-struts.xml"));//加载配置文件
logger.debug("初始化配置对象:/n"+moduleCfg);
}catch(Exception e){
throw new ServletException(e);
}
}
public void service(HttpServletRequest request, HttpServletResponse response){
try{
processResource(request , response) ; //读取资源文件
String path = processPath(request, response);
ActionConfig actionCfg = moduleCfg.findActionConfig(path);//首先根据path找到action的信息
logger.debug("获取Action配置信息:"+actionCfg);
ActionForm form = processForm(actionCfg, request, respnse); //根据action的name属性找form
String forwardName = processExecute(actionCfg , form, request,response);
processForward(forwardName, actionCfg , request , response); //响应
}catch(Exception e){
throws new ServletException(e);
}
}
private void processResource(HttpServletRequest request , HttpServletResponse response){
Locale locale = request.getLocale(); //获取浏览器请求协议里面的信息,如语言类型
ResourceBundle rb = resourceBundles.get(locale); //先看看缓存里locale语言种类是否有对应的值
if(rb == null){
rb = ResourceBundle.getBundle("resource" , locale); //如果没有,就读取名为resource的资源文件,
//从这可以看出用缓存的好处,如缓存里已经存在了就不需
// 要再读取资源文件,否则每次都读取会降低性能
resourceBundles.put(locale,rb);
}
request.setAttribute("org.smartstruts.message.resources", rb);//这个key对应了Message类的
logger.debug("获取资源文件:"+rb);
}
private String processPath(HttpServletRequest request , HttpServletResponse response){
String uri = request.getRequestURI(); //获取url路径web.xml中的"/go/ "如: /go/login,想办法得到/login
String path = uri.substring(uri.indexOf(prefix) + prefix.length()); //prefix="/go"
logger.debug("处理请求:"+path);
return path;
}
private ActionForm processForm(ActionConfig actionCfg,
HttpServletRequest request , HttpServletResponse response)
throws Exception{
String formName = actionCfg.getName();
ActionForm form = null;
if(formName != null){ //判断action中是否存在name属性
String scope = actionCfg.getScope(); //获取scope属性
if("request".equals(scope)){ //如果是request就放到request范围中
form = (ActionForm)request.getAttribute(actionCfg.getAttribute());//获取名为action元素
//的attribute属性的值所对应的值
} else{
form = (ActionForm)request.getSession().getAttribute(
actionCfg.getAttribute());
}
if(form == null){ //如果为空,则表示第一次用这个formbean,就创建新的form对象
FormBeanConfig formBeanCfg = moduleCfg.findFormBeanConfig(formName);//根据action元素
//的name属性找到对应的form
if(formBeanCfg != null){ //如果配置了<form-bean>元素则操作如下
String formBeanType = formBeanCfg.getType() ;
Class formBeanClass = Class.forName(formBeanType); //通过反射建立formBeanType对应的类
form = (ActionForm) formBeanClass.newInstance() ; //所以每个formbean类都继承了ActionForm
if("request".equals(scope)){ //重新判断要存的范围
request.setAttribute(actionCfg.getAttribute() , form);
}else{
request.getSession().setAttribute(actionCfg.getAttribute() , form);
}
}
}
BeanUtils.populate(form,request.getParameterMap());//将页面表单值赋值给formbean中对应的属性
logger.debug("构建ActionForm:" + form);
}
return form;
}
private String processExecute(ActionConfig actionCfg ,ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
Action action = actions.get(actionCfg.getPath()); //看看缓存里是否已经有action了
if(action == null){
String actionType = actionCfg.getType();
Class actionClass = Class.forName(actionType);//根据type值建类
action = (Action) actionClass.newInstance();
actions.put(actionCfg.getPath,action);
}
String forwardName = action.execute(form, request,response);//获得Action类中execute方法的返回值
logger.debug("调用Action:" +action);
return forwardName;
}
private void processForward(String forwardName, ActionConfig actionCfg ,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
ForwardConfig forwardCfg = actionCfg.findForward(forwardName);//找到配置文件中forward属性
if (forwardCfg == null){ //找不到就到全局中找
forwardCfg = moduleCfg.findForward(forwardName);
}
if(forwardCfg != null){
String fowardPath = forwardCfg.getPath();
if(forwardPath != null){
if(!forwardCfg.isRedirect())
logger.debug("转发操作:"+ forwardPath);
request.getRequestDispatcher(forwardPath).forward(request,response);
}else{
logger.debug("重定向操作:" + forwardPath);
String ctxPath = request.getContextPath();
response.sendRedirect(ctxPath + forwardPath);
}
}
}
}
}//终于写完这个类
关于反射、绝对路径和相对路径问题在下一篇讲解。。
最后总结:
第一步读取资源文件实现国际化
Locale locale = request.getLocale(); //获取浏览器请求协议里面的信息,如语言类型
ResourceBundle rb = resourceBundles.get(locale); //先看看缓存里locale语言种类是否有对应的值
if(rb == null){
rb = ResourceBundle.getBundle("resource" , locale); //如果没有,就读取名为resource的资源文件,
//用缓存的好处,如缓存里已经存在了就不需要再读取资源文件,否则每次都读取会降低性能
resourceBundles.put(locale,rb);
}
request.setAttribute("org.smartstruts.message.resources", rb);//这个key对应了Message类的
logger.debug("获取资源文件:"+rb);
第二:获取请求的绝对路径
String uri = request.getRequestURI(); //获取url路径web.xml中的"/go/ "如: /go/login,想办法得到/login
String path = uri.substring(uri.indexOf(prefix) + prefix.length()); //prefix="/go"
第三:根据path找到对应的action元素
第四:先判断action元素中是否有name属性,如果有,则判断scope属性是request还是session值,是request,则通过form = (ActionForm)request.getAttribute(actionCfg.getAttribute());获取曾经存到form中的值,否则在session中获取,如果在request和session中的到的form值为null,则说明是第一次使用该form,这时候根据action的name属性值去找对应的form-bean元素,如果存在则根据type属性的值通过反射创建form实例,再来判断scope值是request还是session,然后决定将form值放到request还是session中,并以action的 attribute属性值作为key,然后将页面表单值赋给form,
这样完后,就开始做业务逻辑了
第五:其实action相关信息是存在缓存中的,所以到这一步的时候先去缓存找key为action元素的path的值所对应的action, 如果不存在则根据action元素type属性值通过反射创建Action类,然后将心创建的action放入缓存,根据新创建Action类中execute方法的返回值找到forward元素,如果forward元素不存在则在全局中找,如果得到的forward元素存在,则找到path属性,如果不为空且redirect属性值为fale则转发到path所指定的路径,如果redirect为true则重定向到path所指定的路径。
总结得不好请大家指点!