Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产一级免费片,成人永久免费视频,日韩免费高清视频网站

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          想到達(dá)人生巔峰嗎?大數(shù)據(jù)教你這樣做

          用人工智能挖掘與大數(shù)據(jù),科學(xué)家們總結(jié)了抵達(dá)創(chuàng)作“爆發(fā)期”的重要規(guī)律。

          模型中反映梵高(Van Gogh)后印象主義藝術(shù)風(fēng)格特征的像素圖

          在開發(fā)著名的“滴畫法”之前,抽象藝術(shù)家杰克遜·波洛克曾涉足素描、版畫以及超現(xiàn)實主義繪畫。美國西北大學(xué)凱洛格管理學(xué)院的一項新研究發(fā)現(xiàn),“滴畫法”的運用使波洛克進(jìn)入了一段“爆發(fā)期”(即一系列具有高度影響力的作品連續(xù)緊密地產(chǎn)出),在此期間創(chuàng)作的作品令他至今仍家喻戶曉。利用人工智能挖掘與大數(shù)據(jù),研究人員發(fā)現(xiàn),這種模式并非特例,而是一條普遍規(guī)律——爆發(fā)期直接來自多年的探索(對于不同風(fēng)格或主題的研究),緊接著是多年的深耕(專注于某一狹窄領(lǐng)域進(jìn)行鉆研和積累)。這項研究于9月13日發(fā)表在《自然通訊》雜志上。

          “單獨的探索或深耕都與爆發(fā)期沒有關(guān)聯(lián),這兩者必須遵循一定的順序。盡管探索具有一定風(fēng)險,可能不會帶來任何好處,卻能增加偶然發(fā)現(xiàn)偉大想法的可能性;相比之下,深耕則是一種比較保守的策略。有趣的是,‘先探索后深耕’似乎和爆發(fā)期的出現(xiàn)具有高度相關(guān)性。”研究的領(lǐng)導(dǎo)者、西北大學(xué)工業(yè)工程與管理科學(xué)教授王大順(音譯)說。

          2018年,王教授和同事在《自然》雜志上發(fā)表了一篇論文,描述了藝術(shù)、文化和科學(xué)職業(yè)生涯中的爆發(fā)期現(xiàn)象,當(dāng)時,他很好奇到底是什么驅(qū)動了爆發(fā)期。在參觀梵高博物館時,王教授發(fā)現(xiàn)了線索。1888~1890年是梵高的藝術(shù)突破時期,期間他創(chuàng)作了《星空》、《向日葵》等最為著名的作品。在此之前,他的作品更為寫實,還不屬于印象派。“如果你看他在1888年之前的作品,你會發(fā)現(xiàn)這些作品與他的巔峰時期作品有很大不同。”王教授說。

          在本次的研究中,王教授團(tuán)隊基于深度學(xué)習(xí)和網(wǎng)絡(luò)科學(xué)開發(fā)了一種算法,然后將其用于大數(shù)據(jù)探索,追蹤藝術(shù)家、電影導(dǎo)演和科學(xué)家的職業(yè)產(chǎn)出。對于藝術(shù)家,團(tuán)隊使用了圖像識別算法,對80萬幅視覺藝術(shù)圖像進(jìn)行數(shù)據(jù)挖掘。這些圖像涵蓋2128名藝術(shù)家,其中便包括波洛克和梵高。此外,研究人員收集的數(shù)據(jù)還覆蓋4337位導(dǎo)演和20040位科學(xué)家。

          根據(jù)拍賣價格、電影評級和學(xué)術(shù)論文的引用量,研究人員量化了每位創(chuàng)作者職業(yè)生涯中的巔峰作品。他們發(fā)現(xiàn),當(dāng)進(jìn)行了一段時間探索而沒有深耕時,出現(xiàn)爆發(fā)期的概率就會降低。同樣,缺乏前期的探索也不能保證爆發(fā)期的出現(xiàn)。只有當(dāng)探索后緊跟著深耕時,爆發(fā)期出現(xiàn)的概率才會持續(xù)而顯著地增加。爆發(fā)期持續(xù)的平均時間大約是有五年,在此之后,個體將會恢復(fù)到“正常”狀態(tài),不再展現(xiàn)出特有規(guī)律。這一規(guī)律適用于不同的創(chuàng)作領(lǐng)域。

          論文合著者吉莉安·喬恩說:“這些發(fā)現(xiàn)讓我們認(rèn)識到,要想獲得足夠的影響力,就必須參與不同類型的活動,比如探索新領(lǐng)域或利用現(xiàn)有的知識進(jìn)行深耕研發(fā),而且要有一定的先后順序。”

          編譯:攀汗 審稿:西莫 責(zé)編:陳之涵

          期刊來源:《自然通訊》

          期刊編號:2041-1723

          原文鏈接:https://phys.org/news/2021-09-secret-van-gogh-success.html

          中文內(nèi)容僅供參考,一切內(nèi)容以英文原版為準(zhǔn)。轉(zhuǎn)載請注明來源。

          求轉(zhuǎn)發(fā)(forward):發(fā)送一次請求,將表單數(shù)據(jù)或封裝到url中的數(shù)據(jù)一并轉(zhuǎn)發(fā)到新頁面。

          方法:request.getRequestDispatcher(URL地址).forward(request, response)

          重定向(redirect):發(fā)送兩次請求,一次請求會收到302狀態(tài)碼,第二次請求收到新地址。

          方法:response.sendRedirect(URL地址)

          轉(zhuǎn)發(fā)與重定向過程圖

          轉(zhuǎn)發(fā)過程:客戶瀏覽器發(fā)送http請求----》web服務(wù)器接收此請求,---》調(diào)用內(nèi)部的一個方法在容器內(nèi)部完成請求處理和轉(zhuǎn)發(fā)動作----》將目的資源展示給客戶。轉(zhuǎn)發(fā)的路徑依然在當(dāng)前的web容器中,中間的數(shù)據(jù)傳輸靠 request共享。轉(zhuǎn)發(fā)行為瀏覽器只做了一次訪問請求。

          重定向過程:客戶瀏覽器發(fā)送http請求----》web服務(wù)器接收后發(fā)送302狀態(tài)碼響應(yīng)及對應(yīng)新的location給客戶端瀏覽器----》客戶端瀏覽器發(fā)現(xiàn)是302的狀態(tài)碼之后,自動再發(fā)送一個新的http請求,請求url是新的location地址----》服務(wù)器根據(jù)此請求尋找資源并發(fā)送給客戶。在這里location可以重定向到任意的URL,既然是瀏覽器重新發(fā)送了請求,則沒有什么request傳遞的概念了。地址欄的地址是需要改變的,重定向行為瀏覽器至少做了兩次訪問請求。

          兩者的區(qū)別:

          • 1)從地址欄顯示看:

          forward是服務(wù)器請求資源,服務(wù)器直接訪問目標(biāo)中的URI獲取響應(yīng),將響應(yīng)中的內(nèi)容發(fā)送給瀏覽器,瀏覽器不知道內(nèi)容來自于哪里,所以地址欄不變。

          redirect服務(wù)器根據(jù)邏輯,發(fā)送一個狀態(tài)碼302,告訴瀏覽器去請求地址(url),url可以是其他應(yīng)用。所以地址欄是要改變的。

          • 2)從數(shù)據(jù)共享來說:

          forward轉(zhuǎn)發(fā)頁面和轉(zhuǎn)發(fā)到的頁面可以共享request中的內(nèi)容。

          redirect不能共享。

          • 3)從運用應(yīng)用方面:

          forward 用于登錄注冊頁面

          redirect 用于注銷登錄返回主頁面或跳轉(zhuǎn)其他網(wǎng)站,不再使用response輸出數(shù)據(jù),否則會異常。

          • 4)從效率看

          forward 效率高

          redirect 效率低

          原理:轉(zhuǎn)發(fā)是服務(wù)器行為,重定向是客戶端行為。

          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          	//response.setCharacterEncoding("utf-8");
          	response.setContentType("text/hrml;charset=utf-8");
          	String username= request.getParameter("username");
          	String passwd= request.getParameter("passwd");
          	if("admin".equals(username)&& "123".equals(passwd)) {
          		/*
          		 * System.out.println("登陸成功:"); //response.getWriter().write("登錄成功");
          		 * response.setStatus(302); response.setHeader("Location","login_success.html");
          		 */
          		//重定向
          		//response.sendRedirect("login_success.html");
          		//請求轉(zhuǎn)發(fā)
          		request.getRequestDispatcher("login_success.html").forward(request, response);
          	}else {
          		response.getWriter().write("登錄失敗");
          	}
          }
          
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          	// TODO Auto-generated method stub
          	doGet(request, response);
          }

          路徑問題(轉(zhuǎn)發(fā)和重定向的URLString前有加 / 為絕對路徑 反之為相對路徑)

          • 絕對路徑

          1、重定向的 / 表示:http://服務(wù)器ip:端口/

          //生成的地址:web服務(wù)器本身地址+參數(shù)生成完整的URL 
             //即:http://localhost:8080/Manager/index.html
          response.sendRedirect("/Manager/index.html")

          2、請求轉(zhuǎn)發(fā)的 / 表示:http://服務(wù)器ip:端口/項目名

          //生成的地址:http://localhost:8080/項目名/index.html
          request.getRequestDispatcher("/index.html").forward(request, response);
          • 重定向是瀏覽器發(fā)來的,只知道發(fā)到某個服務(wù)器,但是不知道發(fā)到服務(wù)器的哪個project,所以需要自己用代碼聲明;
          • 請求轉(zhuǎn)發(fā)是服務(wù)器某個project內(nèi)部的轉(zhuǎn)發(fā),轉(zhuǎn)來轉(zhuǎn)去都是在某個project內(nèi)部,所以不需要手動聲明項目名.
          • 相對路徑

          假設(shè)通過表單請求指定的Url資源 action=“LoginServlet”

          則表單生成的請求地址為:http://localhost:8080/項目名/LoginServlet

          重定向:response.sendRedirect(“Manager/index.html”)
          // 生成相對路徑:http://localhost:8080/項目名/Manager/index.html
          請求轉(zhuǎn)發(fā):相對路徑情況下生成的完整URL與重定向方法相同。

          【注】重定向其實 是兩次request。第一次,客戶端request A,服務(wù)器響應(yīng),并response回來,告訴瀏覽器,你應(yīng)該去B,此時,瀏覽器再次發(fā)送request,請求 B的資源。重定向可以訪問當(dāng)前web之外的資源,在重定向的過程中,傳輸?shù)男畔G失。

          系列文章旨在記錄和總結(jié)自己在Java Web開發(fā)之路上的知識點、經(jīng)驗、問題和思考,希望能幫助更多(Java)碼農(nóng)和想成為(Java)碼農(nóng)的人。


          目錄

          1. 介紹
          2. 四大架構(gòu)(技術(shù)、業(yè)務(wù)、應(yīng)用、數(shù)據(jù))
          3. 思路
          4. 展示層 - 注冊頁面 register.html
          5. User實體類
          6. DAO層 - UserMapper接口和元數(shù)據(jù)
          7. 服務(wù)層 - UserService
          8. 控制器層 - 添加Handler
          9. 總結(jié)

          介紹

          到現(xiàn)在為止,我們的租房網(wǎng)應(yīng)用只是實現(xiàn)了簡單的用戶登錄(實際上僅有前端頁面,后臺還沒有實現(xiàn)登錄驗證的業(yè)務(wù)邏輯)、查看自己感興趣的房源(也是僅有一個接口)、查看某個房源的詳情、編輯某個房源的信息等功能。

          本篇文章將為我們的租房網(wǎng)應(yīng)用實現(xiàn)一個簡單的用戶注冊功能。

          四大架構(gòu)(技術(shù)、業(yè)務(wù)、應(yīng)用、數(shù)據(jù))

          前面的工作只是一直不斷的在使用新技術(shù)改造我們的項目,從最開始的Servlet,經(jīng)過使用JSP和JSTL、Spring MVC和Spring IoC、關(guān)系數(shù)據(jù)庫H2Database和JDBC、Maven、Spring JDBC,到目前采用ORM框架MyBatis、連接池框架Druid等技術(shù),我們的項目在開發(fā)效率、可維護(hù)性、性能等方面不敢說達(dá)到完美,但也算很不錯了。

          所以,既然我們的技術(shù)架構(gòu)搭建的也還夠用了,現(xiàn)在暫時讓我們的重心轉(zhuǎn)移到業(yè)務(wù)架構(gòu)上來。當(dāng)然,還有應(yīng)用架構(gòu)數(shù)據(jù)架構(gòu)

          應(yīng)用架構(gòu)我們就暫時采取獨立的單塊應(yīng)用架構(gòu)吧,就是說把所有業(yè)務(wù)功能都放在一個應(yīng)用中。目前很流行微服務(wù)架構(gòu),但那適合于業(yè)務(wù)功能很復(fù)雜,需要拆分為成百上千個應(yīng)用,數(shù)據(jù)庫也動輒上百個庫,上萬張表,我們的租房網(wǎng)應(yīng)用現(xiàn)在還早著呢,全部功能放在一起又快又簡單。

          數(shù)據(jù)架構(gòu)上目前也僅僅使用了房源數(shù)據(jù),而房源數(shù)據(jù)里面也只是模擬了若干個字符串類型的數(shù)據(jù),并沒有涉及到文檔、圖片、視頻、音頻等其他類型的數(shù)據(jù),我們就先用關(guān)系數(shù)據(jù)庫吧。

          業(yè)務(wù)架構(gòu)我們也不作深入分析和設(shè)計,我們就怎么簡單怎么來吧,現(xiàn)在缺用戶注冊功能,那我們就實現(xiàn)一個用戶注冊功能,還是趕緊把租房網(wǎng)應(yīng)用改造成至少像模像樣要緊。

          思路

          首先從租房網(wǎng)平臺的最終用戶的角度來看,我們應(yīng)該有一個注冊頁面。

          這個注冊頁面跟登錄頁面類似,不涉及任何其他業(yè)務(wù)的數(shù)據(jù),因此可以采用靜態(tài)頁面的方式來實現(xiàn)。

          有了這個注冊頁面,我們就可以直接打開這個頁面進(jìn)行注冊。當(dāng)然,也可以從其他頁面比如登錄頁面鏈接到注冊頁面。

          用戶填寫注冊信息完畢之后,需要將它們提交到我們的租房網(wǎng)應(yīng)用的后臺。首先到達(dá)的是我們的控制器。因此,我們需要為控制器設(shè)計一個Handler,采用POST方法映射到 register.action 。

          注冊信息至少應(yīng)該包含用戶名和密碼,當(dāng)然實際應(yīng)用中還應(yīng)該包含更多用戶信息,所以,我們應(yīng)該設(shè)計一個用戶實體類,類名就叫 User 吧。

          根據(jù)分層的思維,控制器的Handler需要調(diào)用一個服務(wù)來實現(xiàn)用戶注冊的業(yè)務(wù)邏輯,我們就設(shè)計一個 UserService 吧,它專門用來處理用戶相關(guān)的業(yè)務(wù),比如注冊、登錄等等。

          現(xiàn)在我們已經(jīng)使用了MyBatis作為DAO層,所以還需要設(shè)計一個 UserMapper 。

          最后,我們需要把用戶數(shù)據(jù)保存在數(shù)據(jù)庫中,所以要建立一個 user 表。

          展示層 - 注冊頁面 register.html

          注冊頁面的內(nèi)容很簡單,主要是使用表單元素<form>(HTML的基礎(chǔ)知識大家可以參考這篇文章):

          <!DOCTYPE html>
          <html>
          <head>
          <meta charset="UTF-8">
          <title>租房網(wǎng) - 注冊</title>
          </head>
          <body>
          	<form action="register.action" method="post">
          		<h2>用戶注冊</h2>
          		<label for="user_name">請輸入用戶名</label><input type="text" id="user_name" name="userName" />
          		<label for="password">請輸入密碼</label><input type="password" id="password" name="password" />
          		<label for="password_confirmed">請再次輸入密碼</label><input type="password" id="password_confirmed" name="passwordConfirmed" />
          		<input type="submit" value="注冊?" />
          	</form>
          	<p><a href="login.html">已經(jīng)注冊,直接登錄!</a></p>
          </body>
          </html>

          這里重點關(guān)注的就是:

          • 表單元素的action屬性和method屬性;
          • <input>元素的name屬性;

          它們的值都需要與后臺代碼一致。


          為了方便用戶使用注冊功能,一般的Web應(yīng)用都會在登錄頁面設(shè)計一個鏈接跳轉(zhuǎn)到注冊頁面,于是我們的登錄頁面 login.html 變?yōu)椋?/p>

          <!DOCTYPE html>
          <html>
          <head>
          <meta charset="UTF-8">
          <title>租房網(wǎng) - 登錄?</title>
          </head>
          <body>
          	<form action="login.action" method="post">
          		<label for="user_name">用戶名</label><input type="text" id="user_name" name="userName" />
          		<label for="password">密碼</label><input type="password" id="password" name="password" />
          		<input type="submit" value="登錄?" />
          	</form>
          	<p><a href="register.html">還沒有注冊?</a></p>
          </body>
          </html>

          主要是在表單元素<form>之后添加了一個<a>元素。

          現(xiàn)在我們可以發(fā)布一下應(yīng)用并啟動Tomcat,驗證一下我們的頁面是否有錯誤,除了最后提交注冊請求時會出現(xiàn)404的錯誤之外,顯示上應(yīng)該沒什么問題,登錄頁面變成這樣:


          注冊頁面是這樣的:


          看到上面的用戶注冊頁面,我們很容易想到這里還有一個要考慮的問題,就是用戶一旦點擊注冊按鈕提交了注冊請求之后,如何知道自己是否注冊成功呢?如果不成功,那是因為什么導(dǎo)致不成功呢?即我們的系統(tǒng)應(yīng)該給用戶返回何種響應(yīng)呢?

          我這里的設(shè)計是提供一個注冊結(jié)果的響應(yīng)頁面,它顯然是動態(tài)的,它要么提示注冊成功,要么提示導(dǎo)致注冊失敗的原因。所以,我設(shè)計了兩個頁面,一個是靜態(tài)頁面,一個是JSP頁面。

          register-success.html:

          <!DOCTYPE html>
          <html>
          <head>
          <meta charset="UTF-8">
          <title>租房網(wǎng) - 注冊成功!</title>
          </head>
          <body>
          	<h1>注冊成功!請<a href="login.html">登錄</a>!</h1>
          </body>
          </html>

          register-failure.jsp:

          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
          <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
          <!DOCTYPE html>
          <html>
          <head>
          <meta charset="UTF-8">
          <title>租房網(wǎng) - 注冊失敗!</title>
          </head>
          <body>
          <h2>注冊失敗!請重新<a href="register.html">注冊</a>!</h2>
          <h3>失敗原因:${errorMessage}</h3>
          </body>
          </html>

          注意,這里使用EL表達(dá)式來訪問數(shù)據(jù),所以轉(zhuǎn)發(fā)給此頁面的時候必須附加上數(shù)據(jù) errorMessage

          User實體類

          雖然我們的思路中是按照前端頁面、控制器層、服務(wù)層、數(shù)據(jù)訪問層(DAO層)來分析的,但是由于我們的控制器層依賴于服務(wù)層,而服務(wù)層又依賴于數(shù)據(jù)訪問層,所以開發(fā)的時候我們可以自下而上,這樣的話代碼層面不會出現(xiàn)錯誤提示。

          我們先來考慮User實體類,前面已經(jīng)提到過用戶提交的注冊信息至少包含用戶名和密碼,所以User實體類也必須有這兩項。

          難道這樣就夠了嗎?我們再以用戶的角度思考一下,假如用戶有一天突然覺得自己的用戶名不好,希望修改為另外一個用戶名怎么辦?是不是需要將關(guān)聯(lián)到該用戶名的其他記錄都需要修改為新的用戶名?這種方案實際上也可以,不過就會產(chǎn)生牽一發(fā)而動全身的不良效果。

          所以,為了靈活考慮,我們應(yīng)該為每個用戶賦予一個全局唯一的且不可變的用戶ID,然后跟該用戶有關(guān)的記錄都關(guān)聯(lián)到這個用戶ID。從這里我們可以看出,這個用戶ID是由我們的系統(tǒng)自動生成的(那該如何生成呢?下面介紹),對用戶是不可見的。

          然后,需要考慮用戶ID的數(shù)據(jù)類型(實際上它與如何生成也有一定關(guān)系),我們這里仍然選擇字符串。

          綜合起來我們的User實體類有如下特點:

          • 用戶ID由系統(tǒng)自動生成,唯一且不可變;
          • 用戶名也唯一,但是可以被用戶修改;
          • 用戶密碼的安全性問題,暫且不考慮。
          package houserenter.entity;
          
          public class User {
          
          	private String id;
          	private String name;
          	private String password;
          	public String getId() {
          		return id;
          	}
          	public void setId(String id) {
          		this.id = id;
          	}
          	public String getName() {
          		return name;
          	}
          	public void setName(String name) {
          		this.name = name;
          	}
          	public String getPassword() {
          		return password;
          	}
          	public void setPassword(String password) {
          		this.password = password;
          	}
          	@Override
          	public String toString() {
          		return "User [name=" + name + ", password=" + password + "]";
          	}
          	
          }
          

          實體類中的getter方法、setter方法、toString()方法可以使用IDE提供的功能快速添加。

          DAO層 - UserMapper接口和元數(shù)據(jù)

          我們很容易根據(jù)業(yè)務(wù)功能設(shè)計出 UserMapper 包含哪些接口。

          因為用戶注冊功能相當(dāng)于是新增一個用戶,所以需要一個插入用戶的接口。

          又因為用戶注冊時需要判斷用戶名是否已經(jīng)被注冊,所以需要一個根據(jù)用戶名查找用戶的接口。

          不過,由于我們采用的是H2Database的嵌入式模式,所以必須由應(yīng)用自己來創(chuàng)建用戶表(實際生產(chǎn)環(huán)境中一般是由DBA來建表),所以還需要一個創(chuàng)建用戶表的接口。但是用戶表不能每次都重復(fù)創(chuàng)建,所以使用了 if not exists 語法。

          UserMapper.java:

          package houserenter.mapper;
          
          import houserenter.entity.User;
          
          public interface UserMapper {
          
          	int cteateTable();
          	
          	int insert(User user);
          	
          	User selectByName(String name);
          }
          

          UserMapper.xml:

          <?xml version="1.0" encoding="UTF-8" ?>
          <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          <mapper namespace="houserenter.mapper.UserMapper">
          
          	<update id="cteateTable">
          		create table if not exists user(id varchar(36) primary key, name varchar(32), password varchar(16))
          	</update>
          
          	<insert id="insert" parameterType="houserenter.entity.User">
          		insert into user(id, name, password) values(#{id}, #{name}, #{password})
          	</insert>
          	
          	<select id="selectByName" parameterType="java.lang.String" resultType="houserenter.entity.User">
          		select id,name,password from user where name = #{name}
          	</select>
          </mapper>

          服務(wù)層 - UserService

          服務(wù)層實現(xiàn)業(yè)務(wù)邏輯,所以就目前來說 UserService 最主要的一個方法是處理用戶的注冊請求。

          用戶注冊的業(yè)務(wù)邏輯我這里設(shè)計的比較簡單,僅僅包括判斷兩次密碼是否一致和用戶名是否已經(jīng)被注冊,實際上還有用戶名和密碼的長度、字符要求等限制:



          另外,由于我們采用的是H2Database的嵌入式模式,所以必須由應(yīng)用自己來創(chuàng)建用戶表(實際生產(chǎn)環(huán)境中一般是由DBA來建表),所以在UserService組件實例化之后首先需要判斷用戶表是否存在,如果不存在則創(chuàng)建。

          package houserenter.service;
          
          import java.util.UUID;
          
          import javax.annotation.PostConstruct;
          
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.stereotype.Service;
          
          import houserenter.entity.User;
          import houserenter.mapper.UserMapper;
          
          @Service
          public class UserService {
          
          	@Autowired
          	private UserMapper userMapper;
          	
          	@PostConstruct
          	public void init() {
          		userMapper.cteateTable();
          	}
          	
          	public User register(String userName, String password, String passwordConfirmed) throws Exception {
          		
          		if (!passwordConfirmed.equals(password)) {
          			throw new Exception("兩次輸入的密碼不一致,請重新輸入!");
          		}
          		
          		User user = userMapper.selectByName(userName);
          		if (user != null) {
          			throw new Exception("用戶名 " + userName + " 已經(jīng)注冊過,請選擇其他用戶名!");
          		}
          		
          		user = new User();
          		user.setId(UUID.randomUUID().toString());
          		user.setName(userName);
          		user.setPassword(password);
          		userMapper.insert(user);
          		
          		return user;
          	}
          }
          

          這個UserService其實也沒有太多可說的,它依賴于 UserMapper,然后依照業(yè)務(wù)流程來編寫代碼即可。

          需要重點關(guān)注的是,我在這里使用了Java異常(可以參考這篇文章),一旦兩次密碼不一致,或者用戶名已經(jīng)被注冊過,就拋出異常。

          還有一點是,我使用了 java.util.UUID 類來生成全局唯一的用戶ID。

          最后要提醒的是,register() 方法的返回值類型是 User 。這是因為在此業(yè)務(wù)邏輯中我們生成了用戶ID,而此用戶ID有可能被上層組件用到。一般情況下,方法一旦生成了新數(shù)據(jù),則需要將該新數(shù)據(jù)返回給調(diào)用者。

          還有一個比較容易忽略的問題是數(shù)據(jù)庫操作的事務(wù)。因為我們的這段業(yè)務(wù)邏輯中訪問數(shù)據(jù)庫的地方有兩處,一處是判斷用戶名是否已經(jīng)被注冊過,一處是插入新用戶。如果有兩個用戶注冊的請求到來,且它們要注冊的用戶名相同,那么很有可能一個請求剛剛判斷完用戶名不存在(尚未插入到數(shù)據(jù)庫),另一個請求接著也判斷該用戶名是否已經(jīng)被注冊過(顯然是沒有),最后導(dǎo)致該用戶名被注冊兩次。我們可以在數(shù)據(jù)庫層面為user表的name列加上唯一性約束,也可以在應(yīng)用層面將若干操作封裝為事務(wù)。我們這里暫且忽略這個問題。

          控制器層 - 添加Handler

          控制器類 HouseRenterController 首先要注入 UserService :

          <!DOCTYPE html>
          <html>
          <head>
          <meta charset="UTF-8">
          <title>租房網(wǎng) - 注冊</title>
          </head>
          <body>
          	<form action="register.action" method="post">
          		<h2>用戶注冊</h2>
          		<label for="user_name">請輸入用戶名</label><input type="text" id="user_name" name="userName" />
          		<label for="password">請輸入密碼</label><input type="password" id="password" name="password" />
          		<label for="password_confirmed">請再次輸入密碼</label><input type="password" id="password_confirmed" name="passwordConfirmed" />
          		<input type="submit" value="注冊?" />
          	</form>
          	<p><a href="login.html">已經(jīng)注冊,直接登錄!</a></p>
          </body>
          </html>

          添加處理注冊請求的Handler也很簡單:

          	@PostMapping("/register.action")
          	public ModelAndView postRegister(String userName, String password, String passwordConfirmed) {
          		System.out.println("userName: " + userName + ", password: " + password + ", passwordConfirmed: " + passwordConfirmed);
          		ModelAndView mv = new ModelAndView();
          		try {
          			userService.register(userName, password, passwordConfirmed);
          			mv.setViewName("register-success.html");
          		} catch (Exception e) {
          			mv.addObject("errorMessage", e.getMessage());
          			mv.setViewName("register-failure.jsp");
          		}
          		return mv;
          	}

          唯一要關(guān)注的是,注冊成功和注冊失敗分別轉(zhuǎn)發(fā)到了不同的頁面。

          總結(jié)

          本篇文章簡單實現(xiàn)了用戶注冊的功能,還有很多可以優(yōu)化改進(jìn)的地方:

          • 注冊請求的參數(shù)綁定和校驗還可以進(jìn)一步簡化,目前即便用戶名為空也可以注冊成功、長度和字符也沒有限制等;
          • 注冊結(jié)果的展示還不夠友好,用戶還需要點擊一次才能繼續(xù)登錄或注冊;
          • 不夠安全,密碼是明文存儲、沒有驗證碼等;
          • 用戶ID的生成可以采用更好的方案;
          • 具有原子性的業(yè)務(wù),訪問數(shù)據(jù)庫需要放到一個事務(wù)中;
          • 異常處理還不夠完善;
          • 等等。

          不管怎樣,我們還是實現(xiàn)了一個基本可用的用戶注冊功能,而且開發(fā)起來還是相當(dāng)快、相當(dāng)清晰的,因為我們之前已經(jīng)搭建好了整個技術(shù)框架啊,正所謂磨刀不誤砍柴工!


          上一篇:HTML 文本格式化
          下一篇:HTML 布局
          主站蜘蛛池模板: 国产一区玩具在线观看| 无码国产精品一区二区免费模式 | 亚洲一区视频在线播放| 一区二区三区国模大胆| 风间由美性色一区二区三区| 伊人久久精品无码麻豆一区 | 亚洲国产精品一区二区第一页| 亚洲一区动漫卡通在线播放| 波多野结衣一区在线观看| 久久中文字幕一区二区| 成人精品一区久久久久| 五十路熟女人妻一区二区 | 国产精品无码一区二区在线 | 中文字幕亚洲综合精品一区| 欧美成人aaa片一区国产精品| 国精产品一区一区三区免费视频 | 精品3d动漫视频一区在线观看| 一区二区三区亚洲| 国产精品乱码一区二区三区 | 亚洲av色香蕉一区二区三区蜜桃 | 风间由美在线亚洲一区| 一区高清大胆人体| 国产在线精品一区二区高清不卡 | 亚洲一区二区三区高清在线观看| 国产精久久一区二区三区| 成人免费一区二区无码视频| 色婷婷一区二区三区四区成人网| 国产一区二区三区不卡AV| 国产伦精品一区二区三区| 国产午夜福利精品一区二区三区 | 久久精品无码一区二区三区不卡| 国产精品夜色一区二区三区| 激情内射亚洲一区二区三区爱妻| 亚洲国产韩国一区二区| 无码日本电影一区二区网站| 免费高清av一区二区三区| 精品无码国产一区二区三区51安 | 色狠狠一区二区三区香蕉蜜桃| 中文字幕一区二区免费| 秋霞午夜一区二区| 偷拍激情视频一区二区三区|