jdbc 클래스 캐리어, spi 서비스 메커니즘
public static void main(String[] args)
{
Enumeration drivers = DriverManager.getDrivers();
Driver driver;
while (drivers.hasMoreElements())
{
driver = drivers.nextElement();
System.out.println(driver.getClass() + "------" + driver.getClass().getClassLoader());
}
System.out.println(DriverManager.class.getClassLoader());
}
출력 결 과 는 다음 과 같 습 니 다.
class com.mysql.jdbc.Driver------sun.misc.Launcher$AppClassLoader@2a139a55
class com.mysql.fabric.jdbc.FabricMySQLDriver------sun.misc.Launcher$AppClassLoader@2a139a55
null
코드 에 Class. forName (") 코드 가 호출 되 지 않 았 음 을 볼 수 있 습 니 다. 그러나 DriverManager 에 두 개의 jdbc 드라이브 가 불 러 왔 습 니 다. 그러나 이 두 드라이브 는 모두 사용 하 는 응용 클래스 로 더 (AppClassLoader) 로 불 러 왔 습 니 다. DriverManager 자체 의 클래스 로 더 는 null 즉 BootstrapClassLoader 입 니 다. 부모 위임 모델 의 규칙 에 따라위임 체인 은 다음 과 같 습 니 다: SystemApp classloader - > Extension classloader - > Bootstrap classloader, 부모 로 더 BootstrapClassLoader 는 AppClassLoader 가 불 러 온 클래스 를 찾 을 수 없습니다. 이 때 스 레 드 컨 텍스트 로 더 를 사 용 했 습 니 다. Thread. current Thread (). setContextClassLoader () 는 위임 체인 왼쪽 의 클래스 로 더 를 스 레 드 컨 텍스트 로 더 로 설정 할 수 있 습 니 다.이 때 오른쪽 에 있 는 로 더 는 스 레 드 컨 텍스트 로 더 의뢰 서브 로 더 를 불 러 올 수 있 습 니 다.
DriverManager 의 원본 코드 를 볼 수 있 습 니 다.
/**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then use the {@code ServiceLoader} mechanism
*/
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
DriverManager 가 초기 화 할 때 ServiceLoader 를 사용 하여 java. sql. Driver 의 실현 클래스 를 불 러 오 는 것 을 볼 수 있 습 니 다. 여 기 는 spi 서비스의 사상 으로 ServiceLoader 의 load 코드 를 볼 수 있 습 니 다.
public static ServiceLoader load(Class service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
public static ServiceLoader load(Class service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
private ServiceLoader(Class svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
ServiceLoader 를 만 들 었 습 니 다. reload 방법 으로 불 러 옵 니 다. ServiceLoader 의 주요 매개 변수 와 reload 의 코드 는 다음 과 같 습 니 다.
private static final String PREFIX = "META-INF/services/";
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
Lazy Iterator 는 게 으 른 로 딩 교체 기 입 니 다. 이 교체 기의 실현 을 보 세 요.
private class LazyIterator
implements Iterator
{
Class service;
ClassLoader loader;
Enumeration configs = null;
Iterator pending = null;
String nextName = null;
private LazyIterator(Class service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction action = new PrivilegedAction() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction action = new PrivilegedAction() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
DriverManager 의 초기 화 코드 를 돌 이 켜 보면 다음 코드 를 볼 수 있 습 니 다.
while(driversIterator.hasNext()) {
driversIterator.next();
}
DriverManager 가 META - INF / services / java. sql. Driver 에서 모든 종류의 Class. forName () 방법 을 반복 적 으로 호출 하 는 것 을 알 수 있 습 니 다. 그러면 이 불 러 오 는 드라이버 는 어떻게 DriverManager 에 등록 되 어 있 습 니까?mysql 의 드라이버 구현 클래스 를 보면 드라이버 의 구현 이 초기 화 될 때 등 록 된 것 을 볼 수 있 습 니 다. 코드 는 다음 과 같 습 니 다.
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
이 코드 는 java. sql. Driver 의 실현 클래스 를 DriverManager 에 등록 할 수 있 습 니 다. 이 코드 에 new Driver () 는 com. mysql. jdbc. Driver 가 좋 습 니 다. java. sql. Driver 를 마지막 으로 보면 spi 서 비 스 를 실현 하 는 데 없어 서 는 안 될 파일 META - INF / services / java. sql. Driver (이 특정한 자바. sql. Driver 의 인 터 페 이 스 를 실현 하 는 spi 서비스) 의 내용 은 다음 과 같 습 니 다.
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver
이 두 가지 유형, 즉 글 의 시작 을 위 한 두 개의 jdbc 구동 을 볼 수 있다.
모든 버 전의 jdbc 드라이브 가 spi 서 비 스 를 실현 한 것 은 아 닙 니 다. 5.1.5 및 그 후의 버 전에 서 이 서 비 스 를 실현 한 것 같 습 니 다. 이전 버 전 은 Class. forName 방법 으로 드라이버 를 불 러 와 야 합 니 다. 그리고 ojdbc 의 드라이브 가 spi 서 비 스 를 실현 하지 못 한 것 같 습 니 다.
드라이버 관리자 에 spi 서 비 스 를 불 러 오 는 과정 을 알 게 되 었 습 니 다. 우 리 는 간단 한 jdbc 구동 (클래스 로 딩 부분 만 실현) 을 시도 할 수 있 습 니 다.
public class Driver implements java.sql.Driver
{
static
{
try
{
DriverManager.registerDriver(new com.lcy.mysql.Driver());
}
catch (SQLException e)
{
throw new RuntimeException("register driver fail");
}
}
@Override
public Connection connect(String url, Properties info)
throws SQLException
{
// TODO Auto-generated method stub
return null;
}
@Override
public boolean acceptsURL(String url)
throws SQLException
{
// TODO Auto-generated method stub
return false;
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info)
throws SQLException
{
// TODO Auto-generated method stub
return null;
}
@Override
public int getMajorVersion()
{
// TODO Auto-generated method stub
return 0;
}
@Override
public int getMinorVersion()
{
// TODO Auto-generated method stub
return 0;
}
@Override
public boolean jdbcCompliant()
{
// TODO Auto-generated method stub
return false;
}
@Override
public Logger getParentLogger()
throws SQLFeatureNotSupportedException
{
// TODO Auto-generated method stub
return null;
}
}
초기 화 방법 만 적 었 습 니 다. 다른 방법 은 모두 기본 빈 칸 으로 이 루어 집 니 다. src / mian / resources 디 렉 터 리 에 새 파일 / META - INF / services / java. sql. driver 입력 내용
com.lcy.mysql.Driver
을 포장 하여 이전 글 에서 시 작 된 테스트 프로젝트 에 프로젝트 의존 도 를 도입 합 니 다 (같은 프로젝트 라면 직접 실행 하면 됩 니 다). 실행 결 과 는 다음 과 같 습 니 다.class com.mysql.jdbc.Driver------sun.misc.Launcher$AppClassLoader@2a139a55
class com.mysql.fabric.jdbc.FabricMySQLDriver------sun.misc.Launcher$AppClassLoader@2a139a55
class com.lcy.mysql.Driver------sun.misc.Launcher$AppClassLoader@2a139a55
null
사용자 정의 com. lcy. mysql. Driver 를 불 러 왔 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
자바 문자열 풀우리는 Java에서 문자열이 힙 메모리 영역에 저장된다는 것을 알고 있습니다. 이 힙 메모리 내부에는 String Pool이라는 특정 메모리 영역이 있습니다. 문자열 프리미티브를 생성하면 자바 문자열의 불변성 덕분에...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.