Struts2开山篇【引入Struts、自定义MyStruts框架】
前言
这是Strtus的开山篇,主要是引入struts框架…为什么要引入struts,引入struts的好处是什么….
为什么要引入struts?
首先,在讲解struts之前,我们来看看我们以前写的Servlet,下面我就随便截几张图来说明问题了…
Servlet作为MVC的Controller,无非就是三个步骤:
- 得到web层的数据、封装到JavaBean
- 调用Service的逻辑代码
- 跳转到相对应的JSP页面
当我们写Servlet的时候,一般都离不开这三个步骤,也可以说,这是Servlet的“固定写法”
那我们这样写代码,有啥不好的地方呢??再重新看回上图,我们发现几个弊端
- 我们写一个项目需要非常非常多的Servlet,这就造成十分冗余..为了职责分明,我们却不得不这么做…
- 跳转到JSP页面的路径被我们写死了【一旦有别的需求,就需要改源代码】
为了解决上边的弊端,struts就应运而生了
自定义struts
在正式讲解struts之前,我们来看一下,以我们现在的水平,能够怎么优化它。。
以用户的登陆注册案例来进行说明
传统的用户登陆注册
- dao
public class UserDao {
public User login(User user) {
if ("aaa".equals(user.getUsername()) && "123".equals(user.getPsd())) {
System.out.println("登陆成功!");
return user;
} else {
System.out.println("登陆失败!");
return null;
}
}
public void register(User user) {
System.out.println("注册成功!" + user.getUsername());
}
}
- service
public class UserService {
private UserDao userDao = new UserDao();
public User longin(User user) {
return userDao.login(user);
}
public void register(User user) {
userDao.register(user);
}
}
- loginServlet
@javax.servlet.annotation.WebServlet(name = "LoginServlet",urlPatterns = "/LoginServlet")
public class LoginServlet extends javax.servlet.http.HttpServlet {
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
//得到用户带过来的数据,封装到Bean对象中
String username = request.getParameter("username");
String psd = request.getParameter("psd");
User user = new User();
user.setPsd(psd);
user.setUsername(username);
try {
//调用Service方法
UserService userService = new UserService();
userService.longin(user);
//登陆成功跳转到首页
request.getRequestDispatcher("/index.jsp").forward(request, response);
} catch (Exception e) {
e.printStackTrace();
//登陆失败,跳转到相关的提示页面
request.setAttribute("message","登陆失败了!!!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
}
}
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
this.doPost(request, response);
}
}
- registerServlet
@javax.servlet.annotation.WebServlet(name = "RegisterServlet",urlPatterns = "/RegisterServlet")
public class RegisterServlet extends javax.servlet.http.HttpServlet {
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
//得到用户带过来的数据,封装到Bean对象中
String username = request.getParameter("username");
String psd = request.getParameter("psd");
User user = new User();
user.setPsd(psd);
user.setUsername(username);
try {
//调用Service方法
UserService userService = new UserService();
userService.register(user);
//注册成功跳转到登陆界面
request.getRequestDispatcher("/login.jsp").forward(request, response);
//注册成功,我也可以跳转到首页
//request.getRequestDispatcher("/index.jsp").forward(request, response);
} catch (Exception e) {
e.printStackTrace();
//注册失败,跳转到相关的提示页面
request.setAttribute("message","注册失败了!!!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
}
}
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
this.doPost(request, response);
}
}
- login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/LoginServlet" method="post">
用户名:<input type="text " name="username">
密码:<input type="password " name="psd">
<input type="submit" value="登陆">
</form>
</body>
</html>
- register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/RegisterServlet" method="post">
用户名:<input type="text " name="username">
密码:<input type="password " name="psd">
<input type="submit" value="注册">
</form>
</body>
</html>
上面的代码已经经过了测试,是可以跑起来的。
①:跳转页面的路径是写死的。我在注册成功了以后,我可以跳转到首页上,也可以跳转到登陆的界面上。如果我要选择其中的一个,就必须修改源代码…
②:一个功能对应一个Servlet,太麻烦了…写了LoginServlet,还要写RegisterServlet….
新型的用户登陆注册
我们会发现,无论什么Servlet上最终还是跳转到相对应的JSP页面的...也就是说,第一和第二步骤【封装数据、调用Service】我们可以封装起来…只要返回uri给Servlet跳转到JSP页面就好了。
LoginAction
返回的uri分两种情况:
- 如果是转发,那么返回的是RequestDispatcher对象
- 如果是重定向,那么返回的是字符串
/**
* Created by ozc on 2017/4/26.
* <p>
* 一个Action对应一个Servlet,Action负责处理具体的请求
*/
public class LoginAction {
public Object login(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
Object uri ;
//得到用户带过来的数据,封装到Bean对象中
String username = request.getParameter("username");
String psd = request.getParameter("psd");
User user = new User();
user.setPsd(psd);
user.setUsername(username);
try {
//调用Service方法
UserService userService = new UserService();
userService.longin(user);
//登陆成功跳转到首页
request.getSession().setAttribute("user", user);
//跳转到首页的时候需要重定向
//response.sendRedirect(request.getContextPath() + "/index.jsp");
//如果是重定向,那么返回的是字符串
uri = "/index.jsp";
return uri;
} catch (Exception e) {
e.printStackTrace();
//登陆失败,跳转到相关的提示页面
request.setAttribute("message","登陆失败了!!!");
//request.getRequestDispatcher("/message.jsp").forward(request, response);
//如果是转发,那么返回的是RequestDispatcher对象
uri = request.getRequestDispatcher("/message.jsp");
return uri;
}
}
}
- LoginServlet就可以写成这样了:
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
//得到LoginAction对象
LoginAction loginAction = new LoginAction();
Object uri = loginAction.login(request, response);
//是重定向
if (uri instanceof String) {
response.sendRedirect(request.getContextPath() + uri);
} else {
//是转发,强转成是RequestDispatcher对象
((RequestDispatcher) uri).forward(request, response);
}
}
RegisterAction
- RegisterAction
/**
* Created by ozc on 2017/4/26.
*
* 一个Action对应一个Servlet,Action负责处理具体的请求
*/
public class RegisterAction {
public Object register(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
Object uri ;
//得到用户带过来的数据,封装到Bean对象中
String username = request.getParameter("username");
String psd = request.getParameter("psd");
User user = new User();
user.setPsd(psd);
user.setUsername(username);
try {
//调用Service方法
UserService userService = new UserService();
userService.register(user);
//登陆成功跳转到登陆页面
uri = request.getRequestDispatcher("/login.jsp");
return uri;
} catch (Exception e) {
e.printStackTrace();
//注册失败,跳转到相关的提示页面
request.setAttribute("message","注册失败了!!!");
//request.getRequestDispatcher("/message.jsp").forward(request, response);
uri = request.getRequestDispatcher("/message.jsp");
return uri;
}
}
}
- RegisterServlet
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
//得到RegisterAction
RegisterAction registerAction = new RegisterAction();
Object uri = registerAction.register(request, response);
//是重定向
if (uri instanceof String) {
response.sendRedirect(request.getContextPath() + uri);
} else {
//是转发,强转成是RequestDispatcher对象
((RequestDispatcher) uri).forward(request, response);
}
}
思考
到目前为止,我们搞了两个Action类来封装Servlet的逻辑代码,我们再次看回Servlet的代码。
可以很清楚地发现:两个实现不同功能的Servlet仅仅是调用的Action不同….如果是仅仅调用的Action不同【通过反射来调用不同的Action】,那么我们应该想到使用一个Servlet来管理整个项目,也就是说:整个web项目只有一个核心的控制器
问题:
①:我们在之前是直接指明Servlet的映射路径了,现在要ActionServlet处理所有的请求,我们只要定一个规则:只要后缀为.action的,那么都交由核心控制器ActionServlet来控制….
②:现在全部的请求已经交由ActionServlet控制,那怎么知道调用的是哪个Action???我们可以通过请求的uri,比如:http://localhost:8080/login.action
,其中login就代表的是调用LoginAction..也就是说login=LoginAction,我们可以通过properties文件来配置..
③:现在我们已经知道了调用的是哪个Action了,但是Action可能不仅仅只有一个方法,我们还要在调用的时候,指定的方法名是什么.这很简单,一般我们都是职责分工明确的,method=login….并且,调用的Action和具体的方法也是有关系的,不可能是孤立存在的。因此,我们的配置文件是不能使用properties的,需要使用XML
④:在调用方法的时候,是返回一个Object的uri的,uri的类型可能是String、也可以能是RequestDispatcher、并且返回的结果可能有几种情况的【可能跳转到首页,也可能跳转到登陆界面】
⑤:Action调用的方法和返回的uri也是是有关系的!…..不同的Action调用不同的方法,返回的uri也是不同的….
⑥:要跳转到哪个页面上,可以通过标识量来识别….比如:success表示成功执行,如果要重定向那么多加个type类型,如果不重定向就没有type类型..路径使用path来表示..因此,在具体的Action中,就不需要返回具体的uri,只要返回一个标识量即可
画一张图来梳理一下思路:
XML配置
我们可以写出这样的XML配置,当ActionServlet初始化的时候,读取XML配置文件,就知道调用的是什么Action,Action中的什么方法,以及跳转到哪个页面上了。
<?xml version="1.0" encoding="UTF-8" ?>
<mystruts>
<package>
<action name="login" className="zhongfucheng.servlet.LoginServlet" method="login">
<!--是否存在type属性,存在则是重定向,不存在则是转发-->
<!--result的值表示的就是跳转的路径-->
<result name="success" type="redirect">/index.jsp</result>
<result name="fail">/message.jsp</result>
</action>
<action name="register" className="zhongfucheng.servlet.RegisterServlet" method="register">
<!--是否存在type属性,存在则是重定向,不存在则是转发-->
<!--result的值表示的就是跳转的路径-->
<result name="success">/message.jsp</result>
<result name="fail">/message.jsp</result>
</action>
</package>
</mystruts>
为了更好地管理这些信息,我们应该使用JavaBean来对它们封装
- ActionMappingManager——-管理全部的Action
/**
* Created by ozc on 2017/4/26.
*
* 该类管理着全部的Action
*
* 要管理全部的Action,就需要用一个容器来装载这些Action
*
* 选择Map集合是最合适的,可以通过key来得到Action,key就是<action name=><action/>中的name属性
*
*/
public class ActionMappingManager {
private Map<String, ActionMapping> map = new HashMap<>();
//注意:外界都是通过name来得到对应的Action的,并不会获取得到整个Manager
public ActionMapping getActionMapping(String name) {
return map.get(name);
}
}
- ActionMapping—-表示单个的Action
public class ActionMapping {
//所有的results
private Map<String, Results> results;
//关键字name
private String name;
//要调用的Action路径
private String className;
//Action中的方法
private String method;
public Map<String, Results> getResults() {
return results;
}
public void setResults(Map<String, Results> results) {
this.results = results;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
}
- Results—表示的是结果视图
/**
* Created by ozc on 2017/4/26.
*
* 该类表示的是结果视图
*
*
*
*/
public class Results {
//方法返回的标识
private String name;
//要跳转的方式
private String type;
//要跳转的页面
private String page;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
}
ActionMappingManager读取配置文件
在ActionMappingManager中,应该读取配置文件,然后把信息全部封装到里边去…
/**
* Created by ozc on 2017/4/26.
*
* 该类管理着全部的Action
*
* 要管理全部的Action,就需要用一个容器来装载这些Action
*
* 选择Map集合是最合适的,可以通过key来得到Action,key就是<action name=><action/>中的name属性
*
*/
public class ActionMappingManager {
private Map<String, ActionMapping> allAction ;
public ActionMappingManager() {
this.allAction = new HashMap<>();
//读取配置文件信息
init();
}
public void init() {
/********通过DOM4J读取配置文件信息*********/
try {
//得到解析器
SAXReader saxReader = new SAXReader();
//读取在类目录下的mystruts.xml文件
InputStream stream = ActionMappingManager.class.getClassLoader().getResourceAsStream("mystruts.xml");
//得到代表XML文件的Document对象
Document document = saxReader.read(stream);
//通过XPATH直接得到所有的Action节点
List list = document.selectNodes("//action");
//得到每个Action节点
for (int i = 0; i < list.size(); i++) {
Element action = (Element) list.get(i);
//把得到每个Action的节点信息封装到ActionMapping中
ActionMapping actionMapping = new ActionMapping();
String name = action.attributeValue("name");
String method = action.attributeValue("method");
String className = action.attributeValue("className");
actionMapping.setName(name);
actionMapping.setMethod(method);
actionMapping.setClassName(className);
//得到action节点下的所有result节点
List results = action.elements("result");
//得到每一个result节点
for (int j = 0; j < results.size(); j++) {
Element result = (Element) results.get(j);
//把得到每个result节点的信息封装到Results中
Results results1 = new Results();
//得到节点的信息
String name1 = result.attributeValue("name");
String type = result.attributeValue("type");
String page = result.getText();
results1.setName(name1);
results1.setType(type);
results1.setPage(page);
//把result节点添加到ActionMapping的集合中
actionMapping.getResults().put(name1, results1);
}
//最后把得到每个ActionMapping的信息添加到ActionMappingManager中
allAction.put(name, actionMapping);
}
} catch (DocumentException e) {
new RuntimeException("初始化的时候出错了!“" + e);
}
}
//注意:外界都是通过name来得到对应的Action的,并不会获取得到整个Manager
public ActionMapping getActionMapping(String name) {
return allAction.get(name);
}
}
ActionServlet
使用init()方法只加载创建一个ActionManagerMapping对象,并设置在Web容器启动了该Servlet就启动
/**
* Created by ozc on 2017/4/26.
*
*
* Web容器一启动的时候,该类就应该加载了,在web.xml文件中配置onloadStart
*/
public class ActionServlet extends HttpServlet {
//该对象封装了所有的XML信息
ActionMappingManager actionMappingManager ;
@Override
public void init() throws ServletException {
//让ActionMappingManager对象只有一个!
actionMappingManager = new ActionMappingManager();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//得到用户的uri
String uri = request.getRequestURI();
//截取uri的关键部分-----截完应该是login
uri = uri.substring(uri.lastIndexOf("/") + 1, uri.lastIndexOf("."));
//通过uri得到配置文件中的action信息
ActionMapping actionMapping = actionMappingManager.getActionMapping(uri);
//得到action的类名,方法名
String className = actionMapping.getClassName();
String method = actionMapping.getMethod();
//通过反射创建出Action的对象,调用对应的方法
Class t = Class.forName(className);
Object o = t.newInstance();
//注意:这里的参数是接口的class,不是单纯的request的class,单纯的class是实现类
Method m = t.getMethod(method, HttpServletRequest.class, HttpServletResponse.class);
//调用方法,得到标记
String returnFlag = (String) m.invoke(o, request, response);
//通过标记得到result的具体信息
Results result = actionMapping.getResults().get(returnFlag);
String type = result.getType();
String page = result.getPage();
//判断是重定向还是转发,为空就是转发,反则是重定向
if (type == null) {
response.sendRedirect(page);
} else {
request.getRequestDispatcher(request.getContextPath() + page).forward(request, response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
具体的Action的方法只要返回一个标识量即可,我们通过标识量来得到具体的跳转页面url和跳转的方法的。。。
效果:
总结:
由于传统web的Controller模块存在弊端:
- 需要创建非常多的Servlet
- 跳转的页面写死了。改变需求的时候需要更改源代码
因此struts就应运而生了,本博文主要模拟Struts的开发流程
使用一个ActionServlet核心控制器来管理全部的Web请求,写XML配置文件,读取配置文件。通过uri来判断要调用具体的Action,Action中的方法。得到返回值,再根据XML文件的配置信息来确定跳转方法、跳转的url
Struts2开山篇【引入Struts、自定义MyStruts框架】的更多相关文章
- Struts2第二篇【开发步骤、执行流程、struts.xml讲解、defalut-struts讲解】
前言 我们现在学习的是Struts2,其实Struts1和Struts2在技术上是没有很大的关联的.Struts2其实基于Web Work框架的,只不过它的推广没有Struts1好,因此就拿着Stru ...
- Struts2 自定义MVC框架
一.Model1与Model2: Model1:就是一种纯jsp开发技术,将业务逻辑代码和视图渲染代码杂糅在一起. Model2:Model2是在Model1的基础上,将业务逻辑的代码分离开来,单独形 ...
- 基于Struts自定义MVC-1
自定义MVC 数据库:Oracle表:User(id,uname,upwd)自定义Struts框架一.定义Action接口 1 import javax.servlet.http.*; ...
- 通过Struts了解MVC框架,兼说如何在面试中利用Struts证明自己
虽然目前Struts MVC框架不怎么用了,但它确是个能帮助大家很好地入门Web MVC框架,而且,一些历史项目可能还用Struts,反正技多不压身,大家如果能在面试中通过项目证明自己Struts这块 ...
- 在Eclipse中使用Struts和Hibernate框架搭建Maven Web项目
前言 学习使用Java还是2012年的事情,刚开始学习的Java的时候,使用的是MyEclipse工具和SSH框架.初学者适合使用MyEclipse,因为他将struts.Spring和Hiberna ...
- WCF开山篇__图片传输
WCF开山篇__图片传输 一. 简介 Windows Communication Foundation(WCF)是由微软发展的一组数据通信的应用程序接口,可以翻译为Windows通讯接口,它是. ...
- (转)hibernate-5.0.7+struts-2.3.24+spring-4.2.4三大框架整合
http://blog.csdn.net/yerenyuan_pku/article/details/70040220 SSH框架整合思想 三大框架应用在JavaEE三层结构,每一层都用到了不同的框架 ...
- iOS开发UI篇—CAlayer(自定义layer)
iOS开发UI篇—CAlayer(自定义layer) 一.第一种方式 1.简单说明 以前想要在view中画东西,需要自定义view,创建一个类与之关联,让这个类继承自UIView,然后重写它的Draw ...
- 自定义MVC框架
我们在学习自定义MVC框架的时候常常会听到Model1 ,Model2和MVC.那么什么是Model1 什么是Model2什么又是MVC呢? 什么是Model1? Model1就是一种纯jsp开发技术 ...
随机推荐
- vue小组件练习之文字居中对齐组件
时隔多月,继续学习Vue,这次是一个组件的制作过程 先让我们来看一下组件的预期效果 上图为公司自营的一个微信商城的某一部分截图,可以看到红框内部分的文字多行与单行是居中对齐的,我们现在要做的就是使用V ...
- Python 正确重载运算符
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica } 有些事情让我不安,比如运算符重载.我决定不支持运算符重载,这完全是个人 ...
- ubuntu下发布asp.net core并用nginx代理之旅
asp.net core 1.0.1发布已有些日子了,怀着好奇的心情体验了把ubuntu下的asp.net core 系统运行环境:ubuntu 16.0.4 for developer 首先搭建.n ...
- ios逆向过程中lldb调试技巧
在ios逆向过程中,善于运用lldb,会给逆向带来很大的方便 一般的命令: 1.image list -o -f 看看各个模块在内存中的基址 2.register read r0 读取寄存器r0的 ...
- 7.31.1 java内存的主要划分
java内存的主要划分:class文件加载到方法区,方法区还存放静态变量和常量,方法区开始执行程序,当调用方法时,会将该方法的栈帧压到栈区,该栈帧中存放局部变量,当方法中new出一个对象,则会在堆中开 ...
- 一起来学Go --- (go的枚举以及数据类型)
枚举 枚举指一系列的相关的常量,比如下面关于一个星期的中每天的定义,通过上篇博文,我们可以用在const后跟一对圆括号的方式定义一组常量,这种定义法在go语言中通常用于定义枚举值.go语言并不支持众多 ...
- Spring Data Jpa(Hibernate) OneToMany
这个其实非常简单.假设有topic 和 subscriber两个实体类,不考虑关联关系,则连个类的代码如下: /** * Created by csonezp on 2017/8/31. */ @En ...
- linux(十三)之磁盘分区、创建文件系统、挂载
前面学习了linux的用户管理 ,感觉是不是多lnux的多用户多任务的系统感觉十分了解了,但是其实并不然的.你还需要了解更多.接下来给大家分享的是 在vmware中添加硬盘创建分区,然后挂载到指定目录 ...
- C++语法细节笔记
1.数据类型转换 当赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数. 把负数转换成无符号数类似于直接给无符号数赋一个负值,结果等于这个负数加上无符号数后的模. ...
- MongoDB-python的API手记
-------------------python调用MongoDB------------------- 1.官方文档:http://api.mongodb.org/python/current/t ...