第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號(hào)安全,請(qǐng)及時(shí)綁定郵箱和手機(jī)立即綁定
4.4. 靜態(tài)資源過濾

我們需要把非業(yè)務(wù)請(qǐng)求,也就是靜態(tài)資源的請(qǐng)求給過濾掉,避免資源的浪費(fèi),具體實(shí)現(xiàn)如下所示:public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> { @Override protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { if(msg instanceof HttpRequest) { //1.打印瀏覽器的請(qǐng)求地址 System.out.println("客戶端地址" + ctx.channel().remoteAddress()); //2.強(qiáng)制轉(zhuǎn)換成HttpRequest HttpRequest httpRequest = (HttpRequest) msg; //3.獲取uri, 過濾指定的資源 URI uri = new URI(httpRequest.uri()); if("/favicon.ico".equals(uri.getPath())) { System.out.println("請(qǐng)求了 favicon.ico, 不做響應(yīng)"); return; } //4.給瀏覽器發(fā)送的信息,封裝成ByteBuf ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); //5.構(gòu)造一個(gè)http的相應(yīng),即 httpresponse FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content); //6.設(shè)置響應(yīng)頭信息-響應(yīng)格式 response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); //7.設(shè)置響應(yīng)頭信息-響應(yīng)數(shù)據(jù)長(zhǎng)度 response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); //8.將構(gòu)建好 response返回 ctx.writeAndFlush(response); } }}代碼說明:需要獲取瀏覽器請(qǐng)求的 uri,并且手工判斷 uri 是否等于 /favicon.ico,如果是則不往下處理;同類我們可以判斷是否是 js、css、img 等資源文件。

2.1 屬性詳細(xì)解釋

2.1.1 id 和 name 標(biāo)簽的使用我們目前已經(jīng)知道所有被實(shí)例化后的對(duì)象都存在于 Spirng 的容器中,那么從容器中獲取這些對(duì)象需要一個(gè)屬性 id 對(duì)吧?那么 name 和 id 有什么關(guān)系呢?查看官方文檔得知 Spring 的容器會(huì)給初始化的每個(gè) bean 都定義一個(gè)或多個(gè)標(biāo)識(shí)符。這些標(biāo)識(shí)符在容器內(nèi)必須是唯一的。一個(gè) bean 通常只有一個(gè)標(biāo)識(shí)符。而 name 和 id 都可以起到標(biāo)識(shí)符的作用。所以在 XML 配置文件,我們一般使用 id 或者 name 屬性,定義 bean 的唯一標(biāo)識(shí),這樣我們才能通過定義好的唯一標(biāo)識(shí),從 Spring 的容器中獲取他們。代碼實(shí)例:xml 的配置文件如下:<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" name="user2" class="com.wyan.entity.User" ></bean></beans>測(cè)試代碼如下: public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); System.out.println(context.getBean("user")); System.out.println(context.getBean("user2")); }結(jié)果如圖所示:結(jié)論證明:我們通過 bean 標(biāo)簽中的 id 屬性 user, 或者使用 bean 標(biāo)簽中的 name 屬性 user2, 都可以得到 Spring 容器中的 user 對(duì)象的示例,而且打印的地址是同一個(gè)。我們之前說過一句,默認(rèn)在容器中的實(shí)例都是單例的,在這里也得到了證明。2.1.2 class 屬性bean 標(biāo)簽的定義實(shí)質(zhì)上是創(chuàng)建一個(gè)或多個(gè)對(duì)象的方法。當(dāng) xml 文件被解析加載的時(shí)候,使用該 bean 定義封裝的配置數(shù)據(jù)來創(chuàng)建(或獲?。?shí)際對(duì)象,而創(chuàng)建獲取的對(duì)象是誰呢?就是通過 class 屬性中定義的類的全路徑來指定 。一般來講 class 中的類實(shí)例化有兩種方式:? 一種是反射 ,相當(dāng)于我們使用的 new 關(guān)鍵字。這種也是我們常用的方式。當(dāng)然不要忘記提供無參數(shù)的構(gòu)造方法(類中默認(rèn)有無參構(gòu)造,但是如果自定義了有參構(gòu)造,默認(rèn)的無參不會(huì)提供)? 一種是工廠模式 ,需要借助于 factory-bean 和 factory-method 兩個(gè)屬性,這種方式不常用,我們可以了解下。2.1.3 factorybean 和 factorymethod 屬性這兩個(gè)屬性主要用于工廠模式實(shí)例化 bean 的時(shí)候使用,不是很常見。工廠模式有兩種,這里分別做個(gè)實(shí)例,幫助大家理解。靜態(tài)工廠模式實(shí)例:<!--applicationContext的配置bean節(jié)點(diǎn)--><bean id="user" class="com.wyan.entity.User" factory-method="createUserInstance"/>創(chuàng)建 bean 示例的 Java 工廠類:public class User { private static User user = new User(); private User() {} public static User createInstance() { return user; }}解釋:在定義使用靜態(tài)工廠方法創(chuàng)建的 bean 時(shí),class 屬性指定的是被創(chuàng)建的類,包含靜態(tài)的方法,并使用 factory-method 屬性來指定工廠方法本身名稱。普通工廠模式:<!--spring實(shí)例化工廠對(duì)象 用于創(chuàng)建java實(shí)例 --><bean id="beanFactory" class="com.wyan.factory.BeanFactory"></bean><!-- 被工廠創(chuàng)建的對(duì)象實(shí)例 --><bean id="user1" factory-bean="beanFactory" factory-method="createUser1"/>工廠類代碼:public class BeanFactory { private static User1 user1 = new User1(); private static User2 user2 = new User2(); public User1 createUser1() { return user1; } public User2 createUser2() { return user2; }}解釋:先實(shí)例化先創(chuàng)建各個(gè)對(duì)象示例的工廠對(duì)象到容器中,自身的 bean 標(biāo)簽將 class 屬性保留為空,并在 factory-bean 屬性中指定當(dāng)前容器中的工廠 Bean 名稱,再使用 factory-method 屬性設(shè)置創(chuàng)建示例的方法名稱。2.1.4 init-method 和 destroy-method 屬性的使用這兩個(gè)屬性比較好理解 init-method 就是 bean 被初始化后執(zhí)行的方法,destory-method 就是 bean 被銷毀執(zhí)行的代碼。我們來個(gè)測(cè)試類:public class User { public User(){ System.out.println("我被spring實(shí)例化了"); } public void initMethod(){ System.out.println("user類實(shí)例化時(shí)候執(zhí)行的代碼"); } public void destoryMethod(){ System.out.println("user類實(shí)例被銷毀時(shí)候執(zhí)行的代碼"); }}配置文件: <bean id="user" name="user2" class="com.wyan.entity.User" init-method="initMethod" destroy-method="destoryMethod" ></bean>**測(cè)試代碼: public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); }加載 Spring 的配置文件控制臺(tái)打印如下:有個(gè)小疑問:銷毀語句沒打印呢?那是因?yàn)椴]有調(diào)用容器的銷毀方法。改造測(cè)試代碼如下: public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); context.close(); }解釋:ApplicationContext 沒有 close 方法使用它的子類運(yùn)行結(jié)果:2.1.5 其余屬性作用scope :指定示例的作用范圍,后續(xù)章節(jié)詳細(xì)講解;lazy-init :表示是否為懶加載;autowire :指定屬性注入方式,后續(xù)章節(jié)詳解;depends-on: 表示是否有依賴的 bean 對(duì)象,后續(xù)依賴注入章節(jié)詳細(xì)解釋。

2.1 android:background 和 android:src 的區(qū)別

前面有提到,android:background也可以直接設(shè)置成圖片,那么在設(shè)置成圖片的場(chǎng)景下,它和android:src是否是一樣的呢?我們來進(jìn)一步考察一下:<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#CC1010" android:src="@drawable/image" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:background="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="bottom" android:background="#CC1010" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="bottom|right" android:background="@drawable/image" /></FrameLayout>在上面的代碼中,我們?cè)谄聊坏?4 個(gè)角放置了 4 個(gè) ImageView ,分別從尺寸大小、圖片設(shè)置方式兩個(gè)維度做了區(qū)分,為了方便對(duì)比,在使用android:src屬性之后,通過android:background加了紅色的背景,效果如下:我們首先看上排的兩個(gè)圖片效果:長(zhǎng)寬均為wrap_content左邊一個(gè)用的android:src右邊一個(gè)用的是android:background可以看到兩者效果完全一樣,再來看下排的兩個(gè)圖片:長(zhǎng)寬均為200dp左邊一個(gè)用的android:src右邊一個(gè)用的android:background可以清楚的看到,在將 ImageView 的大小固定之后,左邊的圖片內(nèi)容仍然保持原始比例,并且露出 ImageView 的紅色背景(說明 ImageView 大小和我們?cè)O(shè)置的一樣),而右邊的圖片會(huì)拉伸圖片大小直至占滿整個(gè) ImageView。這樣一來就可以得出結(jié)論:在 ImageView 的尺寸和圖片尺寸比例一致的情況下,使用android:background設(shè)置圖片和使用android:src效果一樣在 ImageView 的尺寸和圖片尺寸比例不一致的情況下,使用android:src會(huì)保留圖片原始比例并居中顯示,而用android:background設(shè)置的會(huì)將圖片拉伸直至鋪滿整個(gè) ImageView。這里有一個(gè)疑問,為什么尺寸不一致的時(shí)候,Android 系統(tǒng)是采取居中顯示,而不是其他的樣式呢?這就是我們接下來要說明的屬性了。特別注意以上說的都是尺寸比例,并非長(zhǎng)和寬的值。

3.3 多狀態(tài)變化

Button 的多狀態(tài)變化是很常用也是效果非常棒的一個(gè)效果,它的可以最大化的增強(qiáng)互動(dòng)感。通過 StateListDrawable 設(shè)置 Button 在不同狀態(tài)下的樣式效果,比如在按下、抬起、選中、無效等等不同狀態(tài)下可以呈現(xiàn)不同的形狀和顏色,這樣可以給用戶更多的點(diǎn)擊反饋。StateListDrawable 用來記錄各個(gè)狀態(tài)列表,并通過 Drawable 的形式描述各個(gè)狀態(tài)下要呈現(xiàn)的樣式。它支持以下設(shè)置項(xiàng):drawable: Button 的背景樣式,搭配后面的狀態(tài)使用表示當(dāng)前狀態(tài)下的樣式。如果沒有設(shè)置狀態(tài),則為默認(rèn)樣式state_pressed: 按下態(tài)state_enabled: 可用狀態(tài)state_focused: 獲得焦點(diǎn)狀態(tài)state_window_focused: 獲得窗口焦點(diǎn)狀態(tài)state_checkable: 可選狀態(tài)(針對(duì) checkbox)state_checked: 勾選態(tài)state_selected: 選擇態(tài)(針對(duì)滾輪的場(chǎng)景)state_active: 活動(dòng)狀態(tài)(針對(duì) slidingTab)state_single: 包含多個(gè)子控件時(shí),只顯示一個(gè)子控件的狀態(tài)state_first: 包含多個(gè)子控件時(shí),第一個(gè)子控件處于顯示狀態(tài)state_middle: 包含多個(gè)子控件時(shí),中間一個(gè)子控件處于顯示狀態(tài)state_last: 包含多個(gè)子控件時(shí),最后一個(gè)子控件處于顯示狀態(tài)其中最常用就是前 3 個(gè)狀態(tài)。我們新增一個(gè) button_pressd_background.xml,內(nèi)容如下:<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#DF866B" /> <corners android:bottomLeftRadius="50dp" android:bottomRightRadius="50dp" android:topLeftRadius="50dp" android:topRightRadius="50dp" /> <stroke android:width="3dp" android:color="#99CCFF" /></shape>我們?cè)谥暗臉邮缴闲薷牧硕c(diǎn)的弧度及背景顏色,希望他在點(diǎn)擊的時(shí)候能夠變成新的樣式,接下來還需要一個(gè) StateListDrawable 文件。我們?nèi)匀辉?drawable 目錄下創(chuàng)建文件:button_selector.xml,代碼如下:<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/button_pressd_background" android:state_pressed="true" /> <item android:drawable="@drawable/button_background" /></selector>這里采用<selector/>標(biāo)簽,直譯過來就是“選擇器”,即在不同狀態(tài)下選擇哪種樣式。最后我們將 Button 的android:background屬性指向 button_selector.xml 文件: android:background="@drawable/button_selector"大功告成,這時(shí)候只需要輕輕點(diǎn)擊 Button,就會(huì)發(fā)現(xiàn)神奇的現(xiàn)象:這樣是不是更有互動(dòng)感?采用 StateListDrawable 還可以指定很多的狀態(tài)變化,這里就留給大家去發(fā)明創(chuàng)造。

2. 創(chuàng)建一個(gè)自定義機(jī)器人

開啟自定義機(jī)器人步驟,首先找到想要加入群機(jī)器人的釘釘群,在【群設(shè)置】-【智能群助手】,進(jìn)入后如下圖所示。選擇【添加機(jī)器人】,進(jìn)入到添加界面,如下圖所示。在這里展示本群已加入的機(jī)器人,如需添加新的機(jī)器人,點(diǎn)擊【添加機(jī)器人】進(jìn)入到添加界面,如下圖所示。在添加界面,選擇自定義機(jī)器人,如下圖所示。配置項(xiàng)解釋:機(jī)器人名字:即添加到釘釘群中的機(jī)器人所顯示的名稱,本小節(jié)中命名為” 小 Q“添加到群組:即該機(jī)器人所加入的釘釘群,這里為只讀顯示,不可更改安全設(shè)置:自定義關(guān)鍵詞:即當(dāng)發(fā)送消息中包含至少一個(gè)指定關(guān)鍵詞才可以發(fā)送成功,最多可以設(shè)置 10 個(gè)關(guān)鍵詞。例如:添加了一個(gè)自定義關(guān)鍵詞:監(jiān)控報(bào)警,則這個(gè)機(jī)器人所發(fā)送的消息,必須包含 “監(jiān)控報(bào)警” 這個(gè)詞,才能發(fā)送成功。加簽:即通過時(shí)間戳 + 密鑰當(dāng)做簽名字符串,使用 HmacSHA256 算法計(jì)算簽名,然后進(jìn)行 Base64 encode,最后再把簽名參數(shù)再進(jìn)行 urlEncode,得到最終的簽名,在發(fā)送請(qǐng)求時(shí)帶著加密后的簽名進(jìn)行通信。IP 地址(段):即設(shè)定后,只有來自 IP 地址范圍內(nèi)的請(qǐng)求才會(huì)被正常處理。支持兩種設(shè)置方式:IP、IP 段。本小節(jié)中采用第一種自定義關(guān)鍵詞的形式進(jìn)行添加自定義機(jī)器人小 Q,配置內(nèi)容如下圖所示。勾選服務(wù)條款,點(diǎn)擊完成按鈕,如下圖所示。到這一步,我們就已經(jīng)完成了自定義機(jī)器人的創(chuàng)建,但只是第一步,第二步還需要在代碼中操作機(jī)器人。這里注意獲取到 Webhook 地址后,用戶可以向這個(gè)地址發(fā)起 HTTP 請(qǐng)求,即可實(shí)現(xiàn)給該釘釘群發(fā)送消息。Tips:在發(fā)起 HTTP 請(qǐng)求時(shí),必須將字符集編碼設(shè)置成 UTF-8。

1. TextView 的基本屬性

android:text:用來定義TextView上要展示的文本內(nèi)容。android:textSize:設(shè)置TextView顯示文本的字體大小。android:textColor:設(shè)置TextView顯示文本的顏色。android:textAllCaps:設(shè)置文本是否全是大寫,true 表示大寫,false 表示保持原樣。android:letterSpacing:設(shè)置每個(gè)字之間的間距android:hint:設(shè)置一個(gè)默認(rèn)文本作為,當(dāng)沒有設(shè)置文本內(nèi)容的時(shí)候會(huì)展示在 TextView 當(dāng)中。很多時(shí)候我們的 TextView 會(huì)根據(jù)服務(wù)端的內(nèi)容動(dòng)態(tài)設(shè)置,此時(shí)就可以通過android:hint為 TextView 設(shè)置一個(gè)默認(rèn)值,在沒有拉到服務(wù)器數(shù)據(jù)的時(shí)候展示默認(rèn)文本。以上是最常用的屬性,大多數(shù)場(chǎng)景已經(jīng)足夠,通過這些屬性我們就可以展示我們需要的文本信息了,如下:<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="wrap_content" android:padding="20dp" android:text="跟著超哥學(xué)Android" android:textColor="#CE4F2F" android:textSize="40sp" />注意:通常我們的 xml 最外層是一個(gè) ViewGroup,它幫我們擺放各種 View,但是如果只有一個(gè) View 的時(shí)候,是可以直接放在 xml 里面的。代碼比較簡(jiǎn)單,直接看效果:

3.1 配置文件

首先,我們?cè)趓esources目錄下新建mybatis-config.xml配置文件,并在其中添加上如下配置:<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/imooc?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments></configuration>有了上面編程式 API 的使用經(jīng)驗(yàn),那么你一定可以輕松的看懂配置項(xiàng),configuration 標(biāo)簽對(duì)應(yīng) Configuration 類,environment 標(biāo)簽對(duì)應(yīng) Environment 類,transactionManager標(biāo)簽和dataSource標(biāo)簽分別對(duì)應(yīng) JdbcTransactionFactory 和 PooledDataSource 類。有了配置文件后,我們無需一個(gè)挨著一個(gè)的新建類,而是在配置文件中指定即可,如driver的值指定為com.mysql.cj.jdbc.Driver。當(dāng)后續(xù)需要修改的時(shí)候,也不需要去代碼中找,而是直接在配置文件中修改即可。TIPS: 注意, 請(qǐng)?jiān)谀阕约旱呐渲梦募行薷臄?shù)據(jù)庫(kù)配置,以滿足你自己的數(shù)據(jù)庫(kù)環(huán)境。

6. 學(xué)習(xí)基礎(chǔ)

Netty 非常的復(fù)雜,不建議剛參加工作的同學(xué)直接學(xué)習(xí)它,這樣會(huì)給自己在學(xué)習(xí)上增加難度。列舉一些學(xué)習(xí) Netty 之前最好先掌握的技術(shù)點(diǎn):掌握多線程、線程池的使用;掌握傳統(tǒng) IO(BIO)的使用,以及了解網(wǎng)絡(luò) IO 和磁盤 IO 的基本使用;掌握 Socket 的客戶端和服務(wù)端之間通訊實(shí)現(xiàn),了解其缺點(diǎn)是什么;掌握 NIO 的思想,和 BIO 比較 NIO 的優(yōu)勢(shì)以及如何基于 NIO 去操作磁盤文件和網(wǎng)絡(luò)通訊,核心組件 Buffer、Channel、Selector 的使用;掌握什么是直接緩沖區(qū)、非直接緩沖區(qū)、零拷貝;掌握什么是序列化,序列化的原理、常用技術(shù);了解 TCP 協(xié)議、Http 協(xié)議之間的聯(lián)系、大概原理;了解 IO 的多路復(fù)用大致原理,Epoll 的大概原理;了解什么是長(zhǎng)連接、短連接的概念和區(qū)別,以及它們的應(yīng)用場(chǎng)景。以上是列出一些可以說必備的技術(shù)點(diǎn),掌握之后再去學(xué)習(xí) Netty 將會(huì)非常的容易。關(guān)注慕課網(wǎng)了解更多更優(yōu)質(zhì)的Netty教程。

3. 自定義站點(diǎn)外觀

默認(rèn)情況下,如果用戶想要自定義站點(diǎn)的外觀,需要在src/site目錄下創(chuàng)建 site.xml 文件,在該文件中定義其中的參數(shù)和配置。<project name="ximi-mall"> <!-- 定義左側(cè)banner --> <bannerLeft> <name>Sonatype</name> <src>http://idcbgp.cn/static/img/index/logo.png</src> <href>http://maven.apache.org/</href> </bannerLeft> <!-- 定義菜單欄 --> <body> <menu ref="reports"/> </body> <!-- 定義皮膚 --> <skin> <groupId>org.apache.maven.skins</groupId> <artifactId>maven-fluido-skin</artifactId> <version>1.9</version> </skin></project>這里,我們簡(jiǎn)單配置了 site.xml 文件,重新定義了站點(diǎn)的 Logo,以及站點(diǎn)的皮膚(skin)。重新打開站點(diǎn)后,樣式已經(jīng)發(fā)生了很大的變化。這里面的皮膚可以直接在 Maven 的官網(wǎng)中找到皮膚列表。在這其中選擇自己喜歡的皮膚類型。

3.1 XMLHttpRequest 的屬性

3.1.1 標(biāo)準(zhǔn)屬性屬性名備注onreadystatechange當(dāng) readyState 發(fā)生變化時(shí)候觸發(fā)readyState請(qǐng)求狀態(tài)碼。response返回一個(gè) Blob 、ArrayBuffer 、Document 或 DOMString。類型受 responseType 影響。responseText返回一個(gè) DOMString。請(qǐng)求不成功或者未發(fā)送情況下返回 null。responseType響應(yīng)類型。responseURL返回序列化 URL,URL 為空則返回空字符串。responseXML返回一個(gè) Document。請(qǐng)求未發(fā)送、不成功或者解析失敗,返回 null。status請(qǐng)求的響應(yīng)狀態(tài)。statusText返回一個(gè) DomString, 包含 http 響應(yīng)狀態(tài)。timeout超時(shí)。定義一個(gè)最大的請(qǐng)求響應(yīng)時(shí)間。ontimeout超時(shí)調(diào)用事件。upload上傳過程。withCredentials指定跨域請(qǐng)求是否帶有授權(quán)信息。3.1.2 非標(biāo)準(zhǔn)屬性屬性名備注channel一個(gè) nsIChannel,當(dāng)執(zhí)行請(qǐng)求時(shí),對(duì)象使用的通道。mozAnon布爾值。為 true 情況下,請(qǐng)求在將在沒有身份驗(yàn)證 header 頭和 cookie 的情況下發(fā)送。mozSystem布爾值。為 true 情況下,請(qǐng)求將不強(qiáng)制執(zhí)行同源策略。mozBackgroundRequest布爾值。指示是否是服務(wù)器的請(qǐng)求。

3.4 使用 Swagger2 生成在線 API 文檔

使用 Swagger2 生成在線文檔比較簡(jiǎn)單,直接在控制器方法上添加注解即可。如下:實(shí)例:@Api(tags = "商品API") // 類文檔顯示內(nèi)容@RestControllerpublic class GoodsController { @Autowired private GoodsService goodsService; @ApiOperation(value = "根據(jù)id獲取商品信息") // 接口文檔顯示內(nèi)容 @GetMapping("/goods/{id}") public GoodsDo getOne(@PathVariable("id") long id) { return goodsService.getGoodsById(id); } @ApiOperation(value = "獲取商品列表") // 接口文檔顯示內(nèi)容 @GetMapping("/goods") public List<GoodsDo> getList() { return goodsService.getGoodsList(); } @ApiOperation(value = "新增商品") // 接口文檔顯示內(nèi)容 @PostMapping("/goods") public void add(@RequestBody GoodsDo goods) { goodsService.addGoods(goods); } @ApiOperation(value = "根據(jù)id修改商品信息") // 接口文檔顯示內(nèi)容 @PutMapping("/goods/{id}") public void update(@PathVariable("id") long id, @RequestBody GoodsDo goods) { goods.setId(id); goodsService.editGoods(goods); } @ApiOperation(value = "根據(jù)id刪除商品") // 接口文檔顯示內(nèi)容 @DeleteMapping("/goods/{id}") public void delete(@PathVariable("id") long id) { goodsService.removeGoods(id); }}此時(shí)再次打開 http://127.0.0.1:8080/swagger-ui.htm ,會(huì)發(fā)現(xiàn)相關(guān)接口都已經(jīng)有文字描述了。Swagger2 生成在線 API 文檔

2. TLS服務(wù)認(rèn)證案例

Kubernets 是一個(gè)開源的容器編排工具,它內(nèi)部包含了多個(gè)職能組件。Kubernetes 提供了基于 CA 簽名的雙向認(rèn)證和基于 HTTP BASE 或 TOKEN 的認(rèn)證方式,其中 CA 是安全性最高的一種。(不了解 Kubernetes 的把他想成一個(gè)多組件服務(wù)的系統(tǒng)即可)Kubernetes 有上圖那么多組件,具體每個(gè)組件的用途我們這里不用去了解,其中 ApiServer 是一個(gè)核心服務(wù)。圖中服務(wù)需要的證書:Kube-APIserver對(duì)外提供服務(wù),要有一套 kube-apiserver server 證書;kube-scheduler、kube-controller-manager、kube-proxy、kubelet;和其他可能用到的組件,需要訪問 kube-APIserver,要有一套 kube-APIserver client 證書;kube-controller-manager 要生成服務(wù)的 service account,要有一對(duì)用來簽署 service account 的證書(CA證書);kubelet 對(duì)外提供服務(wù),要有一套 kubelet server 證書;kube-APIserver 需要訪問 kubelet,要有一套 kubelet client 證書;

2.4 七層負(fù)載均衡

七層負(fù)載均衡工作在 OSI 模型的應(yīng)用層,應(yīng)用層協(xié)議較多,常用 http、dns 等。七層負(fù)載就可以基于這些協(xié)議來負(fù)載。這些應(yīng)用層協(xié)議中會(huì)包含很多有意義的內(nèi)容。比如同一個(gè) Web 服務(wù)器的負(fù)載均衡,除了根據(jù) IP 加端口進(jìn)行負(fù)載外,還可根據(jù)七層的 URL、瀏覽器類別、語言來決定是否要進(jìn)行負(fù)載均衡。業(yè)界已經(jīng)有很多開源的負(fù)載均衡工具,大部分是工作在第四層和第七層的。代表的開源工具有Nginx/LVS/Haproxy。值得一提的是,LVS 是國(guó)內(nèi)前淘寶網(wǎng)高級(jí)研究員章文嵩博士的作品。LVS 主要用來做四層負(fù)載均衡,Nginx 和 Haproxy 主要是做七層的負(fù)載均衡,但它們都支持四層的負(fù)載均衡,例如在 Nginx 中的 stream 模塊除了支持四層的反向代理功能,也支持四層負(fù)載均衡功能。

3. 打印自己的 Gradle 日志

如果我們要打印編譯日志,我們要怎么做呢?Gradle 也為我們提供了一個(gè) logger 屬性,它是一個(gè) Logger 實(shí)例。我們?cè)?build.gradle 中定義一個(gè)名為 logTest 的任務(wù),打印不同級(jí)別的日志。如下所示:task logTest{ doLast{ logger.debug("This is Debug Log Message") logger.info("This is Info Log Message") logger.warn("This is Warn Log Message") logger.lifecycle("This is Lifecycle Log Message") logger.quiet("This is Quiet Log Message") logger.error("This is Erroe Log Message") }}我們按照上面所降到的 我們不添加任何的額外命令,直接執(zhí)行g(shù)radle logTest 我們看下輸出,我們會(huì)發(fā)現(xiàn)只輸出了 LIFECYCLE 級(jí)別以上的日志:$ gradle logTest> Configure project :orderWARNING: Configuration 'compile' is obsolete and has been replaced with 'implementation' and 'api'.It will be removed at the end of 2018. For more information see: http://d.android.com/r/tools/update-dependency-configurations.html> Task :app:logTestThis is Warn Log MessageThis is Lifecycle Log MessageThis is Quiet Log MessageThis is Erroe Log MessageDeprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.Use '--warning-mode all' to show the individual deprecation warnings.See https://docs.gradle.org/6.0.1/userguide/command_line_interface.html#sec:command_line_warningsBUILD SUCCESSFUL in 1s1 actionable task: 1 executed那么下面我們?cè)?gradle 后依次添加 -q 、-i 、-d 命令來過濾不同級(jí)別的日志。

4. 數(shù)據(jù)傳輸

然后我們就用 gin 來寫一個(gè) post 服務(wù) check 用來接收驗(yàn)證登錄頁面發(fā)送過來的賬號(hào)密碼。代碼示例:package mainimport ( "fmt" "net/http" "github.com/gin-gonic/gin")func main() { router := gin.Default() router.LoadHTMLGlob("view/*") router.GET("/index", index) router.POST("/check", check) router.Run("127.0.0.1:9300")}func index(c *gin.Context) { c.HTML(http.StatusOK, "index.html", nil)}func check(c *gin.Context) { accountID, _ := c.GetPostForm("username") password, _ := c.GetPostForm("password") fmt.Println(accountID, password) if accountID == "Codey" && password == "12345" { //跳轉(zhuǎn)到主頁 c.HTML(http.StatusOK, "home.html", nil) } else { //跳轉(zhuǎn)到登錄 c.Writer.Write([]byte("<script>alert('賬號(hào)或者密碼不正確')</script>")) c.HTML(http.StatusOK, "index.html", nil) }}home.html 代碼如下:<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>Go語言實(shí)戰(zhàn)2</title></head><body> <div> <h3>主頁</h3> 這里是主頁 </div></body></html>執(zhí)行上述 Go 語言代碼,在瀏覽器中輸入127.0.0.1:9300/index。輸入正確的賬號(hào):Codey,密碼:12345然后點(diǎn)擊登錄,會(huì)跳轉(zhuǎn)到主頁若輸入錯(cuò)誤的賬號(hào)密碼,則不跳轉(zhuǎn)隨后跳轉(zhuǎn)回登錄頁面在 gin 中一個(gè)簡(jiǎn)易的登錄功能就搭建完成了。

3. Postman 特性

工作空間:支持軟件開發(fā)期間的團(tuán)隊(duì)協(xié)作用 Postman 的免費(fèi)賬號(hào)就可以創(chuàng)建個(gè)人工作空間,你可以邀請(qǐng)團(tuán)隊(duì)其他成員加入該工作空間,在該工作空間協(xié)同合作。集合:按目的組織的一系列 HTTP 請(qǐng)求集合是最常用的API格式。使用集合可以將請(qǐng)求、參數(shù)、描述、測(cè)試和腳本都組織在一個(gè)文件夾中??梢酝ㄟ^共享集合在 API 上進(jìn)行協(xié)作,并在集合上構(gòu)建測(cè)試套件、文檔、模擬服務(wù)器和監(jiān)視器。環(huán)境:私人信息的存儲(chǔ)和保護(hù)為了在不同的服務(wù)器上運(yùn)行 API,可以在 Postman 中使用環(huán)境變量。這些變量幾乎可以在任何地方使用。使用雙括號(hào)訪問變量。測(cè)試腳本:使用 JavaScript 為每個(gè)請(qǐng)求編寫和運(yùn)行測(cè)試。一個(gè)測(cè)試 API 是否返回 JSON 字符串“127.0.0.1”的測(cè)試腳本:pm.test("IP Address", function(){ pm.expect(pm.response.json()).to.equal("127.0.0.1"); });變量——允許引用本地存儲(chǔ)的數(shù)據(jù)而不泄露敏感信息

4. 真實(shí)案例分享

北京大學(xué)官網(wǎng)<table> <thead> <tr> <th>課號(hào)</th> <th>課程名稱</th> <th>開課單位</th> <th>學(xué)分</th> <th>總周數(shù)<br>(起止周)</th> <th>課程類型</th> <th>上課時(shí)間</th> <th>班號(hào)</th> <th>上課教師</th> <th>備注</th> </tr> </thead> <tbody> <tr> <td>01132632</td> <td><p style="text-align:center;padding:5px;"><a target="_blank">生物化學(xué)討論課<br>Current topics on Biochemistry</a></p></td> <td>生命科學(xué)學(xué)院</td> <td>2</td> <td>2(1-2)</td> <td>A</td> <td><p>星期一(第10節(jié)-第12節(jié))</br>星期二(第10節(jié)-第12節(jié))</br>星期三(第10節(jié)-第12節(jié))</br>星期四(第10節(jié)-第12節(jié))</br>星期五(第10節(jié)-第12節(jié))</br>星期六(第10節(jié)-第12節(jié))</br></p></td> <td>1</td> <td>鐘上威</td> <td><p>6月29-7月7日,10-12節(jié),選修同學(xué)需先修生物化學(xué)理論課</p></td> </tr> <tr> <td>01132022</td> <td><p><a target="_blank">遺傳學(xué)討論<br>Current topics on Genetics</a></p></td> <td>生命科學(xué)學(xué)院</td> <td>2</td> <td>2(3-4)</td> <td>A</td> <td><p>星期一(第10節(jié)-第12節(jié))</br>星期二(第10節(jié)-第12節(jié))</br>星期三(第10節(jié)-第12節(jié))</br>星期四(第10節(jié)-第12節(jié))</br>星期五(第10節(jié)-第12節(jié))</br></p></td> <td>1</td> <td>范六民</td> <td><p>上課時(shí)間:7月13日-7月24日,10-12節(jié)。選修同學(xué)需先修遺傳學(xué)理論課</p></td> </tr> </tbody></table>

3.2 XML 方式

XML 方式是更加強(qiáng)大和易用的一種方式,雖然它沒有注解那么方便,但是功能更強(qiáng)、更易維護(hù),是 MyBatis 官方推薦的一種方式。在 mapper 包中,我們新建另一個(gè)文件UserMapper.xml,并添加如下內(nèi)容:<?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="com.imooc.mybatis.mapper.UserMapper"></mapper>mapper 標(biāo)簽對(duì)應(yīng)一個(gè) mapper 接口類,這里應(yīng)該對(duì)應(yīng) UserMapper,所以在 mapper 標(biāo)簽里面我們還需要加上 namespace 這個(gè)屬性,它的值為 UserMapper 的類全路徑,這樣 UserMapper.xml 配置文件就與 UserMapper.java 對(duì)應(yīng)起來了。提示,namespace 命名空間是每一個(gè) mapper 文件所獨(dú)有的,它唯一標(biāo)識(shí)著一個(gè) mapper。注意: 在這里,.xml 配置文件必須與其對(duì)應(yīng)的接口在同一個(gè)包內(nèi)。二者在目錄中的位置如下:src/main/java/com/imooc/mybatis/mapper├── UserMapper.java└── UserMapper.xml在 UserMapper 接口中,我們?cè)傩略鲆粋€(gè)方法selectUserAgeById,該方法的作用是通過用戶 id 查詢用戶年齡。如下:package com.imooc.mybatis.mapper;import org.apache.ibatis.annotations.Select;public interface UserMapper { @Select("SELECT username FROM imooc_user WHERE id = #{id}") String selectUsernameById(Integer id); Integer selectUserAgeById(Integer id);}與之對(duì)應(yīng)的 xml 文件中,我們也需要添加上對(duì)應(yīng)的 SQL 語句。如下:<?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="com.imooc.mybatis.mapper.UserMapper"> <select id="selectUserAgeById" resultType="java.lang.Integer"> SELECT age FROM imooc_user WHERE id = #{id} </select></mapper>在 mapper 標(biāo)簽中,我們新增了 select 標(biāo)簽,對(duì)應(yīng) SQL 中的 select 查詢;select 標(biāo)簽中有兩個(gè)必填屬性,第一個(gè)是 id ,它對(duì)應(yīng)接口的方法名,即 selectUserAgeById,通過它 MyBatis 才能將二者對(duì)應(yīng)起來,第二個(gè)是 resultType,它對(duì)應(yīng) SQL 語句的返回類型,與接口方法的返回值相同,為 Integer 類型。好了,注解和 XML 的兩種方式的簡(jiǎn)單使用已經(jīng)介紹完畢了,這里仍然有一個(gè)可以完善的點(diǎn),我們可以為 UserMapper 類打上一個(gè) Mapper注解,雖然這個(gè)注解并不是必須的,但是增強(qiáng)了代碼的可讀性。如下:// 省略import org.apache.ibatis.annotations.Mapper;@Mapperpublic interface UserMapper { // 省略其它諸多代碼}

3.3 調(diào)用 render 函數(shù)返回

HTML 文本或者模板文件。其實(shí),通過查看 Django 的源代碼,可以看到 render 函數(shù)會(huì)調(diào)用 loader.render_to_string() 方法將 html 文件轉(zhuǎn)成 string,然后作為 content 參數(shù)傳遞給 HttpResponse 類進(jìn)行實(shí)例化:# django/shortcuts.pydef render(request, template_name, context=None, content_type=None, status=None, using=None): """ Return a HttpResponse whose content is filled with the result of calling django.template.loader.render_to_string() with the passed arguments. """ content = loader.render_to_string(template_name, context, request, using=using) return HttpResponse(content, content_type, status)它的用法如下:# 第一步,在django工程中準(zhǔn)備一個(gè)靜態(tài)文件,放到templates目錄下(django-manual) [root@server first_django_app]# cat templates/index.html <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h1>這是首頁</h1></body></html># 第二步,在first_django_app/setting.py文件,指定靜態(tài)資源的目錄(django-manual) [root@server first_django_app]# cat first_django_app/settings.py...TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 指定項(xiàng)目目錄下的templates目錄 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, },]...# 第三步,添加視圖函數(shù)以及URLconf,位置first_django_app/urls.pydef index(request, *args, **kwargs): return render(request, "index.html")urlpatterns = [ path('admin/', admin.site.urls), path('index/', index),]就這樣一個(gè)簡(jiǎn)單的配置,我們請(qǐng)求 /index/ 路徑時(shí),會(huì)返回 index.html 文件內(nèi)容:[root@server ~]# curl "http://127.0.0.1:8881/index/"<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h1>這是首頁</h1></body></html>另一方面,index.html 還可以是一個(gè)模板文件,我們通過 render 函數(shù)最后會(huì)將該模板文件轉(zhuǎn)成一個(gè)完整的 HTML 文件,返回給客戶端:# index.html(django-manual) [root@server first_django_app]# cat templates/index.html <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h1>{{ title }}</h1><p>{{ content }}</p></body></html># 修改 urls.py 中的視圖函數(shù)(django-manual) [root@server first_django_app]# cat first_django_app/urls.py...def index(request, *args, **kwargs): return render(request, "index.html", {"title":"首頁", "content": "這是正文"}) ...最后請(qǐng)求結(jié)果可以看到完整的 HTML 文本:[root@server ~]# curl "http://127.0.0.1:8881/index/"<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h1>首頁</h1><p>這是正文</p></body></html>

3. 小結(jié)

本小節(jié)介紹了如何使用 REGEXP 正則表達(dá)式查詢字段匹配符合正則表達(dá)式的數(shù)據(jù),列舉了正則表達(dá)式的元字符,介紹了在 MySQL 查詢中正則表達(dá)式的基本匹配用法,需要注意的是正則表達(dá)式更多具體知識(shí)這里不做詳細(xì)的介紹,這里主要介紹如何在查詢 sql 語句中如何對(duì)字段進(jìn)行已有的正則表達(dá)式正則匹配,下表列舉了幾個(gè)正則表達(dá)式:(1) 11手機(jī)號(hào)正則表達(dá)式:^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$(2) 域名網(wǎng)址正則表達(dá)式^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$(3) 日期+時(shí)間正則表達(dá)式^[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\s+(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$更多的正則表達(dá)式可根據(jù)自己的需求來改寫。

1.1 RadioButton 的基本用法

當(dāng)你的 App 需要提供幾個(gè)選項(xiàng)讓用戶做單選的時(shí)候,RadioButton 毫無疑問是最佳選擇。 RadioButton 需要配合 RadioGroup 一起使用, RadioGroup 可以包含一個(gè)或若干個(gè) RadioButton ,每個(gè) RadioButton 對(duì)應(yīng)一個(gè)選項(xiàng)可供用戶點(diǎn)擊,而一個(gè) RadioGroup 中只有一個(gè) RadioButton 可以進(jìn)入點(diǎn)擊態(tài)。比如我們做一個(gè)二選一的單選框,布局代碼如下:<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/group" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="20dp"> <RadioButton android:id="@+id/rb_male" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="男" /> <RadioButton android:id="@+id/rb_female" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="女" /></RadioGroup>在代碼中我們?cè)O(shè)置了一個(gè) RadioGroup ,里面包含兩個(gè) RadioButton 分別表示“男”、“女”兩個(gè)選項(xiàng),最終用戶只能選擇其一,效果如下:注意:類似的功能我們還可以用 Spinner View 實(shí)現(xiàn),與之不同的是 Spiner View 只會(huì)展示當(dāng)前選中的選項(xiàng),其他選項(xiàng)會(huì)被收集到下拉列表中,具體的使用我們會(huì)在后面的教程中詳細(xì)介紹。

3.4 服務(wù)端處理流程

假設(shè)瀏覽器訪問 URL http://www.bbs.com/topics/12373,處理的邏輯如下圖所示:服務(wù)端收到 /topics/12373 的頁面請(qǐng)求;查詢合適的函數(shù)處理路徑為 /topics/12373 的頁面;showTopic 處理形式為 /topics/xxx 的頁面請(qǐng)求;showUser 處理形式為 /users/xxx 的頁面請(qǐng)求;因此選擇 showTopic 來處理此次頁面請(qǐng)求。將頁面路徑拆分為 /topics 和 12373,把 12373 作為 topicID;將 topicID 傳遞給 showTopic,執(zhí)行該函數(shù)。對(duì)每一個(gè)頁面處理請(qǐng)求都需要進(jìn)行如上的 4 步處理,在這 4 個(gè)步驟中,只有第 4 步才是應(yīng)用關(guān)心的業(yè)務(wù)邏輯。如果基于 Web 框架開發(fā) Web 應(yīng)用,Web 框架完成第 1 步、第 2 步、第 3 步的工作,應(yīng)用程序完成第 4 步的工作。即應(yīng)用程序只需要編寫具體處理頁面的函數(shù),其余繁瑣的、與業(yè)務(wù)無關(guān)的工作交給 Web 框架,因此使用 Web 框架可以大大地減輕開發(fā)的工作量。

3.1 集成思路

SAML 2.0 認(rèn)證登錄,可以理解為 SP 從 IDP 獲取 XML 格式斷言消息的過程。目前有兩種認(rèn)證流程:IDP 端發(fā)起方式。首先用戶直接在 IDP(例如 Okta 認(rèn)證中心)登錄,然后選擇一個(gè)將要授權(quán)的 SP(例如 Web 應(yīng)用),IDP 隨后發(fā)送斷言消息到 SP。SP 發(fā)起方式。用戶首先訪問一個(gè) SP,SP 向 IDP 發(fā)現(xiàn)認(rèn)證請(qǐng)求,IDP 要求用戶登錄,如果登錄成功,IDP 將發(fā)送斷言消息到 SP。當(dāng)前 Spring Security 對(duì) SAML 2.0 已支持的特性包括:通過 entityId = {baseUrl}/saml2/service-provider-metadata/{registrationId} 形式聲明 SP;通過 HTTP POST \ Redirect 方法,從 {baseUrl}/login/saml2/sso/{registrationId} 接收 SAML 響應(yīng)中的認(rèn)證斷言;斷言簽名;支持?jǐn)嘌詢?nèi)容加密;支持對(duì) Name ID 元素進(jìn)行加密;支持將認(rèn)證斷言的屬性轉(zhuǎn)換為 Converter<Assertion, Collection<? extends GrantedAuthority>> 對(duì)象;允許使用 GrantedAuthoritiesMapper 管理權(quán)限白名單;使用 java.security.cert.X509Certificate 公鑰格式;SP 通過 AuthNRequest 初始化認(rèn)證流程。

1. 簡(jiǎn)介

瀏覽器訪問服務(wù)端,需要將相應(yīng)的數(shù)據(jù)發(fā)送給服務(wù)端,可能有如下場(chǎng)景:通過 URL 參數(shù)進(jìn)行查詢,瀏覽器需要將查詢參數(shù)發(fā)送給服務(wù)端提交表單 form 進(jìn)行查詢,瀏覽器需要將表單 form 中的字段發(fā)送給服務(wù)端上傳文件,瀏覽器需要將文件發(fā)送給服務(wù)端服務(wù)端收到將客戶端發(fā)送的數(shù)據(jù)后,封裝形成一個(gè)請(qǐng)求對(duì)象,在 Flask 中,請(qǐng)求對(duì)象是一個(gè)模塊變量 flask.request,它包含了如下常用屬性:屬性說明method當(dāng)前的請(qǐng)求方法form表單參數(shù)及其值的字典對(duì)象args查詢字符串的字典對(duì)象values包含所有數(shù)據(jù)的字典對(duì)象json如果 mimetype 是 application/json,這個(gè)參數(shù)將會(huì)解析 json 數(shù)據(jù),如果不是則返回 Noneheadershttp 協(xié)議頭部cookiescookie 名稱和值的字典對(duì)象files與上傳文件有關(guān)的數(shù)據(jù)假設(shè) URL 等于 http://localhost/query?userId=123,request 對(duì)象中與 URL 參數(shù)相關(guān)的屬性如下:屬性說明urlhttp://localhost/query?userId=123base_urlhttp://localhost/queryhostlocalhosthost_urlhttp://localhost/path/queryfull_path/query?userId=123

1. a 標(biāo)簽的使用

a 標(biāo)簽為雙標(biāo)簽,需要有首尾標(biāo)簽。a 標(biāo)簽的 href 屬性為必填屬性,表示該 a 標(biāo)簽點(diǎn)擊過后跳轉(zhuǎn)網(wǎng)頁的地址。例如:<a href="http://idcbgp.cn/">去往慕課網(wǎng)</a>在網(wǎng)頁中會(huì)呈現(xiàn)以下效果:注意:a 標(biāo)簽的 href 屬性必須添加網(wǎng)絡(luò)協(xié)議,如:HTTP 或者 HTTPS 協(xié)議,一般為 HTTPS 協(xié)議,不能單純的編寫網(wǎng)址;a 標(biāo)簽有很多默認(rèn)樣式,默認(rèn)字體為藍(lán)色,文本有下劃線,點(diǎn)擊過后,字體變?yōu)槠仙?;a 標(biāo)簽?zāi)J(rèn)在本頁面跳轉(zhuǎn),既不會(huì)新開一個(gè)網(wǎng)頁跳轉(zhuǎn);a 標(biāo)簽的 href 屬性如果為錯(cuò)誤的網(wǎng)絡(luò)地址,則頁面會(huì)跳轉(zhuǎn),但不會(huì)顯示網(wǎng)頁的內(nèi)容。a 標(biāo)簽還有一個(gè)屬性為 target,表示跳轉(zhuǎn)的網(wǎng)頁為在當(dāng)前頁面跳轉(zhuǎn),還是新開一個(gè)頁面跳轉(zhuǎn)。默認(rèn)值為 _self,表示為在當(dāng)前頁面跳轉(zhuǎn),如果將 a 標(biāo)簽的 target 屬性設(shè)置為 _blank,則表示新開一個(gè)網(wǎng)頁跳轉(zhuǎn),代碼如下:<a href="http://idcbgp.cn/" target="_blank">去往慕課網(wǎng)</a>a 標(biāo)簽的 target 屬性為可選屬性。

2.1 使用 gunicorn 工具

gunicorn 是一個(gè) Unix 上被廣泛使用的高性能的 Python WSGI UNIX HTTP Server。和大多數(shù)的 Web 框架兼容,并具有實(shí)現(xiàn)簡(jiǎn)單,輕量級(jí),高性能等特點(diǎn)。用它部署 Flask/Django 這樣的 Python Web 項(xiàng)目再合適不過了。它的安裝和使用都十分方便,安裝直接在虛擬環(huán)境下執(zhí)行: pip install gunicorn -i https://pypi.tuna.tsinghua.edu.cn/simple,使用有兩種方式:直接使用:# 參數(shù)說明:# -b: 啟動(dòng)綁定ip和端口,0.0.0.0 是指運(yùn)行外面的所有機(jī)器訪問# -w: 啟動(dòng) worker 進(jìn)程數(shù)# --daemon: 后臺(tái)啟動(dòng)(django-manual) [root@server first_django_app]# gunicorn first_django_app.wsgi -b 0.0.0.0:8888 --daemon -w 4配置文件使用:(django-manual) [root@server first_django_app]# cat gunicorn_config.py# gunicorn_config.pyimport loggingimport logging.handlersfrom logging.handlers import WatchedFileHandlerimport osimport multiprocessingbind = '0.0.0.0:8888'# backlog: 服務(wù)器中在pending狀態(tài)的最大連接數(shù),即client處于waiting的數(shù)目。超過這個(gè)數(shù)目, client連接會(huì)得到一個(gè)errorbacklog = 512timeout = 30 workers = multiprocessing.cpu_count() * 2threads = 2loglevel = 'info' access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' accesslog = "/var/log/django-manual/gunicorn_access.log" errorlog = "/var/log/django-manual/gunicorn_error.log"(django-manual) [root@server first_django_app]# gunicorn first_django_app.wsgi -c gunicorn_config.py --daemon關(guān)閉進(jìn)程:# 簡(jiǎn)單粗暴的關(guān)閉方式(django-manual) [root@server first_django_app]# killall gunicorn

3. 屬性 properties

通過 properties 配置,我們可以將一些重要的配置屬性抽離到其它的 .properties 文件。比如,dataSource 中的數(shù)據(jù)庫(kù) url、用戶名和密碼,我們可以單獨(dú)以 datasource.properties 文件來存儲(chǔ),然后在 mybatis-config.xml 文件中導(dǎo)入使用。在 resources 目錄下新建 datasource.properties 文件,并填入以下內(nèi)容:url=jdbc:mysql://localhost:3306/imooc?useSSL=falseusername=rootpassword=123456然后在 mybatis-config.xml 文件中通過 properties 配置來引入 datasource.properties 文件:<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <!-- 引入datasource.properties --> <properties resource="datasource.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!-- 占位符動(dòng)態(tài)替換配置 --> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments></configuration>通過 properties 中的 resource 屬性引入 datasource.properties 后,我們就可以使用占位符的方式去動(dòng)態(tài)替換配置,如 ${url},表示從 datasource.properties 文件中取出 url 項(xiàng)并填充在此處。它們?cè)谀夸浿械奈恢萌缦拢簊rc/main/resources├── datasource.properties├── mybatis-config.xml

2.2 ImageView 的縮放裁剪模式

在實(shí)際開發(fā)過程中,大多數(shù)場(chǎng)景我們都是沒辦法保證每張圖片的尺寸比例都一致的,所以需要有大量的縮放和裁剪,如何讓縮放裁剪的適配更加得心應(yīng)手,就需要用到 ImageView 的另一個(gè)關(guān)鍵屬性:android:scaleType。這里我之所以稱之為縮放裁剪模式,就是要強(qiáng)調(diào)這個(gè)屬性的兩個(gè)維度:是否改變圖片比例以及超出部分如何裁剪。其實(shí)scaleType這個(gè)屬性要規(guī)范的就是這兩個(gè)維度,下面就從這兩個(gè)維度來學(xué)習(xí):matrix:按照矩陣方式縮放。好吧說人話就是不調(diào)整圖片大小,從左上角開始往右下角繪制,如果超出的 ImageView 的范圍則直接舍棄。不改比例、會(huì)裁剪。fitXY:從橫縱兩個(gè)方向?qū)D片進(jìn)行縮放,以占滿整個(gè) ImageView,可以參考android:background的樣式。改比例、不裁剪。fitStart:將圖片等比例縮放,直至能夠完全顯示,然后將圖片至于 ImageView 的左上角。不改比例、不裁剪。fitCenter:和 fitStart 類似,只不過會(huì)將圖片居中放置。不改比例、不裁剪。fitEnd:和 fitStart 類似,只不過會(huì)將圖片放在 ImageView 的右下角。同樣不改比例、不裁剪。center:非常粗暴的直接將圖片原封不動(dòng)的放到 ImageView 中央,多余部分裁剪掉。不改比例、裁剪。centerCrop:等比例縮放圖片,直至圖片能夠完全占滿 ImageView ,注意占滿之后多余部分會(huì)被裁剪掉。不改比例、裁剪。centerInside:保持原始比例的縮放圖片,直至能夠完整顯示圖片的內(nèi)容。不改比例、不裁剪。以上的樣式都還比較好理解,下面我們來寫段代碼測(cè)試一下幾種具有代表性的樣式:<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:background="#CC1010" android:scaleType="matrix" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="right" android:background="#CC1010" android:scaleType="fitXY" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center_vertical" android:background="#CC1010" android:scaleType="fitStart" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center_vertical|right" android:background="#CC1010" android:scaleType="center" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="bottom" android:background="#CC1010" android:scaleType="centerCrop" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="bottom|right" android:background="#CC1010" android:scaleType="centerInside" android:src="@drawable/image" /></FrameLayout>上面我們介紹了 8 種屬性,其中fitStart、fitCenter以及fitEnd三個(gè)屬性只是擺放的位置不同,其余完全一樣,所以只選擇fitStart做樣例。那么去掉fitCenter和fitEnd之后,按照順序我們對(duì)于 6 種屬性按左右排列了 6 張圖片,效果如下:大家可以對(duì)比 2.2 小節(jié)的屬性描述及效果圖學(xué)習(xí)不同的縮放模式的不同樣式。

3. 內(nèi)部pip源搭建

搭建好 yum 源之后,我們來看 pip 源的搭建過程,其實(shí)方式也是差不多。不過這里我們要使用一個(gè)開源的工具來幫我們做這件事情(類似于前面的 createrepo ),這樣會(huì)使得 pip 源的搭建非常方便。下載開源工具$ yum install git -y# 如果機(jī)器無法聯(lián)網(wǎng),則直接去下載最新的包,然后傳到主機(jī)上去$ git clone https://github.com/wolever/pip2pi$ cd pip2pi$ python setup.py install使用該工具安裝好該工具后,有兩個(gè)非常好用的命令:# 這樣子,會(huì)將python模塊連同依賴的模塊全部下載下來,這些文件構(gòu)成了我們pip源的一部分$ pip2tgz /data/pip_source/ ansible -i https://pypi.tuna.tsinghua.edu.cn/simple $ pip2tgz /data/pip_source/ -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple# 目前來看,好像是生成了一個(gè)simple目錄,然后有index.html$ dir2pi /data/pip_source/注意:如果出現(xiàn) “TypeError: ‘module’ object is not callable” 這樣的錯(cuò)誤信息,主要是pip版本太高導(dǎo)致的,需要將 pip 降級(jí)。解決方案如下:# 卸載高版本的pip$ python -m pip uninstall pip# yum安裝舊版本的pip$ yum install python-pip就是簡(jiǎn)簡(jiǎn)單單的這幾個(gè)命令,pip 源文件已經(jīng)準(zhǔn)備好了。我們找臺(tái)能聯(lián)網(wǎng)的機(jī)器,部署該命令,然后下載我們想要的 python 模塊文件及其依賴。最后使用 dir2pi 給這個(gè) pip 目錄生成索引以及 html 文件。使用nginx做靜態(tài)資源服務(wù)器最后就是將這個(gè)目錄打包并上傳到對(duì)應(yīng)的 ceph1 上去,放到 /data 目錄下,解壓得到 pip 源目錄 /data/pip_source。接下來,使用 nginx 作為靜態(tài)資源服務(wù)器,將某個(gè)端口的根路徑指向這里即可。為此,在 nginx 中在添加一個(gè)端口的配置:# 這個(gè)和yum還不一樣,因?yàn)?data/pip_source下有生成了的index.html頁面,在simple目錄中server { listen 8088; server_name localhost; root /data/pip_source; autoindex on; autoindex_exact_size off; autoindex_localtime on; location /{ }}修改 nginx 的配置后重啟,再訪問 ceph1 的8088端口,結(jié)果如下:可以回過頭來,看看網(wǎng)上第三方 pip 源的地址,都是帶上了 simple ,比如:清華源: https://pypi.tuna.tsinghua.edu.cn/simple阿里源: https://mirrors.aliyun.com/pypi/simple豆瓣源: http://pypi.douban.com/simple這樣子的形式和我們搭建的 pip 源好像一致,難道她們也是用這樣的工具搭建 pip 源?使用內(nèi)部pip源使用方式和前面使用第三方 pip 源一樣,加上-i參數(shù),指定 pip 源地址和端口,如下命令是在 ceph2 上用pip安裝 ansible 工具,使用的正是ceph1作為 pip 源。$ pip install ansible -i http://172.16.0.8:8088/simple/ --trusted-host 172.16.0.8從執(zhí)行結(jié)果看,我們成功使用內(nèi)部的 pip 源安裝了 ansible 工具,而且下載速度非???,達(dá)到了百兆每秒的速度,其下載速度瓶頸主要在于內(nèi)部的網(wǎng)絡(luò)帶寬。

1. Scrapy Shell 介紹

Scrapy Shell 是一個(gè)交互終端,類似于 Python 交互式模式,它使我們可以在未啟動(dòng) Scrapy 爬蟲的情況下調(diào)試爬蟲代碼。在 Scrapy 的交互模式下,我們可以直接獲取網(wǎng)頁的 Response 結(jié)果,然后使用 XPath 或 CSS 表達(dá)式來獲取網(wǎng)頁元素,并以此測(cè)試我們獲取網(wǎng)頁數(shù)據(jù)的 Xpath 或者 CSS 表達(dá)式,確保后續(xù)執(zhí)行時(shí)能正確得到數(shù)據(jù)。我們來看看如何進(jìn)入 shell 模式,參考如下的視頻:80在 Scrapy 框架中內(nèi)置了 Selector 選擇器,這個(gè)選擇器是屬于 parsel 模塊的,而 parsel 模塊是由 Scrapy 團(tuán)隊(duì)為解析網(wǎng)頁而開發(fā)的,并且獨(dú)立出來形成了一個(gè)第三方模塊。這樣我們可以在自己的爬蟲程序中使用 parsel 模塊而不必基于 Scrapy 框架 。我們從源碼中來看看這個(gè)選擇器類的定義??梢钥吹?Selector 類有我們熟悉的 xpath() 、css()、re() 以及 extract() 等方法,這些是我們解析網(wǎng)頁的基礎(chǔ)。Selector 類方法我們來思考一個(gè)問題,然后去源碼中找到答案:(scrapy-test) [root@server ~]# scrapy shell https://gz.lianjia.com/ershoufang/ --nolog...>>> type(response)<class 'scrapy.http.response.html.HtmlResponse'>我們?cè)?Scrapy Shell 中可以看到 response 是 HtmlResponse 的一個(gè)實(shí)例,它是怎么會(huì)有 Selector 的方法的?我們?cè)谇懊娴?Scrapy 初步的實(shí)例中看到過 response.xpath() 這樣的用法,源碼里面是怎么做的呢?這個(gè)問題比較簡(jiǎn)單,我們翻看一下源碼就可以找到答案了。首先查看 HtmlResponse 類定義:# 源碼位置:scrapy/http/response/html.pyfrom scrapy.http.response.text import TextResponseclass HtmlResponse(TextResponse): pass這個(gè)夠不夠簡(jiǎn)單?繼續(xù)追看 TextResponse 的定義:# 源碼位置:scrapy/http/response/text.py# ...class TextResponse(Response): # ... @property def selector(self): from scrapy.selector import Selector if self._cached_selector is None: self._cached_selector = Selector(self) return self._cached_selector # ... def xpath(self, query, **kwargs): return self.selector.xpath(query, **kwargs) def css(self, query): return self.selector.css(query) # ...是不是一下子就明白了?response.xpath() 正是調(diào)用的 scrapy.selector 下的Selector,繼續(xù)看這個(gè) Selector 的定義:# 源碼位置:scrapy/selector/unified.pyfrom parsel import Selector as _ParselSelectorclass Selector(_ParselSelector, object_ref): # ...是不是最后又到了 parsel 下的 Selector?所以使用 response.xpath() 等價(jià)于使用 parsel 模塊 下的 Selector 類中的 xpath 方法去定位網(wǎng)頁元素。在給大家留一個(gè)更進(jìn)一步的問題:在前面爬取互動(dòng)出版網(wǎng)的 Scrapy 框架實(shí)例中,我們還用到了這樣的表達(dá)式:book.xpath().extract()[0] 和 book.xpath().extract_first(),這樣的代碼執(zhí)行過程又是怎樣的呢(追蹤到 parsel 這一層即可)?這個(gè)問題追蹤的代碼會(huì)比上面多一點(diǎn),但也不復(fù)雜,這個(gè)問題會(huì)在下一節(jié)的 Reponse 類分析中給出相應(yīng)的回答。

直播
查看課程詳情
微信客服

購(gòu)課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號(hào)

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號(hào)