設計模式之單例模式_貨運

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

目錄:

  • 什麼是單例模式
  • 單例模式的應用場景
  • 單例模式的優缺點
  • 單例模式的實現
  • 總借

一、什麼是單例模式

  單例模式顧名思義就是只存在一個實例,也就是系統代碼中只需要一個對象的實例應用到全局代碼中,有點類似全局變量。例如,在系統運行時,系統需要讀取配置文件中的參數,在設計系統的時候讀取配置文件的類往往設計成單例類。因為系統從啟動運行到結束,只需要讀取一次配置文件,這個讀取配置文件全部由該類負責讀取,在全局代碼中只需要使用該類即可。這樣不僅簡化了配置文件的管理,也避免了代碼讀取配置文件數據的不一致性。

 

 單例模式的特點:

  1、該類的構造方法聲明為private,這樣其他類無法初始花該類,只能通過該類的public方法獲取該類的對象。

  2、裏面有個私有的對象成員,該成員對象是類本身,用於public方法返回該類的實例。

  3、該類中提供一個public的靜態方法,返回該類的私有成員對象。

 

二、單例的應用場景

 

  舉一個小例子,在我們的windows桌面上,我們打開了一個回收站,當我們試圖再次打開一個新的回收站時,Windows系統並不會為你彈出一個新的回收站窗口。,也就是說在整個系統運行的過程中,系統只維護一個回收站的實例。這就是一個典型的單例模式運用。

 

  繼續說回收站,我們在實際使用中並不存在需要同時打開兩個回收站窗口的必要性。假如我每次創建回收站時都需要消耗大量的資源,而每個回收站之間資源是共享的,那麼在沒有必要多次重複創建該實例的情況下,創建了多個實例,這樣做就會給系統造成不必要的負擔,造成資源浪費。

 

  再舉一個例子,網站的計數器,一般也是採用單例模式實現,如果你存在多個計數器,每一個用戶的訪問都刷新計數器的值,這樣的話你的實計數的值是難以同步的。但是如果採用單例模式實現就不會存在這樣的問題,而且還可以避免線程安全問題。同樣多線程的線程池的設計一般也是採用單例模式,這是由於線程池需要方便對池中的線程進行控制

 

  同樣,對於一些應用程序的日誌應用,或者web開發中讀取配置文件都適合使用單例模式,如HttpApplication 就是單例的典型應用。

 

  從上述的例子中我們可以總結出適合使用單例模式的場景和優缺點:  

 

   適用場景:

  1.需要生成唯一序列的環境

  2.需要頻繁實例化然後銷毀的對象。

  3.創建對象時耗時過多或者耗資源過多,但又經常用到的對象。 

  4.方便資源相互通信的環境

 

 

三、單例模式的優缺點

  優點

    1、在內存中只有一個對象,節省內存空間;

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

    2、避免頻繁的創建銷毀對象,可以提高性能;

    3、避免對共享資源的多重佔用,簡化訪問;

    4、為整個系統提供一個全局訪問點。

  缺點

    1、不適用於變化頻繁的對象;

    2、濫用單例將帶來一些負面問題,如為了節省資源將數據庫連接池對象設計為的單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;

    3、如果實例化的對象長時間不被利用,系統會認為該對象是垃圾而被回收,這可能會導致對象狀態的丟失;

 

四、單例模式的實現

  1、餓漢式

public class Mgr{
    //創建自己的實例,並初始化私有靜態final成員
    private static final Mgr mgr = new Mgr();
    //私有構造方法
    private Mgr() {}; 
    //公共方法,返回自己的實例化成員
    public static Mgr getMgr() { 
        return  mgr;
    }
}

  備註:該單例實現方法簡單明了,推薦使用。該類被JVM加到內存的時候,只會加載一次,並且只實例化一個單例,優點是具有線程安全性,缺點是:不用他也在內存中實例化,浪費內存。所以提出了懶散式實現方式。

  2、懶漢式

public class Mgr{
   //聲明私有靜態對象成員,作為返回值
    private static Mgr mgr;
   //私有構造函數
    private Mgr() {}; 
   //懶漢的特點,提供公共靜態方法,如果該成員對象為空,實例化並返回
    public static Mgr getMgr() {
        if(mgr == null){
            mgr =  = new Mgr();
        }
         return  mgr;
     }
}

  備註:(不推薦用)這種實現方法只有程序在調用該類的getMgr方法才實例話對象並返回,特點就是調用的時候再實例化並返回,延遲加載的被動形式。但是該實現方法不是線程安全的,因為當同時有有兩個線程執行到if(mgr==null)語句的時候,由於某些原因其中一個線程先一步執行下一句,實例化了對象並返回;兩一個線程再實例化對象在返回,這兩個線程返回的對象不是同一個對象(這難道還是單例嗎!),所以該實現方法的缺點也很明顯。那為了避免線程不安全問題,在懶漢寫法上提出加鎖的實現方式。

  3、給公共方法加鎖

public class Mgr{
    //聲明私有靜態對象成員,作為返回值
    private static Mgr mgr;
    //私有構造函數
    private Mgr() {}; 
    //給公共方法加鎖,只有一個線程第一次獲得鎖實例化對象並返回
    public static syncnronized Mgr getMgr() {
        if(mgr == null){
            mgr = new Mgr();
        }
        return  mgr;
   }
}

  備註:(推薦使用)這種實現方式比較完善,既保證了懶散式的延遲加載方式,也保證了線程安全。缺點是在整個方法上加鎖,導致性能下降。因為只有第一次獲得鎖的線程實例化對象並返回,以後的線程獲得鎖的時候執行 if(mgr == null)語句的時候,由於mgr已經實例化了不為空,直接跳過返回實例。整個過程要競爭鎖,不能併發執行導致性能下降。那為優化性能下降問題,那我只在mgr = new Mgr()上加鎖,保證鎖粒度最小化的同時保證單例實例化。

  4、給實例化加鎖

public class Mgr{
    //私有靜態成員對象
    private static Mgr mgr;
    //私有構造函數
    private Mgr() {}; 
    //公共方法,在實例化語句塊加鎖,保證單例
    public static  Mgr getMgr() {
        if(mgr == null){
            syncnronized(Mgr.class){
                mgr = new Mgr();
            }
        }
         return  mgr;
  }
}

  備註:(不推薦使用)該實現方法雖然相較方法3性能有所提升,但並不能保證線程安全。因為當兩個線程同時執行if(mgr == null)語句時,其中線程1獲取鎖,實例化對象並返回,線程2在獲得鎖又在實例化對象並返回。線程1和線程2獲取的對象並不是同一個。所以在此基礎上提出了雙重判斷方式。

5、雙重判斷加鎖

public class Mgr{
    //私有靜態成員對象
    private static  Mgr mgr;
    //私有構造函數
    private Mgr() {}; 
    //公共方法提供雙重判斷並在實例化代碼塊加鎖
    public static  Mgr getMgr() {
        if(mgr == null){ //第一次判斷
            syncnronized(Mgr.class){
                if(mgr == null){ //第二次判斷
                    mgr =  = new Mgr();
                }      
            }
        }
         return  mgr;
  }
}

  備註:(推薦使用)相較於方法4,該方法雙重判定,如果多線程同時執行到第一次判斷語句位置,其中一個線程獲得鎖,由於是第一次該對象成員為空,實例化后並返回。其後其它線程調用公共方法的時候,由於實例化了,在第一次判斷自接返回實例,不在產生鎖競爭。大大提高了效率,保證了線程的安全性,也保證了延遲加載的特性。

 6、靜態內部類

public class Mgr{
    private Mgr() {};
    //定義靜態內部類
    private static class MgrHolder{
        private final static Mgr mgr = new Mgr();
    } 
    //公共方法直接返回靜態內部類的實例對象
    public static  Mgr getMgr() {
        return  MgrHolder.mgr;
  }
}

  備註:(可使用)該實現方法通過JVM來保證線程安全性,靜態內部類MgrHolder來New一個Mgr對象,JVM只會加載一次Mgr類(靜態內部類不會加載),當類調用getMgr方法的時候,也只會調用一次,公共方法調用靜態內部類,獲取一個對象(也是實現懶加載)。所以也是線程安全的。

7、枚舉類單例模式

public enum Mgr{
    mgr;
    public void m(){} //業務方法
}

  備註:(推薦使用)jdk1.5之後才能正常達到單例效果,參考來自《Effective Java》。注意枚舉類的枚舉變量必須寫在第一行,後面實現業務代碼。調用方式是:Mgr.mgr.Function_Name();具備枚舉類型的特點,有點是:線程同步,防止反序列化。

五、總結

  通過上面幾種單例模式的實現方式的列舉,但是在實際應用中其中的2,3,4三種方式並不適用,列出來只是讓讀者更好的理解方式5的由來,起到拋磚引玉的作用,更好的理解單例模式。總之常用的四種,懶漢,雙重校驗鎖,靜態內部類,枚舉單例。

  餓漢:類加載的時候就創建實例,所以是線程安全的,但不能延遲加載。

  雙重校驗鎖:線程安全,效率較高,延遲加載。

  靜態內部類:實現起來比較麻煩,在不同的編譯器上會出現不可預知的錯誤。

  枚舉單例:很好,不僅避免了多線程同步問題,而且能反正反序列化重新創建對象,但是不能延遲加載,用的人少。

 

  • 讀者發現有什麼有問題的地方謝謝留言指正。部分參考自:https://www.cnblogs.com/xuwendong/p/9633985.html#_label0

 

 

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

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

Keycloak快速上手指南,只需10分鐘即可接入Spring Boot/Vue前後端分離應用實現SSO單點登錄_網頁設計公司

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

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

登錄及身份認證是現代web應用最基本的功能之一,對於企業內部的系統,多個系統往往希望有一套SSO服務對企業用戶的登錄及身份認證進行統一的管理,提升用戶同時使用多個系統的體驗,Keycloak正是為此種場景而生。本文將簡明的介紹Keycloak的安裝、使用,並給出目前較流行的前後端分離應用如何快速接入Keycloak的示例。

Keycloak是什麼

Keycloak是一種面向現代應用和服務的開源IAM(身份識別與訪問管理)解決方案

Keycloak提供了單點登錄(SSO)功能,支持OpenID ConnectOAuth 2.0SAML 2.0標準協議,擁有簡單易用的管理控制台,並提供對LDAP、Active Directory以及Github、Google等社交賬號登錄的支持,做到了非常簡單的開箱即用。

Keycloak常用核心概念介紹

首先通過官方的一張圖來了解下整體的核心概念

這裏先只介紹4個最常用的核心概念:

  1. Users: 用戶,使用並需要登錄系統的對象

  2. Roles: 角色,用來對用戶的權限進行管理

  3. Clients: 客戶端,需要接入Keycloak並被Keycloak保護的應用和服務

  4. Realms: 領域,領域管理着一批用戶、證書、角色、組等,一個用戶只能屬於並且能登陸到一個域,域之間是互相獨立隔離的, 一個域只能管理它下面所屬的用戶

Keycloak服務安裝及配置

安裝Keycloak

Keycloak安裝有多種方式,這裏使用Docker進行快速安裝

docker run -d --name keycloak \
    -p 8080:8080 \
    -e KEYCLOAK_USER=admin \
    -e KEYCLOAK_PASSWORD=admin \
    jboss/keycloak:10.0.0

訪問http://localhost:8080並點擊Administration Console進行登錄

創建Realm

創建一個新的realm: demo,後續所有的客戶端、用戶、角色等都在此realm中創建

創建客戶端

創建前端應用客戶端

創建一個新的客戶端:vue-demo,Access Type選擇public

創建後端應用客戶端

創建一個新的客戶端:spring-boot-demo,Access Type選擇bearer-only

保存之後,會出現Credentials的Tab,記錄下這裏的secret,後面要用到

關於客戶端的訪問類型(Access Type)

上面創建的2個客戶端的訪問類型分別是public、bearer-only,那麼為什麼分別選擇這種類型,實際不同的訪問類型有什麼區別呢?

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

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

事實上,Keycloak目前的訪問類型共有3種:

confidential:適用於服務端應用,且需要瀏覽器登錄以及需要通過密鑰獲取access token的場景。典型的使用場景就是服務端渲染的web系統。

public:適用於客戶端應用,且需要瀏覽器登錄的場景。典型的使用場景就是前端web系統,包括採用vue、react實現的前端項目等。

bearer-only:適用於服務端應用,不需要瀏覽器登錄,只允許使用bearer token請求的場景。典型的使用場景就是restful api。

創建用戶和角色

創建角色

創建2個角色:ROLE_ADMIN、ROLE_CUSTOMER

創建用戶

創建2個用戶:admin、customer

綁定用戶和角色

給admin用戶分配角色ROLE_ADMIN

給customer用戶分配角色ROLE_CUSTOMER

Vue應用集成Keycloak簡明指南

創建vue項目

vue create vue-demo

添加官方Keycloak js適配器

npm i keycloak-js --save
npm i axios --save

main.js

import Vue from 'vue'
import App from './App.vue'
import Keycloak from 'keycloak-js'

Vue.config.productionTip = false

// keycloak init options
const initOptions = {
  url: 'http://127.0.0.1:8080/auth',
  realm: 'demo',
  clientId: 'vue-demo',
  onLoad:'login-required'
}

const keycloak = Keycloak(initOptions)

keycloak.init({ onLoad: initOptions.onLoad, promiseType: 'native' }).then((authenticated) =>{
  if(!authenticated) {
    window.location.reload();
  } else {
    Vue.prototype.$keycloak = keycloak
    console.log('Authenticated')
  }

  new Vue({
    render: h => h(App),
  }).$mount('#app')

  setInterval(() =>{
    keycloak.updateToken(70).then((refreshed)=>{
      if (refreshed) {
        console.log('Token refreshed');
      } else {
        console.log('Token not refreshed, valid for '
            + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');
      }
    }).catch(error => {
      console.log('Failed to refresh token', error)
    })
  }, 60000)

}).catch(error => {
  console.log('Authenticated Failed', error)
})

HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <div>
      <p>
        current user: {{user}}
      </p>
      <p>
        roles: {{roles}}
      </p>
      <p>
        {{adminMsg}}
      </p>
      <p>
        {{customerMsg}}
      </p>
    </div>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
      user: '',
      roles: [],
      adminMsg: '',
      customerMsg: ''
    }
  },
  created() {
    this.user = this.$keycloak.idTokenParsed.preferred_username
    this.roles = this.$keycloak.realmAccess.roles

    this.getAdmin()
            .then(response=>{
              this.adminMsg = response.data
            })
            .catch(error => {
              console.log(error)
            })

    this.getCustomer()
            .then(response => {
              this.customerMsg = response.data
            })
            .catch(error => {
              console.log(error)
            })
  },
  methods: {
    getAdmin() {
      return axios({
        method: 'get',
        url: 'http://127.0.0.1:8082/admin',
        headers: {'Authorization': 'Bearer ' + this.$keycloak.token}
      })
    },
    getCustomer() {
      return axios({
        method: 'get',
        url: 'http://127.0.0.1:8082/customer',
        headers: {'Authorization': 'Bearer ' + this.$keycloak.token}
      })
    }
  }
}
</script>

getAdmin()getCustomer()這2個方法內部分別請求restful api

Spring Boot應用集成Keycloak簡明指南

添加Keycloak Maven依賴

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>10.0.0</version>
</dependency>

Spring Boot配置文件

官方文檔及網上大部分示例使用的都是properties格式的配置文件,而yaml格式的配置文件相對更簡潔清晰些,此示例使用yaml格式的配置文件,內容如下

server:
  port: 8082
keycloak:
  realm: demo
  auth-server-url: http://127.0.0.1:8080/auth
  resource: spring-boot-demo
  ssl-required: external
  credentials:
    secret: 2d2ab498-7af9-48c0-89a3-5eec929e462b
  bearer-only: true
  use-resource-role-mappings: false
  cors: true
  security-constraints:
    - authRoles:
        - ROLE_CUSTOMER
      securityCollections:
        - name: customer
          patterns:
            - /customer
    - authRoles:
        - ROLE_ADMIN
      securityCollections:
        - name: admin
          patterns:
            - /admin

除了幾個必填的配置項外,另外需要注意的幾個配置項如下

credentials.secret:上文添加客戶端后Credentials Tab內對應的內容

bearer-only:設置為true,表示此應用的Keycloak訪問類型是bearer-only

cors:設置為true表示允許跨域訪問

security-constraints:主要是針對不同的路徑定義角色以達到權限管理的目的

  • /customer:只允許擁有ROLE_CUSTOMER角色的用戶才能訪問
  • /admin:只允許擁有ROLE_ADMIN角色的用戶才能訪問
  • 未配置的路徑表示公開訪問

Controller內容

@RestController
public class HomeController {
    @RequestMapping("/")
    public String index() {
        return "index";
    }

    @RequestMapping("/customer")
    public String customer() {
        return "only customer can see";
    }

    @RequestMapping("/admin")
    public String admin() {
        return "only admin cas see";
    }
}

項目效果演示

分別啟動前後端項目后,本地8081端口對應vue前端項目,本地8082端口對應Spring Boot實現的restful api項目

首次訪問vue前端項目

第一次訪問vue項目會跳轉Keycloak登錄頁

登錄admin用戶

登錄customer用戶

總結

Keycloak部署及接入簡單,輕量的同時功能又不失強大,非常適合企業內部的SSO方案。

本文示例項目地址:keycloak-demo

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

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

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

java中的垃圾處理機制_包裝設計

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

1.何為垃圾
在Java中,如果對象實體沒有引用指向的話,存儲該實體的內存便成為垃圾。JVM會有一個系統線程專門負責回收垃圾。垃圾同時包括分配對象內存間的碎片塊

2.垃圾處理包含的算法

Java語言規範沒有明確地說明JVM使用哪種垃圾回收算法,但是任何一種垃圾回收算法一般要做2件基本的事情:(1)發現無用的信息對象,(2)回收無用對象佔據的內存,使得該內存可以被程序再次使用。
垃圾回收一面在回收內存,一面使堆中的內存緊密排列。下面介紹幾種算法:

2-1引用計數法
該算法使用引用計數器來區分存活對象和不再使用的對象。一般來說,堆中的每個對象對應一個引用計數器。當每一次創建一個對象並賦給一個變量時,引用計數器置為1。當對象被賦給任意變量時,引用計數器每次加1當對象出了作用域后(該對象丟棄不再使用)或者被置為null時,引用計數器減1,一旦引用計數器為0,對象就滿足了垃圾收集的條件。
基於引用計數器的垃圾收集器運行較快,不會長時間中斷程序執行,適宜必須實時運行的程序。但引用計數器增加了程序執行的開銷,因為每次對象賦給新的變量,計數器加1,而每次現有對象出了作用域,計數器減1。雖然管理引用計數的開銷不大,但是該開銷在整個程序的生命周期

2-2tracing算法(標記-清除)
基於tracing算法的垃圾收集也稱為標記和清除(mark-and-sweep)垃圾收集器,它所依據的思路是,從棧和靜態存儲區出發,遍歷所有的引用,找到存活的對象,每當找到一個存活的對象,就給該對象設一個標記,當標記工作全部完成時,清理工作才會開始。在清理的過程中,沒有標記的對象會被釋放。該方式相當慢,在產生少量垃圾和幾乎不產生垃圾的情況下速度就很快了。

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

2.3. compacting算法(標記-整理)
為了解決堆碎片問題,基於compacting的垃圾回收吸收了tracing算法的思想。在清除無用對象之後,算法將所有的對象移到堆的一端,堆的另一端就變成了一個相鄰的空閑內存區,收集器會對它移動的所有對象的所有引用進行更新,使得這些引用在新的位置能識別原來的對象。解決了內存碎片的問題(不但進行了清理而且進行了對象的搬運,成本更高)。在基於Compacting算法的收集器的實現中,一般增加句柄和句柄表。

2.4. copying算法
它開始時把堆分成一個對象區和多個空閑區,程序從對象區為對象分配空間,當對象滿了,基於coping算法的垃圾回收就掃描活動對象,並將每個活動對象複製到空閑區(使得活動對象所佔的內存之間沒有空閑間隔),這樣空閑區變成了對象區,原來的對象區變成了空閑區,程序會在新的對象區中分配內存。
一種典型的基於coping算法的垃圾回收是stop-and-copy算法,它將堆分成對象區和空閑區域區,在對象區與空閑區域的切換過程中,程序暫停執行。

2.5. generation算法
stop-and-copy垃圾收集器的一個缺陷是收集器必須複製所有的活動對象,這增加了程序等待時間,這是coping算法低效的原因。分代的垃圾回收策略,是基於這樣一個事實:不同的對象的生命周期是不一樣的。因此,不同生命周期的對象可以採取不同的回收算法,以便提高回收效率。generation算法將堆分成兩個或多個,每個子堆作為對象的一代(generation)。1.所有新生成的對象首先都是放在年輕代的。年輕代的目標就是盡可能快速的收集掉那些生命周期短的對象。2.在年輕代中經歷了N次垃圾回收后仍然存活的對象,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。3.持久代用於存放靜態文件,如Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。

3.System.gc()方法
使用System.gc()可以不管JVM使用的是哪一種垃圾回收的算法,都可以請求Java虛擬機進行垃圾回收,值得注意的是,JVM接受這個消息后,並不是立即做垃圾回收(需要搶佔CPU資源),而只是對幾個垃圾回收算法做了加權,使垃圾回收操作容易發生,或提早發生,或回收較多而已。
盡量避免显示的調用gc,若不針對GC的特點進行設計和編碼,就會出現內存駐留等一系列負面影響。此函數建議JVM進行主GC,雖然只是建議而非一定,但很多情況下它會觸發主GC,從而增加主GC的頻率,也即增加了間歇性停頓的次數。

4. finalize()方法
java垃圾處理器只能釋放哪些經由New出來的對象內存,對於其它途徑產生的內存,java允許在類中定義finalize()方法,在垃圾回收時候調用finalize(),處理內存,雖然不一定發生,但是當垃圾回收動作發生時,非new內存會被清理。finalize中添加一些非java能夠處理的垃圾,例如類似C語言中使用的malloc()函數分配的內存,除非調用free(),否則內存得不到釋放,造成泄露。所以,在finalize方法中調用free()方法,(free是C和C++的方法)。當垃圾回收發生時,finalize()函數被調用。絕對不能直接調用finalize(),因為垃圾回收只與內存有關,無論對象是如何創建的,垃圾回收器都會負責釋放那些對象佔有的內存。
當垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。子類重寫 finalize 方法,以配置系統資源或執行其他清除。
java虛擬機在未面臨內存耗盡的情況下,不會浪費時間去執行垃圾回收以恢復內存的。

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

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

這些“消失”的經典汽車設計 你有錢都買不到!_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

而到了現在就是常見手握式把手了。車身防擦條當年因為汽車還不算普及,開車技巧也沒有那麼好,所以會在車身周圍和保險杠的位置安置軟朔膠的防擦條,從而發生輕微碰撞和摩擦的時候損壞到漆面。而現在,幾乎看到不到咯,保險杠還在就算良心了。

隨着時代的進步,科技也在慢慢發展,人們的審美也隨着變化,當然,這也包括了汽車產業,在以前經常看到汽車上的一些設計,現在慢慢的也“消失”不見了。

自動伸縮天線

想當年,汽車身上那根細細的天線成為了當時汽車的一種“時髦”,不管是不是真假的,幾乎有車的人都加裝了這一根“天線”,就好像現在加裝包圍一樣流行。在當年,原配有天線的車可都是豪車。到了現在,為了美觀和空氣動力學,就設計成了“鯊魚鰭”的形狀。

上翻式門把手

也許,在一些很老的車型上還能找到這種設計的把手,捨棄這種設計的原因是把手位置容易給指甲刮花,

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

發生碰撞時,上翻式把手的設計原理容易導致門打不開。而到了現在就是常見手握式把手了。

車身防擦條

當年因為汽車還不算普及,開車技巧也沒有那麼好,所以會在車身周圍和保險杠的位置安置軟朔膠的防擦條,從而發生輕微碰撞和摩擦的時候損壞到漆面。而現在,幾乎看到不到咯,保險杠還在就算良心了。

跳燈

說起跳燈,相信很多人會先想起《頭文字D》裏面的AE86,在當年,跳燈也是一種潮流的設計,到後來,設計師們發現,跳燈會增加車輛的風阻,故障率也高,慢慢的就“消失了”。不過我依然覺得,跳燈很帥。

尾語:考慮到實用性,安全性,美觀性等問題,一些兒時的經典已經慢慢“消失”,當然,這些“消失”的設計並不是完全的消失,而是進化了,新舊交替是必然的事。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

買車算什麼?信不信帶它出門蘭博基尼都給我讓路_網頁設計公司

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

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

所以擁有日內瓦印記的腕錶也是收藏家和鑒賞家的最愛,這意味着工藝達到了瑞士手錶行業的頂級水準。羅傑杜彼年輕,但卻擁有眾多技藝精湛的工匠。在製表領域,能夠獨立製造極其複雜的擺輪遊絲的製表廠可謂屈指可數,但羅傑杜彼能做到真正的自造遊絲自造機芯。

有句話大家應該都聽過:窮玩車,富玩表。但在上個世紀七八十年代,

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

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

這句話中的車指的是三轉一響中的自行車,表指的是西洋表。當時自行車儘管難得,不過無論精密還是精美程度都遠遠不及機械錶,價格自然也天上地下,所以有了這句戲言。

但如今汽車與手錶同為工藝極其複雜的高精度机械,而如此精密的机械作用放在一塊手錶里,難度簡直是汽車的千倍以上。所以,當叫獸看到一塊手錶原來能賣到這個價時:

本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

開瑞汽車重走長征路 致敬歷史_包裝設計

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

在整段的長征路線中,要面對的是各種各樣的不同路況,為了展示自身品牌的實力,廠家肯定要拿一台有足夠實力的車型來走這段長征路,那開瑞汽車派出,銷量和口碑都非常不錯的K50來擔當這個重要的角色,一台4。48萬起、排量1。

我想說

長征如果不偉大,我不知道,有哪件事能配得上偉大這一詞!

1934年10月

八萬六千名革命男女從江西出發

到1936年10月

毛澤東率領的這支第一方面軍抵達陝北時只剩下大約六千人

翻越山脈十八座,其中五座被積雪覆蓋,跨過大河二十四條,歷經十一省二萬五千里

江西瑞金出發-甘肅會寧會師長徵結束

中央紅軍在長征出發時 8.6萬人共有槍支33244支,平均每支槍不到56發子彈。即使加上6101支梭鏢和882把馬刀,一半人赤手空拳。除了有數幾個將領,包括毛澤東在內很多人沒有接受過軍事教育

紅軍將領軍級平均29歲,師、團級平均25歲

林彪,紅1軍團軍團長,28歲

長征開始那一年,少共國際師師長肖華18歲

長徵結束那一年,廖漢生25歲,已是紅二方面軍前鋒師政委

在今天,這個歲數還是多數年輕人剛剛開始工作的年齡

在今天看來我們的汽車工業發展一路走來,其實跟當年的長征路,還是有一定的相似度的,“國外軍”長期佔領國內的大部分市場份額,“國內軍”技術研發慢,市場難於突破,只能退居三線,默默耕耘,但是這三年的時間“國內軍”的發展已經是影響着“國外軍”的發展了,技術也在不斷強大,市場也在不斷的侵蝕,

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

這是一場勝利的戰,而且還在繼續影響着。

那今年是中國紅軍長征勝利八十周年。由多个中國品牌汽車組成的“最強中國車”車隊,9月9日從江西瑞金出發,要走一場“輪上長征路”向歷史致敬,展示中國品牌汽車的最新面貌和最強實力。

在整段的長征路線中,要面對的是各種各樣的不同路況,為了展示自身品牌的實力,廠家肯定要拿一台有足夠實力的車型來走這段長征路,那開瑞汽車派出,銷量和口碑都非常不錯的K50來擔當這個重要的角色,一台4.48萬起、排量1.5升、馬力只有109匹,真的可以應付的了泥濘顛簸起伏的爛山路嗎?

而且開瑞還是整個車隊中,唯一的七座MpV,它除了要兢兢業業的走好每一段路外,還要肩負大部分的食物及行李的運輸工作,可謂面面俱到啊。

開瑞汽車作為奇瑞汽車的下屬企業,其在微面市場已耕耘多年,而開瑞K50則是其在2015年推向市場的首款MpV車型,該車2+2+3的七座式布局、前置前驅的驅動方式以及四輪盤剎等硬件裝備,使其與此前的微面車型形成了本質上的差異,而較為親民的定價也使其更多的瞄準了家用市場,2017款開瑞K50將延續同樣的產品定位,在中國品牌MpV市場發力。

2017款開瑞K50/K50S在外觀、內飾方面的提升,以及更多車身顏色的提供使其產品新鮮度有所增加,而這也使開瑞K50在市場中的存在感更加強烈。同時,結合其較低的定價來看,性價比依然是2017款開瑞K50的優勢之一,但競爭對手的不斷增加使開瑞K50所要面對的市場環境更加嚴峻,那K50作為這次的“重走長征路”的重要角色之一,更好的體現了在同級車型中,是具有相當大的競爭能力,那新款車型能否保住現款開瑞K50在MpV熱銷榜前十名的位置,我們拭目以待。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

最多10萬元 選國產SUV還是合資家用轎車_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

朗逸較簡單內飾這東西跟價格絕對是成正比的,雖然這兩個車價格差不多,但是由於GS是自主品牌,所以內飾絕對要比朗逸“豪華”一個檔次。尺寸和空間基本處在一個檔次視覺上朗逸稍微大一點,但是真的坐進去了會發現其實都差不多,可能朗逸確實大那麼一點點。

如今十萬級別的車子日漸成為大多數消費者的主流選擇,所以今天我們就選取了目前比較熱的兩款車,帝豪GS和大眾朗逸做對比,看看誰的性價比更高。

為了對比的公平,我們就選取了帝豪GS的頂配車型-運動版 1.3T 自動臻尚型,指導價為10.88萬。朗逸為1.6L 自動風尚版,指導價為12.19萬元(優惠下來和帝豪GS價格相近)。為了方便閱讀下文帝豪GS簡稱為GS。之所以選擇朗逸和GS對比,是因為後台有很多讀者經常糾結這兩個車怎麼選擇,所以今天就統一給大家做個介紹。

外觀

GS的更具設計感;朗逸更中庸

因為帝豪GS是吉利精品2.0時代的最新產品,所以外觀設計上緊跟時代的步伐,看起來非常時尚動感。朗逸則是更多的採用大眾中庸化的設計,整體上並沒有太大的特色。不過,有很多消費者就是很喜歡朗逸的外觀設計。

內飾

GS更精緻;朗逸較簡單

內飾這東西跟價格絕對是成正比的,雖然這兩個車價格差不多,但是由於GS是自主品牌,所以內飾絕對要比朗逸“豪華”一個檔次。

尺寸和空間

基本處在一個檔次

視覺上朗逸稍微大一點,但是真的坐進去了會發現其實都差不多,可能朗逸確實大那麼一點點。

配置

毫無懸念GS完勝,

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

不管舒適性配置還是安全配置GS都“吊打”朗逸

動力系統

GS雙離合不太靠譜;朗逸動力系統較老但是穩定耐操

雖然帝豪1.3T發動機是最新研發的,性能還是比較先進的,但是雙離合變速箱遭到了不少消費者的吐槽。朗逸的動力系統雖然沒有什麼亮點,但是質量確實非常好。

由於朗逸車子較輕,再加上調教的很好,所以朗逸的百公里油耗約為7.5L,GS則比較高,約為9L。對於駕駛感受來說,由於朗逸屬於老舊平台的產物,所以駕駛感受很一般,帝豪GS由於調教的很不錯,駕駛起來會給你不小的驚喜。

其實這兩台車子硬件比起來肯定是GS完勝,但是軟實力也就是品牌知名度來看,GS顯然差距還很大。雖然GS外觀好看,還是大家喜歡的SUV車型,但是大家還是更傾向於選擇朗逸。畢竟,大家覺得,朗逸的質量,要比GS好太多。

其實我們並不是十分推薦GS的雙離合車型,本文只是為了方便做對比,因為只有最貴的GS才和1.6L 自動風尚版的朗逸價格差不多。所以我們更推薦GS的手動擋車型,質量更可靠,最值得推薦。如果真的喜歡雙離合,那麼小編還是更推薦大眾的雙離合。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

光便宜還不夠 這些10幾萬的家用車還能賺些買菜錢_網頁設計公司

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

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

如果這些政策真的落實這類打車軟件很有可能成為最新的相親軟件就像網上說過的一個梗本地戶口、價格不便宜的新車、還有本地牌照滿足這些條件估計都有車有房比普通的交友平台更牛逼。下班也要出去做網約司機了。說了這麼多,究竟什麼車才適合跑滴滴。

我們沒車一族的出行方式基本就是靠走、或者踩個自行車、乘坐公共交通工具、部分地區短途還能坐摩托車、土豪一點就去坐計程車。從Uber進入中國以及和滴滴出行一起相互大肆燒錢開始,我們終於找到了一個舒服、價格便宜的出行方式,也讓有車一族找到了一個月入破萬的工作。

還記得那時全民皆滴滴的時代

人們出行叫網約車

不僅隨傳隨到、關鍵是便宜

再也不用碰到那些讓人厭惡的出租車!

對於很多駕駛員來說

終於擁有一個月入破萬的工作

還記得網約車剛興起的時候

做幾個月新買的車就回本了

每天做幾單就有巨額獎勵

也讓不少人辭掉了原本的工作

紛紛投入了網約車的行列

作為一個開放的平台,各位車主只需要身份證、駕駛證、行駛證就能完成註冊,投入到偉大的網約車行列,也正是因為手續如此簡單,也造就了網約車行列出現一些混亂的狀況,也存在一些比較極端的負面新聞。

下面我們來扒一扒你打滴滴遇過的奇葩事情…

王小姐:我曾經叫了一輛滴滴,然後來的竟然是一檯面包車!麵包車!而且還有很大一股異味!

楊先生:叫了滴滴,看着App,盯着地圖上的小汽車離自己越來越近了,然後趕時間的我馬上就能上車了….後來司機開過了,結果司機越開越遠,最後又兜了很大一個圈子轉回來!!!我勒個去!

好日子總會到頭的

網約車車存在已經嚴重影響到計程車行業

為了保障利益

終極BOSS受不了壓力要出手了!

《網約車徵求意見稿》

已經從北上廣深這幾個一線城市出台

用最簡單的話術總結這個意見稿

車、牌、人三方面都作出明確規定!

在尺寸上

北、深、上三大城市軸距要求≥2.7米

新能源車軸距≥2.65米

廣州長寬高分別大於4.6米、1.7米、1.42米

在排量上

北、上、深≥2.0L或1.8T;上海沒要求

在年限上

廣州要求1年內准新車

深圳要求2年內車型

北京、上海並沒要求

總結下來最嚴格的還是廣州

另外網約車要求是本地牌照

北京需要搖號

上海:拍牌均價88359元(10月)

廣州:拍牌均價19614元(9月)

深圳:拍牌均價39729元(9月)

在駕駛員方面

北、上兩城需要本地戶口

深圳需要有居住證

廣州並沒要求(這是一個包容的城市)

看起來要滿足這一些條件真不容易…

如果這些政策真的落實

這類打車軟件很有可能成為

最新的相親軟件

就像網上說過的一個梗

本地戶口、價格不便宜的新車、還有本地牌照

滿足這些條件估計都有車有房

比普通的交友平台更牛逼!

下班也要出去做網約司機了!

說了這麼多,究竟什麼車才適合跑滴滴?

要跑滴滴首先要滿足以下幾個條件!

中級車以上(價格還不能太高)

動力在2.0L或1.8T以上

車子省油、保養便宜、而且耐用

最後當然要空間大

1日系选手

凱美瑞

車身尺寸:4850*1825*1480mm

軸距:2775mm

符合車型:全系

售價:18.48-32.98萬

點評:在看來,滴滴的法規上,日系車佔優!先趴一趴日系車的優點,空間大、省油、保養便宜、而且還耐操,用來做網約車還真是合適,而這一次所說的豐田凱美瑞全系車型均已滿足各項條件,這裏更推薦選擇雙擎車型,雖然是2.5L的排量但是油耗才5-7L左右的表現就已經能獨領風騷,更不用說2.0L車型本身的表現了。照這麼說雅閣也是個不錯的選擇囖?其實不然,針對網約車來說,凱美瑞的後排舒適度是在雅閣之上的,

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

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

自己搭車也會選擇凱美瑞。

2德系选手

帕薩特

車身尺寸:4872*1834*1484mm

軸距:2803mm

符合車型:1.8T以上車型

售價:22.29-33.29萬

點評:帕薩特可以說是在德系品牌中能夠達標而且價格是最便宜的一個車型,在空間、動力、排量、尺寸方面均已經滿足要求,雖然帕薩特有一個比較大的優惠幅度,但是1.8T以上的車型辦下來也要20多萬,專門拿來做網約車就真的比較浪費了,而且德系車保養並不便宜,另外還存在一定程度的燒機油情況,拿來做網約車,未免投入太大了。

3美系选手

邁銳寶XL

車身尺寸:4923*1854*1477mm

軸距:2829mm

符合車型:2.5L版本

售價:21.99-24.99萬

點評:外觀帥氣、空間大、配置高是邁銳寶XL主要賣點之一,只是用來做網約車就必須選擇2.5L的版本,在油耗方面會偏高,而且後期投入比較大,除了這一款車型,你也可以選擇2.0L的君威或者2.0T的蒙迪歐,但是百公里油耗隨便都能超過10L的車型來說,拿來做網約車加油加到怕啊。

4國產选手

博瑞

車身尺寸:4956*1861*1513mm

軸距:2850mm

符合車型:全系

售價:11.98-22.98萬

點評:實際上博瑞也是為數不多能夠參与到網約車序列的車型之一,首先就是國產的中型車並不多,其中就博瑞賣得不錯,雖然博瑞的油耗也是百公里油耗超過10L的貨,但關鍵是車型起售價比其他車型便宜,雖然博瑞的小毛病還是有不少,但三大件的大問題還算得上過關,拿來做網約車也是可以的。

總結:

就如所說,這一次網約車的規定就像給日系車量身定做那般,做網約車最關鍵的是什麼?認為關鍵就是要省油!另外就是要售價便宜的,所以真的要狠下心來做網約車,跟偏向於選擇凱美瑞這一類的日系中型車。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

這些十幾萬的車換個殼賣貴3萬 偏偏大家都買貴的 你呢?_包裝設計

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

98-18。98萬元斯柯達-明銳指導價:9。98-17。99萬元大眾-速騰指導價:13。18-21。88萬元總結科雷嘉和逍客,會選擇科雷嘉,因為它有更加高的顏值,內飾設計富含科技感,各方面的用料做工都要比逍客更好,價格可能是唯一需要考慮的地方。

孿生兄弟大家一定聽過,那麼汽車界的孿生兄弟你又知道多少呢?事先聲明一下,想要表達的不是路虎極光和陸風X7,更加不是保時捷MACAN和眾泰SR9。

要講的是真正意義上的孿生兄弟,它們的底子一樣,但是樣子不同,

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

各項的調校也不盡相同,最重要的是售價也不相同。

指導價:16.38-21.98萬元

指導價:13.98-18.98萬元

指導價:9.98-17.99萬元

指導價:13.18-21.88萬元

科雷嘉和逍客,會選擇科雷嘉,因為它有更加高的顏值,內飾設計富含科技感,各方面的用料做工都要比逍客更好,價格可能是唯一需要考慮的地方。

明銳和速騰,會選擇明銳,最大的決定因素是掀背式的尾廂,然後就是內飾的設計,速騰的內飾已經看膩了,再去看看明銳的內飾,有新鮮感。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

Angular 從入坑到挖坑 – 路由守衛連連看_貨運

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

一、Overview

Angular 入坑記錄的筆記第六篇,介紹 Angular 路由模塊中關於路由守衛的相關知識點,了解常用到的路由守衛接口,知道如何通過實現路由守衛接口來實現特定的功能需求,以及實現對於特性模塊的惰性加載

對應官方文檔地址:

  • 路由與導航

配套代碼地址:angular-practice/src/router-combat

二、Contents

  1. Angular 從入坑到棄坑 – Angular 使用入門
  2. Angular 從入坑到挖坑 – 組件食用指南
  3. Angular 從入坑到挖坑 – 表單控件概覽
  4. Angular 從入坑到挖坑 – HTTP 請求概覽
  5. Angular 從入坑到挖坑 – Router 路由使用入門指北
  6. Angular 從入坑到挖坑 – 路由守衛連連看

三、Knowledge Graph

四、Step by Step

4.1、基礎準備

重複上一篇筆記的內容,搭建一個包含路由配置的 Angualr 項目

新建四個組件,分別對應於三個實際使用到的頁面與一個設置為通配路由的 404 頁面

-- 危機中心頁面
ng g component crisis-list

-- 英雄中心頁面
ng g component hero-list

-- 英雄相親頁面
ng g component hero-detail

-- 404 頁面
ng g component page-not-found 

在 app-routing.module.ts 文件中完成對於項目路由的定義,這裏包含了對於路由的重定向、通配路由,以及通過動態路由進行參數傳遞的使用

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

// 引入組件
import { CrisisListComponent } from './crisis-list/crisis-list.component';
import { HeroListComponent } from './hero-list/hero-list.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';

const routes: Routes = [
  {
    path: 'crisis-center',
    component: CrisisListComponent,
  },
  {
    path: 'heroes',
    component: HeroListComponent,
  },
  {
    path: 'hero/:id',
    component: HeroDetailComponent,
  },
  {
    path: '',
    redirectTo: '/heroes',
    pathMatch: 'full',
  },
  {
    path: '**',
    component: PageNotFoundComponent,
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule { }

之後,在根組件中,添加 router-outlet 標籤用來聲明路由在頁面上渲染的出口

<h1>Angular Router</h1>
<nav>
  <a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a> &nbsp;&nbsp;
  <a routerLink="/heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>

4.2、路由守衛

在 Angular 中,路由守衛主要可以解決以下的問題

  • 對於用戶訪問頁面的權限校驗(是否已經登錄?已經登錄的角色是否有權限進入?)
  • 在跳轉到組件前獲取某些必須的數據
  • 離開頁面時,提示用戶是否保存未提交的修改

Angular 路由模塊提供了如下的幾個接口用來幫助我們解決上面的問題

  • CanActivate:用來處理系統跳轉到到某個路由地址的操作(判斷是否可以進行訪問)
  • CanActivateChild:功能同 CanActivate,只不過針對的是子路由
  • CanDeactivate:用來處理從當前路由離開的情況(判斷是否存在未提交的信息)
  • CanLoad:是否允許通過延遲加載的方式加載某個模塊

在添加了路由守衛之後,通過路由守衛返回的值,從而達到我們控制路由的目的

  • true:導航將會繼續
  • false:導航將會中斷,用戶停留在當前的頁面或者是跳轉到指定的頁面
  • UrlTree:取消當前的導航,並導航到路由守衛返回的這個 UrlTree 上(一個新的路由信息)
4.2.1、CanActivate:認證授權

在實現路由守衛之前,可以通過 Angular CLI 來生成路由守衛的接口實現類,通過命令行,在 app/auth 路徑下生成一個授權守衛類,CLI 會提示我們選擇繼承的路由守衛接口,這裏選擇 CanActivate 即可

ng g guard auth/auth

在 AuthGuard 這個路由守衛類中,我們模擬了是否允許訪問一個路由地址的認證授權。首先判斷是否已經登錄,如果登錄后再判斷當前登錄人是否具有當前路由地址的訪問權限

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  /**
   * ctor
   * @param router 路由
   */
  constructor(private router: Router) { }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

    // 判斷是否有 token 信息
    let token = localStorage.getItem('auth-token') || '';
    if (token === '') {
      this.router.navigate(['/login']);
      return false;
    }

    // 判斷是否可以訪問當前連接
    let url: string = state.url;
    if (token === 'admin' && url === '/crisis-center') {
      return true;
    }

    this.router.navigate(['/login']);
    return false;
  }
}

之後我們就可以在 app-routing.module.ts 文件中引入 AuthGuard 類,針對需要保護的路由進行路由守衛的配置

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

// 引入組件
import { CrisisListComponent } from './crisis-list/crisis-list.component';

// 引入路由守衛
import { AuthGuard } from './auth/auth.guard';

const routes: Routes = [
  {
    path: 'crisis-center',
    component: CrisisListComponent,
    canActivate: [AuthGuard], // 添加針對當前路由的 canActivate 路由守衛
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule { }

4.2.2、CanActivateChild:針對子路由的認證授權

與繼承 CanActivate 接口進行路由守衛的方式相似,針對子路由的認證授權可以通過繼承 CanActivateChild 接口來實現,因為授權的邏輯很相似,這裏通過多重繼承的方式,擴展 AuthGuard 的功能,從而達到同時針對路由和子路由的路由守衛

改造下原先 canActivate 方法的實現,將認證邏輯修改為用戶的 token 信息中包含 admin 即可訪問 crisis-center 頁面,在針對子路由進行認證授權的 canActivateChild 方法中,通過判斷 token 信息是否為 admin-master 模擬完成對於子路由的訪問認證

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router, CanActivateChild } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate, CanActivateChild {

  /**
   * ctor
   * @param router 路由
   */
  constructor(private router: Router) { }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

    // 判斷是否有 token 信息
    let token = localStorage.getItem('auth-token') || '';
    if (token === '') {
      this.router.navigate(['/login']);
      return false;
    }

    // 判斷是否可以訪問當前連接
    let url: string = state.url;
    if (token.indexOf('admin') !== -1 && url.indexOf('/crisis-center') !== -1) {
      return true;
    }

    this.router.navigate(['/login']);
    return false;
  }

  canActivateChild(
    childRoute: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
    let token = localStorage.getItem('auth-token') || '';
    if (token === '') {
      this.router.navigate(['/login']);
      return false;
    }

    return token === 'admin-master';
  }
}

通過 Angular CLI 新增一個 crisis-detail 組件,作為 crisis-list 的子組件

ng g component crisis-detail

接下來在 crisis-list 中添加 router-outlet 標籤,用來定義子路由的渲染出口

<h2>危機中心</h2>

<ul class="crises">
  <li *ngFor="let crisis of crisisList">
    <a [routerLink]="[crisis.id]">
      <span class="badge">{{ crisis.id }}</span>{{ crisis.name }}
    </a>
  </li>
</ul>

<!-- 定義子路由的渲染出口 -->
<router-outlet></router-outlet>

在針對子路由的認證授權配置時,我們可以選擇針對每個子路由添加 canActivateChild 屬性,也可以定義一個空地址的子路由,將所有歸屬於 crisis-list 的子路由作為這個空路由的子路由,通過針對這個空路徑添加 canActivateChild 屬性,從而實現將守護規則應用到所有的子路由上

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

這裏其實相當於將原先兩級的路由模式(父:crisis-list,子:crisis-detail)改成了三級(父:crisis-list,子:’ ‘(空路徑),孫:crisis-detail)

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

// 引入組件
import { CrisisListComponent } from './crisis-list/crisis-list.component';
import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component';

// 引入路由守衛
import { AuthGuard } from './auth/auth.guard';

const routes: Routes = [
  {
    path: 'crisis-center',
    component: CrisisListComponent,
    canActivate: [AuthGuard], // 添加針對當前路由的 canActivate 路由守衛
    children: [{
      path: '',
      canActivateChild: [AuthGuard], // 添加針對子路由的 canActivate 路由守衛
      children: [{
        path: 'detail',
        component: CrisisDetailComponent
      }]
    }]
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule { }

4.2.3、CanDeactivate:處理用戶未提交的修改

當進行表單填報之類的操作時,因為會涉及到一個提交的動作,當用戶沒有點擊保存按鈕就離開時,最好能暫停,對用戶進行一個友好性的提示,由用戶選擇後續的操作

創建一個路由守衛,繼承於 CanDeactivate 接口

ng g guard hero-list/guards/hero-can-deactivate

與上面的 CanActivate、CanActivateChild 路由守衛的使用方式不同,對於 CanDeactivate 守衛來說,我們需要將參數中的 unknown 替換成我們實際需要進行路由守衛的組件

import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class HeroCanDeactivateGuard implements CanDeactivate<unknown> {
  canDeactivate(
    component: unknown,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return true;
  }
  
}

例如,這裏針對的是 HeroListComponent 這個組件,因此我們需要將泛型的參數 unknown 改為 HeroListComponent,通過 component 參數,就可以獲得需要進行路由守衛的組件的相關信息

import { Injectable } from '@angular/core';
import {
  CanDeactivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  UrlTree,
} from '@angular/router';
import { Observable } from 'rxjs';

// 引入需要進行路由守衛的組件
import { HeroListComponent } from '../hero-list.component';

@Injectable({
  providedIn: 'root',
})
export class HeroCanDeactivateGuard
  implements CanDeactivate<HeroListComponent> {
  canDeactivate(
    component: HeroListComponent,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot
  ):
    | Observable<boolean | UrlTree>
    | Promise<boolean | UrlTree>
    | boolean
    | UrlTree {

    // 判斷是否修改了原始數據
    //
    const data = component.hero;
    if (data === undefined) {
      return true;
    }
    const origin = component.heroList.find(hero => hero.id === data.id);
    if (data.name === origin.name) {
      return true;
    }

    return window.confirm('內容未提交,確認離開?');
  }
}

這裏模擬判斷用戶有沒有修改原始的數據,當用戶修改了數據並移動到別的頁面時,觸發路由守衛,提示用戶是否保存后再離開當前頁面

4.3、異步路由

4.3.1、惰性加載

當應用逐漸擴大,使用現有的加載方式會造成應用在第一次訪問時就加載了全部的組件,從而導致系統首次渲染過慢。因此這裏可以使用惰性加載的方式在請求具體的模塊時才加載對應的組件

惰性加載只針對於特性模塊(NgModule),因此為了使用惰性加載這個功能點,我們需要將系統按照功能劃分,拆分出一個個獨立的模塊

首先通過 Angular CLI 創建一個危機中心模塊(crisis 模塊)

-- 查看創建模塊的相關參數
ng g module --help

-- 創建危機中心模塊(自動在 app.moudule.ts 中引入新創建的 CrisisModule、添加當前模塊的路由配置)
ng g module crisis --module app --routing

將 crisis-list、crisis-detail 組件全部移動到 crisis 模塊下面,並在 CrisisModule 中添加對於 crisis-list、crisis-detail 組件的聲明,同時將原來在 app.module.ts 中聲明的組件代碼移除

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { CrisisRoutingModule } from './crisis-routing.module';

import { FormsModule } from '@angular/forms';

// 引入模塊中使用到的組件
import { CrisisListComponent } from './crisis-list/crisis-list.component';
import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component';


@NgModule({
  declarations: [
    CrisisListComponent,
    CrisisDetailComponent
  ],
  imports: [
    CommonModule,
    FormsModule,
    CrisisRoutingModule
  ]
})
export class CrisisModule { }

同樣的,將當前模塊的路由配置移動到專門的路由配置文件 crisis-routing.module.ts 中,並將 app-routing.module.ts 中相關的路由配置刪除

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

// 引入組件
import { CrisisListComponent } from './crisis-list/crisis-list.component';
import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component';

// 引入路由守衛
import { AuthGuard } from '../auth/auth.guard';

const routes: Routes = [{
  path: '',
  component: CrisisListComponent,
  canActivate: [AuthGuard], // 添加針對當前路由的 canActivate 路由守衛
  children: [{
    path: '',
    canActivateChild: [AuthGuard], // 添加針對子路由的 canActivate 路由守衛
    children: [{
      path: 'detail',
      component: CrisisDetailComponent
    }]
  }]
}];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class CrisisRoutingModule { }

重新運行項目,如果你在創建模塊的命令中設置了自動引入當前模塊到 app.module.ts 文件中,大概率會遇到下面的問題

這裏的問題與配置通配路由需要放到最後的原因相似,因為腳手架在幫我們將創建的模塊導入到 app.module.ts 中時,是添加到整個數組的最後,同時因為我們已經將 crisis 模塊的路由配置移動到專門的 crisis-routing.module.ts 中了,框架在進行路由匹配時會預先匹配上 app-routing.module.ts 中設置的通配路由,從而導致無法找到實際應該對應的組件,因此這裏我們需要將 AppRoutingModule 放到聲明的最後

當問題解決后,就可以針對 crisis 模塊設置惰性加載

在配置惰性路由時,我們需要以一種類似於子路由的方式進行配置,通過路由的 loadChildren 屬性來加載對應的模塊,而不是具體的組件,修改后的 AppRoutingModule 代碼如下

import { HeroCanDeactivateGuard } from './hero-list/guards/hero-can-deactivate.guard';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  {
    path: 'crisis-center',
    loadChildren: () => import('./crisis/crisis.module').then(m => m.CrisisModule)
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes, { enableTracing: true })],
  exports: [RouterModule],
})
export class AppRoutingModule { }

當導航到這個 /crisis-center 路由時,框架會通過 loadChildren 字符串來動態加載 CrisisModule,然後把 CrisisModule 添加到當前的路由配置中,而惰性加載和重新配置工作只會發生一次,也就是在該路由首次被請求時執行,在後續請求時,該模塊和路由都是立即可用的

4.3.2、CanLoad:杜絕未通過認證授權的組件加載

在上面的代碼中,對於 CrisisModule 模塊我們已經使用 CanActivate、CanActivateChild 路由守衛來進行路由的認證授權,但是當我們並沒有權限訪問該路由的權限,卻依然點擊了鏈接時,此時框架路由仍會加載該模塊。為了杜絕這種授權未通過仍加載模塊的問題發生,這裏需要使用到 CanLoad 守衛

因為這裏的判斷邏輯與認證授權的邏輯相同,因此在 AuthGuard 中,繼承 CanLoad 接口即可,修改后的 AuthGuard 代碼如下

import { Injectable } from '@angular/core';
import {
  CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router, CanActivateChild, CanLoad, Route, UrlSegment
} from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {

  /**
   * ctor
   * @param router 路由
   */
  constructor(private router: Router) { }


  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

    // 判斷是否有 token 信息
    let token = localStorage.getItem('auth-token') || '';
    if (token === '') {
      this.router.navigate(['/login']);
      return false;
    }

    // 判斷是否可以訪問當前連接
    let url: string = state.url;
    if (token.indexOf('admin') !== -1 && url.indexOf('/crisis-center') !== -1) {
      return true;
    }

    this.router.navigate(['/login']);
    return false;
  }

  canActivateChild(
    childRoute: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
    let token = localStorage.getItem('auth-token') || '';
    if (token === '') {
      this.router.navigate(['/login']);
      return false;
    }

    return token === 'admin-master';
  }

  canLoad(route: Route, segments: UrlSegment[]): boolean | Observable<boolean> | Promise<boolean> {
    let token = localStorage.getItem('auth-token') || '';
    if (token === '') {
      this.router.navigate(['/login']);
      return false;
    }

    let url = `/${route.path}`;

    if (token.indexOf('admin') !== -1 && url.indexOf('/crisis-center') !== -1) {
      return true;
    }
  }
}

同樣的,針對路由守衛的實現完成后,將需要使用到的路由守衛添加到 crisis-center 路由的 canLoad 數組中即可實現授權認證不通過時不加載模塊

import { HeroCanDeactivateGuard } from './hero-list/guards/hero-can-deactivate.guard';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  {
    path: 'crisis-center',
    loadChildren: () => import('./crisis/crisis.module').then(m => m.CrisisModule)
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes, { enableTracing: true })],
  exports: [RouterModule],
})
export class AppRoutingModule { }

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

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。