Struts1的终极复习
1、 为什么要用strusts框架、struts.apache.org官方网站
2、 搭建环境
3、 actionForm的建立
4、 action的开发
5、struts首先是在导包,然后在web中配置actionServlet,最后书写struts-config文件
<1>这是开发struts的struts-config配置文件的书写
<struts-config>
<data-sources />
<form-beans>
<form-bean name="loginForm" type="com.forms.PersonForm"></form-bean><!—-name表示formbean取得名字,type表示form的那个类 -->
</form-beans>
<global-exceptions />
<global-forwards />
<action-mappings>
<action path="/test" type="com.action.PersonAction" name="loginForm"><!—path表示来访问的路径,type表示action的那个类,name表示上面那个formbean的名字 -->
<forward name="loginSuccessful" path="/loginSuccessful.jsp"></forward><!—-name表示名字,path表示jsp页面的路径 -->
<forward name="loginFailed" path="/loginFailed.jsp"></forward>
</action>
</action-mappings>
<message-resources parameter="com.yourcompany.struts.ApplicationResources" />
</struts-config>
<2>这是actionServlet的配置(在web.xml里面)
<servlet>
<servlet-name>caohuan</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>//这是读取配置文件的一个参数
<param-value>/WEB-INF/struts-config.xml</param-value>//这是struts配置文件的路径
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>3</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>3</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>caohuan</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Struts开发的一个流程图
关于页面的填充的数据不正确,但是也不会报错,这是因为,actionServlet的beanUtil对数据进行了处理(一般要对数据进行js(前天的验证)的验证,也可以在form中进行验证(后台的验证))不合法:加入写日期:1990-09-321也可以。
6、 struts中的各个组件
<1>actionServlet的讲解
注意:在web.xml中配置这个actionServlet的时候一定得配置
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<2>action的讲解
注意:action必须要有一个无参数的构造函数
Action初始化是第一次发送请求的时候。
每个action只会初始化一次。(相当于每个人去访问,都是同一个action)
优点:资源可以重用。
缺点:可能会存在不安全问题。Action是线程不安全的,因为所有的请求共享同一个实例。
怎样实现安全性编程?
1、 不要用实例变量或者静态变量共享只是针对某一个请求的数据。
2、 注意资源操作的同步性。
解决办法:
Private Integer count = 0;
synchronized (count) {
count ++;
}
<3>actionMapping的讲解
其实actionMapping就相当于是一个action,action有很多属性,在action类中可以设置、修改这些属性
System.out.println(mapping.getName());
System.out.println(mapping.getPath());
System.out.println(mapping.getType());
System.out.println(mapping.getParameter());
String [] forwardNames = mapping.findForwards();
for (String name : forwardNames) {
ActionForward actionForward = mapping.findForward(name);
System.out.println(actionForward.getName());
System.out.println(actionForward.getPath());
}
<4>actionForward的讲解
<forward name="loginSuccessful" path="/loginSuccessful.jsp"></forward>
Path的属相在这种情况下,是相对于相对路径,所以需要有‘/’开头
<forward name="loginSuccessful" redirect="true" path="/loginSuccessful.jsp"></forward>
Redirect:如果为true,那么就为重定向,相当于,respose.sendRedirect();
Path的属相在这种情况下,是相对于绝对路径,所以不要以‘/’开头
Redirect:如果为false,那么就为转发,相当于,request.getRequestDispatcher().forward();
Path的属相在这种情况下,是相对于相对路径,所以需要有‘/’开头
<5>actionForm的讲解
Form的查找过程。
ActionForm的流程图
首先根据Action的name查找form,再根据作用范围查找,如果有就直接使用,如果不存在就创建一个。根据name和scope去查找。如果找不到,就构造一个,首先调用的是form的构造方法,然后在调用复位(reset)方法,然后在取页面的值填充form的属性(即调用setter方法),然后在进行验证(这是在action的属性validate=true的情况下。),然后在进入到action中。千万注意:formBean必须要有一个无参的构造函数。
<action path="/test" type="com.action.PersonAction" name="loginForm" scope="request">//根据作用于查找formBean,缺省值就是session。
要判断一个form是否真正建立,是否保存在request,session中,可以建立一个监听器进行监听。
这是从过程来监听的。
package com.lisenter;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import com.forms.PersonForm;
publicclass PersonLisenter implements HttpSessionAttributeListener, ServletRequestAttributeListener {
publicvoid attributeAdded(HttpSessionBindingEvent event) {
String name = event.getName();
if(event.getValue() instanceof PersonForm)
{
System.out.println("这是session监听");
System.out.println(event.getValue());
}
}
publicvoid attributeRemoved(HttpSessionBindingEvent event) {
// TODO Auto-generated method stub
}
publicvoid attributeReplaced(HttpSessionBindingEvent event) {
System.out.println("这是session监听replace");
}
publicvoid attributeAdded(ServletRequestAttributeEvent event) {
if(event.getValue() instanceof PersonForm)
{
System.out.println("这是request监听");
System.out.println(event.getValue());
}
}
publicvoid attributeRemoved(ServletRequestAttributeEvent event) {
}
publicvoid attributeReplaced(ServletRequestAttributeEvent event) {
System.out.println("这是request监听replace");
}
}
还可以从过程来判断。
PersonForm personForm = null;
if(mapping.getScope().equals("request"))
{
personForm = (PersonForm) request.getAttribute("loginForm");
}
if(mapping.getScope().equals("session"))
{
personForm = (PersonForm) request.getSession().getAttribute("loginForm");
}
if(personForm == form)
{
System.out.println("personForm == form");//说明它们指向的是同一个地址
}
//参数form和控制器放进去的那个form是同一个对象
从页面传过来的值填充form主要看是否有对应的标准set方法,而不是看是否有这个属性名字。
reset()方法(复位)用于将form的各个属性恢复到默认的状态。最常用的是:当页面的有一个复选框的时候,需要初始的时候就有选择的想,就可以用reset()进行设置。
validate的解释
如果设置action中的属性validate = true 而校验方法什么都没干,那么就返回空。
如果设置validate = false那么就不会调用validate这个方法。
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
System.out.println("这是验证方法");
returnnull;
}
Attribute:是form的关键字,如果你设置了它。那么你要得到该form的时候,那么应该使用attribute的属性值,而不是name的值了,默认情况下它的值就是name的值。
<action path="/test" type="com.action.PersonAction" name="loginForm" scope="request" validate="true" attribute="loginFormBean">
input:如果validate设置为真,而且验证的时候出现错误,那么就会转向input指向的那个页面,一般而言,input属相指向的相对路径,一般以‘/’开头。
<action path="/test" type="com.action.PersonAction" name="loginForm" scope="request" validate="true" input="/error.jsp">
全局跳转:表示任何一个action都可以调用
<global-forwards>
<forward name="error" path="/error.jsp"></forward>
</global-forwards>
在action调用:return mapping.findForward("error");
资源文件
7、 标签的讲解
从一个叫person的bean(保存在scope的作用域)中查找一个叫name的属性保存到person1(保存到toScope的作用域中)中。
Name:要查找的bean。
Id:给bean一个唯一的命名。
Property:从bean中查找某一个属性。
Scope:到哪个作用域中去查找。
ToScope:将这个bean保存到哪个作用域去。
<Bean:define>的用法
<bean:define id="person1" name="person" property="name" scope="request" toScope="session"></bean:define>
<Bean:write >的用法
<bean:write name="person1" scope="request"/>
${ sessionScope.person1}
<bean:message>的用法
首先应该src目录下写资源文件,然后在配置文件中注册
注册:<message-resources key="myKey" parameter="com.yourcompany.struts.ApplicationResources"/>
Key是一个关键字,为每一个设置一个唯一值,parameter是资源文件的基文件。然后在资源文件中写一些键值对。
<bean:message bundle="myKey" key="username"/>
Bundle是制定去哪个资源文件(就是资源文件里面的key),key是资源文件里写的键值对的键,输出的是键对应的值。
8、 逻辑标签
<logic:iterate id="p" name="list" scope="request" indexId="i" offset="0" length="2">
${i}<bean:write name="p" property="i"/>用红色的部分不能输出,会报错
<bean:write name="p" property="name"/><br/>
</logic:iterate>
Id:唯一标示符
Name:保存在作用域中的bean property: bean中的某一个属性 scope:从作用域中去取 offset从哪个开始输出 length:输出的长度 indexed:把每一个的下标保存在它中,千万记得用<bean:write name="p" property="i"/>不能输出来,需要用${i}才能输出来。记住:该标签必须要有集合出现,才能用它输出,否则会出现异常。
双重的使用
<logic:iterate id="p" name="list3" scope="request">
<logic:iterate id="go" name="p">
${go.id}<bean:write name="go" property="name"/>
</logic:iterate><br/>
</logic:iterate>
注意:有时候不能用<bean:write name="go" property="name"/>输出,就用${go.id}
具体原因不是很清楚。
<html:checkbox>
篮球:<html:checkbox name="loginFormBean" property="hobby" value="hobby[0]"></html:checkbox>
这是从一个叫loginFormBean的formBean中取它的一个叫hobby的属性付给property,
然后去hobby对应的值给value.
9、 动态form
form-bean:name表示给这个form取一个名字,type表示动态form这个类。
form-property:name表示给这个form添加一个属性名字,type表示该属性的类型,千万记得得写上包名、类名。
<form-bean name="loginForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="userName" type="java.lang.String"></form-property>
<form-property name="password" type="java.lang.String"></form-property>
<form-property name="date" type="java.sql.Date"></form-property>
<form-property name="hobby" type="java.lang.String[]"></form-property>//类型是字符窜数组
</form-bean>
在action的测试:
DynaActionForm dynaActionForm = (DynaActionForm) form;
String userName = (String) dynaActionForm.get("userName");
String [] hobby = dynaActionForm.getStrings("hobby");
//得到数组的第几个元素,有时候可能得不到,是因为要在jsp页面的控件名字上取名字hobby[0]、hobby[1]-->不过我测试的时候没有出现这种情况,我页面用的是struts的标签,不是用的html的表单标签。
String hobby = (String) dynaActionForm.get("hobby", 0);
System.out.println(userName);
for (String string : hobby) {
System.out.println(string);
}
//用EL表达式取值
${loginFormBean.userName}//这是不是动态form时到formBean里取值的方法
${loginFormBean.map.userName}//这是动态form时到formBean里取值的方法
${loginFormBean.map.hobby[0]}//这种是输出数组的第一个值
动态form的map的用法
得到的是form中的(属性 = 值)这样的键值对保存在map中。
Map<Object, Object> map = dynaActionForm.getMap();
Set<Object> sets = map.keySet();
for (Object object : sets) {
System.out.println(map.get(object));
}
10.异常
异常机制
<exception key="error" path="/error.jsp" type="com.exception.PersonException"></exception>
Key是资源文件中的key,根据key可以得到值,path是要转向的错误页面,type是处理异常的类。在错误页面用<html:errors/>打印出错误
插件Plugin
<plug-in className="com.plugin.PersonPlugin">
<set-property value="classpath:hibernate.cfg.xml" property="hibernateConfigFileasd"/>
</plug-in>
className:是插件类 property:是插件类中的一个属性 value:是属性的值。
Struts插件读取hibernate的配置文件
package com.plugin;
import hibernateUtil.HibernateSessionFactory;
import javax.servlet.ServletException;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ModuleConfig;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public class PersonPlugin implements PlugIn {
private String hibernateConfigFileasd;
public void destroy() {
System.out.println("这是关闭session");
HibernateSessionFactory.closeSession();
}
public void init(ActionServlet arg0, ModuleConfig arg1)
throws ServletException {
System.out.println("这是打开session");
Session session = HibernateSessionFactory.getSession();
}
public String getHibernateConfigFileasd() {
return hibernateConfigFileasd;
}
public void setHibernateConfigFileasd(String hibernateConfigFileasd) {
this.hibernateConfigFileasd = hibernateConfigFileasd;
}
}
ForwardAction的讲解
<action path="/go" type="org.apache.struts.actions.ForwardAction" parameter="/regist2.jsp"></action>
<action path="/go" include="/index.jsp"></action>
与上面那写的功能差不多。在使用功能与案例中有讲解。
Formbean和国际化
Session的序列化问题:举一个例子:假如你想在你的tomcat关闭后把你的类的内容写入你的电脑,那么你的这个类必须实现Serializable这个接口。因为java编译器会用序列化流进行一些处理。
假如几个人做项目,有几台服务器(服务器集群),有一个人买了一个水杯(在某一个服务器上),然后买了另外一件东西(在另外一个服务器上),如果你想看到你之前买的那个东西,那么你需要把之前的那件东西写入服务器的session之中,那么这个类对象就必须实现Serializable这个借口。
解释Serializable这个问题
关于form中的validate的一些讲解
FormBean是保存在request或session中,而validate的errors是保存在request中,request.setAttribute(“Globals.ERROR_KEY”, errors);这个保存的过程被struts进行的封装。
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors actionErrors = new ActionErrors();
if(login.getUserName() == null || "".equals(login.getUserName().trim()))
{
actionErrors.add("error", new ActionMessage("用户名为空",false));//new ActionMessage("用户名为空",false)中的false不需要你管
}
return actionErrors;
}
登录的一个例子(包括出错后有提示值,有出错的显示):
以下的红色部分,表示如果出现错误,返回该页面,那么原来填写的值依然存在。
除了页面要做的处理外,还需要在formBean中重写validate方法,还需要在资源文件中写上相应的userNameError、passwordError的键值对。
<form action="test.do?p=login" method="post">
<br>userName: <input type="text" name="login.userName" value="${loginFormBean.login.userName }">//处理出错后回到页面仍然有提示值的问题
<html:errors property="userNameError"/>//处理出错的提示
<!-- 这里的property里的名字不是资源文件里的那个文件,而是对应的给actionError的那个名字 -->
<br><!-- 这是页面与login的userName对应 -->
password: <input type="password" name="login.password" value="${loginFormBean.login.password }">//处理出错后回到页面仍然有提示值的
<html:errors property="passwordError"/>//处理出错的提示
<!-- 这里的property里的名字不是资源文件里的那个文件,而是对应的给actionError的那个名字 -->
<br><!-- 这是页面与login的password对应 -->
password2: <input type="password" name="password2" value="${loginFormBean.password2 }"><br>//处理出错后回到页面仍然有提示值的
<input type="submit" value="提交"><input type="reset" value="重置">
</form> ${requestScope['org.apache.struts.action.ERROR'] }//这句代码能够写出保存的错误信息,不过一般不这样写,而是用struts的标签。
Globals.ERROR_KEY就是org.apache.struts.action.ERROR类的一种常量代表。
FormBean的validate方法
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors actionErrors = new ActionErrors();
if(login.getUserName() == null || "".equals(login.getUserName().trim()))
{
//actionErrors.add("userNameError", new ActionMessage("用户名为空",false));//new ActionMessage("用户名为空",false)中的false不需要你管
//这个false表示在页面读取错误信息的时候,不是从资源问价里面读的,而是从这里直接读取。
//如果想要从资源文件里里面读取,userNameError是资源文件里面的一个键
actionErrors.add("userName", new ActionMessage("userNameError"));//new ActionMessage("用户名为空",false)中的false不需要你管
}
if(login.getPassword() != "caohuan")
{
//actionErrors.add("password", new ActionMessage("密码出错",false));//new ActionMessage("用户名为空",false)中的false不需要你管
actionErrors.add("password", new ActionMessage("passwordError"));//new ActionMessage("用户名为空",false)中的false不需要你管
}
return actionErrors;
}
//如果找不到相应的国际化语言的文件,首先不会默认就去找默认文件,而是找与操作系统语言类似的语言,然后再找默认的国际化文件。
资源文件:关于中国资源文件的一个常错点。
在src下的文件,如果是.Java编译器就会翻译成classes下的.class文件,如果是其他的文件,就会原封不动的直接放到classes下。不会翻译。
<bean:message>标签的国际化
<bean:messagekey='info.submit'/>//这里的可以就是资源文件的键值对的某个键
<html:link action="#" titleKey="info.submit">等等</html:link>
//这里的titleKey就是资源文件里的键值对的键,还有一个title标签,但是那不能实现国际化。
<html:submit value="<bean:messagekey='info.submit'/>"></html:submit>
<!-- 千万记得在struts的标签里不能再嵌套标签,否则达不到你所想的效果 -->
<!-- 解决方法 -->
<html:submit><bean:message key='info.submit'/></html:submit>
<message-resources
Key = “” parameter="com.yourcompany.struts.ApplicationResources" />
Key是资源文件的不同的一个标示符。千万记得写资源文件的时候只写资源文件的基名。
不同的基名,那么在页面用struts标签进行取值时,那么一定得写上bundle来得到这个资源文件。
例如: <html:link action="#" bundle="xxx"titleKey="info.submit">等等</html:link>
//浏览器用哪种语言,首先是从session(第一次访问的时候的那种语言存储在session中)中去查找这种语言来解析浏览器,如果没有就按照浏览器—>工具栏—->中的语言项的那种语言来解析浏览器。
实现国际化的语言转换。
String language = request.getParameter("language");
HttpSession session = request.getSession();
Locale local = getLocale(request);
local = new Locale(language);
session.setAttribute(Globals.LOCALE_KEY, local);
returnnew ActionForward("/login.jsp");
前台的页面:
<a href="test.do?p=login&language=zh_CN">中文</a>
<a href="test.do?p=login&language=en_US">英文</a>
<html:errors>的讲解
ActionMessage messages = new ActionMessage("userNameError");
ActionMessage messages1 = new ActionMessage("userNameError", false);
actionErrors.add("userName",messages);
actionErrors.add("userName",messages1);
errors中的每个key可以对应几个消息。
业务逻辑验证
ActionErrors(已淘汰) ActionMessages
为什么要淘汰,淘汰了,为什么actionForm中还在使用
//业务逻辑的验证
PersonForm personForm = (PersonForm)form;
// ActionMessages是ActionErrors的父类
ActionMessages errors = new ActionMessages();
//ActionErrors errors = new ActionErrors();//这种方法已淘汰
if(personForm.getPerson().getUserName().equals("caohuan"))
{
ActionMessage actionMessage = new ActionMessage("userNameRepeate");
errors.add("userNameCH", actionMessage);
//第一种方法
// request.setAttribute(Globals.ERROR_KEY, errors);
// return mapping.findForward("go");
//第二种方法
this.saveErrors(request, errors);
//return new ActionForward(mapping.getInput());或
return mapping.getInputForward();
}
//实现资源文件中出现参数,并且可以实现国际化的
ActionMessage actionMessage = new ActionMessage("userNameRepeate", this.getResources(request).getMessage(this.getLocale(request),"go"));
第一个参数是资源文件的错误提示的参数, 第二个参数是使用哪种语言的国际化。里面的go参数是用该参数来查找参数值。(视频33)
关于form的两种形式及区别
<form action="${pageContext.request.contextPath}/test.do?p=login"method="post">
用户名:<input type="text" name="person.userName" value="${personForm.person.userName }">
<html:errors property="userNameCH" footer="" header=""/>
<html:errors property="userName"/>
密码:<input type="text" name="person.password" value="${personForm.person.password }">
<html:errors property="asd"/>
验证密码:<input type="text" name="password2" value="${personForm.password2 }">
<input type="submit" value="提交"><input type="reset" value="重置">
</form>
---------------------------------------------------------------------
<!--form中的action是提交按钮,/test?p=login、/test.do?p=login、test?p=login三种写法都可以,因为struts会自动加上前面的'/'和末尾的'.do' -->
<html:form action="/test?p=login" focus=” person.userName”><!—-一进入这个界面焦点就会在person.userName文本框上-->
用户名:<html:text property="person.userName"></html:text><!-- 不需要加上value,因为struts标签会自动加上 -->
密码:<html:password property="person.password"/>
<html:submit titleKey="" value="提交"></html:submit>
</html:form>
<html:checkbox property="autoLogon"></html:checkbox><!-- 一旦它被选中一次后,那么就会保存它的checked为true在session中,如果你以后不选择它,就会传一个null过去,当以后你再回到这个界面时,它还是会选中。也就是只要选中一次后,以后不管你选不选,它都会自动选中 -->
<!-- 要解决这样的问题,就需要在formBean中调用reset方法,设置autoLogon =false-->
<!—<html:form>的讲解 -->
当你第一次访问这个jsp页面的时候,formBean并不会被actionServlet创建,那么这时候谁会创建这个formBean呢?这个formBean会被struts的<html: form>创建
文件上传的讲解
<html:form action="/test?p=upload" method="post" enctype="multipart/form-data">//千万记得要用这种方式提交,否则会出错。在formBean中有一个formFile对应,把上传的文件放入它中。
上传文件:<html:file onfocus="go(this)" property="formFile"></html:file>
<html:submit value="提交"></html:submit>
</html:form>
关于当文件上传的内容大于制定的大小的时候(struts的配置默认的文件内容的大小为250M)
解释:
首先从页面传进来的一个表单(也就是传进来一个原始的request),原始的request进入到actionServlet(第一个action),然后第一个action对这个原始的request进行包装(第二个图解释了如何包装request),包装过后再交给第2个action,当第二个action返回actionForward的时候,就会处理进过包装的request,返回给页面原始的request.,但是如果第二个action返回的不是actionForward,而是由于出错返回的input指向的页面,那么这时候返回的还是包装的request,不会进行处理。
进行request包装的详细解释----à重点
首先第一个action会对文件上传的大小进行判断,如果超过指定的大小,就不会将参数保存到map中,那么当进入到formBean的时候,那些参数就不会填充值,这时候验证的时候最容易出现空指针异常。
Struts的核心基础
1、当web.xml配置文件中拦截的是*.html的时候,那么struts-config配置文件的action的path=‘/test’text后面的.html struts会自动加上。
2、Struts的配置文件里面的action过多的处理方法。
<1>可以在读取配置文件的时候读取多个
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-configTest.xml</param-value>
<param-value>/WEB-INF/struts-configGo.xml</param-value>
</init-param>
但是千万记得不能<param-value>/WEB-INF/struts-config*.xml</param-value>以为加个*就是读取所有的struts的配置文件,不出错的。
<2>
具体的例子,看struts的核心基础的怎么处理action配置文件过多的问题。
Path中的*表示拦截任何以test开头的访问形式,但是千万记得,当页面访问的时候,*部分的内容就是填充那个{1}的内容,也就是进入到相应的action。
如:
<a href="${pageContext.request.contextPath}/testGo0.do?p=go">去go界面</a>
其实这里的Go0就是去填充{1}的
<a href="${pageContext.request.contextPath}/testGo1.do?p=go">点go1界面</a>
其实这里的Go1就是去填充{1}的
<action path="/test*"name="testForm" validate="true" type="com.actions.Test{1}Action" input="" parameter="p">
<forward name="go" path="/go.jsp"></forward>
<forward name="go1" path="/go1.jsp"></forward>
</action>
加强版
<a href="${pageContext.request.contextPath}/testGoGo0.do?p=go">去go界面</a>
<a href="${pageContext.request.contextPath}/testGoGo1.do?p=go">点go1界面</a>
<action path="/testG*G*" name="testForm" validate="true" type="com.actions.TestG{1}G{2}Action" input="" parameter="p">
<forward name="go" path="/go.jsp"></forward>
<forward name="go1" path="/go1.jsp"></forward>
</action>
所有的请求首先要经过ActionServlet处理,其实真正处理的不是它自己本身,而是RequestProcessor这个实体类。
//面试题
3、写工程名的时候得用${pageContext.request.contextPath}注意,这样的还是一般般啦,当你这样写的时候,在web.xml的头文件中如果配置的是2.2那么最好改成2.5
因为如果是2.2,最还在每个页面写上
2.5的文件头
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
//是EL表达式的功能失去
<%@ page isELIgnored="true"language="java" import="java.util.*" pageEncoding="utf-8"%>
<a href="${pageContext.request.contextPath}/testGoGo1.do?p=go">点go1界面</a>一般般 要是后面我不用struts1了,那就不是拦截以.do结尾的,而是拦截以.htm结尾的,j就得改源代码
<a href="/CoreBasic/testGoGo1.do?p=go">点go1界面</a>很差 要是别人的工程名改了,那不亏大了
<html:link action="/testGoGo1"></html:link>很好 可以自动拦截以任何东西结尾的,action就是在struts中的配置文件中action中的path的路劲
<html:link>的好处
第二点好处的讲解<这是视频12讲的>
浏览器第一次访问后台的时候,该标签里面没有cookie,因为cookie是服务器传过来给浏览器的,但是当浏览器去访问的时候,服务器就会把一个cookie传递给浏览器。第二次访问的时候如果浏览器支持cookie,那么服务器就不会再传cookie到这个标签里面,如果浏览器不支持cookie,那么第二次访问后,服务器又会把cookie传过来。
啥叫url编码:
<%
request.setAttribute("name", "曹欢操");
Date date = new Date();
request.setAttribute("date", date);
Person person1 = new Person();
person1.setId(1);
person1.setName("a");
Person person2 = new Person();
person2.setId(1);
person2.setName("a");
Person person3 = new Person();
person3.setId(1);
person3.setName("a");
Go go = new Go();
Map map = go.getMap();
map.put("x",person1);
map.put("y",person2);
map.put("z",person3);
request.setAttribute("go", go);
%>
<html:link>这种方式最大的功能就是能对传到后台的数据进行url编码,也就是我们经常在地址栏传参数过去,看到一些乱码的一些东西,其实那是用url进行编码的功能
在D:\终极复习版\struts\struts的核心基础\关于url编码有关于url编码的解释
<%
//如果不进行编码的的在后台得到的id的值就是asd,但是如果通过编码的话就是asd&go
String s = URLEncoder.encode("asd&go","utf-8");
request.setAttribute("s",s);
%>
<a href="${pageContext.request.contextPath}/testGoGo1.htm?p=go&id=${s}">点击</a>;
<!-- 这是将保存在request中的name的值赋值给paramId中的userName -->
<html:link action="/testGoGo1.htm?p=go" paramId="userName" paramName="name">点击</html:link>很好 可以自动拦截以任何东西结尾的
<!-- 这是将保存在request中的name对象的time属性赋值给paramId中的now -->
<html:link action="/testGoGo1.htm?p=go" paramId="now" paramName="date" paramProperty="time">点击</html:link>
<!--这是将go对象的map属性(脸面包含喝多对象) -->
<html:link action="/testGoGo1.htm?p=go" name="go" property="map">点击</html:link>
<html:optionsCollection>的用法name表示的是一个集合,value的值就是option中的value的值,label表示的是option中的
<select>
<option value="id">label</option>
</select>
<html:form action="/testGoGo1.htm?p=go">
<html:select property="name">
<html:optionsCollection name="list" value="id" label="name"/>
</html:select>
</html:form>
5、在struts的核心基础中讲叙了<html:checkbox>和<html:multibox>的区别
<html:checkbox>自动选中在basicCore工程中
Jsp页面
<html:form action="/testGoGo1.htm?p=go">
<html:multibox value="吃" property="arr" />吃
<html:multibox value="喝" property="arr"/>喝
<html:multibox value="玩" property="arr"/>玩
<html:multibox value="乐" property="arr"/>乐
<html:select property="name">
<html:optionsCollection name="list" value="id" label="name"/>
</html:select>
</html:form>
<logic:forward>的讲解
假如你想要在地址栏中不打太多的东西,只想打一个工程名就跳到某个界面,那么你可以再
Index.jsp页面使用<logic:forward>标签下面是例子
<logic:forward name="go"/>需要配合和struts配置文件的一个全局action一起使用<global-forwards>
<forward name="go" path="/index.jsp"></forward>
</global-forwards>
该标签的name值必须与forward中的那么值相同。
<logic:redirect>三种不同属性的用法
<logic:redirect forward="go"></logic:redirect>这个是转到struts配置文件的全局action
<logic:redirect page="/index.jsp"></logic:redirect>这是转到工程下的某一个页面
<logic:redirect href="/CoreBasic/index.jsp"></logic:redirect>
这个必须写工程名,否则会报错
Struts1是单实例,struts2不是单实例
单实例就是每个人访问action时都会创建一个action,如果action中有一个变量 i = 0;i++;当每个人访问一次的时候,i都会自加。但是struts2不是单实例,当就不会发生在这种情况。
<html:rewrite>的用法
<form action="<html:rewriteaction="/testGoGo1?p=go"/>
"method="post">
<input type="submit" value="提交">
</form>
<html:rewriteaction="/testGoGo1?p=go"/>和<html:link action="/testGoGo1?p=go"/>的区别在于第一个action的生成的是一个网址,而后面一个生成的是一个完整的<a>标签
注意:
String name = request.getParameter("name");
response.getWriter().print(name);
returnnew ActionForward("/index.jsp");
这样输出时输出值不出的,因为new ActionForward("/index.jsp")会清空你输出的内容,需要最会return null才能输出东西。
<html:form>的用法
防止表单重复提交:首先当你访问第一个action的时候,保存一个token,然后回到有表单的页面,当你进入到第二个action的时候,判断该token是否合法,如果合法就进行操作,然后删除该token,当你再次进行刚才的表单重复提交的时候,你判断该token是否合法,因为上次提交到时候你删除了token,所以这次提交时不合法的。所以就不会进行操作,回到表单页面。在使用案例功能与案例中有这个的例子
第一个action的设置标志
this.saveToken(request);
第二个action判断是否有这个标志
ActionMessages actionMessages = new ActionMessages();
//这是得到表单的数据,对表单进行判断是否重复提交
if(!isTokenValid(request))
{
actionMessages.add("formRepeat", new ActionMessage("error"));
saveErrors(request, actionMessages);
return mapping.findForward("index");
}
//这是将那个标志删除,防止表单重复提交
resetToken(request);
<!-- 可以防止表单重复提交 -->
<forward name="hh" path="/MyJsp.jsp" redirect="true"></forward>
关于用工具生成form、action、jsp页面。可以用myEclipse工具生成。在使用功能与案例的49个视频。超有用。可以很简单啊。
MappingDispatchAction的讲解。
引出这个action的主要原因是,因为要显示一个部门的所有部门到一个注册界面,那么首先必须先经过第一次进入action去数据库里面找部门,然后回到注册页面,当点击注册的时候,在第二次进入action。这时候,问题就来了,因为是注册界面,那么必须要有form,如果经过第一次action的时候,因为第一次进入action不需要form,那么会以为formBean为空,如果是保存在作用域session中,那么从注册界面第二次进入到action就会以为form也为空。那么会出现很到的问题。所以解决这个问题必须首先第一次去查找部门的时候,必须是一个action,第二次去注册的时候又需要一个action这样肯呢过有冗余。所以就有了MappingDispatchAction的存在,因为你每次放问一次就是一次请求,但是他们的方法却可以共用一个action。详细见struts使用功能与案例的关于MappingDispatchAction的案例
五种action的讲解
forwardAction dispatchAction includeAction mapingDispatchAction
downLoadAction localeAction lookUpAction
downLoadAction:这是一个关于下载文件的类,在struts的使用功能与案例的关于一个文件的下载的类的案例
localeAction是用于进行国际化的一个action。
<form-bean name="userForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="language" type="java.lang.String"></form-property>
<form-property name="country" type="java.lang.String"></form-property>
<form-property name="page" type="java.lang.String"></form-property>
</form-bean>
<action path="/test" name="userForm" type="org.apache.struts.actions.LocaleAction"></action>在struts的功能与案例中有这个案例,记得看看localeAction的源代码,这样发现其实就是以前自己写的那种国际化的一个封装。
<action path="/hello" forward="/fail.jsp"></action>
<action path="/hello" type="org.apache.struts.IncludeAction" include="/fail.jsp"></action>
上序两个action的功能基本差不多,只不过第一个forwardAction是request.getRequestDispatcher()的封装,第二个是request.getRequestDispatcher().include().
一个小的知识点:像response.setContentType("text/html;charset=utf-8");
Text/html这样的格式代表啥意思可以再tomcat的config的web.xml中可以找得到。
response.setHeader("Content-Disposition","attachment;filename=asd/txt");
<html:submit property="xxx"/>没有formBean对应也不会出错,但是
<html:textproperty="xxx"/>没有formBean肯定会出错,这说明struts考虑问题很细。
lookupDispatchAction可以解决表单提交要提交到某个方法不需要再action=""中不需要写上p=‘某个方法’,但是这个其实不是很好,因为dispatchAction可以解决这个问题,在struts的功能与案例中有这个例子
Struts的高级应用与源代码分析
页面的数据传到formBean中都是字符串,但是到formBean中却能得到相应的类型,是因为用到了beanUtils这个工具包。
转化器:加入页面有一个日期要传到formBean,在这个传的过程,其实页面传过来的都是字符窜,那么是怎么转换成的日期的呢?那是因为struts有一个BeanUtils里面有很多的转换器,把字符窜转换成对应的类型,但是如果页面穿过来的日期是转换成formBean中的java.util.Date这样是不行的,因为只有java.sql.Date这个转换器,没有java.util.Date的转换器,那么就必须自己写一个转换器。这个例子在struts的高级应用于源代码的转换器的例子里面。但是struts1有一个缺陷,因为它是单项转换器,所以在回到页面的时候,那么显示的日期还是不好看。可以用高级应用于源代码的双向转换器器有这个例子。但是struts2是双向转换器。这就是struts2的优点。自定义标签里面不能定义自定义标签。但是模拟做了一个双向转换器。
看看actionServlet的源代码,如果你在web-xml的配置文件中初始化了convertNUll ,这个在双向转换器器有这个例子有这个的讲解。
关于下拉框,千万记得如果是用枚举类型做的下拉框,那么千万记得不能用<html:optionsCollection>这个标签。
会出错,在struts的高级应用与源代码的用枚举类型做的select下拉框讲到了这个例子
用struts的插件来做注册转换器,插件的主要就是用来在还未加载actionServlet的init方法之前进行一些处理。
requestProcessor其实就是相当于一个校长的秘书,当校长接到任务的时候,不是自己亲自去干,而是叫秘书去干。actionAservlet就是校长,requestProcessor就是秘书。
异常:运行时的异常和非运行时的异常的区别。runtimeException运行时的异常不需要声明(即不需要throws抛出)简称系统异常,非运行期的异常必须要抛出(throws)
关于异常的另外一种处理方式,在web.xml里面配置。在struts的高级应用与源代码的源代码里面有这个例子。还有关于在struts的配置文件里面进行处理的例子。
异常的一个设计模式。在struts的高级应用与源代码的源代码里面有这个例子。
对于异常在业务层抛出,在表现层处理它,don't throw it , catch it ;
怎么样将xml文件里面的数据读到javaBean中,可以用degister框架
张扴祥的高级应用于源代码的69个视频,70个视频讲的都是源代码的一些讲解。
actionServlet的init方法:首先去看看源代码(在张扴祥的高级应用于源代码的69个视频)
initInternal();这个方法主要是读取信息资源包(也就是在控制台打印出来的那些信息),在org.apache.struts.action线面有四个资源包。
initOther();主要就是进行转换器的一些操作。
initServlet();主要就是用digester去读取web.xml里面的actionServlet的配置文件
initChain();
moduleConfig就是用digester去读取struts的配置文件里面的东西。
下面讲解的是actionServlet的requestProcessor的process方法
关于validator框架的讲解
如何把一个中文的properties的配置文件转换成十六进制的(因为在properties文件中不支持中文格式,只支持十六进制),首先你必须将你的jdk的path、classpath、java_home的三个路径配置好,然后用native2ascii -encoding utf-8(编码格式) applicationResource_zh.properties((这是要转换的文件)如果这里面是用中文写的) applicationResource_zh_CN.properties(这是转换后的文件名) (则转换成的这个配置文件就是十六进制写的啦)
这里有一个关于在form中验证的代码,比较完整,在struts的validator的在form中进行国际化验证,而且实现传递参数的国际化
Validator的原理图
Validator的用法流程:
首先在WEB-INF下面拷贝validator-rules.xml(这是校验的一些规则),validation.xml(用于配置一些校验的信息),然后在struts-config的配置文件中读取这两个配置文件,就是用插件配置器读取。---->
//这些信息都可以在struts的官方文档的validator的校验器中去查找
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
//property是固定的名字,value是要读取的配置文件
<set-property
property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
//下面两个参数都是固定的
<set-property value="false" property="stopOnFirstError"/>
</plug-in>
有一点千万注意,在form中不需要写validate方法了,但是form必须继承validatorForm
提出一个问题,加入有一个注册页面需要验证密码1是否与密码2相等,但是一个修改密码不需要验证密码1是否等于密码2。这时候就需要form继承validatorActionForm这个,这是后在validation.xml中配置的form的名字就是struts-config中每个action的path的值。这个例子其实很简单,在struts的validator中有这个例子。
动态form的验证:本来dynaActionForm是不能进行验证,但是dynaVlidatorForm能进行form的验证。在struts的validator中有这个例子。dynaValidatorActionForm就是处理多个请求的动态form的请求。
可以用validator实现javascript的功能。(对页面进行验证)--->这样可以进行后台验证,可能会影响性能,但是安全性更高。
在做validator的时候,如果有不懂的就去看struts包下的common-validator下的org.apache.commons.validator.resources下的validator_1_1_3.dtd下面的那些配置文件,就可以了解,再不行就看张孝祥的validator的第十一个视频,那里面有一些配置的详细讲解。
我们开发者都是去调用框架,但是我们不知道怎么自己去做validator的那些调用的一些方法,这个可以看张孝祥的validator的低十二个视频,里面有详细的讲解。
自己写一个校验器,然后给别人去调用,是一个关于验证身份证的例子。