• Spi机制的必要性


    SpringBoot 为啥单独加载类路径下spring.factories文件中的类?

    SpringBoot 应用运行过程中存在两种类型的类初始化:一部分为已经提前装载到IOC容器中的bean,另一部分则为实时new的bean。

    IOC容器中的bean包含:启动类所在包路径下全部的类 以及 spring.factories文件中的类。

    类路径下的类并非全部需要加载到JVM中,简单理解为主动使用的类才会被加载到JVM中。spring.factories文件中的类对所有jar包中的类启动选择作用,只有位于spring.factories文件中指定jar中的类才会被加载IOC容器中,当然这些类运行过程中还有通过new方法、反射方式实例化jar包中的其他类。


    为什么会存在Spi机制呢?

    只是为了框架中实现灵活的扩展。如果没有Spi机制则需要显式写死实现类,后期扩展改动比较麻烦。


    在配置数据库相关属性时存在以下选项:

    1. spring:
    2. datasource:
    3. url: jdbc:mysql://localhost:3306/folder?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&serverTimezone=GMT%2B8
    4. driver-class-name: com.mysql.jdbc.Driver
    5. username: root
    6. password: root

    其中driver-class-name会触发SPI机制加载Driver的实现类。

    mysql-connector-java是MySQL提供实现了JDBC定义的驱动(JDBC是一种规范,定义了Java语言如何去操作数据库,也就是实现相关API,是一种接口规范。mysql-connector-java是MySQL提供实现的JDBC定义的驱动),是Java程序中真正操作MySQL数据库的客户端。简单来说mysql-connector-java允许了通过Java访问MySQL。

    spring-boot-starter-jdbc,是Spring提供的,它基于mysql-connector-java,又进行了封装,使得代码更加简单。也就是mysql-connector-java提供的是JDBC,而spring-boot-starter-jdbc提高了简化版的JDBC——JdbcTemplate。

    mybatis-spring-boot-starter是Mybatis操作数据库层面的封装。

    不管是HikariCP还是Druid其实最终都是提供java.sql.Driver类型的驱动。获取到驱动之后进一步才能获取到数据库连接。

    java.sql.Driver的实现类都会存在一个静态代码块,在静态代码块中利用DriverManager注册当前的驱动Driver。DriverManager中的静态代码块利用SPI机制加载 META-INF/services/java.sql.Driver 接口的实现类,并反通过射方式实例化。实例化过程中又会执行自身静态代码块,即DriverManager注册当前的驱动Driver。

    1. public class DriverManager {
    2. private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>();
    3. static {
    4. loadInitialDrivers();
    5. }
    6. public static synchronized void registerDriver(java.sql.Driver driver,DriverAction da){
    7. if(driver != null) {
    8. registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    9. } else {
    10. throw new NullPointerException();
    11. }
    12. }
    13. private static void loadInitialDrivers() {
    14. String drivers = AccessController.doPrivileged(new PrivilegedAction() {
    15. public String run() {
    16. return System.getProperty("jdbc.drivers");
    17. }
    18. });;
    19. AccessController.doPrivileged(new PrivilegedAction() {
    20. public Void run() {
    21. // SPI 机制加载 META-INF/services/java.sql.Driver 接口的实现类
    22. ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
    23. Iterator driversIterator = loadedDrivers.iterator();
    24. while(driversIterator.hasNext()) {
    25. driversIterator.next();
    26. }
    27. return null;
    28. }
    29. });
    30. if (drivers == null || drivers.equals("")) {
    31. return;
    32. }
    33. String[] driversList = drivers.split(":");
    34. println("number of Drivers:" + driversList.length);
    35. for (String aDriver : driversList) {
    36. Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());
    37. }
    38. }
    39. @CallerSensitive
    40. public static Connection getConnection(String url,java.util.Properties info){
    41. return (getConnection(url, info, Reflection.getCallerClass()));
    42. }
    43. private static Connection getConnection(String url, java.util.Properties info, Class caller) {
    44. ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
    45. synchronized(DriverManager.class) {
    46. // synchronize loading of the correct classloader.
    47. if (callerCL == null) {
    48. callerCL = Thread.currentThread().getContextClassLoader();
    49. }
    50. }
    51. if(url == null) {
    52. throw new SQLException("The url cannot be null", "08001");
    53. }
    54. SQLException reason = null;
    55. for(DriverInfo aDriver : registeredDrivers) {
    56. if(isDriverAllowed(aDriver.driver, callerCL)) {
    57. return aDriver.driver.connect(url, info);
    58. }
    59. }
    60. }
    61. private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
    62. boolean result = false;
    63. if(driver != null) {
    64. Class aClass = null;
    65. try {
    66. aClass = Class.forName(driver.getClass().getName(), true, classLoader);
    67. } catch (Exception ex) {
    68. result = false;
    69. }
    70. result = ( aClass == driver.getClass() ) ? true : false;
    71. }
    72. return result;
    73. }
    74. }

  • 相关阅读:
    做视频素材资源(free视频,音频,图片)
    利用MySQL主从复制延迟拯救误删数据
    Java常见跳出循环的4种方式总结、switch中的break与return、lamada表达式中foreach如何正确选择退出方式
    kafka日志文件详解及生产常见问题总结
    SpringCloud链路追踪SkyWalking-第一章-介绍
    Ansible自动化运维工具介绍与部署
    python基础语法——类和对象
    ReLU,Sigmoid,Tanh,softmax【基础知识总结】
    Blazor和Vue对比学习(进阶2.2.4):状态管理之持久化保存(2),Cookie/Session/jwt
    [云原生] [kubernetes] K8S安装存储类 - StorageClass
  • 原文地址:https://blog.csdn.net/qq_36851469/article/details/134438622