教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

Java設(shè)計模式之單例模式以及單例所引發(fā)的思考

更新時間:2018年12月07日13時58分 來源:傳智播客 瀏覽次數(shù):

SpringSecurity方法級別的權(quán)限控制

引言

Spring Security是一個能夠為基于Spring的企業(yè)應(yīng)用系統(tǒng)提供安全訪問控制解決方案的安全框架,它利用Spring IOC、DI和AOP功能,為企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問控制功能,簡化企業(yè)系統(tǒng)為了安全控制而編寫大量重復(fù)代碼的工作,Spring Security支持Url級別的權(quán)限控制,同樣也支持方法級別的權(quán)限控制,今天主要介紹Spring Security方法級別的權(quán)限控制。

Spring Security方法級別權(quán)限控制方式

Spring Security方法級別權(quán)限控制主要有以下幾種方式:

• intercept-methods定義方法權(quán)限控制。

• 使用pointcut定義方法權(quán)限控制。

• 使用JSR-250注解定義方法權(quán)限控制。

• 使用@Secured注解定義方法權(quán)限控制。

• 注解使用表達式定義方法權(quán)限控制。

項目搭建

要想實現(xiàn)Spring Security方法級別的權(quán)限控制,必須先將項目搭建起來。

創(chuàng)建名稱為Spring-Security的web項目

在pom.xml文件中引入相關(guān)的依賴包。

/**
 * 單例設(shè)計模式之 懶漢式
 * @author nianqiang
 */
public class SingletonClass {
   
    // 私有化及volatile修飾成員類
    private static volatile SingletonClass instance = null;

    // 向外提供靜態(tài)創(chuàng)建對象實例
    public static SingletonClass getInstance() {
           // 只有調(diào)用當前靜態(tài)方法,才返回當前類的實例
           if (instance == null) {
              instance = new SingletonClass();
           }
       return instance;
    }

    // 私有化構(gòu)造
    private SingletonClass() {  }
}
運行測試:
public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        SingletonClass singletonClass = SingletonClass.getInstance();
        System.out.println(singletonClass);
    }
}

java

3.2. 餓漢式單例

 
/**
 * java 設(shè)計模式之 餓漢式實現(xiàn)
 * @author nianqiang
 */
public class Singleton {

    // 私有化構(gòu)造
    private Singleton() {}

    // 設(shè)置成員屬性時就創(chuàng)建當前的實例
    private static final Singleton instance = new Singleton();

    // 向外提供靜態(tài)方法訪問返回該類的實例
    public static Singleton getInstance() {
       return instance;
    }
}
 
運行測試:
public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        Singleton singleton = Singleton.getInstance();
        System.out.println(singleton);
    }
}
 
結(jié)果:
java
由上述實現(xiàn)方式,我們可以得到結(jié)論:餓漢式單例中 instance 的初始化是在類加載時進行的,而類的加載是由 ClassLoader 來完成,這個過程由 JVM 來保證同步,所以這種方式天生是線程安全的。它的缺點也顯而易見:容易造成資源的浪費,并且如果構(gòu)造方法中處理過多,還有可能引發(fā)性能問題。

3.3. 雙重檢查鎖單例

 
/**
 * Java設(shè)計模式之雙重檢查鎖單例
 * @author nianqiang
 */
public class Singleton {

    private static volatile Singleton instance = null;

    private Singleton() {}

    public static Singleton getInstance() {

       if (instance == null) {

           synchronized (Singleton1.class) {

              if (instance == null) {

                  instance = new Singleton();
              }
           }
       }
       return instance;
    }
}
 
運行測試:
public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        Singleton singleton = Singleton.getInstance();
        System.out.println(singleton);
    }
}
 
結(jié)果:
java

上述的設(shè)計是當前用的比較多的一種,也是設(shè)計的比較巧妙的方式。我們接下來就仔細的分析一下上面的單例模式。

• 懶漢式的加強版:保證第一次使用的時候創(chuàng)建該類的實體對象。

• 線程安全:在多線程環(huán)境中如何高效的使用線程鎖機制來解決線程安全的問題,這里的設(shè)計是使用同步代碼塊最大限度的提高效率。

• 提高性能:在多線程環(huán)境下,如果只有一次檢查if (instance == null) 的話,相當于為了解決 1% 幾率的同步問題,而使用了一個 100% 出現(xiàn)的防護盾。雙重檢查就是把 100% 出現(xiàn)的防護盾,也改為 1% 的幾率出現(xiàn)。只有 instance 為 null 的時候,才進入 synchronized 的代碼段——大大減少了幾率。

• private static volatile Singleton instance = null;

關(guān)鍵詞 volatile 的作用:

– 內(nèi)存可見性:可見性的意思是當一個線程修改一個共享變量時,另外一個線程能讀到這個修改的值。就是相當于共享同一塊內(nèi)存區(qū)域。

– 禁止指令重排:雙重檢查鎖單例中利用的就是這一點。 那什么是指令重排呢?指令重排是指計算機為了提高執(zhí)行效率,會做一些優(yōu)化,在不影響最終結(jié)果的情況下,可能會對一些語句的執(zhí)行順序進行調(diào)整。

3.4. 靜態(tài)內(nèi)部類單例

/**
 * 單例設(shè)計模式之靜態(tài)內(nèi)部類實現(xiàn)
 *
 * @author nianqiang
 */
public class Singleton {

    // 提供私有構(gòu)造,目的防止外部new對象
    private Singleton() { }

    // 私有靜態(tài)內(nèi)部類提供當前對象的的實例
    private static class InnerClass {
       private static final Singleton INSTANCE = new Singleton();
    }

    // 外部提供當前類的對象的實例
    public static Singleton getInstance() {
       return InnerClass.INSTANCE;
    }
}
 
運行測試:
public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        Singleton singleton = Singleton.getInstance();
        System.out.println(singleton);
    }
}
 
結(jié)果:
java

上述內(nèi)部類實現(xiàn)的單例中,由于 InnerClass 是一個內(nèi)部類,只在外部類的 Singleton 的 getInstance() 中被使用,所以它被加載的時機只會在 getInstance() 方法第一次被調(diào)用的時候。并且 InnerClass 初始化的時候會由 ClassLoader 來保證同步。 這種設(shè)計單例是一個巧妙的利用的內(nèi)部類的加載機制來實現(xiàn)的,但是為什么只能是靜態(tài)的內(nèi)部類呢?如果內(nèi)部類不是靜態(tài)的結(jié)果如何呢? 答案是必須要把內(nèi)部類設(shè)置成靜態(tài)的,如果內(nèi)部類設(shè)置的不是靜態(tài)的,那么編譯器會報一個異常信息如下:

java

那么我們來解釋一下出現(xiàn)上述問題的原因,如果使用一個類的靜態(tài)成員,需要先把這個類加載到虛擬機中,而成員內(nèi)部類是需要由外部類對象 new 一個實例才可以使用,這就無法做到靜態(tài)成員的要求。所以 Java 不允許非靜態(tài)內(nèi)部類持有靜態(tài)的聲明。此時必須將當前的內(nèi)部類加上static修飾。

4. 總結(jié)

以上我們就學習了關(guān)于設(shè)計模式中的單例模式在實際開發(fā)中常用的實現(xiàn)方式,以及各個實現(xiàn)方式的優(yōu)缺點,那么在開發(fā)中根據(jù)實際的業(yè)務(wù)需求選擇不同的單例模式即可。一般情況下單例模式在多線程環(huán)境下使用的時候需要特別注意的是線程安全相關(guān)的問題。

0 分享到:
和我們在線交談!