Struts2一览

本文介绍了Struts2的初始化过程,Action配置及其执行流程。讲解了Action如何获取表单数据,比较了Servlet与Action的区别,并探讨了值栈的存储位置和数据设置。此外,还深入解析了Struts2的拦截器机制,包括默认拦截器的执行时机,以及AOP和责任链模式在拦截器中的应用。最后,提到了过滤器与拦截器的区别,以及解决Struts2中的一些常见问题和模块化开发的实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述(版本:struts2.3.10)
框架的底层很多都会用到反射技术
过滤器在程序启动的时候创建,创建过滤器会执行init方法()

init:加载自带的配置文件和用户创建的


struts执行过程以及Action配置:
为了方便开发者,在servlet的基础上封装了一些繁杂的操作,比如接收参数,页面跳转等,利用反射技术,
开发者只需要在配置文件中配置相应的action即可。struts2就是一个过滤器(implements Filter),因此要在web.xml配置,
它的配置/*,可以拦截一切request请求,进行相应的处理之后,跳转页面。

<filter>
	<filter-name>struts2</filter-name>
	<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>struts2</filter-name>
	<url-pattern>/*</url-pettern>
</filter-mapping>
①、浏览器request请求 https://ip:port/demo1/hello.action
②、filter拦截,读取url,得到action名字hello
③、在src找struts.xml,利用dom4j解析xml中的配置,找到<action name="hello">的节点,读取得到action全路径(class属性)
④、利用反射技术执行Action的默认方法execute:

Class clazz = Class.forName("action全路径");
Method m = clazz.getMethod("execute");  //默认action标签有个属性叫method,默认值为"execute",可以改变
Object obj = m.invoke();
⑤、执行完方法之后,读取result标签,得到要返回的jsp,在用servlet返回到相应页面(request.getDispacher().forword())
<struts>
	<package name="delloDemo" extends="struts-default" namespace="/">
		<action name="hello" class="com.dhh.action.HelloAction" method="属性占位">
			<result name="ok" type="属性占位">/hello.jsp</result>
		</action>
	</package>
</struts>
标签:
extends:一定继承struts-default才具有action的功能
namespace:namespace的值和action的name属性构成url的路径
method:默认值就是execute,则直接action类中的execute方法

action的name属性:可以写通配符*(常用) 

具体配置:

<action name="hello_*" class="com.dhh.action.HelloAction" method="{1}">
type属性:
(1)dispatcher转发(浏览器路径不变,一次请求)--默认值
(2)redirect重定向(路径改变,二次请求)

(3)chain:转发到action,一般不用,缓存问题

(4)redirectAction: 重定向到action

具体配置:

<result name="success" type="redirectAction">orders</result>
<!--orders为另外一个action的name属性值,也就是action的访问名称,可以加.action,也可以不加-->


action编写三个方法

1、创建类,不继承任何类,不实现任何接口
2、创建类,实现接口Action
3、创建类,继承类ActionSupport(常用)-ActionSupport也是现实Action
action方法,必须返回String类型。如果没有返回页面,则返回NONE,在Action中有定义NONE


Action获取表单提交数据:
servlet的获取方式:getParameter,getParameterMap

Action没有request对象,获取表单提交数据有三种方式:
①、使用ActionContext类

//得到提交数据
Map<String,Object> map = ActionContext.getContext().getParameters();  //Object是数据格式,因为可能有复选框
②、ServletActionContext
HttpServletRequest request = ServletActionContext.getRequest();
String username = request.getParameter("username");
③、使用接口注入

Action实现ServletRequestAware接口,会把HttpServetRequest对象引入进来,写法麻烦,不用

④、属性封装
在action中定义属性,名字和表单元素的name属性字段一致,提供set,get方法,struts2会采用内省技术把表单值通过set方法赋值进去。
缺点:属性值写在action里面,分层思想体现不出来,(而且因为action中定义属性,所以action是线程不安全的,多线程访问会导致数据紊乱,所以action才是多实例的,不能设置为单例,与spring整合,spring创建action bean时,要把scope设置成prototype)想把属性值放到单独一个bean中,不能采用属性封装来实现,于是就有了下面的模型驱动。

private User user = new User();
public User getModel(){
	return user;
}
⑤、模型驱动(Model)--开发用的最多
特点:把表单数据封装到模型中(bean)

action方法实现接口ModelDriven

public class HelloAction extends ActionSupport implements ModelDriven<User>
属性封装和模型驱动只能用一个

⑥、表达式封装(不常用)
表单写法:<input type="text" name="user.username"/>
action写法:private User user ;  //提供set,get方法

表达式封装能把数据封装到两个bean中,但是模型驱动不可以
还可以封装到集合

<input type="text" name="list[0].name"/>
<input type="text" name="list[1].name"/>
<!--private List<User> list; -->

<input type="text" name="map['one'].name"/>
<input type="text" name="map['two'].name"/>
<!--private Map<String,User> map; -->

操作对象:

//操作三个预对象
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = ServletActionContext.getSession();
ServletContext context = ServletActionContext.getServletContext();

ognl:
1、是一种表达式。比el表达式更加强大
2、el表达式,在jsp中读取域对象里面的值
3、不是struts2的一部分,是单独的项目,经常和struts2一起使用,要引入jar包,和struts2标签一起操作值栈

<%@ taglib uri="/struts-tags" prefix="s" %>

<!--运用函数-->
<s:property value="'haha'.length()"/>

<!--获取action中设置值栈中的值-->
<!--private String getUsername(){}-->
<s:property value="username"/>

<!--如果值栈中是对象User-->
<s:property value="user.username"/>

<!--如果值栈中是list-->
<s:property value="list[0].username"/>

<!--循环-->
<s:iterator value="list">
	<s:property value="username"/>
</s:iterator>

<!--循环,常用-->
<s:iterator value="list" var="user">
	<!-- 遍历list集合,得到每个user对象
	机制:把每次遍历出来user对象引用放到context里面,获取context里面数据特点:
	写ognl表达式,所以要用特殊符号:# -->
	<s:property value="#user.username"/>
</s:iterator>
Struts2标签:
1、s:property :  和ognl表达式在jsp页面中获取值栈数据
2、s:iterator :  获取之战list集合数据,遍历数据
3、s:debug    :  直接写在jsp中,可以查看值栈的结构
4、s:if s:else 
5、表单标签,基本不用


特殊符号# %使用:
①用ognl表达式取域对象中的数据,要加#号
②如果在struts2标签中使用ognl表达式,不能识别,要加%

<s:textarea name="username" value="%{#request.req}"></s:textarea>
ServletActionContext.getRequest().setAttribute("req","xxx");


值栈:
1、原来的现状:在servlet中,把数据存入域对象里面,在页面中是用el表达式获取,域对象在一定范围内,存值和取值。

request.setAttribute("xxx","xxxxxx");
2、struts2提供的机制,类似域对象
用法:在action中,把数据存入值栈,在页面中获取,当然也可以不使用值栈,用原始方式,把数据存入域对象。

3、servlet和action的区别:
servlet:默认第一次访问时创建,创建一次,单实例对象。
action:访问时创建,每次访问都会创建,多实例对象。
验证方式:在action对象的构造方法中打印一句话,再用浏览器请求这两个url


4、值栈存储位置:
①每次访问action,都会创建action对象
②每个action对象里面都会有一个值栈对象(仅仅一个)

ValueStack stack1 = ActionContext.getContext().getValueStack();
ValueStack stack2 = ActionContext.getContext().getValueStack();
stack1 == stack2;
5、值栈结构:
值栈分为两个部分:
①root , 结构是list集合 , CompoundRoot extends ArrayList
②context , 结构map集合, OgnlContext implements Map

context结构:

key(固定)value
requestrequest对象引用
sessionsession对象引用
applicationServletContext对象引用
parameters传递相关的参数
attr 

//当我们这样操作:会放到context中
ServletActionContext.getContext().getRequest().setAttribute("req","xxxx");

取值:

<s:property value="#request.req"/>   

request名字是固定的
常用的是root里面的数据
怎么查看这个结构:用struts标签<s:debug></s:debug>写到jsp就行

6、给值栈设置数据
值栈是一个栈,就当然有压栈操作,有栈顶元素,如果不设置值,他的栈顶元素就是action地址的引用。
这样做的目的是(在action中能获取值栈, 在值栈中能获取action)

添加数据有三种方法:
①set方法:(用的次多)

ActionContext.getContext().getValueStack().set("xxx","xxxx");  //添加操作,操作之后栈顶元素为Map类型
②push方法:
ActionContext.getContext().getValueStack().push("xxxx");  //压栈操作,操作后栈顶元素为String类型,取的时候<s:property value="[0].top"/>
③在action中定义变量,提供get方法即可(用的最多)
前两种方式,都是另外生成了一个结构,第一种生成了map集合,第二种生成了String字符串
而第三种方式,不会生成结构,只是在action中多个一个属性,而值栈中存的是action的引用地址,不额外向值栈中添加数据,所以能节约一点空间


设计模式思想:
为什么el表达式能获取值栈中的值??

<c:foreach items="list" var = "user">
	${user.username}
</c:foreach>
el表达式是从域对象里面读取getAttrbute方法来获取值的,要想el得到值应该request.setAttribute("xx","xxx");
如果我们在action中把值存到值栈,el表达式应该拿不到值才对。
原因:Struts2底层增强了request对象
具体实现:
①struts2的入口是一个拦截器,在拦截器里,doFilter中,对request进行了改写封装,增强了HttpServletRequest的getAttribute方法
②class StrutsRequestWrapper extends HttpServletRequestWrapper,struts2自定义类StrutsRequestWrapper,继承了HttpServletRequest的父类HttpServletRequestWrapper
③自己实现getAttribute方法,调用父类的getAttribute,如果能得到值就直接返回,得不到值,就从值栈中查询后再放到域对象request中的setAttribute
因此,如果放到值栈,再用el表达式,效率有点低


拦截器:
1、struts2封装了很多功能,封装的功能都是在拦截器里面,于是struts2有很多拦截器,
不是每次这些拦截器都执行,每次执行默认的拦截器。
2、struts2默认拦截器的位置:
在struts2核心jar里面:struts-default.xml


拦截器在什么时候执行?
在action对象创建之后,action方法之前执行

拦截器底层使用两个原理:
(1)aop思想:面向切面编程:
在基本功能之上扩展功能,通过不修改源代码的方法来扩展功能
(2)责任链模式:
类似流水线

①设计模式的一种
②和过滤链相似:一个请求可以有很多过滤器进行过滤,每个过滤器只有放行了,才能经过下一个过滤器。

aop思想和责任链模式应用到拦截器:
aop思想:在action方法(切入点)执行之前执行默认拦截器(增强--Advice),使用aop思想,在action中没有直接调用拦截器的方法(切面),
而是使用配置文件的方式进行操作。
责任链模式:经过很多拦截器,运用责任链模式
(括号里是aop的专业术语)


过滤器和拦截器:
过滤器:理论上可以过滤任意内容,比如html,jsp,servlet,图片路径
拦截器:只可以拦截action

拦截器借口Interceptor, 抽象类AbstractInterceptor
我们实现自定义拦截器,最好集成MethodFilterInterceptor

注册拦截器:
1、在action所在的package标签中加入配置

<interceptors>
	<interceptor name="loginInterceptor" class="cn.xxx.xx.LoginInterceptor"></interceptor>
</interceptors>
2、在具体的action标签里面使用声明的拦截器
<interceptor-ref name="loginInterceptor">
	<!--可以针对某些方法名不拦截,多个方法名逗号像相隔-->
	<param name="excludeMethods">login</param>
</interceptor-ref>
3、如果只这样引用,就只会执行自定义的拦截器,不会执行默认的拦截器,所以要把默认的拦截器都引入过来
<intercepor-ref name="defaultStack"></interceptor>
在action配置中添加了拦截器,那么拦截器会对action中的所有方法进行拦截,除非在拦截器中配置不拦截的方法名

其他配置:

<constant name="struts.i18n.encoding" value="UTF-8"></constant>


常见的一个使用frameset的bug解决:

<form target="_parent"></form>

表示提交表单之后,在父页面打开!解决frameset中嵌套的问题


分模块开发(多人开发,每个模块建一个xml,再在struts.xml中引入这些xml即可)

<include file="com/dhh/action/hello.xml"></include>

全局结果页面配置:

<global-results>
	<result name="success">/hello.jsp</result>
</global-results>

<form action="${pageContext.request.contextPath}/hello.action" method="post">


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值