一、拦截器 – interceptor
什么是拦截器interceptor
拦截器有点类似以前Servlet阶段的Filter(过滤器) , 能够在请求到达Action之前进行拦截操作, 可以在里面进行判断、校验。 典型的例子: 登录拦截.
拦截器和过滤器的不同
过滤器可以过滤servlet, 拦截器可以拦截Action , 过滤器除了可以过滤servlet之外,还可以过滤网络资源(html / jsp ) , 拦截器只能拦截Action
拦截器底层实现原理:AOP切面编程+责任链模式
拦截器的实现原理使用的是 AOP +责任链模式
AOP :
全称是面向切面编程, 其实就是在不改动原来代码基础上,我们需要额外加一些权限控制, 或者做一些日志输出。 那么我们可以在需要加的位置 切开,然后嵌入我们需要执行的逻辑代码即可。在struts里面,这一套机制都已经做好了,我们如果想在执行action之前加入我们自己的拦截器,只需在 struts.xml中配置即可。
责任链模式:
拦截器可以配置多个, 表示在到达某一个Action的时候需要经过这么多的拦截器。 struts里面把这个东西称之为拦截器栈。 也就是表示一组拦截器栈里面包含多个拦截器。只有A拦截器做出类似放行的操作,那么后续的拦截器才会执行,以此类推、直到Action的方法执行。
1. 自定义拦截器
- 1.1)实现接口Interceptor
特点: 要实现三个方法,其中init 和 destroy ,平常我们都不太用。
public class MyInterceptor implements Interceptor {
@Override
public void destroy() {
}
@Override
public void init() {
}
@Override
public String intercept(ActionInvocation arg0) throws Exception {
System.out.println("拦截器test01执行了");
return arg0.invoke();
}
}
- 1.2)继承实现类 AbstractInterceptor
好处就是使用复写一个interceptor的函数,坏处就是被拦截action里面的所有方法都被拦截了,无差别性。
声明拦截器的类:
public class MyInterceptor02 extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation arg0) throws Exception {
System.out.println("拦截器test02执行了");
return arg0.invoke();
}
}
struts.xml里面拦截器的配置
<!-- 配置action -->
<struts>
<package name="demo" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="myInterceptortest02" class="com.itheima.interceptor.InterceptorTest02"></interceptor>
</interceptors>
<action name="demo_*" class="com.itheima.action.ActionInterceptor" method="{1}">
<interceptor-ref name="myInterceptortest02"></interceptor-ref>
<result name="success" type="redirect">/other.jsp</result>
</action>
</package>
</struts>
- 1.3)继承MethodFilterInterceptor
> 可以精确的控制拦截或者不拦截哪一个方法
Interceptor:
public class MyInterceptor extends MethodFilterInterceptor {
@Override
protected String doIntercept(ActionInvocation arg0) throws Exception {
System.out.println("来到了拦截器里面~~~");
return arg0.invoke();
}
}
struts.xml中配置:
配置的时候, 使用 excludeMethods不拦截某些方法 或者 includeMethods拦截某些方法 来控制
<!-- 配置action -->
<struts>
<package name="demo" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="myInterceptortest02" class="com.itheima.interceptor.InterceptorTest02"></interceptor>
<interceptor name="myInterceptortest03" class="com.itheima.interceptor.InterceptorTest03">
<param name="excludeMethods">test</param>
</interceptor>
</interceptors>
<action name="demo_*" class="com.itheima.action.ActionInterceptor" method="{1}">
<interceptor-ref name="myInterceptortest02"></interceptor-ref>
<interceptor-ref name="myInterceptortest03">
</interceptor-ref>
<result name="success" type="redirect">/other.jsp</result>
</action>
</package>
</struts>
2. 自定义拦截器入门 – 步骤
- 定义拦截器类
public class MyInterceptor02 extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation arg0) throws Exception {
System.out.println("来到了拦截器里面~~~");
//如果后面还有拦截器,就调用下一个拦截器, 如果没有了拦截器,就执行action的方法
return arg0.invoke();
}
}
- 在struts.xml里面声明拦截器
<!-- 配置拦截器以下代码,仅仅是声明了拦截器。写在package包下 -->
<interceptors>
<interceptor name="myInterceptor02" class="com.itheima.interceptor.MyInterceptor02"></interceptor>
<interceptor name="myInterceptortest03" class="com.itheima.interceptor.InterceptorTest03">
<!-- <param name="excludeMethods">test</param> -->
<param name="includeMethods">test</param>
</interceptor>
<!-- 指定了的包含了多个拦截器,然后又声明一个名字 -->
<!-- <interceptor-stack name=""></interceptor-stack> -->
</interceptors>
- 在action里面使用拦截器
<action name="actionDemo_*" class="com.itheima.action.ActionDemo" method="{1}">
<!-- 执行这个action,就必然要走名字叫做myInterceptor02的拦截器 -->
<interceptor-ref name="myInterceptor02"></interceptor-ref>
</action>
3. 拦截器的处理结果
拦截器的处理结果,莫过于两种
一是放行: 如果后面还有拦截器就执行下一个拦截器,如果后面没有了拦截器,就执行action
一是拦截: 但是注意,拦截后也需要返回到一个具体的页面。 所以需要配置result标签
@Override
protected String doIntercept(ActionInvocation arg0) throws Exception {
System.out.println("来到了拦截器里面~~~");
//请求被拦截,要求现在立即回到页面去。
//不想放行. 回到页面。
return "login"; //这里的login要匹配result的name
}
<action name="actionDemo_*" class="com.itheima.action.ActionDemo" method="{1}">
<!-- 执行这个action,就必然要走名字叫做myInterceptor02的拦截器 -->
<interceptor-ref name="myInterceptor"></interceptor-ref>
<result name="login">/index.jsp</result>
</action>
4. 自定义拦截器的问题
一旦自定义了自己的拦截器,那么struts默认的哪一套 拦截器就不会走。 有点像有参构造和无参构造的关系。
- struts默认的拦截器都有哪些呢?
在struts-default.xml里面有一个package 名字也叫作 struts-default 在这个包中定义了一个拦截器栈,名字叫做 defaultStack
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="datetime"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
<interceptor-ref name="deprecation"/>
</interceptor-stack>
然后下面有一个默认的拦截器引用
<default-interceptor-ref name="defaultStack"/>
如果我们给自己的action使用了自己定义的拦截器,那么上面默认的这些拦截器都不会生效了,失去了这些功能,如果还想使用默认的这些拦截器。那么可以在action里面再引用它即可。
<action name="actionDemo_*" class="com.itheima.action.ActionDemo" method="{1}">
<!-- 执行这个action,就必然要走名字叫做myInterceptor02的拦截器 -->
<!-- 使用自定义的拦截器 -->
<interceptor-ref name="myInterceptor"></interceptor-ref>
<!-- 使用默认的拦截器 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<result name="login">/index.jsp</result>
</action>
二、 struts标签
其实就是 类似 jstl标签 , 然后把基本的html标签改造…
1)普通标签
2)UI标签
struts标签分类
普通标签
a)if 和 else标签
jsp页面:
<%
request.setAttribute("age", 10);
%>
<%-- 判断标签 --%>
<s:if test="#request.age > 19"> 大于19 </s:if>
<s:else>小于19</s:else>
b)遍历 -- iterator标签
<%
List<User> list = new ArrayList<User>();
list.add(new User("admin1","admin01"));
list.add(new User("admin2","admin02"));
list.add(new User("admin3","admin03"));
list.add(new User("admin4","admin04"));
request.setAttribute("list", list);
%>
遍历方式一:
<br> 遍历出来的每一个对象,都放置在栈顶, 我们直接用属性取就可以了。 这里是放置list区域<br>
<s:iterator value="#request.list" >
<s:property value="username"/>,<s:property value="password"/><br/>
</s:iterator>
遍历方式二:
<br> 遍历出来的每一个对象,都会在context区域放置一个对象 , user . 遍历第一次,这个user执行的对象就是01的对象。<br>
<s:iterator value="#request.list" var="user" >
<s:property value="username"/>,<s:property value="password"/><br/>
</s:iterator>
<hr/>
<s:iterator value="#request.list" var="user" status="status">
<s:property value="#status.count"/>,<s:property value="username"/>,<s:property value="password"/><br/>
</s:iterator>
c)取值 -- property标签
<s:property value="username"/>,<s:property value="password"/><br/>
2. UI标签
<s:textfield name="username" label="用户名"/><br/>
<s:textarea name="text" label="个性签名"></s:textarea><br/>
<s:submit value="提交" ></s:submit>
<s:form method="post" action="aaa.ation" >---</s:form>
三、 关于struts的模板和主题
在struts里面的default.property里面,有一个默认的参数:
struts.ui.theme=xhtml
这是struts的主题,struts默认提供了四种主题:默认是xhtml的主体,其他三种是simple,css_xhtml和ajax,要修改主题就需要在struts.xml里面配置常量
<constant name="struts.ui.theme" value="simple"></constant>
四、拓展: 国际化 、文件下载
4.1)国际化 i18n(internationalization)
1)在src下新建两个properties文件 , login_zh_CN.properties , login_en_US.properties
命名规则是: 名字语言国家地区.properties
注意:名字必须一致,只是后面的语言 & 国家可以不同而已。
如何创建properties:建立一个Untitled Text File,然后创建的时候写上后缀名properties
如: login_zh_CN.properties(注意:空格符号的两端不能有空格符号,否则无法解析)
login.username=用户名
login.pwd=密码
login.submit=登录
login_en_US.properties
login.name=username
login.pwd=password
login.submit=submit
2)告诉struts框架,使用这个国际化文件 ,配置手法,可以在struts.properties里面配置
也可以在struts.xml里面配置常量,这里给出的是struts.xml中的配置样例。
struts.custom.i18n.resources这个常量名字可以在default.properties里面找得到
<constant name="struts.custom.i18n.resources" value="login"></constant>
假设我们的项目要对很多个国家都进行国际化,那么都放在src下面,就显得很乱
所以我们可以建一个文件夹来包含他们, 假设文件夹的名字叫做 i18n 那么 常量就应该写成
<constant name="struts.custom.i18n.resources" value="i18n.login"></constant>
- 页面内容如下:
<a href="demo_test?request_locale=zh_CN">中文</a>
<a href="demo_test?request_locale=en_US">English</a>
这两个超链接是用来跳转到一个action,让struts 根据提交过去的request_locale 参数值来获取对应的properties文件内容
那个action里面没有什么实质性内容,只要返回success,然后还跳转到这个页面来即可。
<s:form id="loginform" action="login" method="post">
<s:textfield name="username" key="login.username"></s:textfield>
<s:textfield name="psd" key="login.pwd" ></s:textfield>
<s:submit key="login.submit"></s:submit>
</s:form>
4.2)文件下载
action写法:
private FileInputStream stream = null;
public FileInputStream getStream() {
return stream;
}
public void setStream(FileInputStream stream) {
this.stream = stream;
}
public String download() throws Exception{
resourceAsStream = ServletActionContext.getServletContext().getResourceAsStream("296822.jpg");
return SUCCESS;
}
struts.xml:
<action name="file_*" class="com.itheima.action.FileAction" method="{1}">
<result name="success" type="stream">
<param name="contentType">image/jpeg</param>
<param name="inputName">resourceAsStream</param>
<param name="contentDisposition">attachment;filename="document.jpg" </param>
<param name="bufferSize">1024</param>
</result>
</action>
4.3)返回json数据
记得导入struts-json-plugin-xxx.jar
public class JsonAction extends ActionSupport {
private String json;
public String getJson() {
return json;
}
public void setJson(String json) {
this.json = json;
}
public String register() throws Exception {
User user = new User();
user.setUsername("奥巴马");
user.setPassword("123456");
json = new Gson().toJson(user);
return SUCCESS;
}
}
<package name="json" extends="json-default" namespace="/">
<action name="json_*" class="com.itheima.action.JsonAction" method="{1}">
<result type="json">
<param name="root">json</param>
// 这里的name必须是root 至于这个json 是我们在action里面的成员 变量 json
</result>
</action>
</package>
页面发起请求:
记得导入jquery的库
<script type="text/javascript">
$(function() {
var url = "${pageContext.request.contextPath}/json_register";
$.post(url , function(result){
alert("result="+result)
},"json");
});
</script>