Struts2访问ServletAPI:
在action获取表单提交数据
- 之前web阶段,提交表单到servlet里面,在servlet里面使用request对象里面的方法获取,getParameter,getParameterMap
提交表单到action,但是action没有request对象,不能直接使用request对象
使用ActionContext类获取(官方推荐)
- 使用ServletActionContext类获取(这个好写),但是操作Aplication域对象和response用这个。
- 使用接口注入方式获取(一般不会用,了解)
ActionContext类获取
方法
void put(String key,Object value)
相当于设置HttpServletRequest的setAttribute()方法Object get(String key)
通过参数key来查找当前HttpServletRequest中的值Map<String,Object> getApplication(
) 返回一个Application级的Map对象static ActionContext getContext()
返回当前线程的ActionContext对象Map<String,Object> getParameters()
返回一个包含所有HttpServletRequest参数信息的Map参数Map<String,Object> getSession()
返回一个Map类型的HttpSession对象void setApplication(Map<String,Object> application)
设置Application上下文void setSession(Map<String,Object> session)
设置一个Map类型的Session值
使用ActionContext类获取
- 因为获取参数的方法不是静态的。所以需要创建ActionContext类的对象
- 这个ActionContext类对象不是new出来的
- 使用 static ActionContext getContext() 获取当前线程的ActionContext 对象
具体演示
- 创建表单,提交表单到aciton里面
- 在action使用 ActionContext 获取数据
ActionContext context=ActionContext.getContext();
Map<String,Object> map=context.getParameters();
Set<String,Object> keys=map.keySet();
for(String key:keys){
Object[] obj=(Object[])map.get(key);
System.out.println(Arrays.toString(obj));
}
通过ServletActionContext访问
- 为了直接访问Servlet API,Struts2 框架还提供了 ServletActionContext类,该类包含了几个常用的静态方法,具体如下:
- static HttpServletRequest getRequest(): 获取Web应用的HttpServletRequest对象。
- static HttpServletResponse getResponse(): 获取Web应用的HttpServletResponse对象。
- static ServletContext getServletContext(): 获取Web应用的ServletContext对象。就是常说的Application。
- static PageContext getPageContext(): 获取Web应用的PageContext 对象。
- 这里是不会出现乱码的,因为在struts.xml中已经配置了常量
<constant name="struts.i18n.encoding" value="UTF-8">
- 在这里说明一下在action里面通过方法获取pageContext会获取到空对象。
ServletActionContext.getPageContext()
通过接口注入方式获取参数
- 让action 实现接口 ,为了得到request对象。
public class aaa extends ActionSupport impliments ServletRequestAware{
private HttpServletRequest request=null;
@Override
private void setServletRequest(HttpServletRequest request){
this.request=request;
}
public String execute() throws Exception {
request.getParameter("xxx");//这样就能够获取到request
}
}
为什么通过实现一个接口就能够获取到request呢???看下拦截器的工作过程 判断action是否为ServletRequestAware 就设置了request对象进去
package org.apache.struts2.interceptor;
public class ServletConfigInterceptor extends AbstractInterceptor implements StrutsStatics {
private static final long serialVersionUID = 605261777858676638L;
public String intercept(ActionInvocation invocation) throws Exception {
final Object action = invocation.getAction();
final ActionContext context = invocation.getInvocationContext();
if (action instanceof ServletRequestAware) {
HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
((ServletRequestAware) action).setServletRequest(request);
}
if (action instanceof ServletResponseAware) {
HttpServletResponse response = (HttpServletResponse) context.get(HTTP_RESPONSE);
((ServletResponseAware) action).setServletResponse(response);
}
if (action instanceof ParameterAware) {
((ParameterAware) action).setParameters((Map)context.getParameters());
}
if (action instanceof ApplicationAware) {
((ApplicationAware) action).setApplication(context.getApplication());
}
if (action instanceof SessionAware) {
((SessionAware) action).setSession(context.getSession());
}
if (action instanceof RequestAware) {
((RequestAware) action).setRequest((Map) context.get("request"));
}
if (action instanceof PrincipalAware) {
HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
if(request != null) {
// We are in servtlet environment, so principal information resides in HttpServletRequest
((PrincipalAware) action).setPrincipalProxy(new ServletPrincipalProxy(request));
}
}
if (action instanceof ServletContextAware) {
ServletContext servletContext = (ServletContext) context.get(SERVLET_CONTEXT);
((ServletContextAware) action).setServletContext(servletContext);
}
return invocation.invoke();
}
}
Struts的数据封装
- 在struts中不继承ActionSupport甚至不继承任何类都能够实现 属性驱动封装。
属性驱动封装(会用)
在Struts2中,可以直接在Action中定义各种Java基本数据类型的字段,使这些字段与表单数据对应,并利用这些字段进行数据传递。
- 属性驱动方式一:提供属性的set方法的方式
这种方式的原理是,通过内省的方式获取Action的所有字段,如果成功获取字段就通过获取字段的写方法(也就是set),来写入客户端传来的数据。和bean-utils原理一致。
模型驱动封装(重要)
在上面我们介绍了属性驱动封装数据,但是属性驱动封装数据有个缺点,就是不能把数据直接封装到实体类对象里面。所以下面我将介绍模型驱动封装。
模型驱动封装数据的原理和bean-utils封装数据是差不多的。
实现方法:
action实现接口,ModelDriven
里面的泛型写要封装数据的类名实现接口里面的方法 getModel方法
在action里面声明对象。你可以在成员变量区域创建对象。也可以在getModel方法里创建变量。
<!-- 客户端jsp代码 -->
<form action="mod" method="post">
用户名:<input name="name" type="text"><br>
密&em;码:<input name="password" type="text"><br>
<input type="submit" value="提交数据">
</form><br>
//服务端代码
public class pr_mod implements ModelDriven<User>{
private User user;
@Override
public User getModel() {
this.user=new User();
return user;
}
public String login(){
System.out.println(user.getName());
System.out.println(user.getPassword());
return "success";
}
public User getUser() {//写这个方法是为了在el表达式中直接获取该对象。和ModelDriven的执行过程无关
return user;
}
}
我认为模型驱动的封装数据思路和bean-utils封装数据的思路一样。
我的猜测是:
判断action是否实现了ModelDriven接口,如果实现了就调用getModel方法,获取user对象。然后通过内省获取user对象的所有字段。并且获取到客户端传来的所有数据。通过遍历user对象的所有字段来匹配map数据进行赋值。
使用模型驱动和属性驱动封装注意问题:
在模型驱动封装和属性驱动封装同时被使用的时候,action底层只会执行模型驱动(ModelDriven)而不会执行属性驱动。
表达式封装(很多书里也把表达式封装称作属性驱动封装的一部分),会用就行了
第一步:
表单页面设置:
<form action="biaodashi" method="post">
用户名:<input name="user.name" type="text"><br>
密 码:<input name="user.password" type="text"><br>
<input type="submit" value="提交数据">
</form><br>
注意这里的name属性格式,user.name,告诉服务端这个属性是要存在user里面的name属性
第二步:
服务器端:
public class pr_biao {
private User user;
public void setUser(User user) {
this.user = user;
}
public User getUser() {
return user;
}
public String execute(){
return "success";
}
}
声明 User变量,提供get,set方法。必须提供!!对于对象的创建不用你来创建,它会自己来帮你创建。由于写了get方法所以客户端也可以用${user.name },${user.password }
直接显示!!
有些书上是把表达式封装归类为属性封装!!这个要知道!!!
比较表达式封装和模型驱动封装
相同点:
- 使用表达式封装和模型驱动封装都可以把数据封装到实体类对象里面去。
不同点:
- 我们使用模型驱动在一次请求里面只能把数据封装到一个实体类对象里面。因为实现了ModelDriven,ModelDriven有个泛型。并且只能放一个。
- 在一个action里面不能使用模型驱动把数据封装到不同的实体类对象里面。
- 然而使用表达式封装却可以把数据封装到不同的实体类对象里面。
看例子:
表单的写法:
<form action="biaodashix" method="post">
用户名:<input name="us.name" type="text"><br>
密 码:<input name="us.password" type="text"><br>
书 名:<input name="bk.bookName" type="text">
<input type="submit" value="提交数据">
</form><br>
//us是服务器端的字段命名
服务端Action的写法:
public class pr_biao02 {
private User us;
private Book bk;
public User getUs() {
return us;
}
public void setUs(User us) {
this.us = us;
}
public Book getBk() {
return bk;
}
public void setBk(Book bk) {
this.bk = bk;
}
public String execute(){
System.out.println(us);
System.out.println(bk);
return "success";
}
}
我个人的看法是,struts里面的很多数据封装都是通过反射来实现,比如就拿这个表达式封装数据来说。
服务端收到us.name字符串的时候。会按一下流程来做:
判断action里面是否有字段us,如果有的话就通过invoke()创建对象,然后获取该字段的写方法,也就是set方法。把服务端的user赋值。之后再判断user对象是否有name字段,如果有的话就给name赋值。
封装表单数据到集合里面(会用)
封装数据到List集合
第一步:在aciton声明List
第二步:生成List变量的set和get方法
第三步:在表单输入项里面写表达式
具体代码:
客户端表单中的表达式:
<form action="biaodashilist" method="post">
用户名:<input type="text" name="lll[0].name">
密码:<input type="text" name="lll[0].password">
<br>
<br>
用户名:<input type="text" name="lll[1].name">
密码:<input type="text" name="lll[1].password">
<input type="submit">
</form>
这里的lll是action中声明类型为List的字段名称。
服务端action中的代码:
public class pr_list {
private List<User> lll;
public List<User> getLll() {
return lll;
}
public void setLll(List<User> lll) {
this.lll = lll;
}
public String execute(){
System.out.println(lll);
return "success";
}
}
必须声明get,和set方法。这个也是表达式的应用。
个人理解:感觉也是通过解析字符串实现。
在结果页面的配置:
${lll[0].name }
${lll[0].password }<br>
<hr><br>
${lll[1].name }
${lll[1].password }
封装数据到Map集合
第一步:声明map集合
第二步:生成get和set方法
第三步:在表单输入项的name属性值里面写表达式
在表单中:
<form action="biaodashimap" method="post">
用户名:<input type="text" name="mmmp['fire'].name">
密码:<input type="text" name="mmmp['fire'].password">
<br>
<br>
用户名:<input type="text" name="mmmp['fox'].name">
密码:<input type="text" name="mmmp['fox'].password">
<input type="submit">
</form>
注意这里是mmmp['fire']
而不是 mmmp[fire]
,如果写成后者会存储不了数据。因为语法是错误的!!!
在action中:
public class pr_map {
private Map<String, User> mmmp;
public Map<String, User> getMmmp() {
return mmmp;
}
public void setMmmp(Map<String, User> mmmp) {
this.mmmp = mmmp;
}
public String execute(){
System.out.println(mmmp);
return "success";
}
}
在结果页面的配置:
${mmmp["fire"].name }
${mmmp["fire"].password }<br>
<hr><br>
${mmmp['fox'].name }
${mmmp['fox'].password }
注意这里mmmp['fire']
和mmmp["fox"]
都可以。