SpringBoot對所有內部日志使用通用日志記錄,但保留底層日志實現。為Java Util Logging、Log4J2和Logback提供了默認配置。在不同的情況下,日志記錄器都預先配置為使用控制臺輸出,同時還提供可選的文件輸出。默認情況下,SpringBoot使用Logback進行日志記錄。
日志級別有(從高到低):FATAL(致命),ERROR(錯誤),WARN(警告),INFO(信息),DEBUG(調試),TRACE(跟蹤)或者 OFF(關閉),默認的日志配置在消息寫入時將消息回顯到控制臺。默認情況下,將記錄錯誤級別、警告級別和信息級別的消息。
PS:Logback does not have a FATAL level. It is mapped to ERROR Logback沒有FATAL致命級別。它被映射到ERROR錯誤級別
詳情請戳官方文檔:https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/htmlsingle/#boot-features-logging
本文主要記錄Logback日志輸出到文件以及實時輸出到web頁面
我們創建SpringBoot項目時,spring-boot-starter已經包含了spring-boot-starter-logging,不需要再進行引入依賴
2014-03-05 10:57:51.112 INFO 45469 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/7.0.52
2014-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2014-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1358 ms
2014-03-05 10:57:51.698 INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2014-03-05 10:57:51.702 INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
如何打印日志?
方法1
/**
* 配置內部類
*/
@Controller
@Configuration
class Config {
/**
* 獲取日志對象,構造函數傳入當前類,查找日志方便定位
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Value("${user.home}")
private String userName;
/**
* 端口
*/
@Value("${server.port}")
private String port;
/**
* 啟動成功
*/
@Bean
public ApplicationRunner applicationRunner() {
return applicationArguments -> {
try {
InetAddress ia = InetAddress.getLocalHost();
//獲取本機內網IP
log.info("啟動成功:" + "http://" + ia.getHostAddress() + ":" + port + "/");
log.info("${user.home} :" + userName);
} catch (UnknownHostException ex) {
ex.printStackTrace();
}
};
}
}
方法2 使用lombok的@Slf4j,幫我們創建Logger對象,效果與方法1一樣
/**
* 配置內部類
*/
@Controller
@Configuration
class Config {
/**
* 獲取日志對象,構造函數傳入當前類,查找日志方便定位
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Value("${user.home}")
private String userName;
/**
* 端口
*/
@Value("${server.port}")
private String port;
/**
* 啟動成功
*/
@Bean
public ApplicationRunner applicationRunner() {
return applicationArguments -> {
try {
InetAddress ia = InetAddress.getLocalHost();
//獲取本機內網IP
log.info("啟動成功:" + "http://" + ia.getHostAddress() + ":" + port + "/");
log.info("${user.home} :" + userName);
} catch (UnknownHostException ex) {
ex.printStackTrace();
}
};
}
}
如果不需要進行復雜的日志配置,則在配置文件中進行簡單的日志配置即可,默認情況下,SpringBoot日志只記錄到控制臺,不寫日志文件。如果希望在控制臺輸出之外編寫日志文件,則需要進行配置
logging:
path: /Users/Administrator/Desktop/雜七雜八/ims #日志文件路徑
file: ims.log #日志文件名稱
level:
root: info #日志級別 root表示所有包,也可以單獨配置具體包 fatal error warn info debug trace off
重新啟動項目
打開ims.log
Spring Boot包含許多Logback擴展,可以幫助進行高級配置。您可以在您的logback-spring.xml配置文件中使用這些擴展。如果需要比較復雜的配置,建議使用擴展配置的方式
PS:SpringBoot推薦我們使用帶-spring后綴的 logback-spring.xml 擴展配置,因為默認的的logback.xml標準配置,Spring無法完全控制日志初始化。(spring擴展對springProfile節點的支持)
以下是項目常見的完整logback-spring.xml,SpringBoot默認掃描classpath下面的logback.xml、logback-spring.xml,所以不需要再指定spring.logging.config,當然,你指定也沒有問題
logging:
config: classpath:logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--日志文件主目錄:這里${user.home}為當前服務器用戶主目錄-->
<property name="LOG_HOME" value="${user.home}/log"/>
<!--日志文件名稱:這里spring.application.name表示工程名稱-->
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<!--默認配置-->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!--配置控制臺(Console)-->
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<!--配置日志文件(File)-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--設置策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件路徑:這里%d{yyyyMMdd}表示按天分類日志-->
<FileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/${APP_NAME}.log</FileNamePattern>
<!--日志保留天數-->
<MaxHistory>15</MaxHistory>
</rollingPolicy>
<!--設置格式-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<!-- 或者使用默認配置 -->
<!--<pattern>${FILE_LOG_PATTERN}</pattern>-->
<charset>utf8</charset>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>100MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 多環境配置 按照active profile選擇分支 -->
<springProfile name="dev">
<!--root節點 全局日志級別,用來指定最基礎的日志輸出級別-->
<root level="INFO">
<appender-ref ref="FILE"/>
<appender-ref ref="CONSOLE"/>
</root>
<!-- 子節點向上級傳遞 局部日志級別-->
<logger level="WARN" name="org.springframework"/>
<logger level="WARN" name="com.netflix"/>
<logger level="DEBUG" name="org.hibernate.SQL"/>
</springProfile>
<springProfile name="prod">
</springProfile>
</configuration>
啟動項目,去到${user.home}當前服務器用戶主目錄,日志按日期進行產生,如果項目產生的日志文件比較大,還可以按照小時進行.log文件的生成
2021-02-24更新:
如果需要按日志級別分別輸出到對應的日志文件,在appender標簽新增filter標簽進行指定
<!-- 時間滾動輸出 level為 【debug / info / warn / error】 日志 -->
<appender name="【DEBUG / INFO / WARN / ERROR】_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 忽略其他配置 -->
<!-- 此日志文件只記錄 【debug / info / warn / error】 級別的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>【debug / info / warn / error】</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
我們已經有日志文件.log了,為什么還要這個功能呢?(滑稽臉)為了偷懶!
當我們把項目部署到Linux服務器,當你想看日志文件,還得打開xshell連接,定位到log文件夾,麻煩;如果我們把日志輸出到Web頁面,當做超級管理員或者測試賬號下面的一個功能,點擊就開始實時獲取生成的日志并輸出在Web頁面,是不是爽很多呢?
PS:這個功能可得小心使用,因為日志會暴露很多信息
使用WebSocket實現實時獲取,建立WebSocket連接后創建一個線程任務,每秒讀取一次最新的日志文件,第一次只取后面200行,后面取相比上次新增的行,為了在頁面上更加方便的閱讀日志,對日志級別單詞進行著色(PS:如何創建springboot的websocket,請戳:SpringBoot系列——WebSocket)
package cn.huanzi.qch.springbootlogback;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* WebSocket獲取實時日志并輸出到Web頁面
*/
@Slf4j
@Component
@ServerEndpoint(value = "/websocket/logging", configurator = MyEndpointConfigure.class)
public class LoggingWSServer {
@Value("${spring.application.name}")
private String applicationName;
/**
* 連接集合
*/
private static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();
private static Map<String, Integer> lengthMap = new ConcurrentHashMap<String, Integer>();
/**
* 連接建立成功調用的方法
*/
@OnOpen
public void onOpen(Session session) {
//添加到集合中
sessionMap.put(session.getId(), session);
lengthMap.put(session.getId(), 1);//默認從第一行開始
//獲取日志信息
new Thread(() -> {
log.info("LoggingWebSocketServer 任務開始");
boolean first = true;
while (sessionMap.get(session.getId()) != null) {
BufferedReader reader = null;
try {
//日志文件路徑,獲取最新的
String filePath = System.getProperty("user.home") + "/log/" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/"+applicationName+".log";
//字符流
reader = new BufferedReader(new FileReader(filePath));
Object[] lines = reader.lines().toArray();
//只取從上次之后產生的日志
Object[] copyOfRange = Arrays.copyOfRange(lines, lengthMap.get(session.getId()), lines.length);
//對日志進行著色,更加美觀 PS:注意,這里要根據日志生成規則來操作
for (int i = 0; i < copyOfRange.length; i++) {
String line = (String) copyOfRange[i];
//先轉義
line = line.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("\"", """);
//處理等級
line = line.replace("DEBUG", "<span style='color: blue;'>DEBUG</span>");
line = line.replace("INFO", "<span style='color: green;'>INFO</span>");
line = line.replace("WARN", "<span style='color: orange;'>WARN</span>");
line = line.replace("ERROR", "<span style='color: red;'>ERROR</span>");
//處理類名
String[] split = line.split("]");
if (split.length >= 2) {
String[] split1 = split[1].split("-");
if (split1.length >= 2) {
line = split[0] + "]" + "<span style='color: #298a8a;'>" + split1[0] + "</span>" + "-" + split1[1];
}
}
copyOfRange[i] = line;
}
//存儲最新一行開始
lengthMap.put(session.getId(), lines.length);
//第一次如果太大,截取最新的200行就夠了,避免傳輸的數據太大
if(first && copyOfRange.length > 200){
copyOfRange = Arrays.copyOfRange(copyOfRange, copyOfRange.length - 200, copyOfRange.length);
first = false;
}
String result = StringUtils.join(copyOfRange, "<br/>");
//發送
send(session, result);
//休眠一秒
Thread.sleep(1000);
} catch (Exception e) {
//捕獲但不處理
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException ignored) {
}
}
}
log.info("LoggingWebSocketServer 任務結束");
}).start();
}
/**
* 連接關閉調用的方法
*/
@OnClose
public void onClose(Session session) {
//從集合中刪除
sessionMap.remove(session.getId());
lengthMap.remove(session.getId());
}
/**
* 發生錯誤時調用
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
/**
* 服務器接收到客戶端消息時調用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
}
/**
* 封裝一個send方法,發送消息到前端
*/
private void send(Session session, String message) {
try {
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
頁面收到數據就追加到div中,為了方便新增了幾個功能:
清屏,清空div內容
滾動至底部、將div的滾動條滑到最下面
開啟/關閉自動滾動,div新增內容后自動將滾動條滑到最下面,點一下開啟,再點關閉,默認關閉
PS:引入公用部分,就是一些jquery等常用靜態資源
<!DOCTYPE>
<!--解決idea thymeleaf 表達式模板報紅波浪線-->
<!--suppress ALL -->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>IMS實時日志</title>
<!-- 引入公用部分 -->
<script th:replace="head::static"></script>
</head>
<body>
<!-- 標題 -->
<h1 style="text-align: center;">IMS實時日志</h1>
<!-- 顯示區 -->
<div id="loggingText" contenteditable="true"
style="width:100%;height: 600px;background-color: ghostwhite; overflow: auto;"></div>
<!-- 操作欄 -->
<div style="text-align: center;">
<button onclick="$('#loggingText').text('')" style="color: green; height: 35px;">清屏</button>
<button onclick="$('#loggingText').animate({scrollTop:$('#loggingText')[0].scrollHeight});"
style="color: green; height: 35px;">滾動至底部
</button>
<button onclick="if(window.loggingAutoBottom){$(this).text('開啟自動滾動');}else{$(this).text('關閉自動滾動');};window.loggingAutoBottom = !window.loggingAutoBottom"
style="color: green; height: 35px; ">開啟自動滾動
</button>
</div>
</body>
<script th:inline="javascript">
//websocket對象
let websocket = null;
//判斷當前瀏覽器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:10086/websocket/logging");
} else {
console.error("不支持WebSocket");
}
//連接發生錯誤的回調方法
websocket.onerror = function (e) {
console.error("WebSocket連接發生錯誤");
};
//連接成功建立的回調方法
websocket.onopen = function () {
console.log("WebSocket連接成功")
};
//接收到消息的回調方法
websocket.onmessage = function (event) {
//追加
if (event.data) {
//日志內容
let $loggingText = $("#loggingText");
$loggingText.append(event.data);
//是否開啟自動底部
if (window.loggingAutoBottom) {
//滾動條自動到最底部
$loggingText.scrollTop($loggingText[0].scrollHeight);
}
}
}
//連接關閉的回調方法
websocket.onclose = function () {
console.log("WebSocket連接關閉")
};
</script>
</html>
有了日志記錄,我們以后寫代碼時就要注意了,應使用下面的正確示例
//錯誤示例,這樣寫只會輸出到控制臺,不會輸出到日志中
System.out.println("XXX");
e.printStackTrace();
//正確示例,既輸出到控制臺,又輸出到日志
log.info("XXX");
log.error("XXX報錯",e);
SpringBoot日志暫時先記錄到這里,點擊官網了解更多:https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/htmlsingle/#boot-features-logging
2019-07-03補充:我們之前只對日志等級關鍵字進行著色,還是覺得不夠,因此又新增了類名著色跟HTML轉義
主要修改:
效果:
2019-08-12補充:我發現有時候顯示的時候,換行不太準確,我們原先是在行末追加<br/>,但有時候讀取出來的一行記錄是自動換行后的數據,頁面顯示效果很丑
因此我改成用正則([\d+][\d+][\d+][\d+]-[\d+][\d+]-[\d+][\d+] [\d+][\d+]:[\d+][\d+]:[\d+][\d+])去匹配日期,然后再對應的起始下標插入<br/>,從而達到與控制臺輸出類似的效果
匹配、插入結果
頁面效果
異步輸出日志
異步輸出日志的方式很簡單,添加一個基于異步寫日志的appender,并指向原先配置的appender即可
<!-- 將文件輸出設置成異步輸出 -->
<appender name="ASYNC-FILE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丟失日志.默認的,如果隊列的80%已滿,則會丟棄TRACT、DEBUG、INFO級別的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默認的隊列的深度,該值會影響性能.默認值為256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一個 -->
<appender-ref ref="FILE"/>
</appender>
<!-- 將控制臺輸出設置成異步輸出 -->
<appender name="ASYNC-CONSOLE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丟失日志.默認的,如果隊列的80%已滿,則會丟棄TRACT、DEBUG、INFO級別的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默認的隊列的深度,該值會影響性能.默認值為256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一個 -->
<appender-ref ref="CONSOLE"/>
</appender>
原理很簡單,主線程將日志扔到阻塞隊列中,然后IO操作日志寫入文件是通過新起一個線程去完成的
2020-05-26補充
e.printStackTrace();會打出詳細異常,異常名稱,出錯位置,便于調試用,但直接調用會輸出到std.err,并沒有輸出到日志文件中,因此需要先輸出到流中再轉成字符串
封裝工具類
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* 捕獲報錯日志處理工具類
*/
public class ErrorUtil {
/**
* Exception出錯的棧信息轉成字符串
* 用于打印到日志中
*/
public static String errorInfoToString(Throwable e) {
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
// 將出錯的棧信息輸出到printWriter中
e.printStackTrace(pw);
pw.flush();
sw.flush();
} finally {
if (sw != null) {
try {
sw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (pw != null) {
pw.close();
}
}
return sw.toString();
}
}
也可以使用騷操作簡化代碼
public static String errorInfoToString(Throwable e) {
//try-with-resource語法糖 處理機制
try(StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw)){
e.printStackTrace(pw);
pw.flush();
sw.flush();
return sw.toString();
}catch (Exception ignored){
throw new RuntimeException(ignored.getMessage(),ignored);
}
}
使用
try {
//省略其他代碼
} catch (Throwable e) {
//之前的操作,輸出控制臺
e.printStackTrace();
//輸出到日志文件中
log.error(ErrorUtil.errorInfoToString(e));
}
2020-08-04更新
SpringBoot默認使用內置Tomcat,那么我們如何配置Tomcat的Access Logging呢?
詳情可查看官方文檔:
SpringBoot配置介紹:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#howto-configure-accesslogs
Apache Tomcat配置介紹:https://tomcat.apache.org/tomcat-8.5-doc/config/valve.html#Access_Logging
SpringBoot對Access Log的默認配置
server.tomcat.accesslog.buffered=true # Whether to buffer output such that it is flushed only periodically.
server.tomcat.accesslog.directory=logs # Directory in which log files are created. Can be absolute or relative to the Tomcat base dir.
server.tomcat.accesslog.enabled=false # Enable access log.
server.tomcat.accesslog.file-date-format=.yyyy-MM-dd # Date format to place in the log file name.
server.tomcat.accesslog.pattern=common # Format pattern for access logs.
server.tomcat.accesslog.prefix=access_log # Log file name prefix.
server.tomcat.accesslog.rename-on-rotate=false # Whether to defer inclusion of the date stamp in the file name until rotate time.
server.tomcat.accesslog.request-attributes-enabled=false # Set request attributes for the IP address, Hostname, protocol, and port used for the request.
server.tomcat.accesslog.rotate=true # Whether to enable access log rotation.
server.tomcat.accesslog.suffix=.log # Log file name suffix.
日志格式說明(摘自上方Apache Tomcat配置介紹官方文檔)
Values for the pattern attribute are made up of literal text strings, combined with pattern identifiers prefixed by the "%" character to cause replacement by the corresponding variable value from the current request and response. The following pattern codes are supported:
There is also support to write information incoming or outgoing headers, cookies, session or request attributes and special timestamp formats. It is modeled after the Apache HTTP Server log configuration syntax. Each of them can be used multiple times with different xxx keys:
All formats supported by SimpleDateFormat are allowed in %{xxx}t. In addition the following extensions have been added:
These formats cannot be mixed with SimpleDateFormat formats in the same format token.
Furthermore one can define whether to log the timestamp for the request start time or the response finish time:
By adding multiple %{xxx}t tokens to the pattern, one can also log both timestamps.
The shorthand pattern pattern="common" corresponds to the Common Log Format defined by '%h %l %u %t "%r" %s %b'.
The shorthand pattern pattern="combined" appends the values of the Referer and User-Agent headers, each in double quotes, to the common pattern.
When Tomcat is operating behind a reverse proxy, the client information logged by the Access Log Valve may represent the reverse proxy, the browser or some combination of the two depending on the configuration of Tomcat and the reverse proxy. For Tomcat configuration options see Proxies Support and the Proxy How-To. For reverse proxies that use mod_jk, see the generic proxy documentation. For other reverse proxies, consult their documentation.
我們只需要配置以下幾個簡單配置即可
#開啟內置Tomcat請求日志 access.log
server.tomcat.accesslog.enabled=true
#日志格式
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b
#日志輸出目錄
server.tomcat.accesslog.directory=${user.home}/log/accesslog/${spring.application.name}
#日志文件名
server.tomcat.accesslog.prefix=access_log
server.tomcat.accesslog.file-date-format=_yyyy-MM-dd
server.tomcat.accesslog.suffix=.log
效果
如何接口統計QPS?
如上圖中,logging接口,我們只要統計同一秒中,logging接口的請求次數即是該接口的QPS
代碼已經開源、托管到我的GitHub、碼云:
GitHub:https://github.com/huanzi-qch/springBoot
碼云:https://gitee.com/huanzi-qch/springBoot
作者:huanzi-qch
出處:https://www.cnblogs.com/huanzi-qch
若標題中有“轉載”字樣,則本文版權歸原作者所有。若無轉載字樣,本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,否則保留追究法律責任的權利.
一篇[附錄A、web 屬性]
下一篇[附錄A、data 屬性]
習總目標
本次學習目標
3. HTML
3.1 HTML概念
HTML是Hyper Text Markup Language的縮寫。意思是『超文本標記語言』。它的作用是搭建網頁結構,在網頁上展示內容
3.1.1 超文本
HTML文件本質上是文本文件,而普通的文本文件只能顯示字符。但是HTML技術則通過HTML標簽把其他網頁、圖片、音頻、視頻等各種多媒體資源引入到當前網頁中,讓網頁有了非常豐富的呈現方式,這就是超文本的含義——本身是文本,但是呈現出來的最終效果超越了文本。
3.1.2 標記語言
說HTML是一種『標記語言』是因為它不是向Java這樣的『編程語言』,因為它是由一系列『標簽』組成的,沒有常量、變量、流程控制、異常處理、IO等等這些功能。HTML很簡單,每個標簽都有它固定的含義和確定的頁面顯示效果。
標簽是通過一組尖括號+標簽名的方式來定義的:
<p>HTML is a very popular fore-end technology.</p>
這個例子中使用了一個p標簽來定義一個段落,<p>叫『開始標簽』,</p>叫『結束標簽』。開始標簽和結束標簽一起構成了一個完整的標簽。開始標簽和結束標簽之間的部分叫『文本標簽體』,也簡稱『標簽體』。
有的時候標簽里還帶有『屬性』:
<a href="http://www.xxx.com">show detail</a>
href=“http://www.xxx.com”就是屬性,href是『屬性名』,“http://www.xxx.com”是『屬性值』。
還有一種標簽是『單標簽』:
<input type="text" name="username" />
3.2 HTML的入門程序
3.3 HTML的結構
3.4 HTML語法規則
3.5 使用idea創建StaticWeb工程
3.6 HTML的各個標簽的使用
3.6.1 標題標簽
代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>這是一級標題</h1>
<h2>這是二級標題</h2>
<h3>這是三級標題</h3>
<h4>這是四級標題</h4>
<h5>這是五級標題</h5>
<h6>這是六級標題</h6>
</body>
</html>
頁面效果
3.6.2 段落標簽
代碼
<p>There is clearly a need for CSS to be taken seriously by graphic artists. The Zen Garden aims to excite, inspire, and encourage participation. To begin, view some of the existing designs in the list. Clicking on any one will load the style sheet into this very page. The code remains the same, the only thing that has changed is the external .css file. Yes, really.</p>
頁面效果
3.6.3 換行標簽
代碼
We would like to see as much CSS1 as possible. CSS2 should be limited to widely-supported elements only. The css Zen Garden is about functional, practical CSS and not the latest bleeding-edge tricks viewable by 2% of the browsing public. <br/>The only real requirement we have is that your CSS validates.
3.6.4 無序列表標簽
代碼
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Grape</li>
</ul>
頁面效果
3.6.5 超鏈接標簽(重要)
代碼
<body>
<!--
超鏈接標簽a的作用: 進行資源跳轉
href: 你要跳轉到的資源的路徑
target: 新頁面的打開方式
-->
<!--
1. 跳轉到本項目的資源: 使用相對路徑
相對路徑: 以當前路徑作為基準,如果資源跟我說同一個目錄下的則直接寫資源名就行了
如果在不同目錄下,要找上一級目錄,則使用../
. 當前目錄
.. 上一級目錄
2. 跳轉到其它服務器的資源: 此時就要使用完整的url訪問路徑
-->
<a href="../01_html的入門/start.html">跳轉到start.html頁面</a><br/>
<!--
target屬性表示新頁面的打開方式,我們目前只需要掌握兩種取值:
_self 表示新頁面在當前頁面打開
_blank 表示新頁面會新打開一個標簽頁
-->
<a href="https://www.baidu.com" target="_blank">跳轉到百度</a>
</body>
點擊后跳轉到href屬性指定的頁面
3.6.6 圖片標簽(重點)
準備圖片文件
代碼
<!--
img標簽是用于顯示圖片的,它有如下屬性
1. src: 用于指定要顯示的圖片的路徑,建議使用相對路徑
項目中的圖片一般存放在一個img的文件夾中
2. width: 圖片的寬度
3. height: 圖片的高度
-->
<img src="../img/mm.jpg" width="409" height="292"/>
頁面效果
3.6.7 塊標簽(重點)
『塊』并不是為了顯示文章內容的,而是為了方便結合CSS對頁面進行布局。塊有兩種,div是前后有換行的塊,span是前后沒有換行的塊。
把下面代碼粘貼到HTML文件中查看他們的區別:
<div style="border: 1px solid black;width: 100px;height: 100px;">This is a div block</div>
<div style="border: 1px solid black;width: 100px;height: 100px;">This is a div block</div>
<span style="border: 1px solid black;width: 100px;height: 100px;">This is a span block</span>
<span style="border: 1px solid black;width: 100px;height: 100px;">This is a span block</span>
3.6.8 HTML實體(了解)
在HTML文件中,<、>等等這樣的符號已經被賦予了特定含義,不會作為符號本身顯示到頁面上,此時如果我們想使用符號本身怎么辦呢?那就是使用HTML實體來轉義。
3.7 路徑介紹
在我們整個Web開發技術體系中,『路徑』是一個貫穿始終的重要概念。凡是需要獲取另外一個資源的時候都需要用到路徑。要想理解路徑這個概念,我們首先要認識一個概念:『文件系統』。
3.7.1 文件系統
我們寫代碼的時候通常都是在Windows系統來操作,而一個項目開發完成后想要讓所有人都能夠訪問到就必須『部署』到服務器上,也叫『發布』。而服務器通常是Linux系統。
Windows系統和Linux系統的文件系統有很大差別,為了讓我們編寫的代碼不會因為從Windows系統部署到了Linux系統而出現故障,實際開發時不允許使用物理路徑。
物理路徑舉例:
D:\aaa\pro01-HTML\page01-article-tag.html
D:\aaa\pro01-HTML\page02-anchor-target.html
幸運的是不管是Windows系統還是Linux系統環境下,目錄結構都是樹形結構,編寫路徑的規則是一樣的。
所以我們以項目的樹形目錄結構為依據來編寫路徑就不用擔心操作系統平臺發生變化之后路徑錯誤的問題了。有了這個大前提,我們具體編寫路徑時有兩種具體寫法:
3.7.2 相對路徑
相對路徑都是以『當前位置』為基準來編寫的。假設我們現在正在瀏覽a頁面,想在a頁面內通過超鏈接跳轉到z頁面。
那么按照相對路徑的規則,我們現在所在的位置是a.html所在的b目錄:
z.html并不在b目錄下,所以我們要從b目錄出發,向上走,進入b的父目錄——c目錄:
c目錄還是不行,繼續向上走,進入c的父目錄——d目錄:
在從d目錄向下經過兩級子目錄——e目錄、f目錄才能找到z.html:
所以整個路徑的寫法是:
<a href="../../../e/f/z.html">To z.html</a>
3.8 使用表格標簽展示數據(重要)
3.8.1 目標頁面效果
3.8.2 第一版代碼
<!-- 使用table標簽定義表格 -->
<table>
<!-- 使用tr標簽定義表格的行 -->
<tr>
<!-- 使用th標簽定義表頭,表頭有字體加粗效果 -->
<th>姓名</th>
<th>屬性</th>
<th>級別</th>
<th>忍村</th>
</tr>
<tr>
<!-- 使用td標簽定義單元格 -->
<td>漩渦鳴人</td>
<td>風</td>
<td>下忍</td>
<td>木葉</td>
</tr>
<tr>
<td>宇智波佐助</td>
<td>雷&火</td>
<td>下忍</td>
<td>木葉</td>
</tr>
<tr>
<td>我愛羅</td>
<td>沙</td>
<td>影</td>
<td>砂隱村</td>
</tr>
</table>
如果只有上面的代碼,頁面顯示效果是:
沒有表格邊框。想要顯示好看的表格邊框可以把下面的style標簽代碼復制粘貼到head標簽里,CSS還沒講,不必在意語法細節,整體照搬即可。
<style type="text/css">
table,th,td {
border-collapse: collapse;
border: 1px solid black;
padding: 5px;
}
</style>
我們發現,相較于目標效果而言,還未實現橫縱向合并單元格
3.8.3 合并單元格
① 橫向合并單元格(列合并)
使用colspan屬性將兩個橫向相鄰的單元格跨列合并:
<tr>
<td>宇智波佐助</td>
<td>雷&火</td>
<td colspan="2">下忍</td>
</tr>
注意: 『被合并』的單元格要刪掉。
② 縱向合并單元格(行合并)
使用rowspan屬性將兩個縱向相鄰的單元格跨行合并:
<tr>
<td>宇智波佐助</td>
<td rowspan="2">雷&火</td>
<td colspan="2">下忍</td>
</tr>
<tr>
<td>我愛羅</td>
<td>影</td>
<td>砂隱村</td>
</tr>
注意: 『被合并』的單元格要刪掉。
3.9 表單標簽(最重要)
3.9.1 表單標簽的作用
在項目開發過程中,凡是需要用戶填寫的信息都需要用到表單。它的作用是接收用戶的輸入信息,并且將用戶輸入的信息提交給服務器
3.9.2 form標簽的介紹
在HTML中我們使用form標簽來定義一個表單。而對于form標簽來說有兩個最重要的屬性:action和method。
<form action="/aaa/pro01-HTML/page05-form-target.html" method="post">
</form>
① action屬性
用戶在表單里填寫的信息需要發送到服務器端,對于Java項目來說就是交給Java代碼來處理。那么在頁面上我們就必須正確填寫服務器端的能夠接收表單數據的地址。
這個地址要寫在form標簽的action屬性中。但是現在暫時我們還沒有服務器端環境,所以先借用一個HTML頁面來當作服務器端地址使用。
② method屬性
『method』這個單詞的意思是『方式、方法』,在form標簽中method屬性用來定義提交表單的『請求方式』。method屬性只有兩個可選值:get或post,沒有極特殊情況的話使用post即可。
什么是『請求方式』?
瀏覽器和服務器之間在互相通信時有大量的『數據』需要傳輸。但是不論是瀏覽器還是服務器都有很多不同廠商提供的不同產品。
常見的瀏覽器有:
常見的Java服務器有:
這么多不同廠商各自開發的應用程序怎么能保證它們彼此之間傳輸的『數據』能夠被對方正確理解呢?
很簡單,我們給這些數據設定『格式』,發送端按照格式發送數據,接收端按照格式解析數據,這樣就能夠實現數據的『跨平臺傳輸』了。
而這里定義的『數據格式』就是應用程序之間的『通信協議』。
在JavaSE階段的網絡編程章節我們接觸過TCP/IP、UDP這樣的協議,而我們現在使用的『HTTP協議』的底層就是TCP/IP協議。
但是在HTML標簽中,點擊超鏈接是GET方式的請求,提交一個表單可以通過form標簽的method屬性指定GET或POST請求,其他請求方式無法通過HTML標簽實現。除了GET、POST之外的其他請求方式暫時我們不需要涉及(到我們學習SpringMVC時會用到PUT和DELETE)。至于GET請求和POST請求的區別我們會在講HTTP協議的時候詳細介紹,現在大家可以從表面現象來觀察一下。
3.10 表單項標簽
表單中的每一項,包括: 文本框、密碼框、單選框、多選框等等,都稱之為表單項,一個表單中可以包含多個表單項
3.10.1 name和value屬性
在用戶使用一個軟件系統時,需要一次性提交很多數據是非常正常的現象。我們肯定不能要求用戶一個數據一個數據的提交,而肯定是所有數據填好后一起提交。那就帶來一個問題,服務器怎么從眾多數據中識別出來收貨人、所在地區、詳細地址、手機號碼……?
很簡單,給每個數據都起一個『名字』,發送數據時用『名字』攜帶對應的數據,接收數據時通過『名字』獲取對應的數據。
在各個具體的表單標簽中,我們通過『name屬性』來給數據起『名字』,通過『value屬性』來保存要發送給服務器的『值』。
但是名字和值之間既有可能是『一個名字對應一個值』,也有可能是『一個名字對應多個值』。
這么看來這樣的關系很像我們Java中的Map,而事實上在服務器端就是使用Map類型來接收請求參數的。具體的是類型是:Map<String,String[]>。
name屬性就是Map的鍵,value屬性就是Map的值。
有了上面介紹的基礎知識,下面我們就可以來看具體的表單項標簽了。
3.10.2 單行文本框
代碼
個性簽名:<input type="text" name="signal"/><br/>
顯示效果
3.10.3 密碼框
代碼
密碼:<input type="password" name="secret"/><br/>
顯示效果
3.10.4 單選框
代碼
你最喜歡的季節是:
<input type="radio" name="season" value="spring" />春天
<input type="radio" name="season" value="summer" checked="checked" />夏天
<input type="radio" name="season" value="autumn" />秋天
<input type="radio" name="season" value="winter" />冬天
<br/><br/>
你最喜歡的動物是:
<input type="radio" name="animal" value="tiger" />路虎
<input type="radio" name="animal" value="horse" checked="checked" />寶馬
<input type="radio" name="animal" value="cheetah" />捷豹
顯示效果
說明:
3.10.5 多選框
代碼
你最喜歡的球隊是:
<input type="checkbox" name="team" value="Brazil"/>巴西
<input type="checkbox" name="team" value="German" checked="checked"/>德國
<input type="checkbox" name="team" value="France"/>法國
<input type="checkbox" name="team" value="China" checked="checked"/>中國
<input type="checkbox" name="team" value="Italian"/>意大利
顯示效果
說明:
3.10.6 下拉框
代碼
你喜歡的運動是:
<select name="interesting">
<option value="swimming">游泳</option>
<option value="running">跑步</option>
<option value="shooting" selected="selected">射擊</option>
<option value="skating">溜冰</option>
</select>
顯示效果
說明:
3.10.7 按鈕
代碼
<button type="button">普通按鈕</button>或<input type="button" value="普通按鈕"/>
<button type="reset">重置按鈕</button>或<input type="reset" value="重置按鈕"/>
<button type="submit">提交按鈕</button>或<input type="submit" value="提交按鈕"/>
顯示效果
說明:
3.10.8 隱藏域
代碼
<input type="hidden" name="userId" value="2233"/>
說明:
通過表單隱藏域設置的表單項不會顯示到頁面上,用戶看不到。但是提交表單時會一起被提交。用來設置一些需要和表單一起提交但是不希望用戶看到的數據,例如:用戶id等等。
3.10.9 多行文本框
代碼
自我介紹:<textarea name="desc"></textarea>
顯示效果
說明:
textarea沒有value屬性,如果要設置默認值需要寫在開始和結束標簽之間。
3.10.10 文件表單
代碼
頭像:<input type="file" name="file"/>
顯示效果
說明:
不同瀏覽器顯示的樣式有微小差異
*請認真填寫需求信息,我們會在24小時內與您取得聯系。