一、使用EL表达式访问Action中的属性
Struts2默认使用OGNL表达式从ValueStack中取值/赋值,EL表达式默认从Page、Request、Session和 Application里顺序取值。但是,在集成了Struts2的项目中,jsp页面可以直接使用${username} 获取Action中的username属性,跟OGNL表达式获取root对象属性的使用方法一样,这是为什么呢?
其实,在struts2中使用的request并非为tomcat提供的,而是经过了struts2所包装过的org.apache.struts2.dispatcher.StrutsRequestWrapper对象。具体可以查看这篇文章struts2 request内幕 为什么在struts2用EL表达式可以取值
二、使用EL表达式访问Action中Model的属性
我所在的项目所有Action都实现了com.opensymphony.xwork2.ModelDriven接口,该接口的作用就是使用getModel()方法将获取的model,并将该model压栈,从而使model位于ValueStack的栈顶,可以使用EL表达式获取model属性值。具体解释可以查看Struts2中的ModelDriven机制及其运用
三、我所遇到的问题
由于项目Action实现了ModelDriven接口,可以直接在页面使用${name}的形式获取model中的name属性。但是从以前的项目拷贝过来一个email-input.jsp页面中仍然使用${entity.name}的形式(name为Action中的model名称),习惯性的将其改成${name}后却取不到值了。添加如下代码进行打印ValueStack根值:
- <%
- ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");
- out.println(vs.getRoot());
- %>
<%
ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");
out.println(vs.getRoot());
%>
获取ValueStack root值如下:
[com.topstcn.web.action.system.EmailAction@17b8cfc6, com.opensymphony.xwork2.DefaultTextProvider@6650dc54]
其中并没有model(本例为Email对象)。查看Action代码
- public class EmailAction extends ActionSupport implements ModelDriven<Email>, Preparable{
- // 基本属性
- private Email entity;
- @Override
- public String execute() throws Exception {
- entity = MailFactory.getServerManager().getEmail();
- return SUCCESS;
- }
- @Override
- public String input() throws Exception {
- entity = MailFactory.getServerManager().getEmail();
- return INPUT;
- }
- @Override
- public String save() throws Exception {
- try {
- emailManager.saveEmail(entity);
- MailFactory.refresh();
- this.addActionMessage("操作成功!");
- this.logToDB(109, "配置邮件服务器");
- } catch (Exception e) {
- this.logger.error(e.getMessage(), e);
- this.addActionMessage("操作失败!");
- return this.input();
- }
- return RELOAD;
- }
- /**
- * 在input()前执行二次绑定.
- */
- public void prepareInput() throws Exception {
- }
- public Email getEntity() {
- return entity;
- }
- public void setEntity(Email entity) {
- this.entity = entity;
- }
- }
public class EmailAction extends ActionSupport implements ModelDriven<Email>, Preparable{
// 基本属性
private Email entity;
@Override
public String execute() throws Exception {
entity = MailFactory.getServerManager().getEmail();
return SUCCESS;
}
@Override
public String input() throws Exception {
entity = MailFactory.getServerManager().getEmail();
return INPUT;
}
@Override
public String save() throws Exception {
try {
emailManager.saveEmail(entity);
MailFactory.refresh();
this.addActionMessage("操作成功!");
this.logToDB(109, "配置邮件服务器");
} catch (Exception e) {
this.logger.error(e.getMessage(), e);
this.addActionMessage("操作失败!");
return this.input();
}
return RELOAD;
}
/**
* 在input()前执行二次绑定.
*/
public void prepareInput() throws Exception {
}
public Email getEntity() {
return entity;
}
public void setEntity(Email entity) {
this.entity = entity;
}
}
这就中了Struts2中的ModelDriven机制及其运用中介绍的ModelDriven的陷阱。但反过来其他页面为什么直接使用${name}形式就正常呢?以AnnouncementAction为例
- public class AnnouncementAction extends ActionSupport implements ModelDriven<Announcement>, Preparable{
- private static final long serialVersionUID = -5772389758597707692L;
- @Autowired
- private AnnouncementManager announcementManager;
- private Long id;
- private Announcement announcement;
- @Override
- public Announcement getModel() {
- return this.announcement;
- }
- @Override
- public String input() throws Exception {
- return "input";
- }
- @Override
- protected void prepareInput() throws Exception {
- if (id != null) {
- this.announcement = this.announcementManager.getAnnouncement(id);
- } else {
- this.announcement = new Announcement();
- }
- }
- public Long getId() {
- return id;
- }
- public void setId(Long id) {
- this.id = id;
- }
- public Announcement getAnnouncement() {
- return announcement;
- }
- public void setAnnouncement(Announcement announcement) {
- this.announcement = announcement;
- }
- }
public class AnnouncementAction extends ActionSupport implements ModelDriven<Announcement>, Preparable{
private static final long serialVersionUID = -5772389758597707692L;
@Autowired
private AnnouncementManager announcementManager;
private Long id;
private Announcement announcement;
@Override
public Announcement getModel() {
return this.announcement;
}
@Override
public String input() throws Exception {
return "input";
}
@Override
protected void prepareInput() throws Exception {
if (id != null) {
this.announcement = this.announcementManager.getAnnouncement(id);
} else {
this.announcement = new Announcement();
}
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Announcement getAnnouncement() {
return announcement;
}
public void setAnnouncement(Announcement announcement) {
this.announcement = announcement;
}
}
很明显,此Action把初始化model的工作方在了prepareInput()方法里,该方法在执行input()方法前被com.opensymphony.xwork2.interceptor.PrepareInterceptor拦截器调用
上图是struts2 defaultStack拦截器栈中拦截器定义图,可见prepare拦截器在modelDriven拦截器之前执行,即prepare拦截器先执行prepareInput()获得Model对象,然后modelDriven拦截器执行getModel()获得实体并压入ValueStack中。