(二)、Struts第二天
回顾:
问题:
1. Struts2的Action类是单例还是多例? Filter? Servlet? Listener?
2. 介绍struts2框架引入的相关jar包及作用?
3. Struts2 配置文件中,名称空间作用?
4. Struts2 执行流程
目标:
0. Action类的几种写法
1. Struts核心配置
* 常量配置
* 通配符/动态方法调用
* 全局视图、全局异常、Action配置各项的默认值
2. 拦截器
1. Action类的几种写法
1。 搭建struts开发环境
2. 写Action类、配置Action类
开发Action:
1. 写一个普通的java类;
2. 写一个普通的java类, 实现Action接口 【可以使用常量】
3. 写一个普通的java类;继承ActionSupport类 【常用; 】
如果使用struts提供的数据效验功能,必须继承ActionSupport!
public class DemoAction {} |
public class DemoAction implements Action {} |
public class DemoAction extends ActionSupport {} |
2. 通配符
语法如: user_* * 的值由访问时候传入的值确定;
引用的时候,用{1}引用*的值
传统的写法,
<!-- 访问: http://localhost:day28/save.action 方式1:传统的写法 <action name="save" class="cn.itcast.a_action.DemoAction" method="save"> <result name="save">/a/save.jsp</result> </action> <action name="delete" class="cn.itcast.a_action.DemoAction" method="delete"> <result name="delete">/a/delete.jsp</result> </action> <action name="update" class="cn.itcast.a_action.DemoAction" method="update"> <result name="update">/a/update.jsp</result> </action> -->
|
使用通配符优化配置,
<!-- 方式2:使用通配符优化配置 访问: http://localhost:day28/demo_save.action 访问: http://localhost:day28/demo_update.action --> <action name="demo_*" class="cn.itcast.a_action.DemoAction" method="{1}"> <result name="{1}">/a/{1}.jsp</result> </action> |
特殊的用法,
<!-- 注意: 访问1: http://localhost:8080/day28/demo_execute 如果方法返回的是success,可以省略名称 <result name=success>/index.jsp</result> <result>/index.jsp</result> 同上 方式2:http://localhost:8080/day28/demo 要求: 1. 必须有execute方法, 默认找execute方法 2. execute方法,必须返回success; --> <action name="demo_*" class="cn.itcast.a_action.DemoAction" method="{1}"> <result>/index.jsp</result> </action> |
3. 动态方法调用(了解)
<!-- 另外的方式: 动态方法调用: 语法: ! 感叹号后面的就是要处理的Action类中的方法名称! 举例: http://localhost:8080/day28/demo!save 表示actionName 是demo对应的Action类,必须有一个save方法! 动态方法调用与通配符区别? 1. 配置 通配符是用* 与 {1} 配合使用 动态方法调用, 是在访问的时候通过!符合指定方法名称 2. 比较 建议使用通配符! 不要用动态方法调用! (相对不安全!) 开发中都会禁用这个功能! --> <action name="demo" class="cn.itcast.a_action.DemoAction"> <result>/index.jsp</result> <result name="save">/a/save.jsp</result> <result name="update">/a/update.jsp</result> <result name="delete">/a/delete.jsp</result> </action> |
4. 全局相关配置
全局视图、全局异常、配置各项默认值!
<?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>
<package name="user_new" extends="struts-default"> <!-- 4. 默认执行的action类 --> <!-- 测试:指定默认执行的action类! 会覆盖调struts-default.xml中默认执行的Action类(ActionSupport) --> <default-class-ref class="cn.itcast.b_config.DefaultAction"></default-class-ref> <!-- 1. 全局跳转视图配置 这里面的配置,可以供当前包下所有的action共享! --> <global-results> <result name="success">/index.jsp</result> <result name="error">/error.jsp</result> </global-results> <!-- 2. 全局异常配置 当此包下的action类执行出现空指针异常,会取找error对应的全局视图对应的页面进行跳转! --> <global-exception-mappings> <exception-mapping result="error" exception="java.lang.NullPointerException"></exception-mapping> </global-exception-mappings>
<action name="user_*" class="cn.itcast.b_config.UserAction" method="{1}"> </action>
<action name="order_*" class="cn.itcast.b_config.OrderAction" method="{1}"> </action> <!-- 3. 各项目配置默认值 name 访问路径资源 class 默认执行的Action类是:ActionSupport! 在struts-default.xml配置文件中配置, <default-class-ref class="com.opensymphony.xwork2.ActionSupport" /> 表示默认执行的action类! 用户开发时候,可以在自己的package中进行配置,那么会覆盖父类的配置! method 默认执行execute方法 且返回success, 但当前没有配置对应页面,所以会取找全局视图配置! --> <action name="test"></action> <!-- 作用:可以跳转到WEB-INF下资源,但不用写Action类! <action name="test"> <result name="success">/WEB-INF/index.jsp</result> </action> -->
</package>
</struts>
|
5. 常量配置
思考:struts2访问后缀能否修改?
-à 先找到定义的位置
-à 定义自己设置的后缀,覆盖默认
常量定义位置:
struts2-core-2.3.4.1.jar/org.apache.struts2/default.properties
在struts.xml中定义常量:
<constant name=" " value=" "/>
指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出 <constant name="struts.i18n.encoding" value="UTF-8"/> 自定义后缀修改常量 <constant name="struts.action.extension" value="do"/> 设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 <constant name="struts.serve.static.browserCache" value="false"/> 当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开 <constant name="struts.configuration.xml.reload" value="true"/> 开发模式下使用,这样可以打印出更详细的错误信息 <constant name="struts.devMode" value="true" /> 默认的视图主题 <constant name="struts.ui.theme" value="simple" /> 与spring集成时,指定由spring负责action对象的创建 <constant name="struts.objectFactory" value="spring" /> 该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性 为 false <constant name="struts.enable.DynamicMethodInvocation" value="false"/> 上传文件的大小限制 <constant name="struts.multipart.maxSize" value=“10701096"/>
|
6. 数据处理
方式1:ActionContext 获取表示域对象的map
这样就可以通过操作map来操作域对象!
方式2: 实现接口的方式, 获取表示域对象的map集合!
RequestAware/SessionAware/ApplicationAware
原理:servletConfig拦截器!注入的map是从ActionContext对象获取的map!
方式3:ServletActionContext 获取原始的ServletApi
(一) 方式2实现:
/** * 所有的action可以继承此类,就可以直接用map集合 保存数据! * @author AdminTH * */ public class BaseAction extends ActionSupport implements RequestAware,SessionAware,ApplicationAware { protected Map<String, Object> request; protected Map<String, Object> session; protected Map<String, Object> application; // 在运行时期(servletConfig拦截器), struts会把表示request的map注入进来! @Override public void setRequest(Map<String, Object> request) { this.request = request; } // 在运行时期(servletConfig拦截器), struts会把表示session的map注入进来! @Override public void setSession(Map<String, Object> session) { this.session = session; } // 在运行时期(servletConfig拦截器), struts会把表示application的map注入进来! @Override public void setApplication(Map<String, Object> application) { this.application = application; } }
|
(二) 方式2实现原理:
Struts-defualt.xml 中关于servletConfig拦截器!
7. 用户库
MyEclipse如何管理jar文件?
答案:用户库!
使用步骤:
1. 新建用户库
--à选中项目,右键,properties,
-àLibrary, 右侧Add Library
-à ……
2. 发布项目
检查tomcat下是否有用户库的jar包!
如果有,直接启动!
注意:
如果用户库中的jar包没有发布到tomcat下,按照
《构建用户库-步骤图解.doc》 操作!
8. 共性问题
问题1: Struts.xml没有提示?
解决a: 配置MyEclipse关联约束!
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> |
步骤:
1. 源码找到文件: struts-2.3.dtd; 存到到指定目录(E:DTD)
2. Windows-à Preferences -à XML Catalog
Location: 定位到dtd文件的目录路径(如:E:DTD)
Key: -//Apache Software Foundation//DTD Struts Configuration 2.3//EN
3. OK; 关闭XML; 重新打开!
解决b: 连接internet即可! 工具会自动下载!
或者,
手写!
问题2: @Override 报错?
解决1.
@Override 删除即可!
解决2. 或者,
修改编译环境为1.6以上的环境!
JDK1.5 只支持对父类方法的@Override, 不支持接口的@Override!
在jdk1.6以后,接口的@Override就支持!
9. 拦截器
概念
Interceptor 表示拦截器!
1. Struts2通过一个拦截器完成一些通用的功能!用户使用哪些功能,自由组合即可!
2. Struts2总共定义了32个拦截器
Struts-defualt.xml中定义
3. 拦截器栈
引入一个个拦截器比较麻烦,可以定义一个栈里面包含多个拦截器;
那么使用的时候,只需要引入拦截器栈即可!
<interceptor-stack name="basicStack"> <interceptor-ref name="拦截器1"/> 引用的拦截器 <interceptor-ref name="拦截器1"/> <interceptor-ref name="拦截器1"/> </interceptor-stack> |
4. 默认执行的拦截器栈
defaultStack为默认执行的拦截器栈;里面共引用了18个拦截器!
执行拦截器栈:
<default-interceptor-ref name="defaultStack"/>
注意:
如果用户没有指定执行哪个拦截器栈,“默认栈”就会被执行!
如果用户指定执行了哪个拦截器栈,默认的栈就不会被执行!
defaultStack 是strtus的基本功能,一般开发都会用到!
5. 面试题: 过滤器与拦截器区别?
共同点: 拦截器请求!
过滤器:
Servlet中的概念,可以拦截器所有的请求!
拦截器:
Struts2中的概念,只能拦截struts的action的请求!
API & 配置
ü API
|-- interface Interceptor 拦截器接口!
|--abstract class AbstractInterceptor 一般开发,可以直接继承这个类即可!
|-- interface ActionInvocation 拦截器的执行状态
拦截器的依次调用,使用的就是这个对象!
接口核心3个方法:(filter类似)
|
ü 配置
Struts-default.xml配置
1. 定义拦截器 <interceptors> 1.1 定义每一个拦截器 <interceptor name="abc" class=""/> 1.2 定义拦截器栈 <interceptor-stack name="basicStack"> <interceptor-ref name="abc"/> </interceptor-stack>
</interceptors>
2. 执行哪些拦截器(通过引用栈指定) <default-interceptor-ref name="basicStack"/> |
自定义拦截器
步骤:
1. 写一个普通java类,实现interceptor接口!
2. struts.xml配置拦截器
/** * 自定义拦截器 * @author AdminTH * */ public class HelloInterceptor implements Interceptor{ // 启动时候,创建实例 public HelloInterceptor(){ System.out.println("1. 创建拦截器实例!"); }
// 启动后,创建实例之后执行初始化方法! @Override public void init() { System.out.println("2. HelloInterceptor.init()"); }
// 拦截器业务处理方法, 在访问时候执行? @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("4. 拦截, 执行开始"); // 放行(去到下一个拦截器,如果没有下一个拦截器,就进入action) String result = invocation.invoke(); System.out.println("6. 拦截, 执行结束"); return result; } @Override public void destroy() { System.out.println("销毁拦截器实例时候执行!"); } } |
<?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>
<package name="interceptor" extends="struts-default"> <!-- 配置拦截器 --> <interceptors> <!-- 自定义的拦截器 --> <interceptor name="hello" class="cn.itcast.e_interceptor.HelloInterceptor"></interceptor> <!-- 定义拦截器栈 --> <interceptor-stack name="myStack"> <!-- 引入默认的拦截器栈 --> <interceptor-ref name="defaultStack"></interceptor-ref> <!-- 自定义的拦截器 --> <interceptor-ref name="hello"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 执行自定义的拦截器栈 --> <default-interceptor-ref name="myStack"></default-interceptor-ref>
<action name="user_*" class="cn.itcast.e_interceptor.UserAction" method="{1}"> <result name="success">/test.jsp</result> </action>
</package>
</struts> |
执行流程
服务器启动:
1. 创建拦截器实例
2. 执行init()初始化方法
用户访问Action:
3. 创建Action实例
4. 执行拦截器intercept(…)方法
invocation.invoke(); 表示进入下一个拦截器,或执行action!
5. 进入action的业务方法,如execute()方法
6. execute()方法完成后,又一次回到执行各个拦截器
ActionInvocation 用法
// 拦截器业务处理方法, 在访问时候执行? @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("4. 拦截, 执行开始"); /* * 测试invocation的方法 */ //1. 获取ActionContext对象 ActionContext ac = invocation.getInvocationContext(); //2. 获取当前拦截的action对象! Object action = invocation.getAction(); //3. 获取action对象的代理对象 ActionProxy proxy = invocation.getProxy(); System.out.println(proxy.getActionName()); // 配置中action名称,即访问路径一部分 System.out.println(proxy.getMethod()); // 当前执行的方法! System.out.println(proxy.getNamespace()); // 名称空间 String result = invocation.invoke(); System.out.println("6. 拦截, 执行结束"); return "error"; } |
拦截器作用:
对action的请求进行拦截,从而处理一些公用的业务逻辑操作!
那么action类中就可以直接用拦截器已经实现的功能了!
案例
1) 需求:
只有登陆后的用户,才可以查看“用户列表”!
功能:
1. 登陆
2. 列表展示
3. 权限控制
只有登陆,才能查看列表!
2) 实现步骤:
先做登陆、列表;
再增加“登陆验证功能”!
1. 环境准备
* 建库建表
* 项目使用组件
# struts2
# DbUtils组件
# C3p0连接池
# MySQL驱动包
# JdbcUtils工具类
2. entity
User.java
3. dao
UserDao.java
Login();
List()
4. service
5. action
LoginAction
UserAction
6. login.jsp / list.jsp
7. “拦截器”实现登陆验证!
只有登陆后,才可以查看列表!
-- 建库 CREATE DATABASE day28 CHARACTER SET utf8;
USE day28; day28 -- 建表 CREATE TABLE t_user( id INT PRIMARY KEY AUTO_INCREMENT, userName VARCHAR(20), pwd VARCHAR(20), remark VARCHAR(200) ); -- 录入测试数据 INSERT INTO t_user VALUES(1,'jack','888','好人'); INSERT INTO t_user VALUES(2,'rose','888','女人'); |
方式1: 在action配置标签体中,引入拦截器
拦截器代码: |
/** * 用户登陆验证拦截器 * @author AdminTH * */ public class UserInterceptor extends AbstractInterceptor {
// 拦截器业务处理方法..... @Override public String intercept(ActionInvocation invocation) throws Exception { //1. 获取ActionContext ActionContext ac = invocation.getInvocationContext(); //2. 获取登陆用户 Object obj = ac.getSession().get("userInfo"); //3. 判断 if (obj == null) { // 没有登陆,不放行 return "input"; } else { // 已经登陆,放行! return invocation.invoke(); } }
}
|
Struts.xml |
<struts>
<package name="user" extends="struts-default"> <!-- 定义拦截器 --> <interceptors> <interceptor name="user" class="cn.itcast.interceptor.UserInterceptor"></interceptor> <interceptor-stack name="userStack"> <!-- 指定栈中定义的拦截器:先引入默认栈,再引入自定义栈! --> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="user"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 全局视图 --> <global-results> <result name="input">/login.jsp</result> </global-results>
<!-- 登陆 --> <action name="login" class="cn.itcast.action.LoginAction" method="login"> <!-- 登陆成功,重定向到列表的action --> <result name="list" type="redirectAction">user_list.action</result> </action> <!-- 用户管理(列表展示、新增、修改等功能 --> <action name="user_*" class="cn.itcast.action.UserAction" method="{1}"> <!-- 哪个action需要登陆验证,就引入相应的拦截器栈 --> <interceptor-ref name="userStack"></interceptor-ref> <result name="list">/WEB-INF/list.jsp</result> </action>
</package>
</struts>
|
总结:
哪个action需要登陆验证,就引入相应的拦截器栈, 比较麻烦!
方式2: 使用全局拦截器引入方式
//方式2: @Override public String intercept(ActionInvocation invocation) throws Exception { /* * 不验证登陆!(或注册!) * 思路: * 拿到当前执行的方法,如果是“login”, 直接放行! * 如果不是"login", 验证 */ // 获取当前执行的方法名称 String method = invocation.getProxy().getMethod(); // 判断:如果是登陆方法,就放行 if("login".equals(method)) { return invocation.invoke(); } //1. 获取ActionContext ActionContext ac = invocation.getInvocationContext(); //2. 获取登陆用户 Object obj = ac.getSession().get("userInfo"); //3. 判断 if (obj == null) { // 没有登陆,不放行 return "input"; } else { // 已经登陆,放行! return invocation.invoke(); } } |