本篇文章目的在于解决之前写的一篇浅谈Strust2的缺点的博文。
在讨论优化此缺点之前复述一下缺点存在的原因:Struts2没有沿用Servlet报错时设置服务器响应码(405),而直接输出模板错误页面。这个机制的设计存在缺陷,当使用Struts2和ajax结合开发时,若后台代码抛出异常,则前台js代码$.ajax的回调函数收到的响应数据data将是一个struts2的错误模板页面的html代码。$.ajax中定义的error对应的回调函数因为只有在服务器响应码为405等错误状态码时才被调用,因此永远不会被执行。这样服务器控制台不会输出错误信息,前台js也不会执行$.ajax中定义的error对应的回调函数,如此开发人员很难发现并处理异常。
解决方案:
利用Spring的AOP,我们可以在业务逻辑层配置一个增强(通知),在业务方法抛出异常后一刻(AOP的连接点),将异常信息
保存在值栈中,在控制层try-catch业务层抛出的异常,若捕获到,则取出值栈中保存的异常信息并以json形式发送到客户端。
这样客户端就可以根据服务器响应的json判断是否有异常,从而及时反映出来。
切入点的选择:
服务器端总共分为三层:控制层、业务逻辑层(Service)、DAO层。控制层负责接收参数和调用Service层的方法,而
Dao层供Service层操作数据。因此,异常的抛出一般处于Service层方法的业务逻辑代码中。所以我们的切入点是Service层
所有方法。而连接点(在何处调用通知)则是方法抛出异常后那一刻。
具体代码实现:
public class OAException {
public void putErrorMsgToStackTop(Throwable ex){ //通知(增强)
ActionContext.getContext().getValueStack().push(ex.getMessage());
}
}
public Collection<Menuitem> findAll() { //Service层的方法
int i = 1/0;
return this.menuitemDao.findAll();
}
public String findAll() {
try{
Collection<Menuitem> menuitems = this.menuitemService.findAll();
this.menuitems = menuitems;
}catch(Exception ex){
this.message = ActionContext.getContext().getValueStack().peek().toString();
}
this.putToStackTop(this);
return SUCCESS;
}
private String message;
public String getMessage(){ return this.message }; //栈顶对象(当前action)中的getter方法会
//以方法名去掉get后首字母小写作为参数名、方法返回值作为参数值发送到客户端
在Spring配置文件中配置AOP:
<aop:config>
<aop:pointcut expression="execution(* org.qinyu.oa.service.*.*(..))" id="serviceMethods"/>
<!-- 配置切面,将通知用于切点-->
<aop:aspect ref="oaException">
<!-- 连接点:抛出异常后;指定通知方法;传递异常对象 .-->
<aop:after-throwing method="putErrorMsgToStackTop" pointcut-ref="serviceMethods" throwing="ex"/>
</aop:aspect>
</aop:config>
<bean id="oaException" class="org.qinyu.oa.exception.OAException"></bean>