一、拦截器(interceptor)概述
struts2是个框架,里面封装了很多功能,封装的很多功能都是在拦截器里面。
(属性封装、模型驱动等都是封装在拦截器里面)
struts2里面封装了很多功能,有很多拦截器,每次执行一部分拦截器,比如一些默认拦截器等
默认拦截器的位置在 core的jar包的struts-default.xml里边<interceptor>标签里边
二、拦截器基本原理
拦截器在什么时候执行:
在action对象创建之后和action方法执行之前进行执行(使用在相关位置打断点,并使tomcat在debug处进行调试验证)
拦截器的底层原理:
主要原理:
AOP思想:
AOP (Aspect Oriented Programming)思想:面向切面编程
(有基本功能,想拓展功能,不用修改源代码,
而是其他方式(增加配置文件等)来实现功能拓展)
(例如,给基本的登陆功能增加权限判断,不修改源代码增加权限判断)
这是第一个浅的层次:不修改源代码,增强、拓展功能
更深层次见 spring
所以AOP的本质是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面
责任链模式: ((诸多设计模式的一种,有点类似web阶段过滤链))
过滤链:每个请求可以有多个过滤器,只有每个过滤器放行了才能到下一个
责任链模式:有多个操作:添加 修改 删除
只有添加后做类似“放行”操作才能到修改,修改后再放行,才能到删除
AOP思想和责任链模式如何运用到拦截器里:
拦截器在action对象创建之后和action方法执行之前进行执行,执行过程使用AOP思想
在action里面并没有直接调用拦截器的方法,而是通过配置文件进行操作执行
要执行很多拦截器,需要用到责任链模式,拦截器1执行之后,放行;拦截器2执行,放行...action方法执行
(拦截器有很多功能,要用到什么功能给什么功能)(类似做手术,每个助手拿着不同的器具,主治医生需要什么器具,助手递过去什么器具)
可以通过查看源代码进一步理解上面的流程(看web.xml中过滤器那个类的源码)
(1)执行action
(2)创建action对象,使用动态代理
(3)执行action的方法
(4)执行很多拦截器 ,遍历执行 if(interceptors.hasNext())
(5)类似于放行的方法 return invocation.invoke()
重要的概念:
过滤器和拦截器的区别(另外一个重要的概念是前一天的servlet 和 action的区别)
(1)过滤器:理论上可以过滤任意资源(HTML ,JSP...)
(2)拦截器:只会可以拦截action (是在action创建后方法执行前,故只能拦截action)
三、自定义拦截器
1) 在struts2中有很多拦截器,struts2中封装了很多拦截器,可能存在我们需要而
struts2中不存在的拦截器。
2) 拦截器的结构:通过查看源码查看一下结构(例如modeDrivern)
继承了 AbstractInterceptor(此抽象类实现了接口Interceptor)
接口里有三个方法:
init() ==初始化
destroy() ==销毁
interceptor() ==拦截操作
可以通过自定义类继承抽象类实现
实际开发中使用的是 继承 MethodFilterInterceptor类
实现接口时必须实现所有方法,即使这个方法什么也不做;所以可以选择继承类来替代(当继承类和实现接口都可以时)
可以让action里某个方法不进行拦截
让自定义拦截器和action有关联,需要用到AOP思想;通过配置文件而不是调用方法的方式
自定义登陆拦截器:
1) 需求:实现只有登陆状态才能点击action的超链接,非登陆状态点击超链接跳转到登陆页面
判断登陆状态是通过 session 实现:
登陆成功后往session里放值,检查session里是否有值来进行判断
2) 基本过程:
继承类:MethodFilterInterceptor;
重写方法;
注册拦截器(让拦截器和action有关联);
注册基本步骤:
在action所在的包package里声明拦截器(可以找默认的拦截器借看声明格式)
在具体的action标签里使用拦截器
但是这样会出现 默认拦截器都不会执行的问题
解决方案是把默认拦截器再手动使用一次:
不会使用直接全部复制一份
把拦截器中默认的那一份里
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
...
把defaultStack 这个name引入即可
<!-- 把默认拦截器再手动使用一次 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
package cn.action.interceptor; import javax.interceptor.InvocationContext; import javax.servlet.http.HttpSession; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; /** * 自定义的登陆拦截器 * @author jiangbei01 * */ public class LoginInterceptor extends MethodFilterInterceptor{ //重写拦截的逻辑 @Override protected String doIntercept(ActionInvocation invocation) throws Exception { HttpSession session = ServletActionContext.getRequest().getSession(); String session_username = (String)session.getAttribute("username"); if(session_username != null){ //放行 return invocation.invoke(); }else{ //到result标签里,找name匹配前往页面 return "login"; } } }
配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <package name="demo1" extends="struts-default" namespace="/"> <!-- 声明拦截器 --> <interceptors> <!-- 分别是拦截器名字和类的全路径 --> <interceptor name="logininterceptor" class="cn.action.interceptor.LoginInterceptor"> </interceptor> </interceptors> <action name="login_*" class="cn.action.interceptor.LoginInterceptor" method="{1}"> <!-- 使用拦截器,name出填上面的name即可 --> <interceptor-ref name="logininterceptor"> <!-- 设置某些方法不拦截,name值为excludeMethods,标签内容为方法不拦截名 --> <param name="excludeMethods">login,regist</param> </interceptor-ref> <!-- 把默认拦截器再手动使用一次 --> <interceptor-ref name="defaultStack"></interceptor-ref> <result name="login">/login.jsp</result> </action> </package> </struts>
产生的问题是 登陆功能也进行拦截,此时登陆也检查session进行拦截,产生永远登陆不进去的问题
解决的思路是让 login()方法不进行拦截
方法是通过配置文件配置某些方法不拦截
<!-- 设置某些方法不拦截,name值为excludeMethods,标签内容为方法不拦截名 -->
<param name="excludeMethods">login,regist</param>
四、struts2的标签库
注意标签库的引入!
常用
1. <s:propertiry> 和OGNL表达式在JSP页面中获取值栈数据
2. <s:iterator> 获取值栈中list集合的数据
3. <s:dubug> 可以查看值栈的结构和数据
其它 <s:if> <s:else> <s:a>(超链接) 等相对不太常用的标签
表单标签(会用):
html表单回顾 form action method enctype
<input .../>(详见HTML小结)
select option
textareas
struts2对应html表单标签大多都有对应
见案例:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP ‘struts2_tag.jsp‘ starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <!-- 对应form标签 --> <s:form name="" action=""> <!-- 普通输入项,不能直接在前面写,否则不在同一行, 应当使用label标签,查看源代码可以看到,是不在一个table里面 代码在一个表格中,不用自己写<br/换行> --> <s:textfield name="username" label="用户名"></s:textfield> <!-- 密码输入项,冒号不用自己写 --> <s:password name="password" label="密码"></s:password> <!-- 单选框,将value值和显示值设置为一致 --> <s:radio list="{‘女‘,‘男‘}" name="sex" label="性别"></s:radio> <!-- 构建使value和显示值不一样,使用map方式 --> <s:radio list="#{‘girl‘:‘女‘,‘boy‘:‘男‘ }"></s:radio> <!-- 复选框,注意是checkboxlist --> <s:checkboxlist list="{‘吃饭‘,‘睡觉‘,‘打豆豆‘}" name="love" label="爱好"></s:checkboxlist> <!-- 下拉框 --> <s:select list="{‘学士‘,‘硕士‘,‘博士‘}" name="xueli" label="学历"></s:select> <!-- 文件上传项 --> <s:file name="file" label="文件上传"></s:file> <!-- 隐藏项 --> <s:hidden name="hidden" value="隐藏项"></s:hidden> <!-- 提交 --> <s:submit value="提交"></s:submit> <!-- 重置 --> <s:reset value="重置"></s:reset> <!-- 文本域 --> <s:textarea cols="10" rows="5"></s:textarea> </s:form> </body> </html>
小问题,提交和重置不在同一行,这是局限性(框架集问题见HTML小结)