1. Struts2中的拦截器
1.1 Struts2的拦截器基本概念
1.1.1 拦截器概述
在Webwork的中文文档的解释为——拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者在定义的action执行的前后加入执行的代码,也可以在一个action执行前阻止其执行。也就是说它提供了一种可以提取action中可重用代码,统一管理和执行的方式。
拦截器链 (Interceptor Chain,在Struts 2中称为拦截器栈Interceptor Stack)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
拦截器和过滤器是有几分相似,但是也有区别:
- 过滤器是servlet规范中的一部分,任何java web工程都可以使用。
- 拦截器是struts2框架自己的,只有使用了struts2框架的工程才能用。
- 过滤器在url-pattern中配置了**/***之后,可以对所有要访问的资源拦截。
- 拦截器它是只有进入struts2核心内部之后,才会起作用,如果访问的是jsp,html,css,image或者js是不会进行拦截的。
同时,拦截器还是AOP编程思想的具体体现形式。**AOP(Aspect-Oriented Programming)**简单的说就是:
- 在不修改源码的基础上,已有的方法进行动态增强。
- 在struts2中,拦截器它就是对我们的动作方法进行增强。(其实就是把重复性的代码提取出来,然后放到拦截器中,统一管理,统一调用)
1.1.2 拦截器的执行时机
在访问struts2核心内部时,在动作方法执行之前先正序执行,然后执行动作方法,执行完动作方法和结果视图之后,再倒序执行。**所以它是先进后出,是个栈的结构。**具体可参考下图:
1.2 自定义拦截器
直接或间接的实现Interceptor接口
public interface Interceptor extends Serializable {
void init();
void destroy();
String intercept(ActionInvocation invocation) throws Exception;
}
该接口提供了三个方法,其具体介绍如下。
- void init():该方法在拦截器被创建后会立即被调用, 它在拦截器的生命周期内只被调用一次. 可以在该方法中对相关资源进行必要的初始化。
- void destroy():该方法与init方法相对应,在拦截器实例被销毁之前,将调用该方法来释放和拦截器相关的资源。它在拦截器的生命周期内,也只被调用一次。
- String intercept(ActionInvocation invocation) throws Exception:该方法是拦截器的核心方法,用来添加真正执行拦截工作的代码,实现具体的拦截操作。它返回一个字符串作为逻辑视图,系统根据返回的字符串跳转到对应的视图资源。每拦截一个动作请求, 该方法就会被调用一次。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过该参数的invoke()方法,将控制权转给下一个拦截器或者转给Action的execute()方法。
继承抽象拦截器类AbstractIntercepter
public abstract class AbstractInterceptor implements Interceptor {
public void init() {}
public void destroy() {}
public abstract String intercept(ActionInvocation invocation) throws Exception;
}
AbstractInterceptor有一个子类MethodFilterInterceptor,该类中提供了两个属性,可以告知拦截器对哪些方法进行拦截或者对哪些方法排除。
1.2.1 第一步:编写普通java类,继承AbstractInterceptor
/**
* 自定义拦截器
* 步骤:
* 第一步:编写一个普通类,继承AbstractInterceptor(也可以实现Interceptor接口)
* 第二步:配置拦截器
* <!-- 声明一个拦截器 -->
* <interceptors>
* <interceptor name="myInterceptor" class="com.itheima.web.interceptors.MyInterceptor"></interceptor>
* </interceptors>
* <!-- 引用拦截器:写在action标签的内部。当我们写了自己的拦截器引用时,默认的拦截器栈就失效了 -->
* <interceptor-ref name="myInterceptor"></interceptor-ref>
* 拦截器的放行:
* invocation.invoke();
* 返回值的内容是:
* 动作方法的返回值
* 关于结果视图的执行时机以及拦截器的返回值问题
* 在放行之前,拦截器的返回值可以控制显示哪个结果视图。一旦放行之后,它一定会显示动作方法返回值所匹配的结果视图,此时已经不管拦截器返回什么内容。
* 多个拦截器的执行顺序问题:
* 是由引用顺序决定的,与声明顺序无关
*
* @author wgy
*/
public class MyInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("访问Action之前:MyInterceptor拦截了。。。。");
//放行
String rtValue = invocation.invoke();
System.out.println("访问Action之后:MyInterceptor拦截了。。。。");
return rtValue;
}
}
1.2.2 第二步:在struts.xml中配置拦截器
<?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>
<constant name="struts.devMode" value="true"/>
<package name="p1" extends="struts-default">
<!-- 声明一个拦截器 -->
<interceptors>
<interceptor name="myIntercepter" class="com.wgy.web.interceptors.MyInterceptor"/>
</interceptors>
<action name="demo1" class="com.wgy.web.action.Demo1Action" method="demo1">
<!-- 引用拦截器:当我们写了自己的拦截器引用时,默认的拦截器栈就失效了 -->
<interceptor-ref name="myIntercepter"/>
<result>/success.jsp</result>
</action>
</package>
</struts>
1.3 案例-检查登录拦截器
1.3.1 定义拦截器
/**
* 检查登录拦截器
*
* @author wgy
*/
public class CheckLoginInterceptor2 extends MethodFilterInterceptor {
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
Object obj = ServletActionContext.getRequest().getSession().getAttribute("userinfo");
if(obj == null){
//没登录,去登录
return "login";
}
//已登录,就放行
return invocation.invoke();
}
}
1.3.2 配置拦截器
<package name="myDefault" extends="struts-default" abstract="true">
<!-- 声明拦截器 -->
<interceptors>
<interceptor name="checkLogin" class="com.wgy.web.interceptors.CheckLoginInterceptor2"/>
<!-- 定义一个自己的拦截器栈 -->
<interceptor-stack name="myDefaultStack">
<interceptor-ref name="checkLogin">
<!-- 告知拦截器,哪些方法需要拦截,哪些方法不需要拦截 -->
<param name="excludeMethods">userLogin</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!-- 把我们自定义的拦截器栈声明为默认拦截器栈 -->
<default-interceptor-ref name="myDefaultStack"/>
<global-results>
<result name="login">/login.jsp</result>
</global-results>
</package>
1.3.3 编写和配置Action
动作类
public class BBSAction extends ActionSupport implements ServletRequestAware {
private HttpServletRequest request;
@Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
/**
* 娱乐
* @return
*/
public String demo1(){
System.out.println(request);
return SUCCESS;
}
/**
* 体育
* @return
*/
public String demo2(){
System.out.println(request);
return SUCCESS;
}
/**
* 军事
* @return
*/
public String demo3(){
System.out.println(request);
return SUCCESS;
}
/**
* 登录方法
* @return
*/
public String userLogin(){
//往session域中存入一个登录标记
ServletActionContext.getRequest().getSession().setAttribute("userinfo", "");
return SUCCESS;
}
}
配置
<package name="p1" extends="myDefault">
<action name="demo1" class="com.wgy.web.action.BBSAction" method="demo1">
<result name="success">/demo1.jsp</result>
</action>
<action name="demo2" class="com.wgy.web.action.BBSAction" method="demo2">
<result name="success">/demo2.jsp</result>
</action>
<action name="demo3" class="com.wgy.web.action.BBSAction" method="demo3">
<result name="success">/demo3.jsp</result>
</action>
<action name="login" class="com.wgy.web.action.BBSAction" method="userLogin">
<result name="success" type="redirect">/main.jsp</result>
</action>
</package>
2. Struts2的注解配置
2.1 使用前提
Struts2框架,它不仅支持基于XML的配置方式,同时也支持基于注解配置的方式。
注解和XML的配置,都是告知struts2框架,当我们jsp页面发送请求,根据配置执行对应动作类的方法,并根据返回值,前往指定的结果视图(jsp页面或者其他动作)。它们只是配置的形式不一样。
其次要想使用struts2的注解,必须要导入一个新的jar包。该jar包是: struts2-convention-plugin-2.3.24.jar
2.2 常用注解
2.2.1 @NameSpace
出现的位置:
它只能出现在package上或者Action类上。一般情况下都是写在Action类上。
作用:
指定当前Action中所有动作方法的名称空间。
属性:
value:指定名称空间的名称。写法和xml配置时一致。不指定的话,默认名称空间是""。
示例:
@Namespace("/customer")
public class CustomerAction extends ActionSupport implements ModelDriven<Customer> {
private Customer customer = new Customer();
@Override
public Customer getModel() {
return customer;
}
}
2.2.2 @ParentPackage
出现的位置:
它只能出现在package上或者Action类上。一般情况下都是写在Action类上。
作用:
指定当前动作类所在包的父包。由于我们已经是在类中配置了,所以无需在指定包名了。
属性:
value:指定父包的名称。
示例:
@ParentPackage("struts-default")
public class CustomerAction extends ActionSupport implements ModelDriven<Customer> {
private Customer customer = new Customer();
@Override
public Customer getModel() {
return customer;
}
}
2.2.3 @Action
出现的位置:
它只能出现在Action类上或者动作方法上。一般情况下都是写在动作方法上。
作用:
指定当前动作方法的动作名称。也就是xml配置时action标签的name属性。
属性:
value:指定动作名称。
results[]:它是一个数组,数据类型是注解。用于指定结果视图。此属性可以没有,当没有该属性时,表示不返回任何结果视图。即使用response输出响应正文。
interceptorRefs[]:它是一个数组,数据类型是注解。用于指定引用的拦截器。
示例:
/**
* 获取添加客户页面
* @return
*/
@Action(value="addUICustomer",results={
@Result(name="addUICustomer",location="/jsp/customer/add.jsp")
})
public String addUICustomer(){
return "addUICustomer";
}
2.2.4 @Result
出现的位置:
它可以出现在动作类上,也可以出现在Action注解中。
作用:
出现在类上,表示当前动作类中的所有动作方法都可以用此视图。
出现在Action注解中,表示当前Action可用此视图。
属性:
name:指定逻辑结果视图名称。
type:指定前往视图的方式。例如:请求转发,重定向,重定向到另外的动作。
location:指定前往的地址。可以是一个页面,也可以是一个动作。
示例:
/**
* 保存客户
* @return
*/
@Action(value="addCustomer",results={
@Result(name="addCustomer",type="redirect",location="/jsp/success.jsp")
})
public String addCustomer(){
customerService.saveCustomer(customer);
return "addCustomer";
}
2.2.5 @Results
出现的位置:
它可以出现在动作类上,也可以出现在Action注解中。
作用:
用于配置多个结果视图。
属性:
value:它是一个数组,数据类型是result注解。
示例:
@Results({
@Result(name="login",location="/login.jsp"),
@Result(name="error",location="/error.jsp")
})
public class CustomerAction extends ActionSupport implements ModelDriven<Customer> {
private Customer customer = new Customer();
@Override
public Customer getModel() {
return customer;
}
}
2.2.6 @InterceptorRef
出现的位置:
它可以出现在动作类上或者Action注解中。
作用:
用于配置要引用的拦截器或者拦截器栈
属性:
value:用于指定拦截器或者拦截器栈
示例:
出现在动作方法上:
/**
* 查询所有客户
* @return
*/
@Action(value="findAllCustomer",
results={
@Result(name="findAllCustomer",location="/jsp/customer/list.jsp")
},
interceptorRefs={
@InterceptorRef("myDefaultStack")
}
)
public String findAllCustomer(){
customers = customerService.findAllCustomer();
return "findAllCustomer";
}
出现在动作类上:
@InterceptorRef("myDefaultStack")
public class CustomerAction extends ActionSupport implements ModelDriven<Customer> {
private Customer customer = new Customer();
@Override
public Customer getModel() {
return customer;
}
}
2.3 案例-注解实现客户保存和查询列表
2.3.1 拷贝必备jar包
导入jar包: struts2-convention-plugin-2.3.24.jar
2.3.2 使用注解配置Action
/**
* 客户的动作类
*
* @author wgy
*/
@ParentPackage("struts-default")//指定当前包的父包
@Namespace("/customer")
@Results({
@Result(name="customerList",type="redirect",location="findAllCustomer.action"),
@Result(name="error",location="/jsp/error.jsp"),
@Result(name="addUICustomer",location="/jsp/customer/add.jsp"),
@Result(name="findAllCustomer",location="/jsp/customer/list.jsp")
})
public class CustomerAction extends ActionSupport implements ModelDriven<Customer> {
private ICustomerService customerService = new CustomerServiceImpl();
private Customer customer = new Customer();
private List<Customer> customers;
@Override
public Customer getModel() {
return customer;
}
/**
* 查询所有客户
* @return
*/
@Action("findAllCustomer")
public String findAllCustomer(){
//1.调用service查询客户
List<Customer> customers = customerService.findAllCustomer();
//2.返回
return "findAllCustomer";
}
/**
* 获取添加客户页面
* @return
*/
@Action("addUICustomer")
public String addUICustomer(){
return "addUICustomer";
}
/**
* 添加客户
* @return
*/
@Action("addCustomer")
public String addCustomer(){
customerService.saveCustomer(customer);
return "customerList";
}
/**
* 删除客户
* @return
*/
@Action("deleteCustomer")
public String deleteCustomer(){
customerService.deleteCustomer(customer);
return "customerList";
}
//------getters and setters--------------
public List<Customer> getCustomers() {
return customers;
}
public void setCustomers(List<Customer> customers) {
this.customers = customers;
}
}