框架的底层很多都会用到反射技术
过滤器在程序启动的时候创建,创建过滤器会执行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 |
request | request对象引用 |
session | session对象引用 |
application | ServletContext对象引用 |
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">