The JDBC API is an excellent example for this. This way you can configure the JDBC driver externally in for example a properties file:
driver = com.dbvendor.jdbc.Driver
url = jdbc:dbvendor://localhost/dbname
username = stackoverflow
password = youneverguess
..which you can use as:
Properties properties = new Properties();
properties.load(Thread.currentThread().getResourceAsStream("jdbc.properties"));
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
String username = properties.getProperty("username");
String password = properties.getProperty("password");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, username, password);
Every JDBC driver implementation basically registers itself in the DriverManager
inside a static
initializer block. It's namely the one which get executed during Class#forName()
.
package com.dbvendor.jdbc;
public class Driver implements java.sql.Driver {
static {
java.sql.DriverManager.registerDriver(new Driver());
}
private Driver() {
// ...
}
public boolean acceptsURL(String url) {
return url.startsWith("jdbc:dbvendor");
}
}
Since the DriverManager
roughly look like this (it actually uses the old fashioned Vector
)
private static final Set<Driver> drivers = new HashSet<Driver>();
public static void registerDriver(Driver driver) {
drivers.add(driver);
}
public static Connection getConnection(String url, String username, String password) throws SQLException {
for (Driver driver : drivers) {
if (driver.acceptsURL(url)) {
return driver.connect(url, username, password);
}
}
throw new SQLException("No suitable driver");
}
...you can get a connection from it without the need to instantiate the driver itself!
This way the JDBC code is highly portable. You can change the DB or distribute the code among users with different DB's without the need to change/hack/rebuild the code itself.
It's not only JDBC which uses this approach, also other API's such as Servlet API, ORM's like Hibernate/JPA, dependency injection frameworks, etcetera uses reflection to load the classes based on externally configureable propertiesfiles, XML config files and/or annotations. It all just makes the code much more portable and pluggable.