Struts2
一、Struts2
1.1为什么要用Struts
1)JSP用HTML与Java代码混用的方式开发,把表现与业务逻辑代码混合在一起给前期开发与后期维护带来巨大的复杂度。
2)解决办法:把业务逻辑代码从表现层中清晰的分离出来。
3)2000年,Craig McClanahan采用了MVC的设计模式开发Struts主流的开发技术,大多数公司在使用。
1.2什么是MVC
1)M-Model 模型
模型(Model)的职责是负责业务逻辑。包含两部分:业务数据和业务处理逻辑。比如实体类、DAO、Service都属于模型层。
2)V-View 视图
视图(View)的职责是显示界面和用户交互(收集用户信息)。属于视图的类是不包含业务逻辑和控制逻辑的JSP。
3)C-Controller 控制器
控制器是模型层M和视图层V之间的桥梁,用于控制流程。比如我们之前项目中写的ActionServlet。
Strust就是一个MVC框架,就是Servlet,即controller层,Hibernate就是model层
Baseservlet就是应用反射找到对应的方法,实现请求和转发。今天我们将一个类似的东西:strust2
在strust2之前还有strust1,当时还有一个webWork。但是webWork没有strust1用的人多,直到现在还有很多公司在用strust1。但是Strust2和strust1是完全不一样的,Strust2设计偏向于webWork(设计理念好)。
1.3 Struts2使用步骤
step1:创建一个JavaWeb Project,命名为struts01(Struts2只能用在Web项目里)
step2:拷贝Struts2的核心Jar包到WEB-INF/lib/下,基本功能核心jar包 5个:
下载地址; http://struts.apache.org/download.cg
1)xwork-core-2.3.32.jar:Struts2核心包,是WebWork内核。
2)struts-core-2.3.32.jar:Struts2核心包,是Struts框架的“外衣”。
3)ognl-3.0.19.jar:用来支持OGNL表达式的,类似于EL表达式,功能比EL表达式强大的多。
4)freemarker-2.3.22.jar:freemarker是比JSP更简单好用,功能更加强大的表现层技术,用来替代JSP的。在Struts2中提倡使用freemarker模板,但实际项目中使用JSP也很多。
5)commons-fileupload.jar:用于实现文件上传功能的jar包。
6) commons-io-2.2.jar
7) commons-lang3-3.2.jar
8) javassist-3.11.0.GA.jar
step3:在web.xml中配置Struts2的前端控制器,Struts2是用Filter(过滤器)实现的前端控制器,它只负责根据请求调用不同的Action
u 注意事项:原来的项目是用Servlet的方式作为控制器。
web.xml内容:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- 上面的内容是规定必须写的,复制粘贴即可 --> <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-pattern> <!-- /*表示所有的请求都要经过该过滤器 --> </filter-mapping> </web-app>
step4:在WebRoot下新建jsp文件夹,并在其中创建index.jsp和welcome.jsp
1)index.jsp
<form action="welcome.action" method="post"> <input name="name" type="text" /><input value="提交" type="submit" /> <span style="color:red;">${error }</span> </form>
2)Welcome.jsp
<h1>Welcome,${name}</h1>
step5:在com.hxh.action包下创建WelcomeAction类
package com.hxh.action; /** * <p>Company: offcn</p> * @author zgf * @date 2017年5月17日 * @version 1.0 */ public class WelcomAction { private String name; private String error; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getError() { return error; } public void setError(String error) { this.error = error; } public String execute() { System.out.println("WelcomeAction.execute()..."); if(name==null||"".equals(name)){ error="用户名不能为空"; return "fail"; } System.out.println("name: " + name);//用于测试 if ("test".equalsIgnoreCase(name)) { error="不能使用名字text登录"; return "fail"; }else{ return "success"; } } } }
step6:写Struts2所需要的控制器配置文件struts.xml,非常重要!该文件写请求与各个Action(Java类)的对应关系,前端控制器会根据配置信息调用对应的Action(Java类)去处理请求
u 注意事项:
v 在编写时struts.xml放在src目录中,而部署后该文件位于WEB-INF/classes/下!
v 同样的,.properties文件也放src下,部署后也在WEB-INF/classes/下!
v 程序一启动,struts.xml就会被加载到内存,并不是现读的。
struts.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"> <!-- 以上内容是规定必须写的,复制粘贴即可 --> <struts><!-- struts的根标签,也是规定 --> <package name="default" namespace="/" extends="struts-default"> <action name="welcome" class="com.offcn.action.WelcomAction"> <result>welcom.jsp</result> <result name="fail">index.jsp</result> </action> </package> </struts>
u 注意事项:xml文件内容严格按照.dtd格式要求,如:<?xml...?>不在第一行可能会报错,标签中随意换行也会引起404!
step7:部署,地址栏输入:http://localhost:8080/StrutsFirst/进行测试,点击提交后执行WelcomeAction类进行处理.
二、Action
2.1 Struts2的核心组件
1)FC:前端控制器
2)VS:ValueStack
3)Action:动作
4)Result:结果
5)Interceptor:拦截器
6)Tags:标记库
2.2 Struts2的工作流程
1)所有请求提交给FC。
2)根据配置信息确定要调用的Action。
3)创建一个ValueStack对象(每个请求都有一个独立的VS对象)。
4)创建Action对象,把Action对象放到VS的栈顶,将VS对象存入到request中,存储的key为“struts.valueStack”。
5)控制器调用Action对象接收请求参数,并在方法中根据输入属性算输出属性。
6)在调用Action之前或之后调用一系列Interceptor。
7)根据Action返回的字符串确定Result(10种类型)。
8)调用Result对象,将VS中的数据按照特定的格式输出。
9)很多情况下,Result将转发到JSP,JSP页面用Tags取出数据并显示。
10)请求处理完后,将ValueStack对象和Action对象销毁。
2.3使用通配符配置Action
1)*_*:代表名称类似*_*的所有Action。
2)例如:
3)
<action name="*_*_*" class="com.offcn.{1}Action" method="{2}"> <result name="success">/WEB-INF/jsp/{3}.jsp</result> </action>
①{1}Action:表示name属性中第一个“*”星号匹配的字符串。
②method="{2}":表示方法名为name属性中第二个“*”星号匹配的字符串。
③{3}.jsp:表示显示的页面为name属性中第三个“*”星号匹配的字符串。
2.4案例:通配符配置(增、改、查)
step1:新建项目struts03,导入Struts2核心包,配置前端控制器
step2:在WebRoot目录下新建curd.jsp
<a href="Add_add.action">增</a> <a href="Delete_delete.action">刪</a> <a href="Update_update.action">改</a> <a href="Search_search.action">查</a>
step3:在action包中新建AddAction,DeleteAction等三个类,并定义相应方法
package com.hxh.action;
import com.opensymphony.xwork2.ActionSupport;
public class AddAction extends ActionSupport{
public String add(){
System.out.println("增");
return "success";
}
}
其他三个方法类似
step4:新建struts.xml
<action name="*_*" class="com.offcn.action.{1}Action" method="{2}"> <result>/success.jsp</result> </action>
step5:部署,测试,地址栏输入:http://localhost:8080/StrutsFirst/curd.jsp,效果如下:
step6:点击四个连接,则分别执行了各自的方法。
2.5常见的Result组件类型
1)dispatcher(默认):以请求转发方式调用一个JSP,生成响应视图。
2)redirect:以重定向方式调用一个JSP,生成响应视图。
3)redirectAction:以重定向方式调用一个Action。
4)chain:以请求转发方式调用一个Action。
三、拦截器
3.1拦截器的作用
拦截器适合封装一些共通处理,便于重复利用。例如:请求参数给Action属性,日志的记录,权限检查,事务处理等。拦截器是通过配置方式调用,因为使用方法比较灵活,便于维护和扩展。
3.2拦截器的常用方法
1)如果不调用下面的两种方法,那么就不会调用Action,也不会调用后面的Interceptor,拦截器中的return的String觉定了最后的Result(极端情况下)。
2)arg0.invoke();调用Action,也包括Result,拦截器中的return的内容无效。
3)arg0.invokeActionOnly();调用Action,不包括后面的Interceptor和Result,拦截器中的return的String决定了最后的Result。
4)在拦截器中,如何调用底层API:
①可调用ActionInvocation对象的getStack方法获取VS。
②可调用ServletActionContext的静态方法获取Servlet API,如getResponse获取response。
3.3登录检查拦截器
step1:在com.hxh.web.interceptor包下,新建AuthorizationInterceptor拦截器类,该拦截器的作用是:从session中取KEY为user的当前的登录用户,如果取到的是null,说明该用户没有登录,返回到登录页面,否则继续执行剩余的拦截器和Action。
package com.hxh.web.interceptor; import java.util.Map; import com.hxh.entity.User; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; /** * 权限验证检查拦截器 * @author */ public class AuthorizationInterceptor extends AbstractInterceptor { public String intercept(ActionInvocation invocation) throws Exception { //获取session中保持的用户信息 Map<String,Object> session = invocation.getInvocationContext().getSession(); User user = (User)session.get("user"); if (user == null) { //如果用户尚未登录,返回到登录页面 return Action.LOGIN; } else { //如果用户已经登录,继续执行剩余的拦截器和Action return invocation.invoke(); } } }
step2:在struts-main.xml中添加拦截器配置
<interceptors> <!--定义权限验证拦截器 --> <interceptor name="myAuthorization" class="com.offcn.web.interceptor.AuthorizationInterceptor" /> <!--定义拦截器栈 --> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack" /> <interceptor-ref name="myAuthorization" /> </interceptor-stack> <action name="security"> <result>/WEB-INF/page/security.jsp</result> <interceptor-ref name="myStack" /> </action> </interceptors>
Step3:部署,测试,地址栏输入:http://localhost:8080/struts2Second/login.jsp,则会跳转到登录页面;地址栏输入:http://localhost:8080/struts2Second/security,也会跳转到登录页面;只有登录后才可正常访问
四、结合hibernate案例
Step1、创建数据库表
Step2、创建实体类和相应的映射文件。
package com.hxh.entity; public class Book { private int id; private String name; private Integer price; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getPrice() { return price; } public void setPrice(Integer price) { this.price = price; }
} <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.offcn.entity"> <!-- Book类映射book表 --> <class name="Book" table="book1"> <!-- 指定实体类的标识属性,对应数据库表的主键字段 --> <id name="id" column="id" type="java.lang.Integer"> <!-- 指定数据库表主键的生成方式, increment获取最大值并自增 --> <generator class="increment" /> </id> <!-- 将类中的属性与数据库表的字段进行映射 --> <property name="name" type="java.lang.String"> <column name="name" length="20" not-null="false" /> </property> <property name="price" type="java.lang.Integer"> <column name="price" length="20" not-null="false" /> </property>
</class> </hibernate-mapping> |
Step4、配置struts.xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts> <package name="default" extends="struts-default"> <!-- 用于列出所有图书 --> <action name="listbook" class="com.hxh.action.BookAction" method="list"> <result>/listBooks.jsp</result> <interceptor-ref name="basicStack" /> </action> <!-- 用于分类的创建和更新 --> <action name="editbook*" class="com.offcn.action.BookAction"> <param name="id">{1}</param> <result>/editBook.jsp</result> <interceptor-ref name="staticParams" /> <interceptor-ref name="basicStack" /> </action>
<!-- 用于保存新创建的分类和更新后的分类 --> <action name="savebook" class="com.hxh.action.BookAction" method="save"> <result name="input">/editBook.jsp</result> <result type="redirect">listbook.action</result> </action>
<!-- 用于删除分类 --> <action name="deletebook*" class="com.hxh.action.BookAction" method="delete"> <param name="id">{1}</param> <result type="redirect">listbook.action</result> <interceptor-ref name="staticParams" /> </action>
</package>
<package name="uitag" extends="struts-default"> <action name="form" class="com.offcn.action.FormAction"> <result>/WEB-INF/jsp/form.jsp</result> </action> </package> </struts> |
step5、编写dao以及action
package com.hxh.action;
import java.util.List;
import com.hxh.dao.BookDao; import com.hxh.entity.Book; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.Preparable;
public class BookAction extends ActionSupport implements Preparable {
private List books; private Book book; private Integer id;
public void setId(Integer id) { this.id = id; }
public void setId(int id) { this.id = id; }
public List getBooks() { return books; }
public Book getBook() { return book; }
public void setBook(Book book) { this.book = book; }
public String list() throws Exception { books = new BookDao().queryAll(); return SUCCESS; }
public String delete() throws Exception { if(null != id){ Book b = new Book(); b.setId(id); new BookDao().delete(b); } return SUCCESS; }
public String save() throws Exception { if(book.getId() != 0){ new BookDao().update(book); }else{ new BookDao().save(book); } return SUCCESS; } @Override public void prepare() throws Exception { if(null!=id) book = new BookDao().queryById(id.toString()); }
}
|
package com.hxh.dao;
import java.util.Iterator; import java.util.List;
import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration;
import com.hxh.entity.Book;
public class BookDao { private Session session = null; private Transaction tran = null;
public BookDao() { Configuration config = new Configuration().configure(); SessionFactory factory = config.buildSessionFactory(); this.session = factory.openSession(); }
public void save(Book book) { try { tran = this.session.beginTransaction(); this.session.save(book); tran.commit();
} catch (RuntimeException re) { if(null != tran){tran.rollback();} throw re; }finally{ this.session.close(); } }
public void update(Book book) { tran = this.session.beginTransaction(); this.session.update(book); tran.commit(); this.session.close(); }
public void delete(Book book) { tran = this.session.beginTransaction(); this.session.delete(book); tran.commit(); this.session.close(); }
public Book queryById(String id) { Book book = null; String hql = "FROM Book AS b WHERE b.id=?"; Query query = this.session.createQuery(hql); query.setString(0, id); Iterator iter = query.list().iterator(); if (iter.hasNext()) { book = (Book) iter.next(); } return book; }
public List queryAll() { List list = null; String hql = "FROM Book as b order by b.id"; Query query = this.session.createQuery(hql); list = query.list(); this.session.close(); return list; } }
|
五、项目经验
5.1主键用int还是Integer
主键用int、Integer都可以,但在Hibernate框架中,习惯用Integer。而非主键最好用封装类Integer,这样可以允许有空值,否则至少有个0。
5.2 “../”表示的意思
表示向上跳一级目录,看的是浏览器地址!如:localhost:8080/应用名/命名空间/xx.action,用“../”则跳到应用名,而“命名空间/xx.action”整体为一级,共同决定响应后显示的页面。
5.3导入静态页面,样式、JS失效问题
项目结构和发布后的结构不一样的,发布后结构为webapps/应用名/然后是WebRoot中那一推东西,即WEB-INF文件夹啊、images文件夹啊、js文件夹啊等。
所以,样式和js的导入路径为:
<link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" />
<script language="javascript" src="../js/jquery-1.4.1.min.js"></script>
即从当前请求地址跳到应用名,然后找到js文件夹,然后找到jquery。
5.4四种情形下的绝对路径写法
绝对路径:前面一定有个“/”,且从应用名开始写。
以下为4中情形的绝对路径写法:
1)链接:/应用名/...
2)提交:/应用名/..
3)重定向:/应用名/..
4)转发:/应用名后开始写
u 注意事项:
例如在list.jsp页面,添加add页面的连接时,可以只写add.action(相对路径),因为它们是同包的,或者写绝对路径,但不能写成book/add.action,会出现叠加问题.