Struts2开山篇【引入Struts、自定义MyStruts框架】

bt365体育投注3 2025-09-18 21:33:11 阅读: 7317

前言

这是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" %>

$Title$

用户名:

密码:

register.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

$Title$

用户名:

密码:

上面的代码已经经过了测试,是可以跑起来的。

①:跳转页面的路径是写死的。我在注册成功了以后,我可以跳转到首页上,也可以跳转到登陆的界面上。如果我要选择其中的一个,就必须修改源代码…

②:一个功能对应一个Servlet,太麻烦了…写了LoginServlet,还要写RegisterServlet….

新型的用户登陆注册

我们会发现,无论什么Servlet上最终还是跳转到相对应的JSP页面的...也就是说,第一和第二步骤【封装数据、调用Service】我们可以封装起来…只要返回uri给Servlet跳转到JSP页面就好了。

LoginAction

返回的uri分两种情况:

如果是转发,那么返回的是RequestDispatcher对象

如果是重定向,那么返回的是字符串

/**

* Created by ozc on 2017/4/26.

*

* 一个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中的什么方法,以及跳转到哪个页面上了。

/index.jsp

/message.jsp

/message.jsp

/message.jsp

为了更好地管理这些信息,我们应该使用JavaBean来对它们封装

ActionMappingManager——-管理全部的Action

/**

* Created by ozc on 2017/4/26.

*

* 该类管理着全部的Action

*

* 要管理全部的Action,就需要用一个容器来装载这些Action

*

* 选择Map集合是最合适的,可以通过key来得到Action,key就是中的name属性

*

*/

public class ActionMappingManager {

private Map map = new HashMap<>();

//注意:外界都是通过name来得到对应的Action的,并不会获取得到整个Manager

public ActionMapping getActionMapping(String name) {

return map.get(name);

}

}

ActionMapping—-表示单个的Action

public class ActionMapping {

//所有的results

private Map results;

//关键字name

private String name;

//要调用的Action路径

private String className;

//Action中的方法

private String method;

public Map getResults() {

return results;

}

public void setResults(Map 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就是中的name属性

*

*/

public class ActionMappingManager {

private Map 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