這是Strtus的開山篇,主要是引入struts框架...爲何要引入struts,引入struts的好處是什麼,以及對Struts2一個簡單的入門....html
既然Servlet可以完成的事,咱們爲啥要用框架呢??java
框架幫咱們封裝了不少經常使用的功能web
因而乎,struts2就應運而生了。apache
在正式講解struts以前,咱們來看一下,以咱們如今的水平,可以怎麼優化它。。安全
以用戶的登錄註冊案例來進行說明服務器
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()); } }
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); } }
@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); } }
@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); } }
<%@ 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>
<%@ 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>
上面的代碼已經通過了測試,是能夠跑起來的。微信
①:跳轉頁面的路徑是寫死的。我在註冊成功了之後,我可以跳轉到首頁上,也能夠跳轉到登錄的界面上。若是我要選擇其中的一個,就必須修改源代碼...app
②:一個功能對應一個Servlet,太麻煩了...寫了LoginServlet,還要寫RegisterServlet....框架
咱們會發現,不管什麼Servlet上最終仍是跳轉到相對應的JSP頁面的...也就是說,第一和第二步驟【封裝數據、調用Service】咱們能夠封裝起來...只要返回uri給Servlet跳轉到JSP頁面就行了。jsp
返回的uri分兩種狀況:
/** * 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; } } }
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); } }
/** * 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; } } }
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配置,當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來對它們封裝
/** * 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); } }
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; } }
/** * 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中,應該讀取配置文件,而後把信息所有封裝到裏邊去...
/** * 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); } }
使用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模塊存在弊端:
本博文主要模擬Struts的開發流程
咱們如今學習的是Struts2,其實Struts1和Struts2在技術上是沒有很大的關聯的。 Struts2其實基於Web Work框架的,只不過它的推廣沒有Struts1好,所以就拿着Struts這個名氣推出了Struts2框架。
所以,學習Struts2的時候,不瞭解Struts1是沒有任何關係的。
在前面,已經說明了爲何要引入Struts框架,其實就是爲了提升開發效率...
Struts2框架預先實現了一些功能:
咱們就直接來說解Struts2的開發步驟是什麼吧....在瞭解它的細節以前,先要把配置環境搭好!
完整的struts中的jar包有80多個,咱們平常開發是不須要那麼多個的。通常咱們導入的jar包有8個:
在web.xml中配置的過濾器,其實就是在爲struts進行初始化工做
值得注意的是:若是該web.xml配置了多個fileter,那麼struts的filter須要在最後面!
<!-- 引入struts核心過濾器 --> <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>
開山篇咱們已經說了,Servlet的業務代碼,咱們都使用Action來代替...Action類通常繼承着ActionSupport
Action類也叫動做類,處理請求的類。
public class HelloAction extends ActionSupport { @Override public String execute() throws Exception { System.out.println("helloworld"); return "success"; } }
至於execute()方法是什麼,咱們先不要去管它,爲啥要返回一個String,咱們也不要去管它....只要記住開發步驟,而且,咱們的Action類是要繼承ActionSupport類的
至於配置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="hello" extends="struts-default"> <action name="hello" class="action.HelloAction" method="execute"> <result name="success">/index.jsp</result> </action> </package> </struts>
看完上面的配置文件,是很是像咱們開山篇寫的struts框架的配置文件的....
在地址欄中直接輸入hello,就跳轉到index.jsp頁面了。而且,execute()中的語句被執行了...
咱們來簡單地瞭解一下Struts的執行流程,而後再慢慢對上面的開發步驟的部分進行講解....
下邊我說的都是struts流程的重點:
Dispatcher dispatcher = init.initDispatcher(config);
,初始化dispatcher下面用GIF圖來看看它的執行過程:
細心的朋友可能會發現,咱們在struts.xml的package節點下,extends了struts-default....那struts-default到底是什麼東西呢?
咱們找到它的源碼:
咱們發現了一大堆的Bean,interceptor,result-type,interceptor-stack...下邊我來說解一下它們是幹嗎用的...
bean指定了struts在運行的時候須要建立的對象類型
interceptor是struts定義的攔截器,一共有32個
result-type是跳轉結果的類型
interceptor-stack是攔截器的棧
還要補充的就是:默認的攔截器棧有18個攔截器....
攔截器和過濾器都是攔截資源的
攔截器只攔截Action請求,是struts的概念...
過濾器攔截web的全部資源,是Servlet的概念...
服務器啓動的時候,其實就是加載了web.xml文件,而後調用init()方法去加載struts.xml和struts-default.xml之類的文件.....
注意:此時的攔截器是尚未被調用的。
在服務器啓動的階段,僅僅是加載了各類的xml文件...那麼當咱們訪問Action的時候,它的執行流程是怎麼的呢?
值得注意的是:每訪問Action一次,它就會建立一個對象...它並非和Servlet同樣只有一個對象...所以它是線程安全的.
這是咱們的struts.xml的內容,相信如今對它也不會太陌生了...
<struts> <package name="hello" extends="struts-default"> <action name="hello" class="action.HelloAction" method="execute"> <result name="success">/index.jsp</result> </action> </package> </struts>
package其實就是包,那包用來幹什麼?包就是用來管理Action
一般來講,咱們都是一個業務模版對應一個package
name是包的名字,值得注意的是,包的名稱是不能重複的。
extends表明的是當前包繼承着哪一個包。在struts中,包必定要繼承着struts-default
在package中還有abstract這個屬性,使用該屬性時:代表當前包被其餘的包繼承...而且,在package下不能有action,不然會出錯!
在package中還有namespace這個屬性---名稱空間....它是做爲路徑的一部分的,默認是"/"
action:配置請求路徑與Action類的映射關係
name是請求路徑的名字
class是處理action類的全名
method是調用的方法名稱
result表明的是Action中業務方法返回的值
name是action處理返回的值
type是跳轉的類型
文本值是跳轉的路徑
前邊已經說了,一個package應該對應一個業務模塊..目的就是把職能細分出來...
struts爲了讓咱們更好地管理xml文件,它還能夠這樣作:在不一樣的模塊中用不一樣的xml文件進行描述...
最後在struts.xml文件中將其引入便可..
<!--struts在運行的時候總會加載這個文件--> <!--總配置文件總引入其餘的文件--> <struts> <include file="privilegeaction/privilege.xml"/> <include file="useraction/hello.xml"/> </struts>
若是文章有錯的地方歡迎指正,你們互相交流。 習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y