重学设计模式-单例模式

引文:

面试官:你在项目中使用过什么设计模式?

发哥讲java:我用过单例模式

面试官:那你简单说一下单例模式以及项目中如何使用的?

思考:当遇到一个问题,我们应该如果回答?如何做到言简意赅,直奔主题,还能让面试官觉得你会,懂得多,如何让自己装个杯~~~

首先:

要理解面试官问的问题,理解好面试官问的问题,

比如: 你了解hashmap嘛?此时只需要回答hashmap是什么样的数据结构(k-v键值对),再说一下这种数据结构的好处(键自动去重,值保留最新的一个,取key对应的值速度快,效率高)以及缺点(遍历速度慢).

此时就ok了,其他的像默认初始容量了,项目中怎么用等就别说了!!!

记住:不要回答面试官问题的其他知识点这个很重要,不然很容易给自己带入坑里面,而且如果自己回答不清楚的话,很可能会拉低你在面试官前的技术形象.

而且大多数面试官,他们都有自己的一个面试路线,有自己引入方向.所以自己备面的时候也要多了解一些,也扩展一下自己的技术知识面,总之有备无患

其次:

讲清楚自己对问题的理解,以及项目中真正使用到的地方,讲自己由零到壹的实现以及遇到实际问题解决的方案.

做到这俩步,足以应付大多数的面试官了. 奥利给~~~~

如何回复单例模式以及项目中如何使用的?

单例模式是设计模式中创建型的一种,他常用于定义整个系统的唯一变量和对象等,实现方式有:饿汉式,懒汉式,懒汉锁式,懒汉双重锁式,枚举等.

在实际项目中像接口返回统一的响应码和响应内容枚举类,老项目或者小项目非springboot项目中的数据库jdbc连接池的使用和常用的全局静态变量(像业务章redis中配置的key的值,请求头的header的key等).


正文到

单例模式

是设计模式中最简单的一种,也是常用的设计模式之一.其定义是单例对象的实例只有一个. 许多时候,我们整个系统只需要拥有一个全局唯一对象,这样有利于协调和全局公用整体的行为.比如:系统的配置文件,数据库jdbc统一的获取DataSource等

实现的步骤:1.类的构造方法私有化,保证外部不能通过构造方法实例化. 2.提供一个静态方法返回值为该类,用于对外开发获取实例的方法

public class XXX {
   // 私有化
   private XXX() {
  }

   // 开放获取实例的方法
   public static XXX getInstance() {
       return new XXX();
  }
}

实现方式:1.饿汉式 我们知道,类加载的方式是按需加载,且加载一次。因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用;而且,由于这个类在整个生命周期中只会被加载一次,因此只会创建一个实例,即能够充分保证单例。

/**
* 饿汉式: 在定义的类加载之后,实例对象已经创建过了,直接使用即可
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/5 9:55
*/
public class Hungry {

   //1. 构造方法私有
   private Hungry() {
  }

   //饿汉式,类加载之后实例对象已经创建完成
   private static final Hungry hungry = new Hungry();

   //2. 提供对外获取该实例对象的方法
   public static Hungry getInstance() {
       return hungry;
  }
}

优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

2.懒汉式

package com.fagejiang.single;

import java.util.concurrent.TimeUnit;

/**
* 懒汉式,在需要的时候创建实例对象
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/5 10:01
*/
public class Lazy {

   //1.构造方法私有化
   private Lazy() {
       System.out.println("验证几个线程进入到创建lazy实例的构造方法中:" + Thread.currentThread().getName());
  }

   //饿汉式变量
   private static Lazy lazy = null;

   //2.提供开发方法
   public static Lazy getInstance() {
       if (lazy == null) {
           lazy = new Lazy();
      }
       return lazy;
  }

   // 单线程环境下使用,没有问题
   // 多线程环境下,如果多个线程同时进入到判断中,则多个线程都会实例化一个lazy对象,测试如下
   public static void main(String[] args) throws InterruptedException {
       // 开启10个线程去获取实例
       for (int i = 0; i < 10; i++) {
           new Thread(() -> Lazy.getInstance()).start();
      }
       // 休眠5秒,保证main方法的10个线程都执行完成
       TimeUnit.SECONDS.sleep(5);
  }
}

3.双重锁

package com.fagejiang.single;

import java.util.concurrent.TimeUnit;

/**
* 懒汉式 加锁,双重判断模式, 解决多线程下多次创建问题
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/5 10:49
*/
public class LazySynchronized {
   //1.构造方法私有化
   private LazySynchronized() {
       System.out.println("验证几个线程进入到创建lazy实例的构造方法中:" + Thread.currentThread().getName());
  }

   //饿汉式变量
   private static LazySynchronized lazy = null;

   //2.提供开发方法
   public static LazySynchronized getInstance() {
       if (lazy == null) {
           synchronized (LazySynchronized.class) {
               if (lazy == null) {
                   lazy = new LazySynchronized();
              }
          }
      }
       return lazy;
  }

   // 多线程环境下 测试是否会多次创建
   public static void main(String[] args) throws InterruptedException {
       // 开启10个线程去获取实例
       for (int i = 0; i < 10; i++) {
           new Thread(() -> LazySynchronized.getInstance()).start();
      }
       // 休眠5秒,保证main方法的10个线程都执行完成
       TimeUnit.SECONDS.sleep(1);
  }
}

4.防止指令重排

package com.fagejiang.single;

import java.util.concurrent.TimeUnit;

/**
* 懒汉式 添加volatile关键字,房子指令重拍,保证原子性操作,加锁,双重判断模式, 解决多线程下多次创建问题
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/5 10:49
*/
public class LazyVolatileSynchronized {
   //1.构造方法私有化
   private LazyVolatileSynchronized() {
       System.out.println("验证几个线程进入到创建lazy实例的构造方法中:" + Thread.currentThread().getName());
  }

   //饿汉式变量
   private volatile static LazyVolatileSynchronized lazy = null;

   //2.提供开发方法
   public static LazyVolatileSynchronized getInstance() {
       if (lazy == null) {
           synchronized (LazyVolatileSynchronized.class) {
               if (lazy == null) {
                   // lazy 添加 volatile 后 ,变成原子性操作,不会发生指令重排
                   lazy = new LazyVolatileSynchronized();
              }
          }
      }
       return lazy;
  }

   // 多线程环境下 测试是否会多次创建
   public static void main(String[] args) throws InterruptedException {
       // 开启10个线程去获取实例
       for (int i = 0; i < 10; i++) {
           new Thread(() -> LazyVolatileSynchronized.getInstance()).start();
      }
       // 休眠5秒,保证main方法的10个线程都执行完成
       TimeUnit.SECONDS.sleep(1);
  }
}

5.静态内部类

package com.fagejiang.single;

/**
* 静态内部类的方式
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/5 23:23
*/
public class StaticInnerClass {

   // 1. 定义私有构造参数
   private StaticInnerClass() {
  }

   // 2. 提供对外获取实例方法
   public static StaticInnerClass getInstance() {
       return InnerClass.STATIC_INNER_CLASS;
  }

   // 静态内部类
   private static class InnerClass {
       private static final StaticInnerClass STATIC_INNER_CLASS = new StaticInnerClass();
  }

}

问题:使用反射打破单例模式

  1. 通过反射获取当构造器对象,然后调用 newInstance 方法创建

package com.fagejiang.single;

import java.lang.reflect.Constructor;

/**
* 通过反射 打破单例模式
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/5 23:22
*/
public class ReflectLazy {
   //1.构造方法私有化
   private ReflectLazy() {
       System.out.println("验证几个线程进入到创建lazy实例的构造方法中:" + Thread.currentThread().getName());
  }

   //饿汉式变量
   private volatile static ReflectLazy lazy = null;

   //2.提供开发方法
   public static ReflectLazy getInstance() {
       if (lazy == null) {
           synchronized (ReflectLazy.class) {
               if (lazy == null) {
                   // lazy 添加 volatile 后 ,变成原子性操作,不会发生指令重排
                   lazy = new ReflectLazy();
              }
          }
      }
       return lazy;
  }

   public static void main(String[] args) throws Exception {
       ReflectLazy instance1 = ReflectLazy.getInstance();
       ReflectLazy instance2 = ReflectLazy.getInstance();
       System.out.println(instance1 == instance2);
       System.out.println(instance1);
       System.out.println(instance2);

       //获取到反射对象
       Class<ReflectLazy> lazyClass = ReflectLazy.class;
       Constructor<ReflectLazy> constructor = lazyClass.getDeclaredConstructor(null);
//       constructor.setAccessible(true);
       // 可以发现 构造方法被重新调用了,则打破单例模式
       ReflectLazy lazy = constructor.newInstance();
       System.out.println(lazy);
  }

}
  1. 通过反射 打破单例模式, 构造方法跳转, 防止反射打破单例模式

package com.fagejiang.single;

import java.lang.reflect.Constructor;

/**
* 通过反射 打破单例模式, 构造方法跳转, 防止反射打破单例模式
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/5 23:22
*/
public class ReflectLazy2 {
   //1.构造方法私有化
   private ReflectLazy2() {
       synchronized (ReflectLazy2.class) {
           if (lazy != null) {
               throw new RuntimeException("不要试图打破单例模式");
          }
           System.out.println("验证几个线程进入到创建lazy实例的构造方法中:" + Thread.currentThread().getName());
      }
  }

   //饿汉式变量
   private volatile static ReflectLazy2 lazy = null;

   //2.提供开发方法
   public static ReflectLazy2 getInstance() {
       if (lazy == null) {
           synchronized (ReflectLazy2.class) {
               if (lazy == null) {
                   // lazy 添加 volatile 后 ,变成原子性操作,不会发生指令重排
                   lazy = new ReflectLazy2();
              }
          }
      }
       return lazy;
  }

   public static void main(String[] args) throws Exception {
//       ReflectLazy2 instance1 = ReflectLazy2.getInstance();
//       ReflectLazy2 instance2 = ReflectLazy2.getInstance();
//       System.out.println(instance1 == instance2);
//       System.out.println(instance1);
//       System.out.println(instance2);

       //获取到反射对象
//       Class<ReflectLazy2> lazyClass = ReflectLazy2.class;
//       Constructor<ReflectLazy2> constructor = lazyClass.getDeclaredConstructor(null);
//       // 可以发现 构造方法被重新调用了,则打破单例模式
//       ReflectLazy2 lazy = constructor.newInstance();
//       System.out.println(lazy);

       /**
        * 此时方式,当我们第一次调用过 getInstance 后, lazy就被赋予值了.之后再通过反射 newInstance 时则会出现 异常
        * ------------------------
        * 那么如果,我先通过反射 newInstance , 只要不调用 getInstance,那么反射就能一直创建出实例对象,
        */
       //获取到反射对象
       Class<ReflectLazy2> lazyClass = ReflectLazy2.class;
       Constructor<ReflectLazy2> constructor = lazyClass.getDeclaredConstructor(null);
       ReflectLazy2 lazy2 = constructor.newInstance(null);
       ReflectLazy2 lazy3 = constructor.newInstance(null);
       ReflectLazy2 lazy4 = constructor.newInstance(null);
       System.out.println("lazy2 = " + lazy2);
       System.out.println("lazy3 = " + lazy3);
       System.out.println("lazy4 = " + lazy4);
  }

}

3.通过反射 打破单例模式, 添加标志位,防止反射打破单例模式

package com.fagejiang.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
* 通过反射 打破单例模式, 添加标志位,防止反射打破单例模式
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/5 23:22
*/
public class ReflectLazy3 {

   // 定义标志位
   private static boolean flag = false;

   //1.构造方法私有化
   private ReflectLazy3() {
       synchronized (ReflectLazy3.class) {
           // 调用 getInstance 是 标志位 flag 是false
           if (flag == false) {
               flag = true;
          } else {
               throw new RuntimeException("不要试图打破单例模式");
          }
           System.out.println("验证几个线程进入到创建lazy实例的构造方法中:" + Thread.currentThread().getName());
      }
  }

   //饿汉式变量
   private volatile static ReflectLazy3 lazy = null;

   //2.提供开发方法
   public static ReflectLazy3 getInstance() {
       if (lazy == null) {
           synchronized (ReflectLazy3.class) {
               if (lazy == null) {
                   // lazy 添加 volatile 后 ,变成原子性操作,不会发生指令重排
                   lazy = new ReflectLazy3();
              }
          }
      }
       return lazy;
  }

   public static void main(String[] args) throws Exception {
       //获取到反射对象
//       Class<ReflectLazy3> lazyClass = ReflectLazy3.class;
//       Constructor<ReflectLazy3> constructor = lazyClass.getDeclaredConstructor(null);
//       ReflectLazy3 lazy1 = constructor.newInstance(null);
//       System.out.println("lazy1 = " + lazy1);

       /**
        * 此时方式,直接通过之前的方式进行 newInstance会发生异常,因为存在标志位flag.
        * 但是反射也可以对属性进行赋值.
        */
       ReflectLazy3 instance = ReflectLazy3.getInstance();
       System.out.println("instance = " + instance);

       Class<ReflectLazy3> lazy3Class = ReflectLazy3.class;
       Field flag = lazy3Class.getDeclaredField("flag");
       flag.setBoolean(lazy3Class, false);
       Constructor<ReflectLazy3> constructor = lazy3Class.getDeclaredConstructor(null);
       ReflectLazy3 lazy3 = constructor.newInstance(null);
       System.out.println("lazy3 = " + lazy3);

       // 可以发现当我们调用flag.setBoolean(lazy3Class, false)给flag标志位修改值之后,又打破了单利模式

       /**
        * 查看反射 newInstance 的源码
        *   if ((clazz.getModifiers() & Modifier.ENUM) != 0)
        *       throw new IllegalArgumentException("Cannot reflectively create enum objects");
        * 对于枚举类不能通过反射 进行实例化,则枚举类是最完美的单例模式
        */
  }

}

6.枚举类单利

package com.fagejiang.single;

import java.lang.reflect.Constructor;

/**
* 最完美的单利模式: 枚举类
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/6 0:12
*/
public enum SingleEnum {
   // 定义单例对象-实例
   INSTANCE;

   // 使用反射打破枚举类单例模式:
   public static void main(String[] args) throws Exception {
//       Class<SingleEnum> enumClass = SingleEnum.class;
//       Constructor<SingleEnum> constructor = enumClass.getDeclaredConstructor(null);
//       SingleEnum anEnum = constructor.newInstance(null);
//       System.out.println("anEnum = " + anEnum);


       // enumClass.getDeclaredConstructor(null); 获取到的构造器,进行newInstance时抛出 NoSuchMethodException 没有此方法异常
       // 奇了怪了, 为什么没有构造方法呢,
       // 此时就需要通过 反编译 去了解一下枚举类 在Java编译后真正的构造对象

       // 1.使用 jdk 命令: javap -p SingleEnum.class 反编译后:
       /**
        * Compiled from "SingleEnum.java"
        * public final class com.fagejiang.single.SingleEnum extends java.lang.Enum<com.fagejiang.single.SingleEnum> {
        *   public static final com.fagejiang.single.SingleEnum INSTANCE;
        *   private static final com.fagejiang.single.SingleEnum[] $VALUES;
        *   public static com.fagejiang.single.SingleEnum[] values();
        *   public static com.fagejiang.single.SingleEnum valueOf(java.lang.String);
        *   private com.fagejiang.single.SingleEnum();
        *   public static void main(java.lang.String[]) throws java.lang.Exception;
        *   static {};
        * }
        */
       // 发现构造函数是空的
       // 2.使用idea,查看target包下的SingleEnum.class,idea自动反编译,查看:
       /**
        * public enum SingleEnum {
        *     INSTANCE;
        *
        *     private SingleEnum() {
        *     }
        *
        *     public static void main(String[] args) throws Exception {
        *         System.out.println(INSTANCE);
        *         Class<SingleEnum> enumClass = SingleEnum.class;
        *         Constructor<SingleEnum> constructor = enumClass.getDeclaredConstructor((Class[])null);
        *         SingleEnum anEnum = (SingleEnum)constructor.newInstance((Object[])null);
        *         System.out.println("anEnum = " + anEnum);
        *     }
        * }
        */
       // 发现构造参数任然为空的
       //3.使用 jad 工具 反编译 https://www.cnblogs.com/dkblog/archive/2008/04/07/1980817.html
       // 命令: jad -sjava SingleEnum.class
       /**
        * public final class SingleEnum extends Enum
        * {
        *
        *     public static SingleEnum[] values()
        *     {
        *         return (SingleEnum[])$VALUES.clone();
        *     }
        *
        *     public static SingleEnum valueOf(String name)
        *     {
        *         return (SingleEnum)Enum.valueOf(com/fagejiang/single/SingleEnum, name);
        *     }
        *
        *     private SingleEnum(String s, int i)
        *     {
        *         super(s, i);
        *     }
        *
        *     public static void main(String args[])
        *         throws Exception
        *     {
        *         System.out.println(INSTANCE);
        *         Class enumClass = com/fagejiang/single/SingleEnum;
        *         Constructor constructor = enumClass.getDeclaredConstructor(null);
        *         SingleEnum anEnum = (SingleEnum)constructor.newInstance(null);
        *         System.out.println((new StringBuilder()).append("anEnum = ").append(anEnum).toString());
        *     }
        *
        *     public static final SingleEnum INSTANCE;
        *     private static final SingleEnum $VALUES[];
        *
        *     static
        *     {
        *         INSTANCE = new SingleEnum("INSTANCE", 0);
        *         $VALUES = (new SingleEnum[] {
        *             INSTANCE
        *         });
        *     }
        * }
        */
       // 此时我们发现 构造方法里面多了俩个参数 private SingleEnum(String s, int i)
       // 重新测试
       Class<SingleEnum> aClass = SingleEnum.class;
       Constructor<SingleEnum> constructor = aClass.getDeclaredConstructor(String.class, int.class);
       SingleEnum anEnum = constructor.newInstance();
       System.out.println("anEnum = " + anEnum);

       // 此时出现我们期望看到的异常: Cannot reflectively create enum objects

  }
}

7.cas[使用AtomicReference]

package com.fagejiang.single;

import java.util.concurrent.atomic.AtomicReference;

/**
* CAS 借用 AtomicReference 保证现场安全
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/6 13:04
*/
public class SingleAtomic {

   private static final AtomicReference<SingleAtomic> ATOMIC_REFERENCE = new AtomicReference<>();

   // 1. 定义私有的构造的方法
   public SingleAtomic() {
  }

   // 2. 提供对外暴露实例的方法
   public static SingleAtomic getInstance() {
       for (; ; ) {
           SingleAtomic atomic = ATOMIC_REFERENCE.get();
           if (null != atomic) {
               return atomic;
          }
           ATOMIC_REFERENCE.set(new SingleAtomic());
           return ATOMIC_REFERENCE.get();
      }
  }

   /**
    * java并发库提供了很多原⼦类来⽀持并发访问的数据安全性;
    * AtomicInteger 、 AtomicBoolean 、 AtomicLong 、 AtomicReference 。
    * AtomicReference 可以封装引⽤⼀个V实例,⽀持并发访问如上的单例⽅式就是使⽤了这样的⼀个特点。
    * <p>
    * 使⽤CAS的好处就是不需要使⽤传统的加锁⽅式保证线程安全,⽽是依赖于CAS的忙等算法,依赖
    * 于底层硬件的实现,来保证线程安全。相对于其他锁的实现没有线程的切换和阻塞也就没有了额外
    * 的开销,并且可以⽀持较⼤的并发性。
    * <p>
    * 当然CAS也有⼀个缺点就是忙等,如果⼀直没有获取到将会处于死循环中。
    */
   public static void main(String[] args) {
       SingleAtomic instance1 = SingleAtomic.getInstance();
       SingleAtomic instance2 = SingleAtomic.getInstance();
       SingleAtomic instance3 = SingleAtomic.getInstance();
       System.out.println("instance1 = " + instance1);
       System.out.println("instance2 = " + instance2);
       System.out.println("instance3 = " + instance3);
  }
}

实战:1.web接口统一返回结果的状态码枚举

package com.fagejiang.single.properties;

/**
* 我还没有写描述
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/6 13:28
*/
public interface IResultCode {
   String getCode();

   String getMsg();
}
package com.fagejiang.single.properties;

import java.io.Serializable;

/**
* web 接口统一返回结果的状态码枚举
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/6 13:23
*/
public enum ResultCodeEnum implements IResultCode, Serializable {
   SUCCESS("00000", "一切ok"),

   USER_ERROR("A0001", "用户端错误"),
   USER_LOGIN_ERROR("A0200", "用户登录异常"),

   USER_NOT_EXIST("A0201", "用户不存在"),
   USER_ACCOUNT_LOCKED("A0202", "用户账户被冻结"),
   USER_ACCOUNT_INVALID("A0203", "用户账户已作废"),

   USERNAME_OR_PASSWORD_ERROR("A0210", "用户名或密码错误"),
   INPUT_PASSWORD_EXCEED_LIMIT("A0211", "用户输入密码次数超限"),
   CLIENT_AUTHENTICATION_FAILED("A0212", "客户端认证失败"), // *
   TOKEN_INVALID_OR_EXPIRED("A0230", "token无效或已过期"),
   TOKEN_ACCESS_FORBIDDEN("A0231", "token已被禁止访问"),

   AUTHORIZED_ERROR("A0300", "访问权限异常"),
   ACCESS_UNAUTHORIZED("A0301", "访问未授权"),
   FORBIDDEN_OPERATION("A0302", "演示环境禁止修改、删除重要数据,请本地部署后测试"),


   PARAM_ERROR("A0400", "用户请求参数错误"),
   PARAM_IS_NULL("A0410", "请求必填参数为空"),
   QUERY_MODE_IS_NULL("A0411", "查询模式为空"),

   USER_UPLOAD_FILE_ERROR("A0700", "用户上传文件异常"),
   USER_UPLOAD_FILE_TYPE_NOT_MATCH("A0701", "用户上传文件类型不匹配"),
   USER_UPLOAD_FILE_SIZE_EXCEEDS("A0702", "用户上传文件太大"),
   USER_UPLOAD_IMAGE_SIZE_EXCEEDS("A0703", "用户上传图片太大"),

   SYSTEM_EXECUTION_ERROR("B0001", "系统执行出错"),
   SYSTEM_EXECUTION_TIMEOUT("B0100", "系统执行超时"),
   SYSTEM_ORDER_PROCESSING_TIMEOUT("B0100", "系统订单处理超时"),

   SYSTEM_DISASTER_RECOVERY_TRIGGER("B0200", "系统容灾功能被出发"),
   FLOW_LIMITING("B0210", "系统限流"),
   DEGRADATION("B0220", "系统功能降级"),

   SYSTEM_RESOURCE_ERROR("B0300", "系统资源异常"),
   SYSTEM_RESOURCE_EXHAUSTION("B0310", "系统资源耗尽"),
   SYSTEM_RESOURCE_ACCESS_ERROR("B0320", "系统资源访问异常"),
   SYSTEM_READ_DISK_FILE_ERROR("B0321", "系统读取磁盘文件失败"),

   CALL_THIRD_PARTY_SERVICE_ERROR("C0001", "调用第三方服务出错"),
   MIDDLEWARE_SERVICE_ERROR("C0100", "中间件服务出错"),
   INTERFACE_NOT_EXIST("C0113", "接口不存在"),

   MESSAGE_SERVICE_ERROR("C0120", "消息服务出错"),
   MESSAGE_DELIVERY_ERROR("C0121", "消息投递出错"),
   MESSAGE_CONSUMPTION_ERROR("C0122", "消息消费出错"),
   MESSAGE_SUBSCRIPTION_ERROR("C0123", "消息订阅出错"),
   MESSAGE_GROUP_NOT_FOUND("C0124", "消息分组未查到"),

   DATABASE_ERROR("C0300", "数据库服务出错"),
   DATABASE_TABLE_NOT_EXIST("C0311", "表不存在"),
   DATABASE_COLUMN_NOT_EXIST("C0312", "列不存在"),
   DATABASE_DUPLICATE_COLUMN_NAME("C0321", "多表关联中存在多个相同名称的列"),
   DATABASE_DEADLOCK("C0331", "数据库死锁"),
   DATABASE_PRIMARY_KEY_CONFLICT("C0341", "主键冲突");

   private String code;
   private String msg;

   ResultCodeEnum(String code, String msg) {
       this.code = code;
       this.msg = msg;
  }

   @Override
   public String getCode() {
       return code;
  }

   @Override
   public String getMsg() {
       return msg;
  }

   public static ResultCodeEnum getValue(String code) {
       for (ResultCodeEnum value : values()) {
           if (value.getCode().equals(code)) {
               return value;
          }
      }
       return SYSTEM_EXECUTION_ERROR;
  }
}

2.常用静态变量key

package com.fagejiang.single.properties;

/**
* 我还没有写描述
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/6 13:29
*/
public interface AuthConstants {

   /**
    * 认证请求头key
    */
   String AUTHORIZATION_KEY = "Authorization";

   /**
    * JWT令牌前缀
    */
   String AUTHORIZATION_PREFIX = "bearer ";


   /**
    * Basic认证前缀
    */
   String BASIC_PREFIX = "Basic ";

   /**
    * JWT载体key
    */
   String JWT_PAYLOAD_KEY = "payload";

   /**
    * JWT ID 唯一标识
    */
   String JWT_JTI = "jti";

   /**
    * JWT ID 唯一标识
    */
   String JWT_EXP = "exp";

   /**
    * Redis缓存权限规则key
    */
   String PERMISSION_ROLES_KEY = "auth:permission:roles";

   /**
    * 黑名单token前缀
    */
   String TOKEN_BLACKLIST_PREFIX = "auth:token:blacklist:";

   String CLIENT_DETAILS_FIELDS = "client_id, CONCAT('{noop}',client_secret) as client_secret, resource_ids, scope, "
           + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
           + "refresh_token_validity, additional_information, autoapprove";

   String BASE_CLIENT_DETAILS_SQL = "select " + CLIENT_DETAILS_FIELDS + " from oauth_client_details";

   String FIND_CLIENT_DETAILS_SQL = BASE_CLIENT_DETAILS_SQL + " order by client_id";

   String SELECT_CLIENT_DETAILS_SQL = BASE_CLIENT_DETAILS_SQL + " where client_id = ?";

   /**
    * 密码加密方式
    */
   String BCRYPT = "{bcrypt}";

   String USER_ID_KEY = "user_id";

   String USER_NAME_KEY = "username";

   String CLIENT_ID_KEY = "client_id";

   /**
    * JWT存储权限前缀
    */
   String AUTHORITY_PREFIX = "ROLE_";

   /**
    * JWT存储权限属性
    */
   String JWT_AUTHORITIES_KEY = "authorities";


   /**
    * 有来商城后台管理客户端ID
    */
   String ADMIN_CLIENT_ID = "xxx-admin";


   /**
    * 有来商城微信小程序客户端ID
    */
   String WEAPP_CLIENT_ID = "xxx-weapp";

   /**
    * 后台管理接口路径匹配
    */
   String ADMIN_URL_PATTERN = "**/api.admin/**";


   String LOGOUT_PATH = "/xxx-auth/oauth/logout";


   String GRANT_TYPE_KEY = "grant_type";

   String REFRESH_TOKEN = "refresh_token";
}

3.jdbc数据库连接池

    <dependencies>
      <!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
      <dependency>
          <groupId>com.zaxxer</groupId>
          <artifactId>HikariCP</artifactId>
          <version>4.0.3</version>
      </dependency>
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.17</version>
      </dependency>
      <!--可以使用自动setter和getter以及日志注解-->
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.4</version>
      </dependency>
      <!--添加 slf4j 实现类-->
      <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
      <dependency>
          <groupId>ch.qos.logback</groupId>
          <artifactId>logback-classic</artifactId>
          <version>1.2.3</version>
      </dependency>
  </dependencies>

配置文件 – hikaricp

driverClassName=com.mysql.cj.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username=root
password=root

配置文件 – logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
   <!-- 配置文件xml 参考:https://logback.qos.ch/manual/configuration.html -->
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
       <!-- encoders are assigned the type
            ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
       <encoder>
           <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
       </encoder>
   </appender>
   <!-- 设置 日志打印级别 -->
   <root level="info">
       <appender-ref ref="STDOUT"/>
   </root>
</configuration>

数据库连接池工具类

package com.fagejiang.single.datasource;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.Objects;
import java.util.Properties;

/**
* HikariCP jdbc 数据库连接池工具
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021/6/6 13:39
*/
@Slf4j
public class HikariCPUti {

   private final static HikariCPUti HIKARI_CP_UTI = new HikariCPUti();

   private static Properties properties = null;
   private static HikariDataSource dataSource = null;

   //1.单例模式中,创建私有构造方法
   private HikariCPUti() {
       // 私有构造
  }

   /**
    * 1.配置和获取数据库连接配置信息
    * 2.扩展HikariCP功能,进行配置
    * 3.获取数据库连接,提供对外获取数据库资源的方法
    */

   private void initConfig() throws IOException {
       String filePath = Objects.requireNonNull(HikariCPUti.class.getClassLoader().getResource("hikaricp.properties")).getFile();
       FileReader fileReader = new FileReader(filePath);
       properties = new Properties();
       properties.load(fileReader);
       properties.forEach((k, v) -> {
           log.debug(String.format("key:%s value:%S", k, v));
      });
       log.info("初始化配置文件成功.....");
  }

   private void registerHikariCP() {
       if (null != dataSource) {
           return;
      }
       HikariConfig config = new HikariConfig(properties);
       dataSource = new HikariDataSource(config);
  }

   //2.提供对外 获取 HikariCPDatasource 的方法
   public static DataSource getHikariCPDataSource() {
       if (null != dataSource) {
           return dataSource;
      }
       try {
           HIKARI_CP_UTI.initConfig();
           HIKARI_CP_UTI.registerHikariCP();
      } catch (IOException e) {
           e.printStackTrace();
      }
       return dataSource;
  }

   public static void main(String[] args) {
       for (int i = 0; i < 10; i++) {
           new Thread(() -> {
               DataSource dataSource = HikariCPUti.getHikariCPDataSource();
               System.out.println(Thread.currentThread().getName() + " dataSource = " + dataSource);
               //Thread-5 dataSource = HikariDataSource (HikariPool-6)
               // 可以明显的看出来 是默认使用了数据库连接池 HikariPool....
          }).start();
      }
       /**
        * 测试和验证 datasource 的准确性
        */
       String sql = "SELECT NOW() nowDate, ROUND(( RAND()* 100 )) randVal;";
       // 获取数据库资源
       DataSource dataSource = HikariCPUti.getHikariCPDataSource();

       // 使用 try-resource-catch 方式,自动关闭资源
       try (
               //获取数据库连接
               Connection connection = dataSource.getConnection();
               //预编译
               PreparedStatement preparedStatement = connection.prepareStatement(sql);
               //获取结果
               ResultSet resultSet = preparedStatement.executeQuery();
      ) {
           ResultSetMetaData metaData = preparedStatement.getMetaData();
           while (resultSet.next()) {
               int columnCount = metaData.getColumnCount();
               for (int i = 1; i <= columnCount; i++) {
                   log.info(metaData.getColumnName(i) + " : " + resultSet.getObject(i));
              }
          }
      } catch (SQLException sqlException) {
           sqlException.printStackTrace();
      }
  }

}


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/20584.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!