四 MethodFilterInterceptor类 源码分析
在实际开发中,配置拦截器,常常使用的是 MethodFilterInterceptor,
从方法名称中,也可以看出,拦截的是Action中的某些方法。
尝试着分析一下源码.(老蝴蝶偷偷不要脸一次)
package com.opensymphony.xwork2.interceptor; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.omg.CORBA.PRIVATE_MEMBER; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptorUtil; import com.opensymphony.xwork2.util.TextParseUtil; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; //继承了AbstractInterceptor类,实现的方法由以前的interceptor()方法改成了doIntercept()方法 public abstract class MethodFilterInterceptor extends AbstractInterceptor { private static final long serialVersionUID = 1L; protected transient Logger log = LoggerFactory.getLogger(getClass()); /** * MethodFilterInterceptor 类的两个参数 ,均用Set集合 * 学到一招,在类中实例时时,不要在private Set<User> userSet=new HashSet<User>();了 * 试试Collections.emptySet()实例化. List时用Collections.emptyList();方法 * @param excludeMethods 不拦截器的方法. 如注册,找回密码等方法 * @param includeMethods 要拦截的方法, 如登录,查看用户列表等方法 */ protected Set<String> excludeMethods = Collections.emptySet(); protected Set<String> includeMethods = Collections.emptySet(); //实现两个参数的setter和getter方法 public void setExcludeMethods(String excludeMethods) { //这个方法如下 this.excludeMethods = TextParseUtil.commaDelimitedStringToSet(excludeMethods); /* * 这个方法很明显,就是将字符串按照,进行分隔,然后分别放置到set集合中。 *public static Set<String> commaDelimitedStringToSet(String s) { Set<String> set = new HashSet(); String[] split = s.split(","); for (String aSplit : split) { //你看看人处理的,先将两边的空格去掉. String trimmed = aSplit.trim(); //又判断一下长度 if (trimmed.length() > 0) set.add(trimmed); } return set; } */ } public Set<String> getExcludeMethodsSet() { return excludeMethods; } public void setIncludeMethods(String includeMethods) { this.includeMethods = TextParseUtil.commaDelimitedStringToSet(includeMethods); } public Set<String> getIncludeMethodsSet() { return includeMethods; } //实现了intercept()方法. public String intercept(ActionInvocation invocation) throws Exception { if (applyInterceptor(invocation)) { //如果包括的方法,执行这一个 return doIntercept(invocation); } //不是包括的方法,执行这一个。 return invocation.invoke(); } protected boolean applyInterceptor(ActionInvocation invocation) { //利用代理模式的方式得到方法 String method = invocation.getProxy().getMethod(); /* * 进入看了看这个方法,如果是排除的方法,就返回false. * 如果是包括的方法,就返回true。 */ boolean applyMethod = MethodFilterInterceptorUtil.applyMethod(excludeMethods, includeMethods, method); if ((log.isDebugEnabled()) && (!applyMethod)) { log.debug("Skipping Interceptor... Method [" + method + "] found in exclude list.", new String[0]); } return applyMethod; } //需要实现的核心方法 protected abstract String doIntercept(ActionInvocation paramActionInvocation) throws Exception; }
五. MethodFilterInterceptor类 的具体使用
用一个具体的登录实例验证一下吧。
其中实例是按照第四章的两个小例子进行相应的仿写的。
五.一 编写控制器类UserAction
在UserAction中常用的方法如下:
public class UserAction extends BaseAction<User>{ private static final long serialVersionUID = 1L; private Logger logger=Logger.getLogger(UserAction.class); private UserService userSerive=new UserService(); /** * 转到登录的页面 */ public String toLogin(){ logger.info("跳转到登录的界面"); return "toLogin"; } /** * 具体的登录方法 */ public String login(){ logger.info("执行具体的登录操作"); User user=userSerive.login(getModel()); if(user!=null) { //说明存在这个用户 //登录成功后,将登录的用户放置到session中. ActionContext.getContext().getSession().put("currentUser",user); return LOGIN; }else { HttpServletRequest request=ServletActionContext.getRequest(); request.setAttribute("message","用户名或者密码错误"); return "toLogin"; } } /** * 跳转到注册的页面 */ public String toRegisterUI(){ logger.info("跳转到注册的界面"); return "toRegisterUI"; } /** * 进行用户的注册,注册成功之后跳转到登录的界面 * 注册成功之后,不把数据带到登录页面进行填充. */ public String register(){ logger.info("执行具体的注册功能"); return "toLogin"; } /** * 用户点击忘记密码后跳转到忘记密码的界面 */ public String forgetPasswordUI(){ logger.info("跳转到忘记密码的界面"); return "forgetPasswordUI"; } /** * 用户在忘记密码界面添加新的密码后执行修改密码 * 修改密码成功后跳转到登录界面 */ public String forgetPassword(){ logger.info("添加新的密码后修改密码"); return "toLogin"; } /** * 显示列表 */ public String list(){ logger.info("显示用户列表"); List<User> userList=userSerive.findAll(); ActionContext.getContext().put("userList",userList); return "list"; } /** *跳转到添加的界面 */ public String addUI(){ logger.info("跳转到添加的界面"); return "addUI"; } /** *执行具体的添加操作后,返回到list列表显示。 */ public String add(){ logger.info("执行具体的添加操作"); return "toList"; } /** * 跳转到修改的界面 */ public String editUI(){ logger.info("跳转到修改的界面"); return "editUI"; } /** * 执行具体的修改操作,修改完成后返回到列表的界面 */ public String edit(){ logger.info("执行具体的修改操作"); return "toList"; } /** * 执行具体的删除操作,删除成功后返回到列表的界面 */ public String delete(){ logger.info("执行具体的删除操作"); return "toList"; } /** * 点击名字,显示具体的详情 */ public String detailUI(){ logger.info("显示具体的详情操作"); return "detailUI"; } }
其中
toLogin(); login(); toRegisterUI(); register();forgetPasswordUI();forgetPassword();是不需要进行登陆拦截的,
list()及其以下方法,是需要登录拦截的。
如果toRegisterUI()方法也被拦截的话,那么是无法进行注册功能的。
五.二 根据Action中的返回值配置相应的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.i18n.encoding" value="UTF-8"></constant> <!--修改是否为开发者模式 --> <constant name="struts.devMode" value="true"></constant> <!--修改ui样式表 --> <constant name="struts.ui.theme" value="simple"></constant> <package name="methodInterceptor" extends="struts-default" namespace="/"> <!-- 配置全局错误页面 --> <global-results> <result name="error">/WEB-INF/content/error.jsp</result> </global-results> <action name="User_*" class="com.yjl.web.action.UserAction" method="{1}"> <!-- 转到登录页面的配置 --> <result name="toLogin">/WEB-INF/content/login.jsp</result> <!-- 登录的配置 --> <result name="login" type="redirectAction">User_list.action</result> <result name="toRegisterUI">/WEB-INF/content/register.jsp</result> <result name="forgetPasswordUI">/WEB-INF/content/forgetPassword.jsp</result> <result name="list">/WEB-INF/content/list.jsp</result> <result name="addUI">/WEB-INF/content/add.jsp</result> <result name="toList" type="redirectAction">User_list.action</result> <result name="editUI">/WEB-INF/content/edit.jsp</result> <result name="detailUI">/WEB-INF/content/detail.jsp</result> <result name="success">/WEB-INF/content/form.jsp</result> </action> </package> </struts>
五.三 根据struts.xml中的配置视图编写相应的jsp页面
每一个jsp 页面 都引入了 struts2 标签库
<%@ taglib uri="/struts-tags" prefix="s"%>
五.三.一 编写登录页面 /content/login.jsp
<body> ${message} <s:form action="User_login.action" namespace="/" method="post"> 用户名: <s:textfield name="name"/> <br/> 密码: <s:password name="password"/><br/> <s:a action="User_forgetPasswordUI" namespace="/">忘记密码</s:a><br/> <s:a action="User_toRegisterUI" namespace="/">注册新用户</s:a><br/> <s:submit value="提交"/> <s:reset value="重置"/> </s:form> </body>
五.三.二 编写注册页面 /content/register.jsp
<body> 这是注册的界面 <s:form action="User_register" namespace="/" method="post"> <s:submit value="注册"></s:submit> </s:form> </body>
五.三.三 编写忘记密码页面 /content/forgetPassword.jsp
<body> 这是忘记密码的界面 <s:form action="User_forgetPassword" namespace="/" method="post"> <s:submit value="确认找回密码"></s:submit> </s:form> </body>
五.三.四 编写查看页面 /content/list.jsp
<body> <div class="container"> <div class="row"> <table class="table table-bordered table-hover"> <caption>查看学生信息</caption> <thead> <tr> <th class="col-xs-2">姓名</th> <th class="col-xs-2">性别</th> <th class="col-xs-2">年龄</th> <th class="col-xs-2">关系</th> <th class="col-xs-4" colspan="3">相关操作 <span style="padding-left:40px"> <s:a action="User_addUI" namespace="/">添加</s:a> </span> </th> </tr> </thead> <tbody> <s:iterator var="user" value="%{userList}"> <tr> <td><s:a action="User_detailUI?id=%{id}" namespace="/">${user.name}</s:a></td> <td>${user.sex}</td> <td>${user.age}</td> <td>${user.relation}</td> <td> <s:a action="User_editUI?id=%{id}" namespace="/">修改</s:a> <s:a action="User_delete?id=%{id}" namespace="/">删除</s:a> </tr> </s:iterator> </tbody> </table> </div> </div> </body>
五.三.五 编写添加页面 /content/add.jsp
<body> 这是添加的界面 <s:form action="User_add" namespace="/" method="post"> <s:submit value="添加"></s:submit> </s:form> </body>
五.三.六 编写修改页面 /content/edit.jsp
<body> 这是修改的界面 <s:form action="User_edit" namespace="/" method="post"> <s:submit value="修改"></s:submit> </s:form> </body>
五.三.七 编写查看详情页面 /content/detail.jsp
<body> 这是修改的界面 <s:form action="User_edit" namespace="/" method="post"> <s:submit value="修改"></s:submit> </s:form> </body>
五.四 重启服务器,看各个链接是否正常跳转
输入网址: http://localhost:8080/Struts_Interceptor/User_toLogin
点击各个链接,发现各个功能正常跳转,
链接跳转显示页面正确,日志打印输出正确。
五.五 编写登录拦截器 LoginInterceptor
package com.yjl.web.interceptor; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; import com.yjl.pojo.User; /** * @author 两个蝴蝶飞 * @version 创建时间:Aug 27, 2018 8:49:26 AM * 类说明 登录拦截器 */ public class LoginInterceptor extends MethodFilterInterceptor{ private static final long serialVersionUID = 1L; private static Logger logger=Logger.getLogger(LoginInterceptor.class); @Override protected String doIntercept(ActionInvocation actionInvocation) throws Exception { HttpSession session=ServletActionContext.getRequest().getSession(); User currentUser=(User) session.getAttribute("currentUser"); //判断currentUser的值 if(currentUser==null){ //获取请求的方法 String method=actionInvocation.getProxy().getMethod(); //为空,表示没有登录 logger.info(method+"方法需要被拦截,必须登录后使用"); //没有登录,就跳转到登录的页面 return "toLogin"; }else{ //登录,就放行 实际操作中,还有一个权限的判断 String result=actionInvocation.invoke(); logger.info("我操作:"+result); return result; } } }
五.六 在struts.xml 配置登录拦截器,注意拦截和不需拦截方法的配置
<package name="methodInterceptor" extends="struts-default" namespace="/"> <!-- 配置拦截器 注意顺序,放在global-results前面--> <interceptors> <interceptor name="loginInterceptor" class="com.yjl.web.interceptor.LoginInterceptor"> </interceptor> </interceptors> <!-- 配置全局错误页面 --> <global-results> <result name="error">/WEB-INF/content/error.jsp</result> <!-- 转到登录页面的配置 --> <result name="toLogin">/WEB-INF/content/login.jsp</result> </global-results> <action name="User_*" class="com.yjl.web.action.UserAction" method="{1}"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="loginInterceptor"> <param name="excludeMethods">toLogin,login,toRegisterUI,register,forgetPasswordUI,forgetPassword</param> <param name="includeMethods">list,toList,addUI,add,editUI,edit,delete,detail</param> </interceptor-ref> <!-- 转到登录页面的配置 --> <result name="toLogin">/WEB-INF/content/login.jsp</result> <!-- 登录的配置 --> <result name="login" type="redirectAction">User_list.action</result> <result name="toRegisterUI">/WEB-INF/content/register.jsp</result> <result name="forgetPasswordUI">/WEB-INF/content/forgetPassword.jsp</result> <result name="list">/WEB-INF/content/list.jsp</result> <result name="addUI">/WEB-INF/content/add.jsp</result> <result name="toList" type="redirectAction">User_list.action</result> <result name="editUI">/WEB-INF/content/edit.jsp</result> <result name="detailUI">/WEB-INF/content/detail.jsp</result> <result name="success">/WEB-INF/content/form.jsp</result> </action> </package>
开发中,不需要拦截的方法少, 一般只需要注入参数excludeMethods即可。
excludeMethods为不需要拦截的方法,
includeMethods为需要拦截的方法。
如果拦截的和不拦截的都有,那么以拦截为主。
五.七 重启服务器,验证拦截器是否起作用
重启服务器,
未登录成功时:
输入 http://localhost:8080/Struts_Interceptor/User_addUI 等网址,会跳转到登录页面,
输入 http://localhost:8080/Struts_Interceptor/User_forgetPasswordUI 等网址,会跳转到指定的 jsp页面。
登录成功后:
输入 http://localhost:8080/Struts_Interceptor/User_addUI 等网址,会正常跳转到指定的 jsp页面。
六. 拦截器与过滤器的区别
摘录于 https://www.cnblogs.com/joyang/p/4973435.html
过滤器,是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,
然后再传入servlet或者struts的 action进行业务逻辑,
比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),
或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符
拦截器,是在面向切面编程的就是在你的service或者一个方法,前调用一个方法,
或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),
也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
拦截器是基于java的反射机制的,而过滤器是基于函数回调。
拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
这一章是关于拦截器的介绍,非常重要。
本章节的代码链接为:
链接:https://pan.baidu.com/s/18lQmPQzMVfpLF7lyfPKKHQ 提取码:enw4
谢谢您的观看!!!