根据王勇老师的Struts视频教程整理而成
概述:
DDD领域对象模型
DDD领域驱动设计(*)
Pattern设计模式 (Gof 23种设计模式)(*)
分析模式RBAC,基于角色的访问控制
角色与资源访问绑定,直接为用户赋于角色来控制资源访问
FrameWork(框架)主要面向开发人员,它是一个半成品
目标:运用当前流行的JAVA开源框架:Struts、Hibernate、Spring来构建灵活的、易于扩展的WEB应用程序。
DRP用于两种开发模型:MODEL 1、MODEL2
WML无线标记语言,显示于手机上
分层会给我带来扩展性等
MVC分层
表示层 业务逻辑层 数据库层
业务逻辑层 可以单独抽出 持久层
单向依赖-》表示层 <- 业务逻辑层 <- 数据库层 自上而下的依赖
UI MVC –> struts1.x 、struts2、 webwork基于请求驱动MVC
AWT SWING JSF(SUN的一个标准、商业产品如:金蝶) -> 基于事件驱动的MVC
C/S以数据库为中心,B/S多层架构才是真正的目的,C/S分发不是很方便
C/S成熟的分发机制:webstart
对于Servlet的url-pattern 的 / 对应于WebRoot目录下,即ContextPath
href=”本身就位于根上” href=”/xxx”定于到端口上了
..返回上级口录
通过一个Servlet完成所有信息的分发:
1、servlet的路径匹配,url-pattern ------ *.action
<url-pattern>*.action</url-pattern> //struts的标准url-pattern *.do
String uri = req.getRequestURI();
req.getRequestURI()会打印出项目路径截出后面的路径:
String uri = req.getRequestURI();
String oldprefix = uri.substring(uri.indexOf("/",1),uri.length());
//截取目录是为了保证其唯一性。
System.out.println(uri);
System.out.println(oldprefix);
同时可以判断
if("/sysmgr/deluser ".equals(oldprefix)){
//调用户用del的代码。。。
}
改进:
策略模式:
public interface Action{ //代表一个策略
public void execute(HttpServletRequest req,HttpServletResponse resp);
}
public class DelUserAction implements Action{
public void execute(HttpServletRequest req,HttpServletResponse resp){
//删除用户方法
}
}
要灵活运用它们,最好我们通过XML来对它进行配置
<classes-config>
<action path=”/sysmgr/adduser” class=”com.bjsxt.servlet.AddUserAction”/>
<action path=”/sysmgr/deluser” class=”com.bjsxt.servlet.DelUserAction”/>
</classes-config>
这样一个servlet是*控制器。
处理成功之后的转面的页面:
<classes-config>
<action path=”/sysmgr/adduser” class=”com.bjsxt.servlet.AddUserAction” success=”/add_success.jsp” error=”add_error.jsp”/>
<action path=”/sysmgr/deluser” class=”com.bjsxt.servlet.DelUserAction”
success=”/del_success.jsp” error=”del_error.jsp”/>
/>
</classes-config>
//MVC框架实现原理:根据URI来处理分发,来转发处理结果
面向请求驱动的MVC框架,struts
MVC是什么,M模型,V视图,C控制。
JSP只做显示,它的职责只是提供显示。
C控制器,业务逻辑的职责就只能是C来负责,调用Model,转向。
M是所有的业务逻辑是一个模型层。
MVC也就是MODEL2
struts1.x简易流程:
struts有一个ActionServlet,处理类是一个Action,它的缺省的配置文件名称:struts-config.xml。struts主要实现了C,流程:
war 、 jar都是ZIP格式
配置STRUTS
1、拷贝struts lib下所有JAR包到WEB-INF/lib下
2、修改Web.xml文件,配置ActionServlet
3、创建struts-config.xml文件,放到WEB-INF文件夹下
JAVA BEAN的规范取决于get/set方法而不是取决于属性。
配置<form-beans>及<form-bean>用于配置FormAction.
配置<action-mapping>及<action>用于配置Action
配置属性的name主要用于关联ActionForm
ActionForm也会存在一个范围内,一般在<action>的scope属性设置
ActionServlet初始化时会初始Map<KEY = path,ActionMapping> //ActionMapping(action配置)是放到了一个MAP里面
ActionMapping{
mapForward.put(“success”,ActionForward);
mapForward.put(“error”,ActionForward);
}
ActionForm的validate是表单级验证,不要访问数据库!
<action>的validate属性决定是否执行ActionForm的validate方法,默认为true
ActionForm的另一个方法reset方法同样会执行(重置的意思)
struts源代码中的ActionServlet的process方法是Servlet主要的处理流程
action也是单实例多线程的,其中的单实例主要是通过synchronized及Map来保证
struts支持<plug-in>
Struts与MVC
大项目Action一般不要放访问数据库的代码,小项目不存在
业务层,DAO层产生的异常不做处理,往上抛一直到Action中再处理
不要在Action中进行业务逻辑的处理,业务逻辑应交给专门的Model层去做
在业务逻辑层抛出异常,并在Action中捕捉和处理
<action>的scope默认是session,一般把它写成request
<action>的name与attribute是关于ActionForm的保存属性的字符串,一般如果设置了attribute那么就用attribute,否则默认attribute与name是一致的
关于Struts的标签库的用法:
1、配置国际化(一般都要引入国际化资源文件)
a) <![endif]>在struts-config.xml文件中,加入国际化的支持,可以参考其空项目,加入
<message-resource parameter=”MessageResource”/>,并将其放到XML文件的后面
拷贝MessageResource.properties到src下
对于标签而言,property主要对JavaBean的属性,而param注要是针对传过来的参数
<bean:write>标签必须属性name
filter\scope\property\format
这里的filter与JSTL下的c:out中的escapeXML的作用一样
代表是否转换字符串如将<转换为>
logic标签:对empty notEmpty present notPresent
“” 为空存在
null 为空不存在
完全不设置属性为空不存在
JSTL与struts的所有标签有个共同点就是它们-般都需要将被处理的数据放入范围中,除value属性以外
标签里的name属性一般指代,JSP属性范围内的数据,而id则是存放当前标签一次运行后的结果。
迭代标签的使用:
<logic:notEmpty name="userlist">
<logic:iterate id="user1" name="userlist">
<tr>
<td>
<bean:writename="user1"property="username"/>
</td>
<td>
<bean:writename="user1"property="age"/>
</td>
<td>
<bean:writename="user1"property="group.name"/>
</td>
</tr>
</logic:iterate>
</logic:notEmpty>
这里要特别注意<bean:write>的使用!!
JSTL不依赖于框架,是SUN的一种标准
Action在整个容器范围内是单实例,单线程!!
JSTL JAVA标准标签库
JSP 2.0主要是嵌入了对于JSTL表达式的支持
java数组的声明:
String xx[] = new String[]{。。。}
<c:forEach>标签中,varState主要表示的是当前迭代元素的一些信息,其方法都有
JSTL之格式化标签
<fmt:formatDate value=”” type=””/>
<fmt:formatNumber value=”” type=””/>
jstl的使用,采用taglib引入就可以了!!
注意:JSTL必须要能够支持J2EE1.4/servlet2.4/jsp2.0版本以上的容器中才能运行,这个环境是目前较为常用的开发环境。
${fn:method(可以直接传范围属性,也可以传”需要的对象”)}
HOW TO WRITE A NEW FUNCTION LIB
<![if !supportLists]>1、<![endif]>首先定义一个类,这个类方法必须是public static
<![if !supportLists]>2、<![endif]>编写自定义tld文件,并且将tld拷贝到WEB-INF/下
<![if !supportLists]>3、<![endif]>引入并使用,这些都可以寻找泛例完成
路径问题:
对于JSP而言:如果用高级模版,它会生成
<%String path = request.getContextPath();%>
<%String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+”/”;%>
然后又有<base href="<%=basePath%>
这意味着其HTML页面下所有路径都从WebRoot下找。即是根下找。
(注意这里不包函JAVASCRIPT下的路径,js下的路径还是只针对于相于路径)
另一种方式就是约定Action的path与真实路径或路径深度一致。
BeanUtils包,一般Struts本身就用了它,我们主要用它VO/Medel与ActionForm之间拷贝属性的方法
主要方法BeanUtils.copyProperties(dest,org)
DynaActionForm:
动态ActionForm,为了避免ActionForm膨胀
FormActon在一个SCOPE中都是存在的,其名字就是为其配置的name属性
法一:在Action中使用,强转(DynaActionForm)类型get方法或getString()等方法,因为它是一个map
法一:${DynaActionForm_name.map.property}
法三:PropertyUtils.getSimpleProperty(form,property);
改变其值:
法一:DynaActionForm_name.set(“name”,vaule)
法二:PropertyUtils.setSimpleProperty(form,property,value)
JAVA中输入流与输出流之前的关系:
数据从外部读入到程序,再由程序输出到外部数据
Struts用作上传:
上传文件ActionForm中最重要的属性FormFile
对文件大小的限制可以在struts-config.xml的最后直接加入<controllermaxFileSize = “10M”/>详细参见DTD
Struts的空字段问题:
如果HTML中没有写input输入域,那么用JSP脚本显示出的值为null,用el表达式是空串,如果HTML中输入域存在但没有输入值,用jsp脚本和EL表达式它接收到的是空串
Struts的类型转换器:(java.util.date等其它类型转换不了)
boolean:yes,1,on,true都会转换成True类型,而且忽略大小写,其它情况转换成false
Date类型:如果是java.sql.Date,页面的格式必须为yyyy-MM-dd,才可以转换
如果是java.util.Date它是无法自动转换的
自定义转换器的实现步骤:(内置转换器在BeanUtils包中)
1、 实现converter接口,实现convert方法:public Object convert(Class type,Object value)
2、 注册转换器ConvertUtils.register(Convert,Clss);实际上其方法把所有的转换器都放入一个Map其Key值是一个Class;而Value就是Convert
一般是创建一个Servlet在其init()方法中调用注册,之后再在WEB-INF中进行配置,其<load-on-startup>10</load-on-startup>中的值越大表示在启动执行时越靠后面执行。也不需要配置<servlet-mapping>
也可以采用Struts的<plug-in>方式的注册方式,用<plug-in>初始会让其与PlugIn绑得比Servlet更紧一些。其实现:
3、<plug-in>,其一般先建立一个类,并实现PlugIn接口,并在init方法写上注册方法,最后在struts-config.xml中进行<plug-in>配置,详情可以参见空项目的struts-config.xml的写法
注意Struts中所有包都是以org打头,并非com
完成相同功能最好用String收集并手工来转换
ActionForm作为传输对象
AcitonForm=DTO,VO
ActionForm使用粗粒度规整减小类维护
应用中的表单一般共享属性
创建一个基本的ActionForm,具有表单的所有属性,ActionForm的设计原则,我们一般建议一个模块建立一个ActionForm就可以了。不用增删改都弄一个,就像我们用户管理用一个就可以了。
ActionForward
它包含一些转向信息
ActionForward的几个属性<forward>
name属性 标识名字
path属性 转向的资源
redirect 重定向
className 可以自己实现一个ActionForward(如action也可以自己写一个,都有className)
关于登录的转向操作:
一可以通过JSP脚 本,直接在JSP页面实现
二可以通过Action或Servlet处理类,这时的转向就必须通过JSP或Action及Jsp或Servlet提交到Action或Servlet路径上(可以重定向也可以转向)
当然也可以通过其它操作如Filter处理类等。
局部forward
<aciton><forward/></action>
全局forward
<global-forward>
<forward name=”” path=”” redirect=””/>
</global-forward>
<form-bean>
<action-mappings>
其先查局部,再查全局,谁有谁先转。
所以对以公共的内容,就可以定义到全局里,例如错误和登录处理等。
struts-config.xml不能够动态运行时改。
对于<forward>当然也是不能动态改.
动态ActionForward:
静态ActionForward在运行期改不了,采用动态ActionForward,在运行期是可以改动的。
String page = request.getParameter("page");
ActionForward af = new ActionForward("/page" + page + ".jsp?name=wenhq",true);
// true不使用转向,默认是false代表转向
return af;
静态的 ActionForward的url是不能加参数的,而使用动态的ActionForward,则可以加参数。
1、正确使用转发和重定向对于网站的url显示会显得更加友好.
2、适当的全局ActionForward 会配置方便很多。
3、善于使用动态ActionForward,也会给程序带来便利。
//可以运用进一些分页模块
ActionMapping
一个ActionMapping实例,对应struts-config文件中<action-mappings>中一个标签
ActionMapping中的属性:
path
forward 属性(标签,直接转向)
type 类
name (actionform)
scope
validate(设置actionform中validate方法是否执行,如果是true就是执行,默认就是true)
input(与异常的相关)
parameter(DispathAction相关)
*unknow ActionMapping
Struts的建议,所有转向信息最好都配到struts-config.xml中,所有的JSP页面都由Action来转,这样我们通过查看XML文件,就可以知道系统的所有的资源的情况。
forword属性,可以解决浪费一个Action类的情况。
如<action path=”/login1” forward=”success”/>,它就不用action类了。
其它属性:
unknow属性,它是一个容错处理。
<action path=”随意写” forward=”unknown.jsp” unknow=”true”/>
它的工作方式,当我们请求的.do路径找不到时,那么系统会自动的转到一个unknow属性为”true”的页面,如我们可以配一个404错误unknow页面。
unknow属性实际上用得很少
input属性,也可以用mapping.getInputForward(),来取得Input属性的ActionForward.另外如果页面发生异常也会默认转到input指向的页面。
EL表达与AtionForm的name结合可用于读取或保存表单数据
采用Struts_tag来保持数据
Struts又封装了一套HTML的标签库,它的这套标签与ActionForm紧密的绑定在一起,它可以把ActionForm属性都读出来
<html:form action="loginin.do" method="post">
用户:<html:text property="username"></html:text>
密码:<html:password property="password"></html:password>
<html:submit value="登录"></html:submit>
</html:form>
由此property与Struts中的属性绑定住了!!
scope属性主要是确保其ActionForm保存的范围
约定优于配置!!
大型的购物网站,对于购物车它可能不会用Session,而用Cookie,因为Session占用的资源会非常的大。(参考)
ActionForm的分步收集
action的scope范围,request的范围仅限于foword的页面,一般改为session,默认也是session
<form action="finish.do">
姓名:${stepform.name} <br/>
产品:<c:forEach items="${stepform.productId}" var="p" varStatus="v">
product${p}
<c:if test="${v.count!= fn:length(stepform.productId)}">
,
</c:if>
</c:forEach>
地址:${stepform.address }<br />
<input type="submit" vlaue="下一步"/>
</form>
ActionForm的reset(),就是每次向ActionForm做收集时都执行reset()方法,默认每次收集都执行reset()方法。
解决ActionForm,session分步收集与单次清空问题,每次进入时都手动执行一个自己在ActionForm里的清空函数。
Action将输入送到数据库中(通常会将这一步操作委托给业务逻辑类去实现)
J2EE是一个多线程的环境,服务器对每一个请求启动一个线程来处理所以有可能会有多线程访问一个Servlet实例的情况,在Struts里面也是一样的,有可能会有多个线程同时访问一个Action的情况,所以必须保证Action类的方法具有”可重入性”,即不能在Action的方法里改变实例变量的值。(单实例多线程)
Action的主要职责:
校验输入数据
调用业务逻辑方法
检测处理异常
根据逻辑进行转向
国际化问题:
国际化提供可以支持多元版本的软件:国际化(Internationalization)I18N,只取首字母和尾字母,中间18个字母
Locale对象存放区域和语言信息,其有方法(java.util.Locale)
Locale defaultLocale = Locale.getDefault(); //操作系统决定的缺省Locale
System.out.println(“default country=” + defaultLocale.getCountry());
System.out.println(“default language=” + defaultLocale.getLanguage());
BaseName_语言_国家.properties //属性文件,格式为key=value
如:
MessageBundle.properties(主要用作基本属性文件及缺省属性文件,任何一个国家都找不到了,它就会找到并用它)
MessageBundle_zh_CN.properties
MessageBundle_en_US.properties
BaseName可以是任意合法的文件名
JAVA的国际化
加载资源文件需要一个类
ResourceBundle
Locale defaultLocale = Locale.getDefault();
ResourceBundle rb = ResourceBundle.getBundle("MessageBundle", defaultLocale);
读取资源文件KEY对应的字符串内容
rb.getString(“key”);
ResourceBundle rb = ResourceBundle.getBundle(资源文件basename,Locale);
属性文件一般位于src目录下 如果在其它目录下,资源文件用.间隔
资源文件中文乱码问题:
JDK提供一个命令
native2ascii
然后接着输入要编码的中文
但这样一个一个写比较麻烦,但是可以通过native2ascii命令行来转整个properties文件
cmd>native2ascii org.properties【GBK(中文)文件格式的中文属性文件】 dest.properties
进行整个文件的编码转换
灵活构建Locale
Locale currentLocale =new Locale("en","US");
ResourceBundle rb = ResourceBundle.getBundle("MessageBundle", currentLocale);
System.out.println(rb.getString("k1"));
System.out.println(rb.getString("k2"));
动态文本的国际化:
如使用占位符
k1=hello,{0},{1}//占位符从0开始
k2=Good Bye,{0}
java.text.MessageFormat填充占位符
MessageFormat mf = new MessageFormat(rb.getString(“k1”));
System.out.println(mf.format(new Object[]{“Tom”}));
或传入多个System.out.println(mf.format(new Object[]{“Tom”,”XXXX”,”XXX”}));
原理取出消息后再用MessageFormat来填充
Struts如何实现国际化
Struts国际化:
*配置资源文件,在Struts-config.xml中加入:(一般格式在空项目中会有说明)
<message-resources parameter="MessageResources" /> parameter属性,如果资源在其它上用目录用点间隔
提供国际化资源文件(一般在src目录下),中文要用native2ascii命令编码为Unicode
引入标签bean
<bean:message key=”user.name” />
其使用的资源文件是<message-resources>加载的资源文件
telnet 127.0.0.1:8080看它能否访问
卡巴斯基有可能会引起拦截正常访问
内部实现
request.getLocale()
在Struts中手工切换Locale信息:
session.setAttribute(Globals.Locale_KEY,local);
默认情况下Struts把Locale信息放在Session中,由此它与客户端绑定
资源文件也可以放进目录里,只是在配置< message-resources >时,目录用点间隔。
Struts动态切换国际化重点
<a href=”login.jsp”>登录</a>
<a href=”changelan.do?lan=cn”>中文</a><a href=”changelan.do?lan=zh”>英文</a>
自动手工切换:
提交的Action类中
Locale curlc = new Locale(“zh”,”CN”);
request.getSession().setAttribute(Globals.LOCAL_KEY,locale);
或者直接用Action类中的this.setLocale(request,Locale);
Struts通过程序抛出来的运行期的信息,同样与JAVA一样添加占位符
如:
user.title=abcdefg,{0}
user.name={1},abcdefg
1、 创建国际化消息文本
ActionMessage msg = new ActionMessage(“user.title”);//对应一条国际化消息文本
ActionMessage msg = new ActionMessage(“user.title”,new Object[]{“dd”,”ss”,”dd”});
ActionMessage msg = new ActionMessage(“user.title”,”xxx”)
L return this(xx,new Object[]{“sad”})
国际化消息文本创建完后,我们要把它放入一个消息集合
ActionMessages messages = new ActionMessages();
messages.add(“login success”,msg);
messages.add(“xxxxx”,xxx);
2、 传递消息
使用action父类的
this.saveMessage(request,messages);//将消息集合放入request范围
对于异常或错误信息则使用action父类的
this.actionErrors(request,messages);
3、 显示消息
要使用特殊的标签
<html:messages id=””></html:messages>它主要是针对读取ActionMessages中,ActionMessage信息,它就类似于跌代所有错误消息(ERRORKEY),如果设置属性message=”true”它就会到message正常消息上找
<html:messages id=”msg” message=”true”>//不用指定Message因为它针对所有Message息息迭代
<bean:write name=”msg”/>
</html:messages>
<html:messages/> 又可以读错误信息又可以读正常信息
<html:errors/>只读取错误信息
<html:errors/>//与<html:messages/>类似但是没有id属性
空项目中的MessageResources.properties
有标准错误信息格式
# -- standard errors --
errors.header=<UL>
errors.prefix=<LI>
errors.suffix=</LI>
errors.footer=</UL>
可以将它们放在我们自己的资源文件中,用来修饰我们自己资源文件的错误文件格式
<Action>的scope应尽量使用scope等于request.
在Struts里的Action是单实例多线程的,使用它要注意线程问题。
异常:
编程式异常:
手工处理,没有涉及exception
截获异常
创建相应的异常信息
传递异常信息
转向相应的页面
声明式异常:
它会使我们的代码极具减少
于<action>标签中用<exception>中来配置异常
<exception key=”对应的国际化消息文本” type=”异常类型(异常类)”/>如果发生异常它会默认转向input指向的页面,同时将文本放向ErrorKey中。
所以如果要取得错误文本使用<html:errors/>或<html:messages>都可以拿出来
实际中都有采用声明式异常和编码方式的情况
<exception>它还有一个path属性,向会指向新的错误处理页面,覆盖掉input页。
同时也提供全局异常:
<global-exceptions>
<exception key=”” type=”” path=””/>
<exception key=”” type=”” path=””/>
</global-exceptions>
但局部比全局优先级别高。
exception还有一个handler属性,它用于指定其异常处理类, 默认是一个exception handler类
exception也有scope属性,它的默认值是request,主要指代exception的scope
个性化异常:(重点)
创建一个异常:ErrorCodeException 它继承自 RuntimeException
属性有一个private String errorCode;
还有一个private Object[] args;
还有一个构造方法:
public ErrorCodeException(String errorCode,Object[] args){\
this.errorCode = errorCode;
this.args =args;
} //对应于国际化资源文件的key
为了简代我们可以再写一个构造:
public ErrorCodeException(String errorCode,Object arg0){
this(errorCode,new Object[]{arg0});
}
public ErrorCodeException(String errorCode){
this.errorCode = errorCode;
}
生成errorCode的get方法,和args的Get方法
然后如果抛出异常的话就全部抛出errorCode异常(主要是我们用了所谓的错误码机制)。
throw new ErrorCodeException (“对应的国际化Key”,对应的占位符信息);
然后覆写ExceptionHandler类中的excute方法,判断ex中的内容是否是我们自己的异常类型,如果不是就返回基类的excute执行方法,如果是就改写后面的ActionMessage相关的代码。
最后我们再配置,
<global-exception>
<exption key=”随便写一个KEY” handler=”我们自已定义的ExceptionHandler类” type=”我的ErrorCodeException类型”>
<global-exception>
对于转向信息我们用<action>中的input指定
同时这样的确有一个缺陷就是同一个Action的不同异常也只能转向到同一个页面。
也许可以通过子异常类继承ErrorCodeException来区分不同的异常转向不同的页面,或许可以行得通。
<action>标签中的forward属性,直接转向forward中的页面。
ForwardAction:(了解)
<action path=”/xx” type=”org.apache.struts.actions.ForwardAction”
patameter=”指向转向的页面”></action>
与forward的区别,ForwardAction它是走完了整个Struts的流程
DispatchAction:(重点)
为了避免太多的Action;
如果不用DispatchAction自己分发,我们可以在Action处理类中进行解析URI或自定义命令
(一个Action也可以完成,可以解析传过来的参数然后通过分支语句来完成选择)
与Action一样,使用DispatchAction我们同样需要继承DispatchAction, DispatchAction同样也是一个Action,因此它有自己的execute方法,在我们自己的继承DispatchAction不能使用execute方法,因为我们自己类里的方法要调用完全要依赖基类DispatchAction的execute方法的调度,所以我们不能覆写DispatchAction的execute方法,我们要想分发Action,需要自己定义execute方法,而方法名改为我们的请求参数,同时在<action>中的parameter参数
配置区分不同请求的参数名,当我们以后要请求时,就应用如:
path.do?parameter=methodname为地址来调用
对于DispatchAction中的转向:也应不同的分发地址对应于不同的转向如:
add_success del_success 等。
add_error del_error
同时我们自己的Action可以覆写unspecified方法,来进行容错处理,也就是当我们指定的parameter参数在自己的Action中没有对应方法时,我们该怎么处理。
对于ActionForward af = new ActionForward();
af.setPath(“/xxxx”); //好像它有问题
af.setRedirect();//重定向
return af;
Struts配置中的路径与模式配置
动态验证框架:
采用得比较少,知道有这么个东西就行了。