Unity中用Mesh畫一個圓環(二)

中目標-生成完整面

在之前的內容中我們已經成功生成了一個面,接下來我們要生成剩下的面就很容易了。

我們把之前生成的面當作頂面,接着我們來生成底面。

還記得前面說過\(\color{#1E90FF}{Depth}\)這個參數用來控制深度,也就是頂面和地面之間的距離,放到坐標系中就是控制Z的位置。

底面和頂面的頂點生成方法是一樣的,唯一不同的地方就是Z軸的不同。 我們只要用生成頂面的方法改下Z坐標,就可以得到底面了。

//下
        for (int i = 0; i <= NumberOfSides; i++)
        {
            float angle = 180 - i * incrementAngle;
            float innerX = (Radius - Thickness) * Mathf.Cos(angle * Mathf.Deg2Rad);
            float innerY = (Radius - Thickness) * Mathf.Sin(angle * Mathf.Deg2Rad);
            vertexList.Add(new Vector3(innerX, innerY, -1 * Depth / 2));
            float outsideX = Radius * Mathf.Cos(angle * Mathf.Deg2Rad);
            float outsideY = Radius * Mathf.Sin(angle * Mathf.Deg2Rad);
            vertexList.Add(new Vector3(outsideX, outsideY, - 1 * Depth / 2));
        }

三角形索引的生成和之前也是一樣的,不同的是一開始的方向,因為頂面的法線是向上的,而底面的法線是向下的:

  direction = 1;
        startIndex += (NumberOfSides + 1) * 2;
        for (int i = 0; i < NumberOfSides * 2; i++)
        {
            int[] triangleIndexs = getTriangleIndexs(i, direction, startIndex);
            direction *= -1;
            for (int j = 0; j < triangleIndexs.Length; j++)
            {
                triangleList.Add(triangleIndexs[j]);
            }
        }

至於UV索引則設置和頂面的一樣即可。

其實生成頂面和底面之後,大部分的工作已經完成了,這時候我們已經生成了我們需要的所有頂點。前後左右也只是用現有的這些頂點進行生成。

我們前面生成的圓柱體是基於圓生成的,如果我們改成基於貝塞爾生成的那也是可以的。

修改方法很簡單,就是改下生成頂點時的過程就好了,其他的不需要動。

具體過程直接看代碼吧,包看包懂。

當然下面的代碼是為了方面理解所以寫得冗餘,如果用到項目中建議優化下,不然會被主程打的。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//[RequireComponent(typeof(MeshFilter))]
//[RequireComponent(typeof(MeshRenderer))]
[ExecuteInEditMode]
public class DrawArch : MonoBehaviour
{
    public float Radius = 20.0f;                //外圈的半徑
    public float Thickness = 10.0f;             //厚度,外圈半徑減去內圈半徑
    public float Depth = 1.0f;                  //厚度
    public int NumberOfSides = 30;         //由多少個面組成
    public float DrawArchDegrees = 90.0f;       //要繪畫多長
    public Vector2[] bezierPoints = new Vector2[4];
    public Material archMaterial = null;

    private int VertexCountOneSide = 0;         //生成一面所需的頂點數
    private Mesh mesh = null;

    private float incrementAngle = 0;
    private List<Vector3> vertexList = new List<Vector3>();
    private List<int> triangleList = new List<int>();
    private List<Vector2> uvList = new List<Vector2>();

    // Start is called before the first frame update
    void Start()
    {
        mesh = new Mesh();
    }

    void GenerateMesh()
    {
        VertexCountOneSide = (NumberOfSides + 1) * 2;
        incrementAngle = DrawArchDegrees / NumberOfSides;

        GenerateVertex();
        GenerateTriangleIndex();
        GenerateUV();

        mesh.vertices = vertexList.ToArray();
        mesh.uv = uvList.ToArray();
        mesh.triangles = triangleList.ToArray();

        mesh.RecalculateNormals();
        gameObject.GetComponent<MeshFilter>().mesh = mesh;
        gameObject.GetComponent<MeshRenderer>().material = archMaterial;
    }

    //生成頂點坐標
    void GenerateVertex()
    {
        //上
        vertexList.Clear();
        for (int i = 0; i <= NumberOfSides; i++)
        {
            //float[] points = GetCirclePoint(Radius - Thickness,  i);
            //vertexList.Add(new Vector3(points[0], points[1], Depth / 2));
            //points = GetCirclePoint(Radius, i);
            //vertexList.Add(new Vector3(points[0], points[1], Depth / 2));
            float[] points = GetBezierPoint(i);
            vertexList.Add(new Vector3(points[0], points[1], Depth / 2));
            points = GetBezierPoint(i);
            vertexList.Add(new Vector3(points[0] - 1, points[1], Depth / 2));
        }

        //下
        for (int i = 0; i <= NumberOfSides; i++)
        {
            //float[] points = GetCirclePoint(Radius - Thickness,  i);
            //vertexList.Add(new Vector3(points[0], points[1], -1 * Depth / 2));
            //points = GetCirclePoint(Radius, i);
            //vertexList.Add(new Vector3(points[0], points[1], -1 * Depth / 2));
            float[] points = GetBezierPoint(i);
            vertexList.Add(new Vector3(points[0], points[1], -1 * Depth / 2));
            points = GetBezierPoint(i);
            vertexList.Add(new Vector3(points[0] - 1, points[1], -1 * Depth / 2));
        }

        //前
        for (int i = 0; i <= NumberOfSides * 2 ; i += 2)
        {
            vertexList.Add(vertexList[i]);
            vertexList.Add(vertexList[i + VertexCountOneSide]);
        }
        //后
        for (int i = 0; i <= NumberOfSides * 2; i += 2)
        {
            vertexList.Add(vertexList[i + 1]);
            vertexList.Add(vertexList[i + VertexCountOneSide + 1]);
        }
        //左
        vertexList.Add(vertexList[0]);
        vertexList.Add(vertexList[1]);
        vertexList.Add(vertexList[VertexCountOneSide + 0]);
        vertexList.Add(vertexList[VertexCountOneSide + 1]);
        //右
        vertexList.Add(vertexList[VertexCountOneSide -2]);
        vertexList.Add(vertexList[VertexCountOneSide - 1]);
        vertexList.Add(vertexList[VertexCountOneSide * 2 - 2]);
        vertexList.Add(vertexList[VertexCountOneSide * 2 - 1]);
    }

    void GenerateTriangleIndex()
    {
        //三角形索引
        triangleList.Clear();
        //上
        int direction = -1;
        int startIndex = 0;
        for (int i = 0; i < NumberOfSides * 2; i++)
        {
            int[] triangleIndexs = getTriangleIndexs(i, direction);
            direction *= -1;
            for (int j = 0; j < triangleIndexs.Length; j++)
            {
                triangleList.Add(triangleIndexs[j]);
            }
        }

        //下
        direction = 1;
        startIndex += (NumberOfSides + 1) * 2;
        for (int i = 0; i < NumberOfSides * 2; i++)
        {
            int[] triangleIndexs = getTriangleIndexs(i, direction, startIndex);
            direction *= -1;
            for (int j = 0; j < triangleIndexs.Length; j++)
            {
                triangleList.Add(triangleIndexs[j]);
            }
        }

        //前
        direction = 1;
        startIndex += VertexCountOneSide;
        for (int i = 0; i < NumberOfSides * 2; i++)
        {
            int[] triangleIndexs = getTriangleIndexs(i, direction, startIndex);
            direction *= -1;
            for (int j = 0; j < triangleIndexs.Length; j++)
            {
                triangleList.Add(triangleIndexs[j]);
            }
        }

        //后
        direction = -1;
        startIndex += VertexCountOneSide;
        for (int i = 0; i < NumberOfSides * 2; i++)
        {
            int[] triangleIndexs = getTriangleIndexs(i, direction, startIndex);
            direction *= -1;
            for (int j = 0; j < triangleIndexs.Length; j++)
            {
                triangleList.Add(triangleIndexs[j]);
            }
        }
        startIndex += VertexCountOneSide;
        //左
        triangleList.Add(startIndex + 0);
        triangleList.Add(startIndex + 1);
        triangleList.Add(startIndex + 2);
        triangleList.Add(startIndex + 3);
        triangleList.Add(startIndex + 2);
        triangleList.Add(startIndex + 1);
        //右
        triangleList.Add(startIndex + 4 + 2);
        triangleList.Add(startIndex + 4 + 1);
        triangleList.Add(startIndex + 4 + 0);
        triangleList.Add(startIndex + 4 + 1);
        triangleList.Add(startIndex + 4 + 2);
        triangleList.Add(startIndex + 4 + 3);
    }

    //UV索引
    void GenerateUV()
    {
        uvList.Clear();
        //上
        for (int i = 0; i <= NumberOfSides; i++)
        {
            float angle = 180 - i * incrementAngle;
            float littleX = (1.0f / NumberOfSides) * i;
            uvList.Add(new Vector2(littleX, 0));
            float bigX = (1.0f / NumberOfSides) * i;
            uvList.Add(new Vector2(bigX, 1));
        }

        //下
        for (int i = 0; i <= NumberOfSides; i++)
        {
            float angle = 180 - i * incrementAngle;
            float littleX = (1.0f / NumberOfSides) * i;
            uvList.Add(new Vector2(littleX, 0));
            float bigX = (1.0f / NumberOfSides) * i;
            uvList.Add(new Vector2(bigX, 1));
        }

        //前
        for (int i = 0; i <= NumberOfSides; i++)
        {
            float angle = 180 - i * incrementAngle;
            float littleX = (1.0f / NumberOfSides) * i;
            uvList.Add(new Vector2(littleX, 0));
            float bigX = (1.0f / NumberOfSides) * i;
            uvList.Add(new Vector2(bigX, 1));
        }

        //后
        for (int i = 0; i <= NumberOfSides; i++)
        {
            float angle = 180 - i * incrementAngle;
            float littleX = (1.0f / NumberOfSides) * i;
            uvList.Add(new Vector2(littleX, 0));
            float bigX = (1.0f / NumberOfSides) * i;
            uvList.Add(new Vector2(bigX, 1));
        }
        //左
        uvList.Add(new Vector2(1.0f, 1.0f));
        uvList.Add(new Vector2(0.0f, 1.0f));
        uvList.Add(new Vector2(0.0f, 0.0f));
        uvList.Add(new Vector2(1.0f, 0.0f));
        //右
        uvList.Add(new Vector2(1.0f, 1.0f));
        uvList.Add(new Vector2(0.0f, 1.0f));
        uvList.Add(new Vector2(0.0f, 0.0f));
        uvList.Add(new Vector2(1.0f, 0.0f));
    }

    int[] getTriangleIndexs(int index, int direction, int startIndex = 0)
    {
        int[] triangleIndexs = new int[3] { 0+ startIndex, 1 + startIndex, 2 + startIndex };
        for (int i = 0; i < triangleIndexs.Length; i++)
        {
            triangleIndexs[i] += index;
        }
        if (direction == -1)
        {
            int temp = triangleIndexs[0];
            triangleIndexs[0] = triangleIndexs[2];
            triangleIndexs[2] = temp;
        }
        return triangleIndexs;
    }

    private void Update()
    {
        GenerateMesh();
    }

    float[] GetCirclePoint(float radius, int index)
    {
        float angle = 180 - index * incrementAngle;
        float[] points = new float[2];
        float x = radius * Mathf.Cos(angle * Mathf.Deg2Rad);
        float y = radius * Mathf.Sin(angle * Mathf.Deg2Rad);
        points[0] = x;
        points[1] = y;
        return points;
    }

    float[] GetBezierPoint(int index)
    {
        float t = 1.0f / (NumberOfSides + 1) * index;
        float[] points = new float[2];
        var vec = Bezier(bezierPoints[0], bezierPoints[1], bezierPoints[2], bezierPoints[3], t);
        points[0] = vec.x;
        points[1] = vec.y;
        return points;
    }

    Vector2 Bezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t)
    {
        Vector2 result;
        Vector2 p0p1 = (1 - t) * p0 + t * p1;
        Vector2 p1p2 = (1 - t) * p1 + t * p2;
        Vector2 p2p3 = (1 - t) * p2 + t * p3;
        Vector2 p0p1p2 = (1 - t) * p0p1 + t * p1p2;
        Vector2 p1p2p3 = (1 - t) * p1p2 + t * p2p3;
        result = (1 - t) * p0p1p2 + t * p1p2p3;
        return result;
    }
}

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

農業和環保引發對立 德國兩派人馬街頭拉鋸

摘錄自2020年01月19日中央通訊社德國報導

德國農民與環保人士對壘加深,農民不滿新環保措施加重成本負擔,環保人士則抗議農業推升氣候變遷危機,兩邊的對立讓政治人物左右為難,也使得德國年度最大農業展蒙上陰影。

柏林一年一度「綠色週」貿易博覽會昨天(18日)開幕,德國農民選在當天展開一場名為「土地創造聯繫」(Land schafft Verbindung, LSV)活動,將數千輛牽引機開上全國各地城市街頭,藉此抗議政府施行更嚴格的農業法規。

德國最近推行的種種農業改革,包括在食品引進「動物福利標籤」、限制農藥使用以保護昆蟲等,都讓農民大喊吃不消,說政府新環保措施引發的成本都是農民在扛。他們也擔心新法規將導致收入大減,走上街頭據理力爭,高舉標語吶喊「別忘了是農民餵飽你們」、「沒有農場、沒有食物、沒有未來」。

農民昨天集體抗議後,今天輪到環保人士走上街頭。主辦單位說有約2萬7000名環保人士齊聚柏林地標布蘭登堡大門(Brandenburg Gate)附近,抗議他們口中的農業損害效應,並要求改變德國現行的務農方法。這場集會由環保團體「我們受夠了」(We are fed up)發起。氣候環保人士指稱近期的農業改革做得還不夠,現場另一邊站著抗議的農民,國會議員則被夾在中間。

農民和環保倡議者之間的鴻溝近幾年來有加深趨勢,這與學生帶領的「週五為未來而戰」(Fridays for Future)罷課抗暖化行動有關。環保人士今天的抗議行動自2011年以來年年登場,這個由綠色團體發起的活動要求全面性改革,以支持小農和對環境友善的農業。他們在網路上說:「農業加深暖化危機和社會衝突,我們必須加以阻止。」

農民組成的LSV抗議團體則自稱是相對於「我們受夠了」團體的反制力量,有憤怒的養蜂人將蜂蜜傾倒在政治人物大門前,也有生氣的農民走上街頭阻撓交通。農業部長克勒克納(Julia Kloeckner)16日呼籲對立的兩方要有妥協的胸襟。

綠黨共同領袖哈柏克(Robert Habeck)警告環保人士勿把氣候暖化歸咎在農民頭上,但也堅持說,拒絕改革並非社會前進之道,「減少對氣候和動物的保障,絕對不會是正確解答」。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

泰國曼谷空品差 民眾抱怨政府無作為

摘錄自2020年1月21日公視報導

泰國曼谷近年來每到冬天,空氣品質就拉警報。曼谷的天空再度為霧霾籠罩,機車騎士紛紛戴上口罩,家庭主婦出門也不例外。

根據曼谷官方監測的數據,曼谷部分地區中午時段PM2.5濃度,飆升到每立方公尺 95微克,幾乎是安全標準的兩倍。官員表示這主要是受到逆溫現象的影響,也就是暖空氣將冷空氣困在地面附近,導致汽機車廢氣滯留且散不出去。泰國政府雖然已經開始管控汽車廢氣,必要時還派出無人機灑水,但霧鎖曼谷的情況依然每年上演。最新民調顯示,曼谷有八成一的民眾,認為政府改善空污沒有效率。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

亞銀:借鏡碳信用建構「藍色信用」 以市場機制籌措海洋基金

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

日本風災損害輻射檢查設備 營養午餐難保食安

文:宋瑞文(媽媽監督核電廠聯盟特約撰述)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

每天3分鐘操作系統修鍊秘籍(14):IO操作和DMA、RDMA

I/O操作和DMA、RDMA

用戶進程想要執行IO操作時(例如想要讀磁盤數據、向磁盤寫數據、讀鍵盤的輸入等等),由於用戶進程工作在用戶模式下,它沒有執行這些操作的權限,只能通過發起對應的系統調用請求操作系統幫忙完成這些操作。這裏因為系統調用產生中斷將陷入到內核,進行一次上下文切換操作。

內核進程幫忙執行IO操作時,由於IO操作相比於CPU來說是極慢的操作,CPU不應該等待在這個過程中,而是切換到其它進程上去執行其它任務。這裏再次涉及到一次上下文切換:從內核態回到用戶態的其它進程。

DMA要求硬件的支持,需要在硬件中集成一個小型的“CPU”,比如現在的机械硬盤、固態硬盤、網卡等硬件都帶有DMA功能,這樣操作系統要執行IO操作時,直接將相關指令發送給這些DMA硬件,DMA處理器負責IO操作,而操作系統這時可以放棄CPU,讓CPU去執行其它進程。例如對於讀磁盤文件時,操作系統將相關指令以及數據應寫在哪個內存地址發送給DMA硬件后,由DMA硬件去讀寫數據到指定內存地址,當IO操作完成后,DMA硬件通過總線發送一個硬件中斷給CPU,於是陷入到內核態(這裏涉及了一次上下文切換),內核就知道了IO已經完成,於是將Kernel Buffer數據拷貝到用戶進程的IO Buffer,並準備調度用戶進程(再次上下文切換)。

假如不使用DMA硬件的話,那麼IO操作過程中,操作系統將多次參与,負責將硬件數據讀入或讀出內存,操作系統參与意味着要陷入到內核態,並且獲取CPU控制權,這也意味着要進行大量的上下文切換以及佔用大量CPU資源。

而使用DMA后,只有4次必要的上下文切換,且IO操作的過程中完全不需要消耗CPU資源。

除了DMA,還有更高級的RDMA(Remote Direct Memory Access)機制,它需要操作系統和硬件的支持,還需要編寫RDMA方式的代碼。

前面介紹緩衝空間時提到過,一般情況下,每個用戶進程要讀、寫數據,都會經過兩個必要的緩衝層:內核空間的Kernel Buffer、用戶空間的IO Buffer。例如讀文件數據時,先將數據拷貝到內核的緩衝空間(page cache),然後陷入內核,內核將該緩衝空間數據拷貝到用戶空間的緩衝空間(IO Buffer),當調度到用戶進程時,用戶進程從自己的緩衝空間讀取數據。

DMA機制並沒有繞過這兩個緩衝層,但使用RDMA機制,程序可以直接繞過Kernel Buffer,內核發現是RDMA操作后,直接告訴RDMA硬件將讀取的數據(寫操作也一樣)寫入到用戶空間的IO Buffer,而不需要先拷貝到Kernel Buffer,再拷貝到IO Buffer。雖然RDMA機制相比DMA不會減少上下文切換次數,但是它減少了內存數據拷貝的過程,相當於是使用了O_DIRECT標記的直接IO技術。

DMA和RDMA兩種技術對比如圖:RDMA一般實現在網卡上,但出於方便理解,下圖直接使用磁盤來描述

像這種繞過內核功能的技術,通常稱為內核旁路(Kernel Bypass),RDMA技術內核旁路的是一種,還有像TOE也是內核旁路的一種。

雖然RDMA比較優秀,但是它需要硬件、操作系統和代碼的同時支持,對編程而言是一個比較大的衝擊,所以目前使用的非常少。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

vue學習筆記(五)條件渲染和列表渲染

前言

在眾多的編程語言中,我們的基礎語法總是少不了一些專業語法,比如像定義變量,條件語句,for循環,數組,函數等等,vue.js這個優秀的前端框架中也有同樣的語法,我們換一個名詞,將條件語句改成專業詞彙叫做條件渲染,循環語句改成專業詞彙叫做列表渲染,這樣比較舒服一點。

本章目標

  • 學會條件渲染的使用

  • 學會可復用的key的使用

  • 學會列表渲染的使用

條件渲染

1.v-if的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app01">
    <span v-if="type==='A'">成績為A</span>
</div>
<script src="../js/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app01',
        data:{
            type:'A'
        },
        methods:{
            
        },
        watch:{
            
        },
        computed:{
            
        }
    })
</script>
</body>
</html>

結果:成績為A

v-if判斷條件是否相等,就像if一樣,如果相等,那麼值就會true,與之對應的還有v-else,v-else-if

2.v-else的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app01">
    <span v-if="type==='A'">成績為A</span>
    <span v-else>成績為B</span>
</div>
<script src="../js/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app01',    
        data:{
            type:'B'
        },
        methods:{
            
        },
        watch:{
            
        },
        computed:{
            
        }
    })
</script>
</body>
</html>

結果:成績為B

小練習

我們做一個小練習,鞏固一下v-if和v-else的使用,需求如下:點擊一個按鈕時,按鈕上的文字變為显示,再次點擊時按鈕上的文字變為隱藏,當按鈕上的文字显示隱藏時,显示紅色,按鈕上的文字變為显示時显示藍色

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <button @click="handleClick">{{text}}</button>
            <div v-if="show" class="box red"></div>
            <div v-else class="blue box"></div>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                    text:'隱藏'
                },
                methods:{
                    handleClick(){
                        this.show=!this.show;
                        this.text=this.show?'隱藏':'显示'
                    }
                }
            })
            
        </script>
    </body>
</html>

結果

 

3.v-else-if的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app01">
    <input type="text" v-model="type"/>
    <div v-if="type==='A'">成績為A</div>
    <div v-else-if="type==='B'">成績為B</div>
    <div v-else-if="type==='C'">成績為C</div>
    <div v-else>不及格</div>
</div>
<script src="../js/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app01',    
        data:{
            type:''
        },
        methods:{
            
        },
        watch:{
            
        },
        computed:{
            
        }
    })
</script>
</body>
</html>

結果:

4.v-show

說起這個v-show,其實和v-if有與曲同工的妙處,但是又有不同的地方,我們來看下示例你就秒懂了

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div v-show="show" class="box red"></div>
            <button @click="handleClick()">{{text}}</button>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                    text:'隱藏',
                },
                methods:{
                    handleClick(){
                        this.show=!this.show;
                        this.text=this.show?'隱藏':'显示'
                    }
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

當按鈕變為显示的時候,背景顏色消失,這裏就不截圖了,有興趣的小夥伴可以自己去嘗試,既然v-if可以幫我們實現元素的显示和隱藏,那我們還需要v-show干什麼呢?不妨看下接下來的實例。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div v-show="show" class="box red"></div>
            <div class="box blue" v-if="show"></div>
            <button @click="handleClick()">{{text}}</button>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                    text:'隱藏',
                },
                methods:{
                    handleClick(){
                        this.show=!this.show;
                        this.text=this.show?'隱藏':'显示'
                    }
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

 當我們點擊按鈕的時候

 

現在結果已經出來了,使用v-show的dom元素,dom元素只是簡單的切換display屬性,而v-if會將dom元素移除,當我們再次點擊時,v-if又會重新渲染元素,可想而知如果頻繁的切換的話,那麼有多麼的耗費性能,因此我總結了如下幾點

  • 頻繁的切換显示/隱藏要使用v-show

  • 只判斷一次時,使用v-if

5.減少dom的生成

我們都知道js操作dom元素是非常消耗性能的,但是我們需要盡量的避免這個問題,vue中為我們提供了一個template標籤,這個標籤叫做模板(至於什麼叫做模板,後期的博客會講到),我們先看一個示例

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div v-if="show">
                <div class="box red"></div>
                <div class="box blue"></div>
            </div>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

 我們想讓圖上的那個div消失,不想為了管理同一組元素而多生成一個節點,這樣是非常消耗性能的,我們將div標籤變成template標籤

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div v-if="show">
                <div class="box red"></div>
                <div class="box blue"></div>
            </div>
            <template v-if="show">
                <div class="box red"></div>
                <div class="box blue"></div>
            </template>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

View Code

 現在我有心中萌生了一個想法,v-if可以使用template,那麼v-show是否可以使用呢?

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .box{
                width: 100px;
                height: 100px;
            }
            .red{
                background: red;
            }
            .blue{
                background: blue;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <template v-if="show">
                <div class="box red"></div>
                <div class="box blue"></div>
            </template>
            <template v-show="show">
                <div class="box red"></div>
                <div class="box blue"></div>
            </template>
            <button @click="handleClick()">{{text}}</button>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    show:true,
                    text:'隱藏',
                },
                methods:{
                    handleClick(){
                        this.show=!this.show;
                        this.text=this.show?'隱藏':'显示'
                    }
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

View Code

 

 答案是v-if可以使用template,而v-show不能使用template

vue中用key管理可復用的元素

Vue 會盡可能高效地渲染元素,通常會復用已有元素而不是從頭開始渲染。這麼做除了使 Vue 變得非常快之外,還有其它一些好處。例如,如果你允許用戶在不同的登錄方式之間切換。

示例一:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app01">
    <template v-if="type==='username'">
        <label>用戶名</label>
        <input type="text" placeholder="請輸入您的賬號" />
    </template>
    <template v-else>
        <label>郵箱</label>
        <input type="text" placeholder="請輸入您的郵箱" />
    </template>
    <p>
        <a href=""@click.prevent="type='username'">用戶名登錄</a>|
        <a href=""@click.prevent="type='email'">郵箱登錄</a>
    </p>
</div>
<script src="../js/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app01',    
        data:{
            isShow:true,
            type:'username'
        },
        methods:{
            
        },
        watch:{
            
        },
        computed:{
            
        }
    })
</script>
</body>
</html>

結果:

 

 

當我們在用戶名登錄和郵箱切換的時候,我們發現我們輸入的內容始終保持,為什麼呢?總的來說,因為兩個模板使用了相同的元素,input不會被替換掉——僅僅是替換了它的 placeholder屬性,這樣也不總是符合實際需求,所以 Vue 為你提供了一種方式來表達這兩個元素是完全獨立的,不要復用它們,只需添加一個具有唯一值的key屬性即可。

示例二:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="app01">
    <template v-if="type==='username'">
        <label>用戶名:</label>
        <input type="text" placeholder="請輸入您的用戶名"  key='usename'/>
    </template>
    <template v-else>
        <label>郵箱:</label>
        <input type="text" placeholder="請輸入您的郵箱"  key='email'/>
    </template>
    <p>
        <a href=""@click.prevent="type='username'">用戶名登錄</a>|
        <a href=""@click.prevent="type='email'">郵箱登錄</a>
    </p>
</div>
<script src="../js/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app01',    
        data:{
            isShow:true,
            type:'username'
        },
        methods:{
            
        },
        watch:{
            
        },
        computed:{
            
        }
    })
</script>
</body>
</html>

結果:

 

現在我們點擊切換的時候,輸入框都會重新渲染,當然我們的<label>標籤依舊的高效的復用,因為它沒有添加key。

列表渲染

我們用v-for指令根據一組數組的選項列表進行渲染,v-for指令需要以item in items的形式的特殊語法,items是原數據數組並且item是元素迭代的別名

1.v-for的基本使用

語法:(item,index) in|of items

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for的基本使用</title>
    </head>
    <body>
        <div id="app">
            <ul>
                <li v-for="(item) in arr">{{item}}</li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    arr:['apple','banana','pear']
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

結果:

當然v-for中也可以帶第二個參數index

2.v-for中帶索引

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for的基本使用</title>
    </head>
    <body>
        <div id="app">
            <ul>
                <li v-for="(item,index) in arr">{{item}}--{{index}}</li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    arr:['apple','banana','pear']
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

View Code

3.v-for迭代字符串

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for的基本使用</title>
    </head>
    <body>
        <div id="app">
            <ul>
                <li v-for="(item,index) in arr">{{item}}--{{index}}</li>
            </ul>
            <ul>
                <li v-for="item in 'helloworld'">{{item}}</li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    arr:['apple','banana','pear']
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

View Code

4.v-for迭代對象

語法:(value,key,index) of | in items

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for迭代對象</title>
    </head>
    <body>
        <div id="app">
            <ul>
                <li v-for="(value,key,index) of obj">{{value}}-{{key}}-{{index}}</li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    obj:{
                        name:'kk',
                        age:18,
                        sex:'male'
                    }
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

結果:

5.v-for迭代整數

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for迭代對象</title>
    </head>
    <body>
        <div id="app">
            <ul>
                <li v-for="(value,key,index) of obj">{{value}}-{{key}}-{{index}}</li>
            </ul>
            <ul>
                <li v-for="item in 10">{{item}}</li>
            </ul>
        </div>
        <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            let vm=new Vue({
                el:'#app',
                data:{
                    obj:{
                        name:'kk',
                        age:18,
                        sex:'male'
                    }
                },
                methods:{
                    
                },
                computed:{
                    
                }
            })
        </script>
    </body>
</html>

結果:

注意:但我們迭代整數的時候,item從1開始而不是從0開始

總結

在本章內容中,我們一共學習了三個知識點,分別是條件渲染的使用(v-if,v-else,v-else-if),管理可復用的key,列表渲染(v-for的基本使用等等),本章的內容也多但是在實際應用上非常廣泛,畢竟這些是非常基礎的語法,基礎不牢,地動山搖,學習任何東西都需要自己一步一個腳印走出來。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

【自然語言處理】利用LDA對希拉里郵件進行主題分析

首先是讀取數據集,並將csv中ExtractedBodyText為空的給去除掉

import pandas as pd
import re
import os

dir_path=os.path.dirname(os.path.abspath(__file__))
data_path=dir_path+"/Database/HillaryEmails.csv"
df=pd.read_csv(data_path)
df=df[['Id','ExtractedBodyText']].dropna()

對於這些郵件信息,並不是所有的詞都是有意義的,也就是先要去除掉一些噪聲數據:

def clean_email_text(text):
    text = text.replace('\n'," ") #新行,我們是不需要的
    text = re.sub(r"-", " ", text) #把 "-" 的兩個單詞,分開。(比如:july-edu ==> july edu)
    text = re.sub(r"\d+/\d+/\d+", "", text) #日期,對主體模型沒什麼意義
    text = re.sub(r"[0-2]?[0-9]:[0-6][0-9]", "", text) #時間,沒意義
    text = re.sub(r"[\w]+@[\.\w]+", "", text) #郵件地址,沒意義
    text = re.sub(r"/[a-zA-Z]*[:\//\]*[A-Za-z0-9\-_]+\.+[A-Za-z0-9\.\/%&=\?\-_]+/i", "", text) #網址,沒意義
    pure_text = ''
    # 以防還有其他特殊字符(数字)等等,我們直接把他們loop一遍,過濾掉
    for letter in text:
        # 只留下字母和空格
        if letter.isalpha() or letter==' ':
            pure_text += letter
    # 再把那些去除特殊字符后落單的單詞,直接排除。
    # 我們就只剩下有意義的單詞了。
    text = ' '.join(word for word in pure_text.split() if len(word)>1)
    return text

然後取出ExtractedBodyText的那一列,對每一行email進行噪聲過濾,並返回一個對象:

docs = df['ExtractedBodyText']
docs = docs.apply(lambda s: clean_email_text(s))  

然後我們呢把裏面的email提取出來:

doclist=docs.values

接下來,我們使用gensim庫來進行LDA模型的構建,gensim可用指令pip install -U gensim安裝。但是,要注意輸入到模型中的數據的格式。例如:[[一條郵件字符串],[另一條郵件字符串], ...]轉換成[[一,條,郵件,在,這裏],[第,二,條,郵件,在,這裏],[今天,天氣,腫么,樣],...]。對於英文的分詞,只需要對空白處分割即可。同時,有些詞語(不同於噪聲)是沒有意義的,我們要過濾掉那些沒有意義的詞語,這裏簡單的寫一個停止詞列表:

stoplist = ['very', 'ourselves', 'am', 'doesn', 'through', 'me', 'against', 'up', 'just', 'her', 'ours',
            'couldn', 'because', 'is', 'isn', 'it', 'only', 'in', 'such', 'too', 'mustn', 'under', 'their',
            'if', 'to', 'my', 'himself', 'after', 'why', 'while', 'can', 'each', 'itself', 'his', 'all', 'once',
            'herself', 'more', 'our', 'they', 'hasn', 'on', 'ma', 'them', 'its', 'where', 'did', 'll', 'you',
            'didn', 'nor', 'as', 'now', 'before', 'those', 'yours', 'from', 'who', 'was', 'm', 'been', 'will',
            'into', 'same', 'how', 'some', 'of', 'out', 'with', 's', 'being', 't', 'mightn', 'she', 'again', 'be',
            'by', 'shan', 'have', 'yourselves', 'needn', 'and', 'are', 'o', 'these', 'further', 'most', 'yourself',
            'having', 'aren', 'here', 'he', 'were', 'but', 'this', 'myself', 'own', 'we', 'so', 'i', 'does', 'both',
            'when', 'between', 'd', 'had', 'the', 'y', 'has', 'down', 'off', 'than', 'haven', 'whom', 'wouldn',
            'should', 've', 'over', 'themselves', 'few', 'then', 'hadn', 'what', 'until', 'won', 'no', 'about',
            'any', 'that', 'for', 'shouldn', 'don', 'do', 'there', 'doing', 'an', 'or', 'ain', 'hers', 'wasn',
            'weren', 'above', 'a', 'at', 'your', 'theirs', 'below', 'other', 'not', 're', 'him', 'during', 'which']

然後我們將輸入轉換成gensim所需的格式,並過濾掉停用詞:

texts = [[word for word in doc.lower().split() if word not in stoplist] for doc in doclist]

再將這所有的單詞放入到一個詞袋中,把每個單詞用一個数字index指代:

from gensim import corpora, models, similarities
import gensim
dictionary = corpora.Dictionary(texts)

再分別統計每一篇email中每個詞語在這個詞袋中出現的次數,並返回一個列表:

corpus = [dictionary.doc2bow(text) for text in texts]

 這個列表告訴我們,第14(從0開始是第一)個郵件中,一共6個有意義的單詞(經過我們的文本預處理,並去除了停止詞后)其中,51號單詞出現1次,505號單詞出現1次,以此類推。。。

最後,就可以開始構建我們的模型了:

lda = gensim.models.ldamodel.LdaModel(corpus=corpus, id2word=dictionary, num_topics=20)
print(lda.print_topic(10, topn=5))

 可以看到,第11個主題最常用的單詞,接下來,我們看下所有的主題:

for i in lda.print_topics(num_topics=20, num_words=5):
    print(i)

 我們再看下第一篇email屬於哪一個主題:

print(lda.get_document_topics(corpus[0]))

 屬於第四個主題的概率是0.95

相關代碼和數據:鏈接: https://pan.baidu.com/s/1sl1I5IeQFDHjVwf2a0C89g 提取碼: xqqf 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

森林大火逼近 坎培拉進入緊急狀態

摘錄自2020年1月31日Yahoo新聞報導

澳洲森林大火逼近首都坎培拉,坎培拉今天(31日)宣布進入緊急狀態,這是近20年來的首次。

有40萬人口的坎培拉宣布進入緊急狀態,主要是考量天氣預報近日將有熱浪來襲,預測森林大火可能延燒到坎培拉南部郊區。澳洲首都特區首席部長巴爾(Andrew Barr)警告:「情勢可能演變到無法控制的地步…進入緊急狀態,是我們能對首都特區民眾發布的最強烈警示,民眾必須為自己和家人做好準備。」

南澳州(South Australia) 昨天氣溫達攝氏40度以上,氣象單位對州內數處容易發生大火的地區發布危險大火警示,並預測今天稍晚熱浪將襲擊墨爾本與坎培拉,而雪梨部分地區週末氣溫將達攝氏45度高溫。

當局表示,灼熱高溫伴隨乾燥熱風,將為新南威爾斯州(New South Wales)與維多利亞州(Victoria)部分地區創造嚴重森林大火的良好條件,而這兩州目前還有80多處有大火在延燒。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

Spring源碼解析之@Configuration

@Configuration簡介

用於標識一個類為配置類,與xml配置效果類似

用法簡介

public class TestApplication {

    public static void main(String args[]) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    }
}

@Configuration
public class AppConfig {

    @Bean
    public A a(){
        return new A();
    }
    @Bean
    public B b(){
        return new B();
    }
} 




public class A {

    public A(){
        System.out.println("Call A constructor");
    }
}



public class B {

    public B(){
        System.out.println("Call B constructor");
    }
}

上面的例子應該是@Configuration最普遍一種使用場景了,在@Configuration class下面配置@Bean method,用於想Spring Ioc容器注入bean.但其實我們把AppConfig的@Configuration註解去掉,對應的Bean也是可以被注入到容器中去的。

那麼問題來了@Configuration到底有什麼作用?

我們給AppConfig改寫一下,如下:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

   @Bean
   public A a(){
      b();
      return new A();
   }
   @Bean
   public B b(){
      return new B();
   }

再去執行TestApplication#main,那麼執行結果會是什麼樣呢?

Call A constructor
Call B constructor

嗯哼?按照Java的語法,B的構造函數應該是被調用了兩次啊?為什麼只有輸出一句Call B constructor?

這其實就是@Configuration再發揮作用啦,不信你去掉@Configuration,再去運行一下,就會發現B的構造函數被執行了兩次。

官方給出了這樣一段解釋對於被@Configuration註解的類

In common scenarios,@Beanmethods are to be declared within@Configurationclasses, ensuring that “full” mode is always used and that cross-method references therefore get redirected to the container’s lifecycle management.

在一般情況下,@Bean method 是被聲明在@Configuration類中的,以確保 full mode總是被使用,並且跨方法的引用會被重定向到容器生命周期管理。

怎麼理解呢?

原來Spring將被@Configuration註解的配置類定義為full configuration, 而將沒有被@Configuration註解的配置類定義為lite configuration。full configuration能重定向從跨方法的引用,從而保證上述代碼中的b bean是一個單例.

源碼中是如何實現@Configuration語意

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
   /**
    * 調用無參構造函數,實例化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
    * 同時會調用父類GenericApplicationContext無參構造函數實例化一個關鍵的工廠DefaultListableBeanFactory
    * 同時還會註冊一些開天闢地的後置處理器到beanDefinitionMap,這些後置處理器有bean工廠後置處理器;有bean後置處理器
    */
   this();
   //將componentClasses註冊到beanDefinitionMap集合中去
   register(componentClasses);
  
   refresh();
}

跟蹤refresh()

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         //供上下文(Context)子類繼承,允許在這裏後置處理bean factory
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         //按順序調用BeanFactoryPostProcessor,這裏的按順序僅實現了PriorityOrdered和Ordered的語意,未實現@Order註解的語意
         //通過調用ConfigurationConfigPostProcessor#postProcessBeanDefinitionRegistry
         //解析@Configuration配置類,將自定義的BeanFactoryPostProcessor、BeanPostProcessor註冊到beanDefinitionMap
         //接着實例化所有(包括開天闢地)的BeanFactoryPostProcessor,然後再調用BeanFactoryPostProcessor#postProcessBeanFactory
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         //按順序將BeanPostProcessor實例化成bean並註冊到beanFactory的beanPostProcessors,
         //這裏的按順序僅實現了PriorityOrdered和Ordered的語意,未實現@Order註解的語意
         //因為BeanPostProcessor要在普通bean初始化()前後被調用,所以需要提前完成實例化並註冊到beanFactory的beanPostProcessors
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         //註冊國際化相關的Bean
         initMessageSource();

         // Initialize event multicaster for this context.
         //為上下文註冊應用事件廣播器(用於ApplicationEvent的廣播),如果有自定義則使用自定義的,如果沒有則內部實例化一個
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         //註冊所有(靜態、動態)的listener,並廣播earlyApplicationEvents
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         //實例化用戶自定義的普通單例Bean(非開天闢地的、非後置處理器)
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

跟蹤invokeBeanFactoryPostProcessors(beanFactory)

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

   // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
   // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
   if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }
}

跟蹤invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

public static void invokeBeanFactoryPostProcessors(
            ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

        // Invoke BeanDefinitionRegistryPostProcessors first, if any.
        Set<String> processedBeans = new HashSet<>();

        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
            List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

            for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryProcessor =
                            (BeanDefinitionRegistryPostProcessor) postProcessor;
                    registryProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryProcessors.add(registryProcessor);
                }
                else {
                    regularPostProcessors.add(postProcessor);
                }
            }

            // Do not initialize FactoryBeans here: We need to leave all regular beans
            // uninitialized to let the bean factory post-processors apply to them!
            // Separate between BeanDefinitionRegistryPostProcessors that implement
            // PriorityOrdered, Ordered, and the rest.
            List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

            // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
            String[] postProcessorNames =
                    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            //此處調用ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry,
            //解析配置類,為配置中的bean定義生成對應beanDefinition,並注入到registry的beanDefinitionMap
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();

            // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();

            // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
            boolean reiterate = true;
            while (reiterate) {
                reiterate = false;
                postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
                for (String ppName : postProcessorNames) {
                    if (!processedBeans.contains(ppName)) {
                        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                        processedBeans.add(ppName);
                        reiterate = true;
                    }
                }
                sortPostProcessors(currentRegistryProcessors, beanFactory);
                registryProcessors.addAll(currentRegistryProcessors);
                invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
                currentRegistryProcessors.clear();
            }

            // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
            //調用ConfigurationClassPostProcessor#postProcessBeanFactory增強配置類(通過cglib生成增強類,load到jvm內存,
            //設置beanDefinition的beanClass為增強類)
            //為什麼要增強配置類?主要是為了讓@Bean生成的bean是單例,
            invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
            invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
        }

        else {
            // Invoke factory processors registered with the context instance.
            invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the bean factory post-processors apply to them!
        String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

        // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
        // Ordered, and the rest.
        List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
        List<String> orderedPostProcessorNames = new ArrayList<>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<>();
        for (String ppName : postProcessorNames) {
            if (processedBeans.contains(ppName)) {
                // skip - already processed in first phase above
            }
            else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
            }
        }

        // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
        sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

        // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
        List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
        for (String postProcessorName : orderedPostProcessorNames) {
            orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }
        sortPostProcessors(orderedPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

        // Finally, invoke all other BeanFactoryPostProcessors.
        List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
        for (String postProcessorName : nonOrderedPostProcessorNames) {
            nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }
        invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

        // Clear cached merged bean definitions since the post-processors might have
        // modified the original metadata, e.g. replacing placeholders in values...
        beanFactory.clearMetadataCache();
    }

跟蹤invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);

private static void invokeBeanFactoryPostProcessors(
      Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {

   for (BeanFactoryPostProcessor postProcessor : postProcessors) {
      postProcessor.postProcessBeanFactory(beanFactory);
   }
}

跟蹤ConfigurationClassPostProcessor#postProcessBeanFactory

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   int factoryId = System.identityHashCode(beanFactory);
   if (this.factoriesPostProcessed.contains(factoryId)) {
      throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + beanFactory);
   }
   this.factoriesPostProcessed.add(factoryId);
   if (!this.registriesPostProcessed.contains(factoryId)) {
      // BeanDefinitionRegistryPostProcessor hook apparently not supported...
      // Simply call processConfigurationClasses lazily at this point then.
      processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
   }
   //為@Configuration註解的類生成增強類(如果有必要),並替換bd中的beanClass屬性,
   enhanceConfigurationClasses(beanFactory);
   beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

到了這一步謎底幾乎已經揭曉了,@Configuration class是通過增強來實現它的語義的。通過增強把跨方法的引用調用重定向到Spring生命周期管理.我們近一步探索下這個enhanceConfigurationClasses方法

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
   Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
   for (String beanName : beanFactory.getBeanDefinitionNames()) {
      BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
      Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
      MethodMetadata methodMetadata = null;
      if (beanDef instanceof AnnotatedBeanDefinition) {
         methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
      }
      if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
         // Configuration class (full or lite) or a configuration-derived @Bean method
         // -> resolve bean class at this point...
         AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
         if (!abd.hasBeanClass()) {
            try {
               abd.resolveBeanClass(this.beanClassLoader);
            }
            catch (Throwable ex) {
               throw new IllegalStateException(
                     "Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
            }
         }
      }
      //在ConfigurationClassUtils.checkConfigurationClassCandidate方法中會標記Configuration is full or lite
      if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
         if (!(beanDef instanceof AbstractBeanDefinition)) {
            throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
                  beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
         }
         else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
            logger.info("Cannot enhance @Configuration bean definition '" + beanName +
                  "' since its singleton instance has been created too early. The typical cause " +
                  "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
                  "return type: Consider declaring such methods as 'static'.");
         }
         configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
      }
   }
   if (configBeanDefs.isEmpty()) {
      // nothing to enhance -> return immediately
      return;
   }

   ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
   for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
      AbstractBeanDefinition beanDef = entry.getValue();
      // If a @Configuration class gets proxied, always proxy the target class
      beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
      // Set enhanced subclass of the user-specified bean class
      Class<?> configClass = beanDef.getBeanClass();
      //為@Configuration註解的類生成增強類
      Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
      if (configClass != enhancedClass) {
         if (logger.isTraceEnabled()) {
            logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
                  "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
         }
         beanDef.setBeanClass(enhancedClass);
      }
   }
}

看到有那麼一句話Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);

很明顯了,使用cglib技術為config class生成一個enhancedClass,再通過beanDef.setBeanClass(enhancedClass);修改beanDefinition的BeanClass屬性,在bean實例化階段,會利用反射技術將beanClass屬性對應的類實例化出來,所以最終實例化出來的@Configuration bean是一個代理類的實例。這裏稍微提一下為什麼要使用cglib,而不是jdk動態代理,主要是因為jdk動態代理是基於接口的,而這裏AppConfig並沒有實現任何接口,所以必須用cglib技術。

總結

被@Configuration 註解的類,是 full configuration class,該類會被增強(通過cglib),從而實現跨方法引用調用被重定向到Spring 生命周期管理,最終保證@Bean method生成的bean是一個單例。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!