【手把手】JavaWeb 入門(mén)級(jí)項(xiàng)目實(shí)戰(zhàn) -- 文章發(fā)布系統(tǒng) (第四節(jié))

首先,更正一下上一章中的一個(gè)小錯(cuò)誤,就是在index.jsp中,banner部分沒(méi)有添加結(jié)束的標(biāo)簽,加上去就OK了,我也是完善頁(yè)面的時(shí)候發(fā)現(xiàn)的。

另外,index.jsp中引入的jQuery也需要換成本地的。

<script src="${basePath}/static/js/jQuery.js"></script>

今天我把頁(yè)面重構(gòu)了一下,添加了內(nèi)容區(qū)和底欄(footer),我會(huì)把目前的代碼上傳的,有需要的自己去看就行了,我們就不在前臺(tái)頁(yè)面花費(fèi)太多的時(shí)間了。div + css,布局等等,這些東西以后有時(shí)間的話(huà),我單獨(dú)開(kāi)貼分享吧。

都已經(jīng)寫(xiě)了三篇文章了,還沒(méi)有寫(xiě)Java代碼,實(shí)在有些說(shuō)不過(guò)去。

1. 登陸頁(yè)面

登陸頁(yè)我已經(jīng)寫(xiě)好了,現(xiàn)在看看效果,簡(jiǎn)單說(shuō)明一下。

點(diǎn)擊登陸按鈕,可以跳轉(zhuǎn)到登陸頁(yè)面。

登陸按鈕就是一個(gè)超鏈接。

JSP頁(yè)面就是一個(gè)servlet,但是省去了很多寫(xiě)Servlet的麻煩,login.jsp已經(jīng)寫(xiě)好了,就放在WebContent目錄下。

昨天憋了一上午,總算寫(xiě)好了登陸頁(yè)面。我不是專(zhuān)業(yè)做前端的,所以只做了一個(gè)大概樣子。用了很多css3的屬性,所以IE678上瀏覽的效果是不好的。

關(guān)于這個(gè)頁(yè)面,今天我調(diào)整了一下,不想搞得那么復(fù)雜了,我去掉了所有的圖標(biāo)和飄動(dòng)白云,關(guān)于css特效,h5的話(huà)呢,以后單獨(dú)拿出來(lái)說(shuō)明吧,畢竟好多人都反應(yīng)說(shuō)太花哨了,因?yàn)槭侨腴T(mén)級(jí)的小項(xiàng)目,我也不想弄得那么復(fù)雜了。

雖然頁(yè)面單調(diào)了些,不過(guò)對(duì)于初學(xué)者來(lái)說(shuō),相對(duì)來(lái)說(shuō)比較好理解。之前的頁(yè)面的確有點(diǎn)太花哨了,還弄了幾朵云飄來(lái)飄去的,說(shuō)不定還影響性能,所以我把這些都去掉了。

Paste_Image.png

2. 新的目錄結(jié)構(gòu)

之前的代碼有很多冗余的地方,比如標(biāo)題欄,每個(gè)頁(yè)面都需要寫(xiě)一遍。而且js和css都是寫(xiě)在本頁(yè)面的。實(shí)際開(kāi)發(fā)一般都不會(huì)這么做。所以,我把這些東西都分離出來(lái)了,放在各自的目錄里。

以下是新的目錄結(jié)構(gòu):

header.jsp


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<div class="header">
    <div class='logo'>原創(chuàng)文字</div>
    <ul>
        <li class='first'><a href="index.jsp">首頁(yè)</a></li>
        <li class='item'><a href="javascript:void(0)">原創(chuàng)故事</a></li>
        <li  class='item'><a href="javascript:void(0)">熱門(mén)專(zhuān)題</li>
        <li  class='item'><a href="javascript:void(0)">欣賞美文</li>
        <li  class='item'><a href="javascript:void(0)">贊助</a></li>
    </ul>
    
    <div class='login'>
        <span><a href="login.jsp">登陸</a></span>  
        <span>|</span> 
        <span><a href="javascript:void(0)">注冊(cè)</a></span>
    </div>
</div>

這就是標(biāo)題欄,以后新增的jsp頁(yè)面,只需要把這個(gè)header.jsp引入就可以了。注意,這種引入就相當(dāng)于把里面的代碼原封不動(dòng)地拷貝進(jìn)去,所以如果用相對(duì)路徑引用資源文件,就還是以原本的頁(yè)面為準(zhǔn)。

引入方式:

<!-- 頭部頁(yè)面 -->
<%@include file="common/header.jsp" %>

footer.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<div class='footer'>
    免責(zé)聲明:本站所有素材均來(lái)自網(wǎng)絡(luò),僅供學(xué)習(xí)交流。如果侵犯了您的權(quán)益,請(qǐng)聯(lián)系我,我會(huì)第一時(shí)間刪除侵權(quán)內(nèi)容。
</div>

taglib.jsp (一些公共的配置等)


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

<%
    String path = request.getContextPath();
    int port = request.getServerPort();
    String basePath  = null;
    if(port==80){
        basePath = request.getScheme()+"://"+request.getServerName()+path;
    }else{
        basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path;
    }
    request.setAttribute("basePath", basePath);
%>

basePath 就是項(xiàng)目的根路徑。這樣做的好處就是,使得JSP看起來(lái)很干凈,沒(méi)有那么多冗余的代碼了。

大概就是這個(gè)樣子,接下來(lái),我們開(kāi)始寫(xiě)業(yè)務(wù)。

3. 登陸功能的MVC流程

登陸框中,目前只有用戶(hù)名和密碼這兩個(gè)選項(xiàng)。

我們首先要做的就是將這兩個(gè)值傳遞到后臺(tái)。所謂的后臺(tái),其實(shí)就是Java代碼。為了看起來(lái)比較清晰,我們?cè)赪ebContent目錄下新建一個(gè)controller包。

這是一個(gè)MVC分層的示意圖


意思就是說(shuō),用戶(hù)登錄之后,我們需要驗(yàn)證它的用戶(hù)名和密碼是否正確,那么就需要將數(shù)據(jù)拿到數(shù)據(jù)庫(kù)里面去匹配。總體的流程大概是這樣:我先在前臺(tái)獲取用戶(hù)名和密碼,然后到controller層(控制層),這一層需要接受你傳過(guò)來(lái)的用戶(hù)名和密碼,進(jìn)行一些基本的控制。

然后繼續(xù)將數(shù)據(jù)傳遞到service層,也就是業(yè)務(wù)層,這一層會(huì)根據(jù)具體的業(yè)務(wù)對(duì)你的數(shù)據(jù)進(jìn)行判斷和分析,最后,才傳遞到dao層,這一層原則上就是和數(shù)據(jù)庫(kù)進(jìn)行交互的。多半是寫(xiě)sql語(yǔ)句然后操作數(shù)據(jù)庫(kù)。

就比如說(shuō)用戶(hù)登錄這個(gè)功能,我需要判斷的就是

  1. 你這個(gè)用戶(hù)是否存在?
  2. 用戶(hù)名和密碼是否正確?

最終,還需要將登錄的標(biāo)志返回給前臺(tái)。

dao -> servide -> controller -> JSP

這樣就是一個(gè)完整的流程。

4. 從JSP到controller層

讓我們打開(kāi)login.jsp頁(yè)面,引入jQuery

<script src="${basePath}/static/js/jQuery.js"></script>

登錄框的HTML代碼:


<!-- 登陸框 -->
<div class='content'>
    <div class='logo'><i style='font-size:90px;' class="iconfont icon-denglu"></i></div>
    <div class='inputBox mt50 ml32'>
        <input type="text" id="username" autofocus="autofocus" autocomplete="off" maxlength="60" placeholder="請(qǐng)輸入賬號(hào)/郵箱/手機(jī)號(hào)">
        <i style='font-size:20px;margin-left:-32px;opacity:0.8;' class='iconfont icon-yonghuming'></i>
    </div>
    <div class='inputBox mt50 ml32'>
        <input type="password" id="password" autofocus="autofocus" autocomplete="off" maxlength="60" placeholder="請(qǐng)輸入密碼">
        <i style='font-size:20px;margin-left:-32px;opacity:0.8;' class='iconfont icon-mima'></i>
    </div>
    
    <div class='mt50 ml32'>
        <button class="login_btn" onclick="#">登陸</button>
    </div>
</div>

我們?cè)谙旅鎸?xiě)一個(gè)script塊,js代碼就全部寫(xiě)在這里。

給登陸按鈕綁定一個(gè)點(diǎn)擊事件:

<div class='mt50 ml32'>
        <button class="login_btn" onclick="login()">登陸</button>
</div>

登陸方法

function login(){
    var username = $('#username').val();
    var password = $('#password').val();
    alert(username + "," + password);
}

當(dāng)成功alert出來(lái)數(shù)據(jù)后,說(shuō)明到此為止的代碼是正確的。

接下來(lái),利用jQuery的ajax方法,將數(shù)據(jù)提交到controller層。


function login(){
    var username = $('#username').val();
    var password = $('#password').val();
    $.ajax({
        type:"post",//請(qǐng)求方式
        url:"${basePath}/controller/loginController.jsp",//請(qǐng)求地址
        data:{"username":username,"password":password},//傳遞給controller的json數(shù)據(jù)
        error:function(){
            alert("登陸出錯(cuò)!");
        },
        success:function(data){ //返回成功執(zhí)行回調(diào)函數(shù)。
            
        }
    });
    
}

我已經(jīng)都寫(xiě)好注釋了,ajax方法在web開(kāi)發(fā)過(guò)程中,是被普遍使用的。

新建一個(gè)loginController.jsp ,這就是所謂的服務(wù)器端。


<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%

    //設(shè)置請(qǐng)求的編碼
    //request.setCharacterEncoding("UTF-8");
    //獲取客戶(hù)端傳遞過(guò)來(lái)參數(shù)
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    System.out.println(username);
    System.out.println(password);
    //如果用戶(hù)名和密碼不為空
    
%>

從JSP頁(yè)面到controller,用戶(hù)名和密碼是被裝在一個(gè)叫做request的變量中,它其實(shí)也就相當(dāng)于一個(gè)json,一個(gè)map,都是差不多的東西,這里就不詳細(xì)說(shuō)明了。當(dāng)然了,他也是JSP九大隱式對(duì)象中的一員。

Paste_Image.png

我們來(lái)測(cè)試一下,點(diǎn)擊登陸按鈕。

成功了!可以看到數(shù)據(jù)已經(jīng)成功傳遞到controller層了。

因?yàn)槲覀冞€沒(méi)有數(shù)據(jù)表和JavaBean,所以我們先不急著寫(xiě)service層,先開(kāi)始編寫(xiě)JavaBean吧。

5. 從JavaBean到數(shù)據(jù)庫(kù)表。

我們?cè)趕rc目錄下新建一個(gè)存放JavaBean的包

關(guān)于JavaBean,如果不是很了解的話(huà),可以看看這篇文章:

http://www.cnblogs.com/skyblue-li/p/5900216.html

一個(gè)記錄用戶(hù)信息的JavaBean,我想了以下這些屬性:

private String id;    //主鍵,采用UUID
private String username;  //用戶(hù)名
private String password;  //密碼
private String headerPic; //頭像
private String email;     //電子郵箱
private Integer male;     //性別 0男 1女 3保密
private String createTime;//創(chuàng)建時(shí)間
private String updateTime;//最后更新時(shí)間
private Integer isDelete; // 刪除狀態(tài)0未刪除1刪除
private String address;   //地址
private String telephone; //電話(huà)

當(dāng)你的JavaBean設(shè)計(jì)好了,差不多對(duì)應(yīng)的數(shù)據(jù)庫(kù)表也就出來(lái)了。

之前寫(xiě)過(guò)一篇關(guān)于注解的文章:

http://www.cnblogs.com/skyblue-li/p/5900228.html

現(xiàn)在可以用這個(gè)知識(shí)點(diǎn)做點(diǎn)有趣的事情了,比如將一個(gè)JavaBean轉(zhuǎn)換成建表語(yǔ)句。

新建一個(gè)注解包,里面添加兩個(gè)注解

column.java

package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)      //注解的目標(biāo)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    
    public String field() ; //字段名稱(chēng)
    public boolean primaryKey() default false;//是否為主鍵
    public String type() default "VARCHAR(80)";//字段類(lèi)型
    public boolean defaultNull() default true; //是否允許為空
    
}

Table.java

package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)       //注解的目標(biāo)是類(lèi)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {

    public String tableName();
}

我們創(chuàng)建了兩個(gè)注解。

接下來(lái),在util包(也就是工具包)中新建兩個(gè)工具類(lèi)

StringUtils 字符串工具類(lèi)

package util;


public class StringUtils {
    
    public static boolean isEmpty(String str) {
        return null == str  || str.equals("")
                || str.matches("\\s*");
    }

    public static String defaultValue(String content,String defaultValue){
        if(isEmpty(content)){
            return defaultValue;
        }
        return content;
    }
}

isEmpty的作用是判斷字符串是否為空。
defaultValue表示給字符串設(shè)置默認(rèn)值,有點(diǎn)類(lèi)似于oracle數(shù)據(jù)庫(kù)中的nvl語(yǔ)法。

TableUtils 數(shù)據(jù)表工具類(lèi)

package util;

import java.lang.reflect.Field;

import annotation.Column;
import annotation.Table;

public class TableUtils {
    
    
    public static String getCreateTableSQl(Class<?> clazz){
        StringBuilder sb = new StringBuilder();
        sb.append("create table ");
        //獲取表名
        Table table = (Table) clazz.getAnnotation(Table.class);
        String tableName = table.tableName();
        sb.append(tableName).append("(\n");
        
        Field[] fields = clazz.getDeclaredFields();
        String primaryKey = "";
        //遍歷所有字段
        for (int i = 0; i < fields.length; i++) {
            Column column = (Column) fields[i].getAnnotations()[0];
            String field = column.field();
            String type = column.type();
            boolean defaultNull = column.defaultNull();
            
            sb.append("\t" + field).append(" ").append(type);
            if(defaultNull){
                if(type.toUpperCase().equals("TIMESTAMP")){
                    sb.append(",\n");
                }else{
                    sb.append(" DEFAULT NULL,\n");
                }
            }else{
                sb.append(" NOT NULL,\n");
                if(column.primaryKey()){
                    primaryKey = "PRIMARY KEY ("+field+")";
                }
            }
        }
        
        if(!StringUtils.isEmpty(primaryKey)){
            sb.append("\t").append(primaryKey);
        }
        sb.append("\n) DEFAULT CHARSET=utf8");
        
        return sb.toString();
    }
    
}

getCreateTableSQl方法是利用反射和注解有關(guān)的知識(shí),給一個(gè)JavaBean自動(dòng)生成建表語(yǔ)句,目前只支持MySQL,因?yàn)檫@方面的知識(shí)我也是剛開(kāi)始學(xué),寫(xiě)得不好的地方還請(qǐng)各位多多包涵。

接下來(lái),給JavaBean添加注解。

@Table(tableName = "t_user")
public class User{
   //屬性
}

屬性如下:

@Column(type = "varchar(30)" ,field = "id" ,primaryKey = true ,defaultNull = false)
private String id;        //主鍵,采用UUID

@Column(type = "VARCHAR(20)", field = "username")
private String username;  //用戶(hù)名

@Column(type = "VARCHAR(20)", field = "password")
private String password;  //密碼

@Column(type = "VARCHAR(60)", field = "headerPic")
private String headerPic; //頭像

@Column(type = "VARCHAR(60)", field = "email")
private String email;     //電子郵箱

@Column(type = "VARCHAR(2)", field = "sex")
private Integer sex;      //性別 0男 1女 3保密

@Column(type = "datetime", field = "create_time")
private String createTime;//創(chuàng)建時(shí)間

@Column(type = "timestamp", field = "update_time")
private String updateTime;//最后更新時(shí)間

@Column(type = "int(1)", field = "is_delete")
private Integer isDelete; // 刪除狀態(tài)  0未刪除  1刪除

@Column(type = "VARCHAR(200)", field = "address")
private String address;   //地址

@Column(type = "VARCHAR(15)", field = "telephone")
private String telephone; //電話(huà)

創(chuàng)建一個(gè)測(cè)試包和測(cè)試類(lèi):

package test;

import bean.User;
import util.TableUtils;

public class TestMain {
    public static void main(String[] args) {
        String sql = TableUtils.getCreateTableSQl(User.class);
        System.out.println(sql);
    }
}

運(yùn)行

OK,拿到sql語(yǔ)句了。

我已經(jīng)安裝了mysql,用root用戶(hù)登陸后,新建一個(gè)database

使用這個(gè)database

將剛才得到的sql語(yǔ)句復(fù)制進(jìn)去,加分號(hào),回車(chē)。

這就表明數(shù)據(jù)庫(kù)表已經(jīng)建好了,默認(rèn)編碼是UTF-8。

本文結(jié)束。

我要下載源碼

您的支持是我寫(xiě)作的最大動(dòng)力:

最后說(shuō)一下更新的問(wèn)題,最近事情比較多,無(wú)奈改為周更了,正常情況下每周日定時(shí)更新。至于源碼,我還沒(méi)有放到github上,打算以后寫(xiě)得差不多了再發(fā)布吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容