全国咨询热线:400-618-4000

Java设计模式之单例模式以及单例所引发的思考

创建时间:2018年12月07日13时58分

SpringSecurity方法级别的权限控制

引言

Spring Security是一个能够为基于Spring的企业应用系统提供安全访问控制解决方案的安全框架,它利用Spring IOC、DI和AOP功能,为企业应用系统提供声明式的安全访问控制功能,简化企业系统为了安全控制而编写大量重复代码的工作,Spring Security支持Url级别的权限控制,同样也支持方法级别的权限控制,今天主要介绍Spring Security方法级别的权限控制。

Spring Security方法级别权限控制方式

Spring Security方法级别权限控制主要有以下几种方式:

• intercept-methods定义方法权限控制。

• 使用pointcut定义方法权限控制。

• 使用JSR-250注解定义方法权限控制。

• 使用@Secured注解定义方法权限控制。

• 注解使用表达式定义方法权限控制。

项目搭建

要想实现Spring Security方法级别的权限控制,必须先将项目搭建起来。

创建名称为Spring-Security的web项目

在pom.xml文件中引入相关的依赖包。

/**
 * 单例设计模式之 懒汉式
 * @author nianqiang
 */
public class SingletonClass {
   
    // 私有化及volatile修饰成员类
    private static volatile SingletonClass instance = null;

    // 向外提供静态创建对象实例
    public static SingletonClass getInstance() {
           // 只有调用当前静态方法,才返回当前类的实例
           if (instance == null) {
              instance = new SingletonClass();
           }
       return instance;
    }

    // 私有化构造
    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 设计模式之 饿汉式实现
 * @author nianqiang
 */
public class Singleton {

    // 私有化构造
    private Singleton() {}

    // 设置成员属性时就创建当前的实例
    private static final Singleton instance = new Singleton();

    // 向外提供静态方法访问返回该类的实例
    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);
    }
}
 
结果:
java
由上述实现方式,我们可以得到结论:饿汉式单例中 instance 的初始化是在类加载时进行的,而类的加载是由 ClassLoader 来完成,这个过程由 JVM 来保证同步,所以这种方式天生是线程安全的。它的缺点也显而易见:容易造成资源的浪费,并且如果构造方法中处理过多,还有可能引发性能问题。

3.3. 双重检查锁单例

 
/**
 * Java设计模式之双重检查锁单例
 * @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);
    }
}
 
结果:
java

上述的设计是当前用的比较多的一种,也是设计的比较巧妙的方式。我们接下来就仔细的分析一下上面的单例模式。

• 懒汉式的加强版:保证第一次使用的时候创建该类的实体对象。

• 线程安全:在多线程环境中如何高效的使用线程锁机制来解决线程安全的问题,这里的设计是使用同步代码块最大限度的提高效率。

• 提高性能:在多线程环境下,如果只有一次检查if (instance == null) 的话,相当于为了解决 1% 几率的同步问题,而使用了一个 100% 出现的防护盾。双重检查就是把 100% 出现的防护盾,也改为 1% 的几率出现。只有 instance 为 null 的时候,才进入 synchronized 的代码段——大大减少了几率。

• private static volatile Singleton instance = null;

关键词 volatile 的作用:

– 内存可见性:可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。就是相当于共享同一块内存区域。

– 禁止指令重排:双重检查锁单例中利用的就是这一点。 那什么是指令重排呢?指令重排是指计算机为了提高执行效率,会做一些优化,在不影响最终结果的情况下,可能会对一些语句的执行顺序进行调整。

3.4. 静态内部类单例

/**
 * 单例设计模式之静态内部类实现
 *
 * @author nianqiang
 */
public class Singleton {

    // 提供私有构造,目的防止外部new对象
    private Singleton() { }

    // 私有静态内部类提供当前对象的的实例
    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);
    }
}
 
结果:
java

上述内部类实现的单例中,由于 InnerClass 是一个内部类,只在外部类的 Singleton 的 getInstance() 中被使用,所以它被加载的时机只会在 getInstance() 方法第一次被调用的时候。并且 InnerClass 初始化的时候会由 ClassLoader 来保证同步。 这种设计单例是一个巧妙的利用的内部类的加载机制来实现的,但是为什么只能是静态的内部类呢?如果内部类不是静态的结果如何呢? 答案是必须要把内部类设置成静态的,如果内部类设置的不是静态的,那么编译器会报一个异常信息如下:

java

那么我们来解释一下出现上述问题的原因,如果使用一个类的静态成员,需要先把这个类加载到虚拟机中,而成员内部类是需要由外部类对象 new 一个实例才可以使用,这就无法做到静态成员的要求。所以 Java 不允许非静态内部类持有静态的声明。此时必须将当前的内部类加上static修饰。

4. 总结

以上我们就学习了关于设计模式中的单例模式在实际开发中常用的实现方式,以及各个实现方式的优缺点,那么在开发中根据实际的业务需求选择不同的单例模式即可。一般情况下单例模式在多线程环境下使用的时候需要特别注意的是线程安全相关的问题。