引文:
面试官:你在项目中使用过什么设计模式?
发哥讲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();
}
}
问题:使用反射打破单例模式
-
通过反射获取当构造器对象,然后调用 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);
}
}
-
通过反射 打破单例模式, 构造方法跳转, 防止反射打破单例模式
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