0x01 环境搭配

如果还不会搭建jsp环境的可以按照下面的文章跟着搭建

Mac版IDEA创建maven web项目-详细过程: https://www.yuque.com/pmiaowu/gpy1q8/npv0fr

0x02 导入数据库驱动

0x02.1 maven自动添加

打开: https://mvnrepository.com/
搜索: MySQL
点进去: https://mvnrepository.com/artifact/mysql/mysql-connector-java
image.png
我这里选择5.1.41
image.png

打开: https://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.41

  1. <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
  2. <dependency>
  3. <groupId>mysql</groupId>
  4. <artifactId>mysql-connector-java</artifactId>
  5. <version>5.1.41</version>
  6. </dependency>

image.png
image.png

0x02.2 手动导入

首先介绍如何手动下载jar包
打开: https://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.41
image.png
下载地址: https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.41/mysql-connector-java-5.1.41.jar

下载完毕以后,打开我们的项目
image.png
image.png

0x03 JDBC链接数据库

  1. // 使用例子
  2. # 目录结构
  3. ├── src
  4. └── main
  5. └── webapp
  6. └── com
  7. └── Servlet
  8. ├── ...
  9. └── DBTest.java
  10. └── WEB-INF
  11. └── web.xml
  12. └── index.jsp
  1. package com.Servlet;
  2. import javax.servlet.annotation.WebServlet;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.sql.*;
  8. @WebServlet("/DBTest")
  9. public class DBTest extends HttpServlet {
  10. @Override
  11. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
  12. String DRIVER_NAME = "com.mysql.jdbc.Driver";
  13. // jdbc:mysql://[mysql地址]:[mysql端口]/[要连接的数据库名]
  14. String URL = "jdbc:mysql://192.168.24.145:3306/mysql";
  15. String USER_NAME = "root";
  16. String PASSWORD = "123456";
  17. Connection connection = null;
  18. try {
  19. //加载mysql的驱动类
  20. Class.forName(DRIVER_NAME);
  21. //获取数据库连接
  22. connection = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
  23. //mysql查询语句
  24. String sql = "SELECT * FROM `user`";
  25. PreparedStatement prst = connection.prepareStatement(sql);
  26. //结果集
  27. ResultSet rs = prst.executeQuery();
  28. while (rs.next()) {
  29. response.getWriter().println(" ");
  30. response.getWriter().println("用户名:" + rs.getString("User")+" "+"密码:" + rs.getString("Password"));
  31. }
  32. rs.close();
  33. prst.close();
  34. } catch (Exception e) {
  35. e.printStackTrace();
  36. }finally {
  37. if (connection != null) {
  38. try {
  39. connection.close();
  40. } catch (SQLException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. }
  45. }
  46. }
  47. // 执行该文件
  48. // 例如: http://127.0.0.1:8081/mavenJspTest_war/DBTest

0x04 问题小结

在开始使用的时候,我一直不理解
为什么第一步需要Class.forName("com.mysql.jdbc.Driver");
不可以删除么?于是删除以后,我发现还是可以正常连接数据库,这让我更加迷惑了

于是本着探讨的心,决定看看源码

0x04.1 为什么需要Class.forName?

经过查看源码终于理解了

首先我们要知道Class.forName("xxxx")的功能: 除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static

那么Class.forName("com.mysql.jdbc.Driver");干了什么?

先进入com.mysql.jdbc.Driver
image.png
看到这里大致就明白了,也就是说
Class.forName("com.mysql.jdbc.Driver");实际上会触发类加载
com.mysql.jdbc.Driver类将会被初始化,并且执行类中的static

实际上这一步就是利用了Java反射+类加载机制往DriverManager中注册了驱动包

0x04.2 删除了Class.forName为什么还可以?

为什么我不加Class.forName("com.mysql.jdbc.Driver");一样可以正常连接数据库?

实际上这里利用了Java的一大特性: Java SPI(Service Provider Interface)
DriverManager在初始化时,会调用java.util.ServiceLoader类提供的SPI机制

然后Java自动扫描jar包中的META-INF/services目录下的文件,并且还会自动的Class.forName文件中定义的类

这就是为什么不需要Class.forName也能够成功连接数据库的原因了

怎么判断有没有用SPI?可以考察如下示例
方法一: 查看包的结构,Mysql驱动包示例:
image.png
如上图, META-INF/services下面能看到长的很像类的完全限定名,就八九不离十了。

方法二: 通过查看源码
例如: 查看DriverManager

查找java.util.ServiceLoader.load看看是否有调用
image.png
image.png

0x05 课外知识补充

如果想反射某个类又不想初始化类方法可以么?
当然可以,解决方案如下:

  1. 使用Class.forName("xxxx", false, loader)方法
  2. 使用ClassLoader.loadClass("xxxx")

    0x06 小结

    在实际的项目中,通常不会使用原生的JDBCDriverManager去连接数据库
    而是使用javax.sql.DataSource(数据源)来代替DriverManager
    因此本文学习如何使用原生来操作数据库,让自己脑子留个底子即可

有关javax.sql.DataSource(数据源)使用的问题,我们后面在一起学习