Relearn the design pattern-Java-singleton pattern

Citation:

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

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

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

Thinking: When we encounter a question, how should we answer it? How to be concise and straight to the topic, but also to make the interviewer feel that you know better, how to make yourself a cup~~~

first of all:

To understand the questions asked by the interviewer, understand the questions asked by the interviewer, such as: Do you know hashmap? At this time, you only need to answer what kind of data structure (kv key-value pair) hashmap is, and then talk about the benefits of this data structure (The key is automatically deduplicated, the value is kept the latest one, and the value corresponding to the key is fast and efficient) and the shortcomings (slow traversal speed). At this time, it is ok. Others are like the default initial capacity, how to use it in the project, etc. Don't say it!!!

Remember: It is very important not to answer other knowledge points of the interviewer's questions , otherwise it is easy to bring yourself into the pit, and if you don't answer clearly, it is likely to lower your technical image in front of the interviewer.

Moreover, most interviewers have their own interview route and their own introduction direction. So when you prepare your own face, you should also learn more and expand your technical knowledge. In short, they are prepared.

Secondly:

Explain clearly your understanding of the problem, and the real use of the project, talk about your zero-to-one implementation and the solution to the actual problem.

Doing these two steps is enough to deal with most interviewers. Ollie gives it~~~~

How to reply to the singleton pattern and how is it used in the project?

The singleton mode is a kind of creation type in the design mode. It is often used to define the unique variables and objects of the entire system. The implementation methods are: hungry, lazy, lazy lock, lazy double lock, enumeration, etc. In the actual project, the interface returns a unified response code and response content enumeration class, the use of the database jdbc connection pool in the old project or small project non-springboot project and the commonly used global static variables (like the key configuration in the business chapter redis Value, the key of the header of the request header, etc.).

text

Singleton mode

It is the simplest design pattern and one of the commonly used design patterns. Its definition is that there is only one instance of a singleton object. In many cases, our entire system only needs to have a globally unique object, which is conducive to coordination and global publicity The overall behavior. For example: system configuration files, database jdbc unified acquisition of DataSource, etc.

Steps to implement: 1. The construction method of the class is privatized to ensure that the external cannot be instantiated through the construction method. 2. Provide a static method with a return value of the class for external development to obtain an instance method

public class XXX {    // 私有化    private XXX() {    }​    // 开放获取实例的方法    public static XXX getInstance() {        return new XXX();    }}

Implementation method: 1. Hungry Chinese style. We know that the method of class loading is to load on demand and load it once . Therefore, when the above singleton class is loaded, it will instantiate an object and give it a reference for the system to use; moreover, since this class will only be loaded once in the entire life cycle, only one instance will be created , Which can fully guarantee the singleton.

/** * 饿汉式: 在定义的类加载之后,实例对象已经创建过了,直接使用即可 * * @author <发哥讲[email protected]> * @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;    }}

Advantages: This writing method is relatively simple, that is, the instantiation is completed when the class is loaded. Avoid thread synchronization problems.

Disadvantages: The instantiation is completed when the class is loaded, which does not achieve the effect of Lazy Loading. If this instance is never used from start to finish, it will cause a waste of memory.

2. Lazy man

package com.fagejiang.single;​import java.util.concurrent.TimeUnit;​/** * 懒汉式,在需要的时候创建实例对象 * * @author <发哥讲[email protected]> * @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. Double lock

package com.fagejiang.single;​import java.util.concurrent.TimeUnit;​/** * 懒汉式 加锁,双重判断模式, 解决多线程下多次创建问题 * * @author <发哥讲[email protected]> * @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. Prevent order rearrangement

package com.fagejiang.single;​import java.util.concurrent.TimeUnit;​/** * 懒汉式 添加volatile关键字,房子指令重拍,保证原子性操作,加锁,双重判断模式, 解决多线程下多次创建问题 * * @author <发哥讲[email protected]> * @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. Static inner class

package com.fagejiang.single;​/** * 静态内部类的方式 * * @author <发哥讲[email protected]> * @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();    }​}​

Problem: Use reflection to break the singleton pattern

Get the constructor object through reflection, and then call the newInstance method to create

package com.fagejiang.single;​import java.lang.reflect.Constructor;​/** * 通过反射 打破单例模式 * * @author <发哥讲[email protected]> * @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);    }​}​

Break the singleton mode through reflection, construct method jumps, and prevent reflection from breaking the singleton mode

package com.fagejiang.single;​import java.lang.reflect.Constructor;​/** * 通过反射 打破单例模式, 构造方法跳转, 防止反射打破单例模式 * * @author <发哥讲[email protected]> * @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. Break the singleton mode by reflection, add flags to prevent reflection from breaking the singleton mode

package com.fagejiang.single;​import java.lang.reflect.Constructor;import java.lang.reflect.Field;​/** * 通过反射 打破单例模式, 添加标志位,防止反射打破单例模式 * * @author <发哥讲[email protected]> * @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. Enumeration simple interest

package com.fagejiang.single;​import java.lang.reflect.Constructor;​/** * 最完美的单利模式: 枚举类 * * @author <发哥讲[email protected]> * @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[Use AtomicReference]

package com.fagejiang.single;​import java.util.concurrent.atomic.AtomicReference;​/** * CAS 借用 AtomicReference 保证现场安全 * * @author <发哥讲[email protected]> * @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);    }}​

Actual combat: 1. The status code enumeration of the unified return result of the web interface

package com.fagejiang.single.properties;​/** * 我还没有写描述 * * @author <发哥讲[email protected]> * @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 <发哥讲[email protected]> * @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. Commonly used static variable key

package com.fagejiang.single.properties;​/** * 我还没有写描述 * * @author <发哥讲[email protected]> * @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 database connection pool

    <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>

Profile-hikaricp

driverClassName=com.mysql.cj.jdbc.DriverjdbcUrl=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghaiusername=rootpassword=root

Configuration file-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>​

Database connection pool tools

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 <发哥讲[email protected]> * @version 1.0 * @date 2021/6/6 13:39 */@Slf4jpublic 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();        }    }​}​

Like small series can be public concern No. Chow tell the Java , click on the link I can get to my personal micro-channel, pull you into the group