Struts2 五大核心部分:
操作(Actions)
拦截器(Interceptors)
值栈(Value Stack)/OGNL
结果(Result)/结果类型
视图技术 --未学习
Struts2 主要功能:struts.xml 中 传值 跳转
struts2最重要的是拦截器,个人认为strut2是一个表述层与业务逻辑层进行数据交互的一个框架
流程: 当通过web程序点击一个超链接或表单时,由控制器(Dispatcher Filter Interceptors)收集输入,发送一个Action 的java类,Action执行后,
Result会选择一个资源给与响应。这个资源通常是一个jsp 或者其他资源类,
----Action 学习-----------------------------------------------------------------------------------------
Servlet和action区别
Servlet默认第一次访问的时候创建,创建一次,单实例对象。
action每次访问的时候创建,创建多次,多实例对象。
Action 实现方式分为两大类:
属性驱动:
使用属性作为贯穿MVC的信息携带者,依附于Action实例,Action实例封装请求参数和处理结果
实现方式
1 普通pojo 类
jsp:
<form action="test/login" method="post">
用户:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登陆"><br>
</form>
struts.xml:
<struts>
// package name 逻辑上的包名,namespace 命名空间 extends 继承某个配置文件
<package name="lee" extends="struts-default" namespace="/test"> // 这里的test 指定url的资源层级
// 根据Struts定义的自动装配策略
<constant name="struts.objectFactory.spring.autoWire.alwaysRespect" value="true" />
<action name="login" class="action.LoginAction" method="execute"> // 指定执行的action方法名称
//result name:Action 类的映射名 class:action类的完整路径 method: 默认 execute 需要自定义
<result name="success" type="dispatcher">/welcome.jsp</result>
<result name="error" type="dispatcher">/fail.jsp</result> // 物理路径(绝对路径),即指在项目中的实际位置
</action>
</package>
</struts>
action 类:
//普通的execute方法
public class LoginAction {
public String execute() throws Exception {
if("test".equals(getUsername())&&"123".equals(getPassword())){
return "success"; // 这里对应的结果集 result name
}else{return "error";}
}
}
2 实现Action接口
action类:
public class LoginAction1 implements Action {
//execute方法,和方式一比较“success”变为SUCCESS ERROR变为ERROR
public String execute() throws Exception {
if("test".equals(getUsername())&&"123".equals(getPassword())){
return SUCCESS;
}else{return ERROR;}
}
}
3 继承ActionSupport
action类: //struts2的拦截器机制 getter setter 方法负责解析用户请求参数 并且将请求参数值赋给action对应的属性
public class LoginAction2 extends ActionSupport {
//execute方法和方式二比较没变
public String execute() throws Exception {
if("test".equals(getUsername())&&"123".equals(getPassword())){
return SUCCESS;
}else{return ERROR;}
}
}
模型驱动: 使用单独的JavaBean 实例来贯穿MVC流程,JavaBean实例封装请求参数和处理结果
实现方式: 1 创建 User 类 implements Serializable
2 创建Action
public class LoginAction3 implements Action,ModelDriven<User> {
private User user=new User(); //定义用于封装请求参数和处理结果的Model
public String execute() throws Exception { //execute方法和方式二比较没变
if("test".equals(user.getUsername())&&"123".equals(user.getPassword())){
return SUCCESS;
}else{return ERROR;}
}
@Override //重写getModel方法
public User getModel() {
return user;
}
}
代码讲解: 前期导包
1:配置 web.xml 添加 filter - name=随便写 class=org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
filter-mapping name=随便写的名称 <url-pattern>/*</url-pattern>
2:配置 struts.xml 位置在WEB-INF classses 下
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration2.3//EN""http://struts.apache.org/dtds/struts-2.3.dtd">
<struts></struts>
如果有很多action,需要创建action 的 xml,然后在 struts.xml 中引入 <include file="struts-user.xml"></include>
自动装配原理:【org.apache.struts2.StrutsConstants类】 <constant name="struts.objectFactory.spring.autoWire.alwaysRespect" value="true" />
首先根据在Struts.xml中定义Action时指定的class属性值获取Action实例即appContext.getBean(beanName)
如果获取到,则直接返回。此时所使用的自动装配策略是applicationContext.xml中设置的装配策略。
applicationContext.xml中beans的默认自动装配策略是no,所以如果没有设置<beansdefault-autowire="autodetect">或者bean的autowire="byName",则Action中的属性比如personManager不会进行自动装配。
如果获取不到,则调用buildBean(beanClazz, extraContext)。
如果struts.objectFactory.spring.autoWire.alwaysRespect为true,此时会根据Struts定义的自动装配策略(struts.objectFactory.spring.autoWire)进行自动装配。
如果struts.objectFactory.spring.autoWire.alwaysRespect为false,则按constructor方式进行自动装配。
一个action 中包含了多个请求处理方式:
方法1: DMI(动态方法调用) Dynamic Method Invocation
表单元素的action不直接等于某个Action的名字,而是以感叹号后加方法名来指定对应的动作名:、
<!-- 动态方法调用HTML标签与Struts2标签 -->
<form action="computeAction!add.action" name="from" >
<s:form action="computeAction!add.action" name="form" theme="simple" >
写法就是 action 加上 ! 加上action内的指定方法名 最后加上 .action
注意:要使用动态方法调用,必须设置Struts2允许动态方法调用 <constant name="struts.enable.DynamicMethodInvocation" value="true" />
例子:
jsp: <s:form name="form" theme="simple">
num1:<s:textfield name="num1" />
num2:<s:textfiled name="num2" />
<s:submit type="button" value="加" οnclick="computeMethod('add')" />
<s:sibmit type="button" vlaue="减" οnclick="computeMethod('substract')" />
<s:form>
<script type="text/javascript">
function computeMethod(op){
document.form.action = "computeAction!"+op; //动态选择处理请求的方法
document.form.submit(); //提交
}
</script>
struts.xml:
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<package name="struts2" extends="struts-default">
<action name="computeAction" class="com.struts.ComputeAction">
<result name="fruitPage" >/fruit.jsp</result> // 这里result name 属性就是action实例方法返回的字符串
</action>
fruit.jsp响应结果的页面
<body>
<!-- 结果页面 -->
计算结果:<s:property value="fruit" /> // fruit 是 action实现类中定义的结果集,注意必须要有get方法 这里就不写了。
</body>
方法2 : action 添加 method 会造成一个请求一个action 这里就不写了
方法3: 使用通配符映射方式
jsp:
<s:form name="form" theme="simple" >
num1:<s:textfield name="num1" />
num2:<s:textfield name="num2" />
<s:submit type="button" value="加" οnclick="computeMethod('addAction')" />
<s:submit type="button" value="减" οnclick="computeMethod('subtractAction')" />
</s:form>
<script type="text/javascript">
function computeMethod(op){
document.form.action=op;//相比mothod属性改动只有这里 这里把参数作为action对象
document.form.submit();//提交}
</script>
struts.xml:
<package name="struts2" extends="struts-default">
<action name="*Action" class="com.struts.ComputeAction" method="{1}" >
<result name="fruitPage" >/fruit.jsp</result>
<!-- <result name="fruitPage" >/{1}.jsp</result>表达式也可以写在这里 -->
</action>
</package>
拦截器----------------------------------------------------------------------------------------------
过滤器和拦截器的区别
过滤器:在启动时创建。过滤器理论上可以过滤任何内容,比如 html、jsp、servlet、图片路径。
拦截器:拦截器可以拦截的内容,只会拦截action类型请求。所以标准写法要在请求后面加上".action"这个后缀。
拦截器是struts2 特有的,struts2 封装了很多功能,封装的功能都在拦截器里
默认拦截器位置: struts2-core-2.xxxx.jar—->struts-default.xml,在这里面配置了很多的拦截器,但是只执行默认配置的那些拦截器。
拦截器的执行时间:
action代理对象创建之后,action目标处理逻辑方法执行之前。只要配置了拦截器,不管是否使用都会被执行。
拦截器依赖的两个技术:动态代理 过滤链
拦截器的两个原理:
AOP思想:面向切片,在不修改代码片的前提下使用配置文件来达到切入代码的功能
责任链模式: 和过滤链类似,一个请求有多个过滤器过滤,每个过滤器执行后才到下一个过滤器。
action目标逻辑方法的执行是通过动态代理方式执行,和直接创建action对象执行没有区别。
原理如何应用到拦截器内:
在action方法执行前执行默认拦截器,执行过程使用AOP思想,action没有直接调用拦截器的方法,只能走配置文件。
执行很多拦截器使用的是责任链模式。
三大组件执行顺序:listener --> filter --> servlet
拦截器的构造:
以模型驱动ModelDriven 为例:
public class ModelDrivenInterceptor extends AbstractInterceptor{}
AbstractInterceptor 有三个方法: 1 init() 初始化 2 destory 销毁 3 String intercept(ActionInvocation invocation) 拦截器逻辑操作
创建拦截器方法:
1: extends AbstractIntercepter 需要些反射代码,比较麻烦 不推荐
2: extends MethodFilterInterceptor 能够让action某个方法不进行拦截
例: 实现action超链接只有登录状态才能进入 这里需要对所有的action都进行拦截才能判断是否登录
jsp:<form action="selfdefinedlogin" method="post"> 首先要有个登录功能页面
<input type="text" name="name" placeholder="用户名"><br>
<input type="text" name="password" placeholder="密码"><br>
<input type="submit" value="登录">
</form>
action: 服务端有个登录action
private User us;
public String execute(){
if(us.getName()==null||us.getPassword()==null)return "error";
if(!us.getName().equals("FireLang")||!us.getPassword().equals("lang")){
return "error";
}
ActionContext.getContext().getSession().put("FireLang", us.getName()); // 将用户写入session
return "success";
}
@Override // 别忘了这里的模型驱动
public User getModel() {
us=new User();
return us;
}
添加登录拦截器,判断session是否有该用户
创建拦截器:public class DefinedInterceptor extends MethodFilterInterceptor{
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
String name=(String)ServletActionContext.getRequest().getSession().getAttribute("FireLang");
if(name==null){return "error";}
return invocation.invoke();// 类似filter 放行 进行下一个拦截器或者action 但是必须有return 返回action结果
}
}
注册拦截器: struts.xml
<!-- 声明拦截器,注意这里还没有使用拦截器 -->
<interceptors>
<interceptor name="definedlogin" class="cn.domarvel.selfdefined.DefinedInterceptor"/>
</interceptors>
在action标签内使用拦截器: <interceptor-ref name="definedlogin"/>
<interceptor-ref name="defaultStack"/> // 为了默认拦截器也能使用 如不用可以不添加
注意,有个问题,我们在action内配置的拦截器,默认对所有方法都拦截,包括登录方法,这里的拦截器如果使用AbstractInterceptor
就要写反射来实现不拦截所有,而 MethodFilterInterceptor 可以通过配置实现不拦截所有。
还是action标签内 <interceptor-ref name="definedlogin">
<!-- param的name属性值不要写错了,不然它本身也不会报错,也会不起作用,这样很难找到错误的 -->
<!-- param里面的值为方法名称,多个方法名称用","逗号隔开 -->
<!-- 这样就能够让execute方法不会被当前配置的拦截器拦截了,但是因为不是配置的默认拦截器,所以默认拦截器还是会执行的 -->
<param name="excludeMethods">execute</param> // 这里是不拦截的方法
</interceptor-ref>
整体拦截器配置:
<!-- 拦截器配置的整体代码 注意 这里只对当前action有效,对其他action无效 -->
<package name="px" extends="struts-default" namespace="">
<interceptors>
<interceptor name="definedlogin" class="cn.domarvel.selfdefined.DefinedInterceptor"/>
</interceptors>
<action name="selfdefinedlogin" class="cn.domarvel.selfdefined.loginAction">
<interceptor-ref name="definedlogin">
<param name="excludeMethods">execute</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
<result name="error">/WEB-INF/content/selfdefined/failed.jsp</result>
<result name="success">/WEB-INF/content/selfdefined/successx.jsp</result>
</action>
</package>
Result 结果集----------------------------------------------------------------------------------------------
Result组件是Struts2中用于输出的组件,实际上就是Java代码,Struts2中预制了10中类型的Result
注意:若JSP页面中表单是用普通<form>编写的,发生错误而返回该页面时,则原数据将消失
若JSP页面中表单是用<s:form/>编写的,发生错误而返回该页面时,则原数据仍存在
<!-- 默认视图主题 -->
<constant name="struts.ui.theme" value="simple"></constant>
<!-- name属性:包名,用于被别的包调用或继承
extends: 继承哪个包,会继承该包下配置信息和拦截器等等
namespace:选填,url连接必须加入/new/action.xxx
-->
<package name="user" namespace="/ss" extends="struts-default">
1 配置 result 映射
指定实际资源的位置 绝对路径 / 开头 相对于当前的Web应用程序的上下文路径;
相对路径 不以/ 开头,相对于当前执行的action的路径,也就是namespace指定的路径。
如: <action name="login" class="com.ibm.LoginAction"> 如果这里class没有指定 默认会跳转到
<result>success.jsp</result>
<result name="error" type="dispatcher">/error.jsp</result>
</action>
param 固定参数:<param name="actionName">****</param>
<param name="location">
<param name="parse">
<param name="root">
<param name="includeProperties">
result 参数: name 要跳转的目标资源 可以是action url
type:要跳转的结果类型,具体如下面 也可以直接写jsp名称,默认dispatcher
action参数:name action名称
namespace 资源位置定义
2 result 结果类型 相当于springmvc 中的 requestmapping(value="") 用于处理请求并转到对应的动作执行
注意: 传递中文,记住永远不要在浏览器的地址栏中传递中文。在传递中文前先进行编码
action中: username=URLEncoder.encode("郭蕾","utf-8");//先进行编码
struts.xml 中 <action name="redirect" class="action.User">
<result type="redirect" name="redirect">
/redirect.jsp?username=${username}//如果要传递两个参数,中间用&代替&</result>
</action>
jsp 中:<body>
重定向
<%String s=request.getParameter("username");
s=new String(s.getBytes("iso8859-1"),"utf-8"); // 这里进行编码转换
s=URLDecoder.decode(s,"utf-8");
out.println(s);
%>
</body>
几种类型:
chain: 用来处理action链, com.opensymphony.xwork2.ActionChainResult
从一个action 转发到另一个action
四个属性:
actionName
namespace
method
skipActions
dispatcher(默认): 用来转向页面,通常处理 JSP org.apache.struts2.dispatcher.ServletDispatcherResult
实现类的两个属性 location和parse 通过 param 设置,如
<!-- 预算管理办导航 -->
<action name="budgeteeringIndex3">
<result name="success" type="dispatcher">
<param name="location">budgeteering_3.jsp?id=${id}</param> //location参数用于指定action执行完毕后要转向的目标资源
<param name="parse">true</param> true 解析 location 参数中的ongl 表达式,false 不解析 默认true
</result>
</action>
redirect: 重定向到一个url org.apache.struts2.dispatcher.ServletRedirectResult
后台使用HttpServletReseponse.sendRedirect() 将请求重定向到指定的url,在这个场景中,用户和服务端交互浏览器需要两次请求,
并且第一次请求的数据在第二次请求中是不可用的,意味着目标资源中无法访问action实例,如果要访问,两种方法:
1:将数据保存到session
2:通过请求参数传递数据 这里涉及到一个问题 参数如果是中文怎么办?
如:<action name="gatherReportInfo" class="...">
<result name="showReportResult" type=""redirect">
<param name="location">generateReport.jsp</param>
<param name="namespace">/genReport</param> 定义资源位置
<param name="reportType">pie</param> 参数1
<param name="width">100</param> 参数2
<param name="height">100</param> 参数3
url = /genReport/generateReport.jsp?reportType=pie&width=100&height=100
redirectAction: 重定向到一个action org.apache.struts2.dispatcher.ServletActionRedirectResult
主要防止用户重复提交表单
<action name="login" class="...">
<result type="redirectAction">
<param name="actionName">dashboard</param>
<param name="namespance">/secure</param> //重定向到不同命名空间下的 action
</result>
</action>
重定向到同一个命名空间下
<action name="dashboard" class="...">
<result>dashboard.jsp</result> //不出错跳转到这里 默认是 dispatcher
<result name="error" type="redirectAction"></result> //如果出错的话跳转到这里
<action name="error" class="..."> // 这里是出错后跳转
<result>error.jsp</result>
</action>
plainText: 显示源文件内容,如文件源码 org.apache.struts2.dispatcher.PlainTextResult
freemarker:处理 FreeMarker 模板
httpheader:控制特殊 http 行为的结果类型
httpheader结果类型很少使用到,它实际上是返回一个HTTP响应的头信息
stream:向浏览器发送 InputSream 对象,通常用来处理文件下载,还可用于返回 AJAX 数据。 org.apache.struts2.dispatcher.StreamResult
验证码例子:
<!-- 生成验证码的Action -->
<action name="createImage"
class="com.netctoss.action.CreateImageAction">
<result name="success" type="stream">
<param name="inputName">
imageStream
</param>
</result>
</action>
action实例:extends BaseAction
private InputStream imageStream; 输出用
public String execute() {
Map<String,BufferedImage> map = ImageUtil.createImage(); //生成验证码图片
String imageCode = map.keySet().iterator().next(); //通过遍历得到唯一生成的验证码
session.put("imageCode", imageCode); //将验证码记录到session,在登录验证时使用
BufferedImage image = map.get(imageCode); //根据验证码,得到图片
try {
imageStream = ImageUtil.getInputStream(image); //将图片转换为输入流,由result作输出
} catch (IOException e) {
e.printStackTrace();
return "error";
}
return "success";
}
velocity:处理 Velocity 模板
xslt: 处理 XML/XLST 模板
json 虽然这里没有,但是常用页面的异步校验,使用方式为:
1:导包:struts2-json-plugin-2.1.8.1.jar
2:struts.xml中将要使用json类型Result的package继承json-default
3:在struts.xml中配置result
如: 输出单个属性
<result name="success" type="json" >
//注意:name="root"是固定用法;如指定的属性是boolean类型,那么Result会把这个属性做成字符串"true";
// 如指定的属性是JavaBean,那么Result会把这个属性做成字符串{"code":"12"}
<param name="root">
指定Action的一个属性名
<param>
</result>
输出多个属性
<result name="success" type="json">
<param name="includeProperties">
属性名1,属性名2,... //Result会将这一组属性做成一个json输出 如 {"code":"aaa","name":"zs"}
</param>
</result>
输出所有属性
<result name="success" type="json">
</result>
值栈-------------------------------------------------------------------------------------------------
ValueStack
private OgnlValueStack iognlvstack_Stack;
public ValueFinder(ServletRequest areq_R) {
iognlvstack_Stack = null;
if (areq_R == null)
System.out.println((new StringBuilder()).append(getClass().getName())
.append(".ValueFinder(): areq_R is null").toString());
else
iognlvstack_Stack = (OgnlValueStack) areq_R.getAttribute("struts.valueStack");
}
1.ValueStack有一个实现类叫OgnlValueStack.
2.每一个action都有一个ValueStack.(一个请求,一个request,一个action,一个valueStack) valueStack生命周期就是request生命周期。
3.valueStack中存储了当前action对象以及其它常用web对象(request,session,application.parameters)
4.struts2框架将valueStack以“struts.valueStack”为名存储到request域中。
Struts2 中valueStack 内部有两部分,Map(OgnlContext) List(root)
ValueStack中 存在root属性 (CompoundRoot)--ArrayList
context 属性 (OgnlContext )--Map 基本很少操作这个对象
list集合中存储的是action相关信息 我们想要从list中获取数据,可以不使用#号.(它就是ognl的root)
下面来讲讲root 存储内容:
root 是一个栈的存储结构,默认存储内容:
action 对象引用 root栈顶存储 向值栈里放数据主要放在root里
DefaultTextProvider
map集合中存储的是相关映射信息,包含
paramters 传递相关的参数
request --RequestMap 底层是 HttpServletRequest
session —>HttpSession对象引用
application —>ServletContext对象引用
attr r—>使用attr操作,能够获取域对象里面的值,获取域范围最小里面的值
如果从map中获取数据,需要使用#. 通过struts2的标签<s:debug></s:debug>
对于 valueStack 获取有两种方式:
1: 通过 request 获取
ValueStack vs=(ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
2: 通过 ActionContext 获取
ValueStack vs=ActionContext.getContext().getValueStack();
向值栈保存数据的两个方法:(主要针对root)
1:push(Object obj)——->底层就是 root.add(0,obj) 将数据存储到栈顶。 调用push之后,就会在root栈顶放入一个String类型的对象
如: 同样放到 execute方法内
ActionContext.getContext().getValueStack().push(us);
ActionContext.getContext().getValueStack().push("FireLang");
2:set(String name,Object obj);—–>底层是将数据封装到HashMap中,在将这个HashMap通过push存储。
如: 放在execute 方法内部
ValueStack stack=ActionContext.getContext().getValueStack();
stack.set("username","FireLang"); // 在用set方法添加值栈数据之后,会在root栈顶多一个HashMap对象
3: 在action定义变量,生成变量的get方法 -- 常用
如:private String name; 这里必须要有 execute 方法 return "success";
public String getName(){ 这里可以返回String 也可以返回对象 如返回对象的话使用为 <s:property value="us.name" /> return us;
return name;
}
获取: <s:property value="name"/> 这里的name是ognl表达式。表示获取action中的name字段值,必须要写get方法
在用第三种方法之后,struts2并不会在值栈root的栈顶放入新的对象,它的存储路径还是存储在action里面,所以这就起到了资源的合理应用,
当想要获取name属性的时候,就会在值栈里面调用action的getName方法。这也就是为什么会在值栈里面存储action的原因了
如果这里要获取list 类型的值: 这里使用的是Ongl 也可以使用el表达式来获取${object.name }
<s:iterator value="object"> //object 有name password 属性 这里的object 是action get 方法返回值的名称
<s:property value="name"/>
<s:property value="password"/>
<br><hr><br>
</s:iterator>
如果使用el表达式的话,el取值能够获取到action里面的值,具体原理就是,它重写的request,进行了request域的增强,里面进行了以下操作:
el取值时,如果request域里面能够找到目标值,那么就把值返回到页面。如果在request域里面不能够取到目标值,那么就通过值栈获取。
el在获取Action里面的值时,action里面的字段也必须提供get方法。否则无法获取到值。
工作原理:https://blog.youkuaiyun.com/pwlazy/article/details/2318494
过滤器 ------------------------------此功能并非struts2特有---------------------------------------------------
Servlet Filter讲解:
请求预处理(改变request)
响应预处理(修改response)
filter其实是客户端与servlet中间的一个传递者,并且它可以对要传递 的东西进行修改。注意 filter只拦截请求响应,不会产生响应,那是servlet干的事。
适用场合: 实现URL级别的权限访问控制,过滤敏感词汇,压缩响应信息等。
拦截步骤: 1 客户端请求,在 HttpServletRequest 到达Servlet 之前进行拦截,
2 检查和修改 HttpServletRequest
3 过滤器中调用doFilter 方法,对请求放行。请求到servlet后,对请求处理并产生HttpServletResponse 发送给客户端。
4 服务端响应 HttpServletResponse 在到达客户端之前,被拦截。
5 检查和修改 HttpServletRespinse
6 HttpServletResponse 到达客户端
实现接口: Filter 接口
三个重要方法: init():初始化参数,创建Filter时自动调用
这里写入web.xml 的 <init-param>在这里初始化
init(FilterConfig filterConfig) init 的时候默认将FilterConfig 传入
如 redirectURL = filterConfig.getInitParameter("redirectURL"); redirectURL param-name名称
doFilter(ServletRequest,ServletResponse): 拦截后的处理
这里可以实例化 HttpServletRequest 和 HttpServletReseponse
分别对这俩对象进行操作 如
httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
最后 filterChain.doFilter(httpServletRequest, httpServletResponse);通过过滤交给servlet/action
如果有异常直接return
否则 FilterChain filterChain.doFilter(servletRequest, servletResponse);
destory(): 销毁 filter时调用
生命周期:
由web服务器决定,服务器启动,创建Filter 对象,调用 init 方法,filter 只会创建一次,
拦截到请求,调用doFilter 方法,可以执行多次。
服务器关闭,销毁Filter 对象。
Filter 接口对象 -- FilterConfig 在Filter 的实现类中的 init()方法内以参数形式传入
用户配置Filter 时,可以使用 <init-param>初始化参数,web容器初始化init,会把封装了filter 初始化参数的 FilterConfig对象传递进来,
开发人员只需要操作FilterConfig 对象的方法可以获取以下信息:
1:String getFilterName() 获取filter名称
2: String getInitParameter(String name) 根据一个参数名获取参数值,不存在返回null
3: Enumeration getInitParameterNames() 获取所有初始化参数的名称枚举集合
4: public ServletContext getServletContext() 获取servlet上下文的引用
过滤器链--FilterChain
一组过滤器对某些web 资源进行过滤,称之为过滤器链 执行顺序和 filter-mapping 有关
首先创建filter <filter>
<filter-name>自定义</>
<filter-class>类位置</>
<init-param><param-name></><param-value></></>
<init-param><param-name></><param-value></></>
</filter>
<filter-mapping> 这里开始过滤 对所有的 action请求进行过滤
<filter-name>menuAuthCheckFilter</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
<filter-mapping> 这里开始过滤 对所有的jsp 请求进行过滤
<filter-name>menuAuthCheckFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
执行顺序: 首先执行第一个filter,doFilter() 前是request 后是response
执行完第一个按照web.xml 顺序执行第二个,最后交给action,返回的时候是先执行最后一个filter的response 最后才是第一个。栈 先进后出顺序
使用filter 的几个例子:
1 关于禁用动态缓存: 为啥子要禁用动态缓存??
// 在response的头部设置Cache-Control、Pragma和Expires即可取消缓存
HttpServletResponse resp = (HttpServletResponse)response;
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
resp.setDateHeader("Expires", -1);
2 分IP统计网站的访问次数过滤器
需要servlet上下文,写入到上下文中去。
init(FilterConfig)
ServletContext context = FilterConfig.getServletContext();
Map<String,Integer> ipCount = new HashMap<String,Integer>();
application.setAttribute("ipCount",ipCount);
doFilter(ServletRequest request, ServletResponse response,FilterChain chain){
String ip = request.getRemoteAddr();//先获取ip
Integer count = ipCount.get(ip);
if(count != null){
//Map中存在该ip
count = count + 1;
}else{
count = 1;
}
ipCount.put(ip, count);
application.setAttribute("ipCount",ipCount); // 这里放到上下文中
chain.doFilter(request, response);
}
前端可以使用js 的标签库处理 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:forEach items="${ipCount}" var="m">
<tr>
<td>${m.key}</td>
<td>${m.value}</td>
</tr>
</c:forEach>
3 自动登录
逻辑流程: 先判断session中是否存在,存在则放行,不存在则判断cookie中是否存在用户名密码,存在则到数据库中查询是否正确,正确则存入session并放行,不正确则放行。
filter:
HttpServletRequest req = (HttpServletRequest)request;
HttpSession session = req.getSession();
UserInfo user = (UserInfo)session.getAttribute("user");
if(user != null){
chain.doFilter(req, response);
}else{
//session中不存在用户
Cookie[] cookies = req.getCookies();
Cookie cookie = CookieUtil.findCookie(cookies, "autoLogin");
if(cookie!=null){ //在cookie中找到该用户
UserInfoBiz ubiz = new UserInfoBizImpl();
String name = cookie.getValue().split("#oracle#")[0]; // 在cookie 中找到这个用户
String pwd = cookie.getValue().split("#oracle#")[1];
String msg = ubiz.login(name, pwd);
if("登陆成功!".equals(msg)){
user = ubiz.getByName(name);
session.setAttribute("user", user);
chain.doFilter(req, response);
}else{
chain.doFilter(req, response);
}
servlet 实现:
HttpSession session = request.getSession();String name = request.getParameter("myname");String pwd = request.getParameter("pwd");
String autoLogin = request.getParameter("autoLogin");
UserInfoBiz ubiz = new UserInfoBizImpl();
String msg = ubiz.login(name, pwd); // 检查是否有权限
if("登陆成功!".equals(msg)){
UserInfo user = ubiz.getByName(name);
session.setAttribute("user", user);
if("true".equals(autoLogin)){
Cookie cookie = new Cookie("autoLogin",user.getUserName()+"#oracle#"+user.getPassword()); //利用cookie记住用户名和密码
cookie.setMaxAge(60*60*24); //设置有效时间
response.addCookie(cookie); //将cookie回写到浏览器
} response.sendRedirect("success.jsp");
cookie 实现:
public static Cookie findCookie(Cookie[] cookies,String name){
if(cookies==null){
return null;
}else{
for(Cookie cookie:cookies){
if(cookie.getName().equals(name)){
return cookie;
}
}
return null;
}
}
String 拦截器 和 Struts2 过滤器filter区别:
两者的本质区别:拦截器是基于java的反射机制的,而过滤器是基于函数回调。从灵活性上说拦截器功能更强大些,Filter能做的事情,他都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的。
session-------------------------------------------------------------------------------------------------
Session 学习:
actipon 获取session 两种方式:
方法1: extends ActionSupport
execute(){
ActionContext actionContext = ActionContext.getContext();
Map session = actionContext.getSession();
session.put("USER_NAME", "Test User");return SUCCESS;
}
方法2:implements SessionAware
private Map session;
实现 public void setSession(Map session) {
this.session = session;
}
struts2的session,也是web应用的session,它是httpsession,存在于服务器上,对应每个浏览器进程,也就是说每个客户端浏览器进程对应服务器上的一个session,它采用key-value的结构。同时这个session还有一个生存周期。由于session会消耗服务器内存,因此尽量不要将大数据放到session中。
hibernate中session,是从获得数据库连接到释放(不一定是关闭)数据库连接,在一个sesion的范围内,对数据库的操作是一个事物。在没有处理分布式事物时,存在于当前线程内
ActionContext和ServletActionContext的区别 -----------------------------------------------------------------------------------
ActionContext主要负责值的操作;ServletActionContext主要负责获取Servlet对象。
优先使用ActionContext
只有ActionContext不能满足功能要求的时候,才使用ServletActionContext,要尽量让Action与Web无关,这对于Action的测试和复用都是极其有好处的。
ActionContext 是action 执行的上下文,里边存放着action执行时所需的对象,我们成为 广义值栈。类似 ValueStack
可获取的信息 request,session,ServletContext等
Struts2 每次执行action 都会创建新的 ActionContext,所以是线程安全的。看源码为:
public class ActionContext implements Serializable {
static ThreadLocal actionContext = new ThreadLocal(); // 线程局部变量,为每个使用该变量的线程创建副本,每一个线程可独立改变副本。
…… // 存放在ActionContext中的值都放在 ThreadLocal 属性内,只对当前请求线程可见。
}
访问的对向是map类型,为了action 和web解耦,不直接和底层httpsession 交互,所以struts2 封装 httpSession 为一个map 对象。
因为是解耦的,可以直接在java中访问action的execute 方法,但是因为 ActionContext 包装的都是web数据,仅在java api内无法获取直接使用ActionContext
struts2 提供了另一种访问ActionContext 对象方式: 使用 SessionAware 接口
该接口通过 Ioc/DI 来为Action 注入 Session Map,是通过servletConfig 拦截器实现的。拦截器在 defaultStack中。
如: public class OgnlAction extends ActionSupport implements SessionAware{ IOC方式
private Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session; } // 注意 这里必须有setSession 方法 为 Ioc 提供注入
public String execute(){
session.put("sessionTestKey", "测试SessionAware");
return this.SUCCESS; }
}
jsp 中获取 Map 内的值:
<%@ taglib prefix="s" uri="/struts-tags"%>
会话中的值:<s:property value="#session['sessionTestKey']"/> <br>
通过Servlet的Api获取会话中的值:<%=session.getAttribute("sessionTestKey") %>
为了能够在普通的Java应用中运行并测试Action,推荐大家使用SessionAware的方式来访问HttpSession。
ServletActionContext 存放了Servlet 相关的API信息 如 Cookie Session
是域对象,一个web应用中只有一个ServletContext,生命周期伴随整个web应用。
这个类直接继承了 ActionContext 相关父类 信息 如 OgnlValueStack Action 名字等访问,更重要的是提供了直接访问Servlet 相关对象的功能。
可获取的对象有:
HttpServletRequest:请求对象
HttpServletResponse:响应对象
ServletContext:Servlet上下文信息
PageContext:Http页面上下文
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
ServletContext servletContext = ServletActionContext.getServletContext();
PageContext pageContext = ServletActionContext.getPageContext();
HttpSession session = ServletActionContext.getRequest().getSession();
通过 implements ServletRequestAware:通过这个接口来获取HttpServletRequest对象
ServletResponseAware:通过这个接口来获取HttpServletResponse对象
如: action实现 public class OgnlAction extends ActionSupport implements ServletRequestAware{ IOC方式
private HttpServletRequest request = null;
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public String execute(){
request.setAttribute("request", "Request的属性值");
request.getSession().setAttribute("sessionTestKey", "测试SessionAware");
return this.SUCCESS;
}
}
jsp 实现: <%@ taglib prefix="s" uri="/struts-tags"%>
Request的属性值:<s:property value="#request['request']"/>br>
会话的属性值:<s:property value="#session['sessionTestKey']"/>
struts2中获得request、response和session ---------------------------------------------------------------------------
非IOC 方式:
ActionContext ctx = ActionContext.getContext(); // 静态方法获取
ctx.put("liuwei", "andy"); //request.setAttribute("liuwei", "andy");
Map session = ctx.getSession(); //session
HttpServletRequest request = ctx.get(org.apache.struts2.StrutsStatics.HTTP_REQUEST);
HttpServletResponse response = ctx.get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE);